Merge "Optimize Autofill Sanitizer to work as a Validator."
diff --git a/Android.bp b/Android.bp
index a740e22..2ea4894 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
             // runtime, as well as the only protos that are actually
             // needed by the device.
             srcs: [
+                "core/proto/android/os/cpufreq.proto",
                 "core/proto/android/os/cpuinfo.proto",
                 "core/proto/android/os/kernelwake.proto",
                 "core/proto/android/os/pagetypeinfo.proto",
@@ -83,6 +84,7 @@
     ],
 
     srcs: [
+        "core/proto/android/os/cpufreq.proto",
         "core/proto/android/os/cpuinfo.proto",
         "core/proto/android/os/kernelwake.proto",
         "core/proto/android/os/pagetypeinfo.proto",
diff --git a/Android.mk b/Android.mk
index 24e717d..08a8b64 100644
--- a/Android.mk
+++ b/Android.mk
@@ -273,7 +273,6 @@
 	core/java/android/os/IRecoverySystemProgressListener.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/ISchedulingPolicyService.aidl \
-	core/java/android/os/IStatsCallbacks.aidl \
 	core/java/android/os/IStatsCompanionService.aidl \
 	core/java/android/os/IStatsManager.aidl \
 	core/java/android/os/IThermalEventListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index b1ca3e8..c6d5430 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7053,27 +7053,6 @@
 
 }
 
-package android.app.slice.widget {
-
-  public class SliceView extends android.view.ViewGroup {
-    ctor public SliceView(android.content.Context);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearSlice();
-    method public java.lang.String getMode();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setMode(java.lang.String);
-    method public void setScrollable(boolean);
-    method public boolean setSlice(android.net.Uri);
-    method public void showSlice(android.app.slice.Slice);
-    field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
-    field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
-    field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
-  }
-
-}
-
 package android.app.usage {
 
   public final class ConfigurationStats implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 592bd3d..94e3786 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7497,27 +7497,6 @@
 
 }
 
-package android.app.slice.widget {
-
-  public class SliceView extends android.view.ViewGroup {
-    ctor public SliceView(android.content.Context);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearSlice();
-    method public java.lang.String getMode();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setMode(java.lang.String);
-    method public void setScrollable(boolean);
-    method public boolean setSlice(android.net.Uri);
-    method public void showSlice(android.app.slice.Slice);
-    field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
-    field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
-    field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
-  }
-
-}
-
 package android.app.usage {
 
   public final class CacheQuotaHint implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 98615ee..aebf380 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7127,27 +7127,6 @@
 
 }
 
-package android.app.slice.widget {
-
-  public class SliceView extends android.view.ViewGroup {
-    ctor public SliceView(android.content.Context);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
-    ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
-    method public void clearSlice();
-    method public java.lang.String getMode();
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void setMode(java.lang.String);
-    method public void setScrollable(boolean);
-    method public boolean setSlice(android.net.Uri);
-    method public void showSlice(android.app.slice.Slice);
-    field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
-    field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
-    field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
-  }
-
-}
-
 package android.app.usage {
 
   public final class ConfigurationStats implements android.os.Parcelable {
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 8239d8e..c8a0883 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "incident_helper"
 
+#include "parsers/CpuFreqParser.h"
 #include "parsers/CpuInfoParser.h"
 #include "parsers/KernelWakesParser.h"
 #include "parsers/PageTypeInfoParser.h"
@@ -60,6 +61,8 @@
             return new KernelWakesParser();
         case 2003:
             return new CpuInfoParser();
+        case 2004:
+            return new CpuFreqParser();
         default:
             return NULL;
     }
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
new file mode 100644
index 0000000..02f1ce7
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+#include <unistd.h>
+
+#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
+#include "ih_util.h"
+#include "CpuFreqParser.h"
+
+using namespace android::os;
+
+status_t
+CpuFreqParser::Parse(const int in, const int out) const
+{
+    Reader reader(in);
+    string line;
+
+    // parse header
+    reader.readLine(&line);
+    header_t header = parseHeader(line, TAB_DELIMITER);
+    if (header.size() < 1) {
+        fprintf(stderr, "Bad header: %s\n", line.c_str());
+        return BAD_VALUE;
+    }
+    const int numCpus = (int)header.size() - 1;
+    vector<pair<int, long long>> cpucores[numCpus];
+
+    // parse freq and time
+    while (reader.readLine(&line)) {
+        if (line.empty()) continue;
+
+        record_t record = parseRecord(line, TAB_DELIMITER);
+        if (record.size() != header.size()) {
+            fprintf(stderr, "Bad line: %s\n", line.c_str());
+            continue;
+        }
+
+        int freq = toInt(record[0]);
+        for (int i=0; i<numCpus; i++) {
+            if (strcmp(record[i+1].c_str(), "N/A") == 0) {
+                continue;
+            }
+            cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
+        }
+    }
+
+    ProtoOutputStream proto;
+
+    long jiffyHz = sysconf(_SC_CLK_TCK);
+    proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+
+    for (int i=0; i<numCpus; i++) {
+        long long token = proto.start(CpuFreq::CPU_FREQS);
+        proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+        for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
+            long long stateToken = proto.start(CpuFreqStats::TIMES);
+            proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
+            proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+            proto.end(stateToken);
+        }
+        proto.end(token);
+    }
+
+    if (!reader.ok(&line)) {
+        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+        return -1;
+    }
+
+    if (!proto.flush(out)) {
+        fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+        return -1;
+    }
+    fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+    return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.h b/cmds/incident_helper/src/parsers/CpuFreqParser.h
new file mode 100644
index 0000000..470d568
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef CPU_FREQ_PARSER_H
+#define CPU_FREQ_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
+ */
+class CpuFreqParser : public TextParserBase {
+public:
+    CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
+    ~CpuFreqParser() {};
+
+    virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif  // CPU_FREQ_PARSER_H
diff --git a/cmds/incident_helper/testdata/cpufreq.txt b/cmds/incident_helper/testdata/cpufreq.txt
new file mode 100644
index 0000000..6472839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpufreq.txt
@@ -0,0 +1,6 @@
+freq		cpu0		cpu1		cpu2		cpu3		
+307200		23860761		23860761		23890935		23890935		
+384000		83124		83124		29383		29383		
+748800		N/A		N/A		10547		10547		
+768000		22652		22652		N/A		N/A		
+825600		N/A		N/A		13173		13173			
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
new file mode 100644
index 0000000..1c2f9e5
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#include "CpuFreqParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpufreq.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuFreqParserTest : public Test {
+public:
+    virtual void SetUp() override {
+        ASSERT_TRUE(tf.fd != -1);
+    }
+
+    string getSerializedString(::google::protobuf::Message& message) {
+        string expectedStr;
+        message.SerializeToFileDescriptor(tf.fd);
+        ReadFileToString(tf.path, &expectedStr);
+        return expectedStr;
+    }
+
+protected:
+    TemporaryFile tf;
+
+    const string kTestPath = GetExecutableDirectory();
+    const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuFreqParserTest, Success) {
+    const string testFile = kTestDataPath + "cpufreq.txt";
+    CpuFreqParser parser;
+    CpuFreq expected;
+
+    long jiffyHz = sysconf(_SC_CLK_TCK);
+    expected.set_jiffy_hz(jiffyHz);
+
+    CpuFreqStats::TimeInState* state;
+
+    CpuFreqStats* cpu0 = expected.add_cpu_freqs();
+    cpu0->set_cpu_name("cpu0");
+    state = cpu0->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23860761);
+    state = cpu0->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(83124);
+    state = cpu0->add_times();
+    state->set_state_khz(768000);
+    state->set_time_jiffy(22652);
+
+    CpuFreqStats* cpu1 = expected.add_cpu_freqs();
+    cpu1->set_cpu_name("cpu1");
+    state = cpu1->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23860761);
+    state = cpu1->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(83124);
+    state = cpu1->add_times();
+    state->set_state_khz(768000);
+    state->set_time_jiffy(22652);
+
+    CpuFreqStats* cpu2 = expected.add_cpu_freqs();
+    cpu2->set_cpu_name("cpu2");
+    state = cpu2->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23890935);
+    state = cpu2->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(29383);
+    state = cpu2->add_times();
+    state->set_state_khz(748800);
+    state->set_time_jiffy(10547);
+    state = cpu2->add_times();
+    state->set_state_khz(825600);
+    state->set_time_jiffy(13173);
+
+    CpuFreqStats* cpu3 = expected.add_cpu_freqs();
+    cpu3->set_cpu_name("cpu3");
+    state = cpu3->add_times();
+    state->set_state_khz(307200);
+    state->set_time_jiffy(23890935);
+    state = cpu3->add_times();
+    state->set_state_khz(384000);
+    state->set_time_jiffy(29383);
+    state = cpu3->add_times();
+    state->set_state_khz(748800);
+    state->set_time_jiffy(10547);
+    state = cpu3->add_times();
+    state->set_state_khz(825600);
+    state->set_time_jiffy(13173);
+
+    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);
+}
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
index 57ad15c..bbc14bc 100644
--- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -56,7 +56,7 @@
     const string kTestDataPath = kTestPath + "/testdata/";
 };
 
-TEST_F(CpuInfoParserTest, HasSwapInfo) {
+TEST_F(CpuInfoParserTest, Success) {
     const string testFile = kTestDataPath + "cpuinfo.txt";
     CpuInfoParser parser;
     CpuInfo expected;
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index b7633a4..30dd339 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <wait.h>
 
+const bool DEBUG = false;
 const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
 const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
 
@@ -71,9 +72,11 @@
             mTimedOut = true;
             break;
         } else if (count < 0) {
+            if (DEBUG) ALOGD("poll failed: %s", strerror(errno));
             return -errno;
         } else {
             if ((pfds.revents & POLLERR) != 0) {
+                if (DEBUG) ALOGD("return event has error %s", strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             } else {
                 ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
@@ -81,6 +84,7 @@
                     if (errno == EAGAIN || errno == EWOULDBLOCK) {
                         continue;
                     } else {
+                        if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno));
                         return -errno;
                     }
                 } else if (amt == 0) {
@@ -95,7 +99,7 @@
 }
 
 status_t
-FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs)
+FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs)
 {
     struct pollfd pfds[] = {
         { .fd = fd,     .events = POLLIN  },
@@ -135,12 +139,18 @@
             mTimedOut = true;
             break;
         } else if (count < 0) {
+            if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno));
             return -errno;
         }
 
         // make sure no errors occur on any fds
         for (int i = 0; i < 3; ++i) {
             if ((pfds[i].revents & POLLERR) != 0) {
+                if (i == 0 && isSysfs) {
+                    if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd);
+                    continue;
+                }
+                if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             }
         }
@@ -155,6 +165,7 @@
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                    if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno));
                     return -errno;
                 } // otherwise just continue
             } else if (amt == 0) {  // reach EOF so don't have to poll pfds[0].
@@ -176,6 +187,7 @@
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                    if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno));
                     return -errno;
                 } // otherwise just continue
             } else {
@@ -202,6 +214,7 @@
         ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
         if (amt < 0) {
             if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+                if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno));
                 return -errno;
             } // otherwise just continue
         } else if (amt == 0) {
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 8857ae7..48dc855 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -47,8 +47,10 @@
      * and stores the processed data from 'fromFd' in memory for later usage.
      * This function behaves in a streaming fashion in order to save memory usage.
      * Returns NO_ERROR if there were no errors or if we timed out.
+     *
+     * Poll will return POLLERR if fd is from sysfs, handle this edge case.
      */
-    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs);
+    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false);
 
     /**
      * Whether we timed out.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 892bcca..c08b9ea 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -232,6 +232,7 @@
      mFilename(filename)
 {
     name = filename;
+    mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
 }
 
 FileSection::~FileSection() {}
@@ -264,7 +265,7 @@
 
     // parent process
     status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
-            this->timeoutMs);
+            this->timeoutMs, mIsSysfs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
             this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 0a1e03e..64558a6 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -69,6 +69,7 @@
 
 private:
     const char* mFilename;
+    bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately
 };
 
 /**
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d860363..45ae56f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -15,7 +15,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 statsd_common_src := \
-    ../../core/java/android/os/IStatsCallbacks.aidl \
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
     src/stats_log.proto \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 10952a9..31d32b4 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -320,9 +320,15 @@
         return UNKNOWN_ERROR;
     }
     auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name));
-    auto sc = getStatsCompanionService();
-    sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
-    ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(), args[2].c_str());
+    sp<IStatsCompanionService> sc = getStatsCompanionService();
+    if (sc != nullptr) {
+        sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+        ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
+              args[2].c_str());
+    } else {
+        ALOGD("Could not access statsCompanion");
+    }
+
     return NO_ERROR;
 }
 
@@ -684,12 +690,6 @@
 }
 
 void StatsService::binderDied(const wp <IBinder>& who) {
-    for (size_t i = 0; i < mCallbacks.size(); i++) {
-        if (IInterface::asBinder(mCallbacks[i]) == who) {
-            mCallbacks.removeAt(i);
-            break;
-        }
-    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 888f97b..4d768f6 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,7 +24,6 @@
 #include "packages/UidMap.h"
 
 #include <android/os/BnStatsManager.h>
-#include <android/os/IStatsCallbacks.h>
 #include <android/os/IStatsCompanionService.h>
 #include <binder/IResultReceiver.h>
 #include <utils/Looper.h>
@@ -195,16 +194,6 @@
      * Whether this is an eng build.
      */
     bool mEngBuild;
-
-    /**
-     * Lock for callback handling.
-     */
-    std::mutex mLock;
-
-    /**
-     * Vector maintaining the list of callbacks for clients.
-     */
-    Vector< sp<IStatsCallbacks> > mCallbacks;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 2618a21..669a4b7 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -86,6 +86,9 @@
         case LogicalOperation::NOR:
             newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
             break;
+        case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+            newCondition = ConditionState::kFalse;
+            break;
     }
     return newCondition;
 }
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 9680e4a..2125609 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -80,15 +80,15 @@
         // Remove from map
         mConfigs.erase(it);
 
-        // Remove from disk
-        remove_saved_configs(key);
-
         // Tell everyone
         for (auto& listener : mListeners) {
             listener->OnConfigRemoved(key);
         }
     }
-    // If we didn't find it, just quietly ignore it.
+
+    // Remove from disk. There can still be a lingering file on disk so we check
+    // whether or not the config was on memory.
+    remove_saved_configs(key);
 }
 
 void ConfigManager::remove_saved_configs(const ConfigKey& key) {
@@ -102,9 +102,7 @@
     while ((de = readdir(dir.get()))) {
         char* name = de->d_name;
         if (name[0] != '.' && strncmp(name, prefix.c_str(), prefix.size()) == 0) {
-            if (remove(StringPrintf("%s/%d-%s", STATS_SERVICE_DIR, key.GetUid(),
-                                    key.GetName().c_str())
-                               .c_str()) != 0) {
+            if (remove(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str()) != 0) {
                 ALOGD("no file found");
             }
         }
@@ -212,6 +210,11 @@
 
 void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) {
     mkdir(STATS_SERVICE_DIR, S_IRWXU);
+
+    // If there is a pre-existing config with same key we should first delete it.
+    remove_saved_configs(key);
+
+    // Then we save the latest config.
     string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(),
                                     key.GetName().c_str(), time(nullptr));
     int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
@@ -265,7 +268,7 @@
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
 
     // Anomaly threshold for screen-on count.
-    Alert* alert = config.add_alerts();
+    Alert* alert = config.add_alert();
     alert->set_name("1");
     alert->set_number_of_buckets(6);
     alert->set_trigger_if_sum_gt(10);
@@ -280,7 +283,7 @@
     keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
 
     // Anomaly threshold for background count.
-    alert = config.add_alerts();
+    alert = config.add_alert();
     alert->set_name("2");
     alert->set_number_of_buckets(4);
     alert->set_trigger_if_sum_gt(30);
@@ -312,7 +315,7 @@
     DurationMetric* durationMetric = config.add_duration_metric();
     durationMetric->set_name("5");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
@@ -326,7 +329,7 @@
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("6");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
@@ -340,7 +343,7 @@
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("7");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
@@ -352,7 +355,7 @@
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("8");
     durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
     durationMetric->set_what("SCREEN_IS_ON");
 
     // Value metric to count KERNEL_WAKELOCK when screen turned on
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 80236703..1032138 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -30,11 +30,10 @@
 using android::util::ProtoOutputStream;
 
 LogEvent::LogEvent(log_msg& msg) {
-    android_log_context context =
+    mContext =
             create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
     mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
-    mContext = NULL;
-    init(context);
+    init(mContext);
 }
 
 LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) {
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 255165c..f7352cd 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -84,6 +84,9 @@
                 }
             }
             break;
+        case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+            matched = false;
+            break;
     }
     return matched;
 }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index de6f365..eba2e06 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -63,6 +63,7 @@
 DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
                                                const int conditionIndex, const size_t startIndex,
                                                const size_t stopIndex, const size_t stopAllIndex,
+                                               const bool nesting,
                                                const sp<ConditionWizard>& wizard,
                                                const vector<KeyMatcher>& internalDimension,
                                                const uint64_t startTimeNs)
@@ -71,6 +72,7 @@
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
       mStopAllIndex(stopAllIndex),
+      mNested(nesting),
       mInternalDimension(internalDimension) {
     // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
     // them in the base class, because the proto generated CountMetric, and DurationMetric are
@@ -109,13 +111,13 @@
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
         vector<DurationBucket>& bucket) {
-    switch (mMetric.type()) {
-        case DurationMetric_AggregationType_DURATION_SUM:
-            return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
+    switch (mMetric.aggregation_type()) {
+        case DurationMetric_AggregationType_SUM:
+            return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
                                                      mCurrentBucketStartTimeNs, mBucketSizeNs,
                                                      bucket);
-        case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
-            return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
+        case DurationMetric_AggregationType_MAX_SPARSE:
+            return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
                                                    mCurrentBucketStartTimeNs, mBucketSizeNs,
                                                    bucket);
     }
@@ -220,10 +222,8 @@
                   (long long)mCurrentBucketStartTimeNs);
 
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
-
     startNewProtoOutputStream(endTime);
-    mPastBuckets.clear();
-
+    // TODO: Properly clear the old buckets.
     return buffer;
 }
 
@@ -270,7 +270,7 @@
     if (matcherIndex == mStartIndex) {
         it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
     } else if (matcherIndex == mStopIndex) {
-        it->second->noteStop(atomKey, event.GetTimestampNs());
+        it->second->noteStop(atomKey, event.GetTimestampNs(), false);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index eea00454..bb5d4d9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -39,7 +39,8 @@
 public:
     DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
                            const size_t startIndex, const size_t stopIndex,
-                           const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
+                           const size_t stopAllIndex, const bool nesting,
+                           const sp<ConditionWizard>& wizard,
                            const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
 
     virtual ~DurationMetricProducer();
@@ -80,6 +81,9 @@
     // Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
     const size_t mStopAllIndex;
 
+    // nest counting -- for the same key, stops must match the number of starts to make real stop
+    const bool mNested;
+
     // The dimension from the atom predicate. e.g., uid, wakelock name.
     const vector<KeyMatcher> mInternalDimension;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index bd288a1..9a94a0e 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -96,7 +96,6 @@
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
-    mByteSize = 0;
 
     return buffer;
 }
@@ -121,14 +120,10 @@
     event.ToProto(*mProto);
     mProto->end(eventToken);
     mProto->end(wrapperToken);
-
-    // TODO: Increment mByteSize with a real value. Until this feature is working, we assume 50
-    // bytes.
-    mByteSize += 50;
 }
 
 size_t EventMetricProducer::byteSize() {
-    return mByteSize;
+    return mProto->bytesWritten();
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 7740621..0dccdf4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -65,8 +65,6 @@
 
 private:
     const EventMetric mMetric;
-
-    size_t mByteSize;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 5c76d0e..18b3349 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -34,6 +34,10 @@
 // Hold duration information for one atom level duration in current on-going bucket.
 struct DurationInfo {
     DurationState state;
+
+    // the number of starts seen.
+    int32_t startCount;
+
     // most recent start time.
     int64_t lastStartTime;
     // existing duration in current bucket.
@@ -42,7 +46,7 @@
     // cache the HashableDimensionKeys we need to query the condition for this duration event.
     ConditionKey conditionKeys;
 
-    DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+    DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
 };
 
 struct DurationBucket {
@@ -53,18 +57,21 @@
 
 class DurationTracker {
 public:
-    DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
-                    uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket)
+    DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                    std::vector<DurationBucket>& bucket)
         : mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
-          mCurrentBucketStartTimeNs(currentBucketStartNs),
           mBucketSizeNs(bucketSizeNs),
+          mNested(nesting),
+          mCurrentBucketStartTimeNs(currentBucketStartNs),
           mBucket(bucket),
           mDuration(0){};
     virtual ~DurationTracker(){};
     virtual void noteStart(const HashableDimensionKey& key, bool condition,
                            const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
-    virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0;
+    virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                          const bool stopAll) = 0;
     virtual void noteStopAll(const uint64_t eventTime) = 0;
     virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
     virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
@@ -75,12 +82,14 @@
 protected:
     sp<ConditionWizard> mWizard;
 
-    int mConditionTrackerIndex;
+    const int mConditionTrackerIndex;
+
+    const int64_t mBucketSizeNs;
+
+    const bool mNested;
 
     uint64_t mCurrentBucketStartTimeNs;
 
-    int64_t mBucketSizeNs;
-
     std::vector<DurationBucket>& mBucket;  // where to write output
 
     int64_t mDuration;  // current recorded duration result
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 43c21a8..8c7bfb6 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -23,10 +23,10 @@
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        std::vector<DurationBucket>& bucket)
-    : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+    : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket) {
 }
 
 void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
@@ -38,10 +38,10 @@
 
     switch (duration.state) {
         case kStarted:
-            // The same event is already started. Because we are not counting nesting, so ignore.
+            duration.startCount++;
             break;
         case kPaused:
-            // Safe to do nothing here. Paused means started but condition is false.
+            duration.startCount++;
             break;
         case kStopped:
             if (!condition) {
@@ -51,11 +51,13 @@
                 duration.state = DurationState::kStarted;
                 duration.lastStartTime = eventTime;
             }
+            duration.startCount = 1;
             break;
     }
 }
 
-void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                                  bool forceStop) {
     VLOG("MaxDuration: key %s stop", key.c_str());
     if (mInfos.find(key) == mInfos.end()) {
         // we didn't see a start event before. do nothing.
@@ -68,16 +70,23 @@
             // already stopped, do nothing.
             break;
         case DurationState::kStarted: {
-            duration.state = DurationState::kStopped;
-            int64_t durationTime = eventTime - duration.lastStartTime;
-            VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime,
-                 (long long)eventTime, (long long)durationTime);
-            duration.lastDuration = duration.lastDuration + durationTime;
-            VLOG("  record duration: %lld ", (long long)duration.lastDuration);
+            duration.startCount--;
+            if (forceStop || !mNested || duration.startCount <= 0) {
+                duration.state = DurationState::kStopped;
+                int64_t durationTime = eventTime - duration.lastStartTime;
+                VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(),
+                     (long long)duration.lastStartTime, (long long)eventTime,
+                     (long long)durationTime);
+                duration.lastDuration = duration.lastDuration + durationTime;
+                VLOG("  record duration: %lld ", (long long)duration.lastDuration);
+            }
             break;
         }
         case DurationState::kPaused: {
-            duration.state = DurationState::kStopped;
+            duration.startCount--;
+            if (forceStop || !mNested || duration.startCount <= 0) {
+                duration.state = DurationState::kStopped;
+            }
             break;
         }
     }
@@ -88,11 +97,13 @@
     }
     // Once an atom duration ends, we erase it. Next time, if we see another atom event with the
     // same name, they are still considered as different atom durations.
-    mInfos.erase(key);
+    if (duration.state == DurationState::kStopped) {
+        mInfos.erase(key);
+    }
 }
 void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
     for (auto& pair : mInfos) {
-        noteStop(pair.first, eventTime);
+        noteStop(pair.first, eventTime, true);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index b095884..167f81e 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,12 +28,13 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+    MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                        std::vector<DurationBucket>& bucket);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
-    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                  const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
     bool flushIfNeeded(uint64_t timestampNs) override;
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index e4f1d21..faf5ce5 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -20,10 +20,14 @@
 namespace android {
 namespace os {
 namespace statsd {
+
+using std::pair;
+
 OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
-                                           uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                                           bool nesting, uint64_t currentBucketStartNs,
+                                           uint64_t bucketSizeNs,
                                            std::vector<DurationBucket>& bucket)
-    : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+    : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
@@ -36,9 +40,9 @@
             mLastStartTime = eventTime;
             VLOG("record first start....");
         }
-        mStarted.insert(key);
+        mStarted[key]++;
     } else {
-        mPaused.insert(key);
+        mPaused[key]++;
     }
 
     if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
@@ -48,11 +52,16 @@
     VLOG("Oring: %s start, condition %d", key.c_str(), condition);
 }
 
-void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
+                                    const bool stopAll) {
     VLOG("Oring: %s stop", key.c_str());
     auto it = mStarted.find(key);
     if (it != mStarted.end()) {
-        mStarted.erase(it);
+        (it->second)--;
+        if (stopAll || !mNested || it->second <= 0) {
+            mStarted.erase(it);
+            mConditionKeyMap.erase(key);
+        }
         if (mStarted.empty()) {
             mDuration += (timestamp - mLastStartTime);
             VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
@@ -60,8 +69,14 @@
         }
     }
 
-    mPaused.erase(key);
-    mConditionKeyMap.erase(key);
+    auto pausedIt = mPaused.find(key);
+    if (pausedIt != mPaused.end()) {
+        (pausedIt->second)--;
+        if (stopAll || !mNested || pausedIt->second <= 0) {
+            mPaused.erase(pausedIt);
+            mConditionKeyMap.erase(key);
+        }
+        }
 }
 void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
     if (!mStarted.empty()) {
@@ -118,11 +133,11 @@
 }
 
 void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
-    vector<HashableDimensionKey> startedToPaused;
-    vector<HashableDimensionKey> pausedToStarted;
+    vector<pair<HashableDimensionKey, int>> startedToPaused;
+    vector<pair<HashableDimensionKey, int>> pausedToStarted;
     if (!mStarted.empty()) {
         for (auto it = mStarted.begin(); it != mStarted.end();) {
-            auto key = *it;
+            const auto& key = it->first;
             if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
                 VLOG("Key %s dont have condition key", key.c_str());
                 ++it;
@@ -130,8 +145,8 @@
             }
             if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
                 ConditionState::kTrue) {
+                startedToPaused.push_back(*it);
                 it = mStarted.erase(it);
-                startedToPaused.push_back(key);
                 VLOG("Key %s started -> paused", key.c_str());
             } else {
                 ++it;
@@ -147,7 +162,7 @@
 
     if (!mPaused.empty()) {
         for (auto it = mPaused.begin(); it != mPaused.end();) {
-            auto key = *it;
+            const auto& key = it->first;
             if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
                 VLOG("Key %s dont have condition key", key.c_str());
                 ++it;
@@ -155,8 +170,8 @@
             }
             if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
                 ConditionState::kTrue) {
+                pausedToStarted.push_back(*it);
                 it = mPaused.erase(it);
-                pausedToStarted.push_back(key);
                 VLOG("Key %s paused -> started", key.c_str());
             } else {
                 ++it;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index b54dafa..78760ba 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,12 +27,13 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+    OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                          uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                          std::vector<DurationBucket>& bucket);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
-    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                  const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
@@ -44,8 +45,8 @@
     // 2) which keys are paused (started but condition was false)
     // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
     //    it means everything has stopped, we then record the end time.
-    std::set<HashableDimensionKey> mStarted;
-    std::set<HashableDimensionKey> mPaused;
+    std::map<HashableDimensionKey, int> mStarted;
+    std::map<HashableDimensionKey, int> mPaused;
     int64_t mLastStartTime;
     std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
 };
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4c63b20..d83c144 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -252,6 +252,8 @@
 
         const auto& simpleCondition = durationWhat.simple_condition();
 
+        bool nesting = simpleCondition.count_nesting();
+
         int trackerIndices[3] = {-1, -1, -1};
         if (!simpleCondition.has_start() ||
             !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
@@ -294,7 +296,7 @@
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
-                wizard, internalDimension, startTimeNs);
+                nesting, wizard, internalDimension, startTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 9a760b1..d3b04ba 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -47,6 +47,7 @@
 }
 
 enum LogicalOperation {
+  LOGICAL_OPERATION_UNSPECIFIED = 0;
   AND = 1;
   OR = 2;
   NOT = 3;
@@ -111,23 +112,12 @@
   optional int64 bucket_size_millis = 1;
 }
 
-message Alert {
-    optional string name = 1;
+message EventConditionLink {
+  optional string condition = 1;
 
-    optional string metric_name = 2;
+  repeated KeyMatcher key_in_main = 2;
 
-    message IncidentdDetails {
-        repeated int32 section = 1;
-  }
-  optional IncidentdDetails incidentd_details = 3;
-
-  optional int32 number_of_buckets = 4;
-
-  optional int32 refractory_period_secs = 5;
-
-  optional int64 trigger_if_sum_gt = 6;
-
-  optional int32 refractory_period_in_buckets = 7;
+  repeated KeyMatcher key_in_condition = 3;
 }
 
 message EventMetric {
@@ -151,9 +141,7 @@
 
     optional Bucket bucket = 5;
 
-    optional bool include_in_output = 6;
-
-    repeated EventConditionLink links = 7;
+    repeated EventConditionLink links = 6;
 }
 
 message DurationMetric {
@@ -166,11 +154,11 @@
     repeated EventConditionLink links = 4;
 
     enum AggregationType {
-        DURATION_SUM = 1;
+      SUM = 1;
 
-        DURATION_MAX_SPARSE = 2;
+      MAX_SPARSE = 2;
     }
-    optional AggregationType type = 5;
+    optional AggregationType aggregation_type = 5 [default = SUM];
 
     repeated KeyMatcher dimension = 6;
 
@@ -208,16 +196,30 @@
 
     repeated EventConditionLink links = 7;
 
-    enum Operation { SUM = 1; }
-    optional Operation operation = 9 [default = SUM];
+    enum AggregationType {
+      SUM = 1;
+    }
+    optional AggregationType aggregation_type = 8 [default = SUM];
 }
 
-message EventConditionLink {
-  optional string condition = 1;
+message Alert {
+    optional string name = 1;
 
-  repeated KeyMatcher key_in_main = 2;
-  repeated KeyMatcher key_in_condition = 3;
-};
+    optional string metric_name = 2;
+
+    message IncidentdDetails {
+      repeated int32 section = 1;
+    }
+    optional IncidentdDetails incidentd_details = 3;
+
+    optional int32 number_of_buckets = 4;
+
+    optional int32 refractory_period_secs = 5;
+
+    optional int64 trigger_if_sum_gt = 6;
+
+    optional int32 refractory_period_in_buckets = 7;
+}
 
 message StatsdConfig {
     optional string name = 1;
@@ -236,5 +238,5 @@
 
     repeated Condition condition = 8;
 
-    repeated Alert alerts = 9;
+    repeated Alert alert = 9;
 }
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index fad5de6..f570522 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -85,7 +85,7 @@
         // TODO: Remove this when we get rid of the fake one, and make this
         // test loading one from disk somewhere.
         EXPECT_CALL(*(listener.get()),
-                    OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq("12345")))
+                    OnConfigUpdated(ConfigKeyEq(1000, "fake"), StatsdConfigEq("12345")))
                 .RetiresOnSaturation();
         manager->Startup();
 
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 58bf1b3..9cc184a 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,13 +45,13 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("", true, bucketStartTimeNs, key1);
-    tracker.noteStop("", bucketStartTimeNs + 10);
+    tracker.noteStop("", bucketStartTimeNs + 10, false);
 
     tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
-    tracker.noteStop("", bucketStartTimeNs + 40);
+    tracker.noteStop("", bucketStartTimeNs + 40, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
@@ -67,7 +67,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
     tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
@@ -77,6 +77,37 @@
     EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
 }
 
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    vector<DurationBucket> buckets;
+    ConditionKey key1;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    MaxDurationTracker tracker(wizard, -1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+    // 2 starts
+    tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+    tracker.noteStart("", true, bucketStartTimeNs + 10, key1);
+    // one stop
+    tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
+    EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+
+    // real stop now.
+    tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+
+    EXPECT_EQ(3u, buckets.size());
+    EXPECT_EQ(5, buckets[2].mDuration);
+}
+
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -93,13 +124,13 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 74a6f11..f495d6b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -47,18 +47,43 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
 }
 
+TEST(OringDurationTrackerTest, TestDurationNested) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
+    tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(2003, buckets[0].mDuration);
+}
+
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -75,18 +100,50 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(5, buckets[0].mDuration);
 }
+
+TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    EXPECT_CALL(*wizard, query(_, key1))  // #4
+            .WillOnce(Return(ConditionState::kFalse));
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 3, false);
+
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(15, buckets[0].mDuration);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
index 66e762c..955e58e 100644
--- a/cmds/webview_zygote/Android.mk
+++ b/cmds/webview_zygote/Android.mk
@@ -21,6 +21,8 @@
 
 LOCAL_SRC_FILES := webview_zygote.cpp
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_SHARED_LIBRARIES := \
 	libandroid_runtime \
 	libbinder \
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 616a5be..a4aeb67 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -62,7 +62,6 @@
      * the content should be used in the shortcut representation of the slice (icon, label, action),
      * normally this should be indicated by adding the hint on the action containing that content.
      *
-     * @see SliceView#MODE_SHORTCUT
      * @see SliceItem#TYPE_ACTION
      */
     public static final String HINT_TITLE       = "title";
@@ -109,9 +108,9 @@
      */
     public static final String HINT_NO_TINT     = "no_tint";
     /**
-     * Hint to indicate that this content should not be shown in the {@link SliceView#MODE_SMALL}
-     * and {@link SliceView#MODE_LARGE} modes of SliceView. This content may be used to populate
-     * the {@link SliceView#MODE_SHORTCUT} format of the slice.
+     * Hint to indicate that this content should not be shown in larger renderings
+     * of Slices. This content may be used to populate the shortcut/icon
+     * format of the slice.
      * @hide
      */
     public static final String HINT_HIDDEN = "hidden";
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 05f4ce6..4f9c168 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -17,7 +17,6 @@
 
 import android.Manifest.permission;
 import android.annotation.NonNull;
-import android.app.slice.widget.SliceView;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -132,7 +131,6 @@
      *
      * @return Uri representing the slice associated with the provided intent.
      * @see {@link Slice}
-     * @see {@link SliceView#setSlice(Intent)}
      */
     public @NonNull Uri onMapIntentToUri(Intent intent) {
         throw new UnsupportedOperationException(
diff --git a/core/java/android/app/slice/widget/ActionRow.java b/core/java/android/app/slice/widget/ActionRow.java
deleted file mode 100644
index c96e6304..0000000
--- a/core/java/android/app/slice/widget/ActionRow.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteInput;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class ActionRow extends FrameLayout {
-
-    private static final int MAX_ACTIONS = 5;
-    private final int mSize;
-    private final int mIconPadding;
-    private final LinearLayout mActionsGroup;
-    private final boolean mFullActions;
-    private int mColor = Color.BLACK;
-
-    public ActionRow(Context context, boolean fullActions) {
-        super(context);
-        mFullActions = fullActions;
-        mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
-                context.getResources().getDisplayMetrics());
-        mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
-                context.getResources().getDisplayMetrics());
-        mActionsGroup = new LinearLayout(context);
-        mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
-        mActionsGroup.setLayoutParams(
-                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        addView(mActionsGroup);
-    }
-
-    private void setColor(int color) {
-        mColor = color;
-        for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
-            View view = mActionsGroup.getChildAt(i);
-            SliceItem item = (SliceItem) view.getTag();
-            boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
-            if (tint) {
-                ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
-            }
-        }
-    }
-
-    private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
-        ImageView imageView = new ImageView(getContext());
-        imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
-        imageView.setScaleType(ScaleType.FIT_CENTER);
-        imageView.setImageIcon(icon);
-        if (allowTint) {
-            imageView.setImageTintList(ColorStateList.valueOf(mColor));
-        }
-        imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
-                android.R.attr.selectableItemBackground));
-        imageView.setTag(image);
-        addAction(imageView);
-        return imageView;
-    }
-
-    /**
-     * Set the actions and color for this action row.
-     */
-    public void setActions(SliceItem actionRow, SliceItem defColor) {
-        removeAllViews();
-        mActionsGroup.removeAllViews();
-        addView(mActionsGroup);
-
-        SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR);
-        if (color == null) {
-            color = defColor;
-        }
-        if (color != null) {
-            setColor(color.getColor());
-        }
-        SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> {
-            if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
-                return;
-            }
-            SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE);
-            if (image == null) {
-                return;
-            }
-            boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
-            SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT);
-            if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
-                addAction(image.getIcon(), tint, image).setOnClickListener(
-                        v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput()));
-                createRemoteInputView(mColor, getContext());
-            } else {
-                addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute(
-                        () -> {
-                            try {
-                                action.getAction().send();
-                            } catch (CanceledException e) {
-                                e.printStackTrace();
-                            }
-                        }));
-            }
-        });
-        setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
-    }
-
-    private void addAction(View child) {
-        mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
-    }
-
-    private void createRemoteInputView(int color, Context context) {
-        View riv = RemoteInputView.inflate(context, this);
-        riv.setVisibility(View.INVISIBLE);
-        addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        riv.setBackgroundColor(color);
-    }
-
-    private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
-            RemoteInput input) {
-        if (input == null) {
-            return false;
-        }
-
-        ViewParent p = view.getParent().getParent();
-        RemoteInputView riv = null;
-        while (p != null) {
-            if (p instanceof View) {
-                View pv = (View) p;
-                riv = findRemoteInputView(pv);
-                if (riv != null) {
-                    break;
-                }
-            }
-            p = p.getParent();
-        }
-        if (riv == null) {
-            return false;
-        }
-
-        int width = view.getWidth();
-        if (view instanceof TextView) {
-            // Center the reveal on the text which might be off-center from the TextView
-            TextView tv = (TextView) view;
-            if (tv.getLayout() != null) {
-                int innerWidth = (int) tv.getLayout().getLineWidth(0);
-                innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
-                width = Math.min(width, innerWidth);
-            }
-        }
-        int cx = view.getLeft() + width / 2;
-        int cy = view.getTop() + view.getHeight() / 2;
-        int w = riv.getWidth();
-        int h = riv.getHeight();
-        int r = Math.max(
-                Math.max(cx + cy, cx + (h - cy)),
-                Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
-        riv.setRevealParameters(cx, cy, r);
-        riv.setPendingIntent(pendingIntent);
-        riv.setRemoteInput(new RemoteInput[] {
-                input
-        }, input);
-        riv.focusAnimated();
-        return true;
-    }
-
-    private RemoteInputView findRemoteInputView(View v) {
-        if (v == null) {
-            return null;
-        }
-        return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
-    }
-}
diff --git a/core/java/android/app/slice/widget/GridView.java b/core/java/android/app/slice/widget/GridView.java
deleted file mode 100644
index 793abc0..0000000
--- a/core/java/android/app/slice/widget/GridView.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class GridView extends LinearLayout implements SliceListView {
-
-    private static final String TAG = "GridView";
-
-    private static final int MAX_IMAGES = 3;
-    private static final int MAX_ALL = 5;
-    private boolean mIsAllImages;
-
-    public GridView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mIsAllImages) {
-            int width = MeasureSpec.getSize(widthMeasureSpec);
-            int height = width / getChildCount();
-            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
-                    height);
-            getLayoutParams().height = height;
-            for (int i = 0; i < getChildCount(); i++) {
-                getChildAt(i).getLayoutParams().height = height;
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    public void setSliceItem(SliceItem slice) {
-        mIsAllImages = true;
-        removeAllViews();
-        int total = 1;
-        if (slice.getType() == SliceItem.TYPE_SLICE) {
-            List<SliceItem> items = slice.getSlice().getItems();
-            total = items.size();
-            for (int i = 0; i < total; i++) {
-                SliceItem item = items.get(i);
-                if (isFull()) {
-                    continue;
-                }
-                if (!addItem(item)) {
-                    mIsAllImages = false;
-                }
-            }
-        } else {
-            if (!isFull()) {
-                if (!addItem(slice)) {
-                    mIsAllImages = false;
-                }
-            }
-        }
-        if (total > getChildCount() && mIsAllImages) {
-            addExtraCount(total - getChildCount());
-        }
-    }
-
-    private void addExtraCount(int numExtra) {
-        View last = getChildAt(getChildCount() - 1);
-        FrameLayout frame = new FrameLayout(getContext());
-        frame.setLayoutParams(last.getLayoutParams());
-
-        removeView(last);
-        frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-        TextView v = new TextView(getContext());
-        v.setTextColor(Color.WHITE);
-        v.setBackgroundColor(0x4d000000);
-        v.setText(getResources().getString(R.string.slice_more_content, numExtra));
-        v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
-        v.setGravity(Gravity.CENTER);
-        frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-        addView(frame);
-    }
-
-    private boolean isFull() {
-        return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
-    }
-
-    /**
-     * Returns true if this item is just an image.
-     */
-    private boolean addItem(SliceItem item) {
-        if (item.hasHint(Slice.HINT_HIDDEN)) {
-            return false;
-        }
-        if (item.getType() == SliceItem.TYPE_IMAGE) {
-            ImageView v = new ImageView(getContext());
-            v.setImageIcon(item.getIcon());
-            v.setScaleType(ScaleType.CENTER_CROP);
-            addView(v, new LayoutParams(0, MATCH_PARENT, 1));
-            return true;
-        } else {
-            LinearLayout v = new LinearLayout(getContext());
-            int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    12, getContext().getResources().getDisplayMetrics());
-            v.setPadding(0, s, 0, 0);
-            v.setOrientation(LinearLayout.VERTICAL);
-            v.setGravity(Gravity.CENTER_HORIZONTAL);
-            // TODO: Unify sporadic inflates that happen throughout the code.
-            ArrayList<SliceItem> items = new ArrayList<>();
-            if (item.getType() == SliceItem.TYPE_SLICE) {
-                items.addAll(item.getSlice().getItems());
-            }
-            items.forEach(i -> {
-                if (i.hasHint(Slice.HINT_HIDDEN)) {
-                    return;
-                }
-                Context context = getContext();
-                switch (i.getType()) {
-                    case SliceItem.TYPE_TEXT:
-                        boolean title = false;
-                        if ((item.hasAnyHints(new String[] {
-                                Slice.HINT_LARGE, Slice.HINT_TITLE
-                        }))) {
-                            title = true;
-                        }
-                        TextView tv = (TextView) LayoutInflater.from(context).inflate(
-                                title ? R.layout.slice_title : R.layout.slice_secondary_text, null);
-                        tv.setText(i.getText());
-                        v.addView(tv);
-                        break;
-                    case SliceItem.TYPE_IMAGE:
-                        ImageView iv = new ImageView(context);
-                        iv.setImageIcon(i.getIcon());
-                        if (item.hasHint(Slice.HINT_LARGE)) {
-                            iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-                        } else {
-                            int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                                    48, context.getResources().getDisplayMetrics());
-                            iv.setLayoutParams(new LayoutParams(size, size));
-                        }
-                        v.addView(iv);
-                        break;
-                    case SliceItem.TYPE_REMOTE_VIEW:
-                        v.addView(i.getRemoteView().apply(context, v));
-                        break;
-                    case SliceItem.TYPE_COLOR:
-                        // TODO: Support color to tint stuff here.
-                        break;
-                }
-            });
-            addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
-            return false;
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/LargeSliceAdapter.java b/core/java/android/app/slice/widget/LargeSliceAdapter.java
deleted file mode 100644
index 267fff6..0000000
--- a/core/java/android/app/slice/widget/LargeSliceAdapter.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceViewHolder;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.widget.RecyclerView;
-import com.android.internal.widget.RecyclerView.ViewHolder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @hide
- */
-public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> {
-
-    public static final int TYPE_DEFAULT       = 1;
-    public static final int TYPE_HEADER        = 2;
-    public static final int TYPE_GRID          = 3;
-    public static final int TYPE_MESSAGE       = 4;
-    public static final int TYPE_MESSAGE_LOCAL = 5;
-    public static final int TYPE_REMOTE_VIEWS  = 6;
-
-    private final IdGenerator mIdGen = new IdGenerator();
-    private final Context mContext;
-    private List<SliceWrapper> mSlices = new ArrayList<>();
-    private SliceItem mColor;
-
-    public LargeSliceAdapter(Context context) {
-        mContext = context;
-        setHasStableIds(true);
-    }
-
-    /**
-     * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
-     */
-    public void setSliceItems(List<SliceItem> slices, SliceItem color) {
-        mColor = color;
-        mIdGen.resetUsage();
-        mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen))
-                .collect(Collectors.toList());
-        notifyDataSetChanged();
-    }
-
-    @Override
-    public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        View v = inflateForType(viewType);
-        v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        return new SliceViewHolder(v);
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return mSlices.get(position).mType;
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return mSlices.get(position).mId;
-    }
-
-    @Override
-    public int getItemCount() {
-        return mSlices.size();
-    }
-
-    @Override
-    public void onBindViewHolder(SliceViewHolder holder, int position) {
-        SliceWrapper slice = mSlices.get(position);
-        if (holder.mSliceView != null) {
-            holder.mSliceView.setColor(mColor);
-            holder.mSliceView.setSliceItem(slice.mItem);
-        } else if (slice.mType == TYPE_REMOTE_VIEWS) {
-            FrameLayout frame = (FrameLayout) holder.itemView;
-            frame.removeAllViews();
-            frame.addView(slice.mItem.getRemoteView().apply(mContext, frame));
-        }
-    }
-
-    private View inflateForType(int viewType) {
-        switch (viewType) {
-            case TYPE_REMOTE_VIEWS:
-                return new FrameLayout(mContext);
-            case TYPE_GRID:
-                return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null);
-            case TYPE_MESSAGE:
-                return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null);
-            case TYPE_MESSAGE_LOCAL:
-                return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null);
-        }
-        return new SmallTemplateView(mContext);
-    }
-
-    protected static class SliceWrapper {
-        private final SliceItem mItem;
-        private final int mType;
-        private final long mId;
-
-        public SliceWrapper(SliceItem item, IdGenerator idGen) {
-            mItem = item;
-            mType = getType(item);
-            mId = idGen.getId(item);
-        }
-
-        public static int getType(SliceItem item) {
-            if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) {
-                return TYPE_REMOTE_VIEWS;
-            }
-            if (item.hasHint(Slice.HINT_MESSAGE)) {
-                // TODO: Better way to determine me or not? Something more like Messaging style.
-                if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) {
-                    return TYPE_MESSAGE;
-                } else {
-                    return TYPE_MESSAGE_LOCAL;
-                }
-            }
-            if (item.hasHint(Slice.HINT_HORIZONTAL)) {
-                return TYPE_GRID;
-            }
-            return TYPE_DEFAULT;
-        }
-    }
-
-    /**
-     * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
-     */
-    public static class SliceViewHolder extends ViewHolder {
-        public final SliceListView mSliceView;
-
-        public SliceViewHolder(View itemView) {
-            super(itemView);
-            mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
-        }
-    }
-
-    /**
-     * View slices being displayed in {@link LargeSliceAdapter}.
-     */
-    public interface SliceListView {
-        /**
-         * Set the slice item for this view.
-         */
-        void setSliceItem(SliceItem slice);
-
-        /**
-         * Set the color for the items in this view.
-         */
-        default void setColor(SliceItem color) {
-
-        }
-    }
-
-    private static class IdGenerator {
-        private long mNextLong = 0;
-        private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
-        private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
-
-        public long getId(SliceItem item) {
-            String str = genString(item);
-            if (!mCurrentIds.containsKey(str)) {
-                mCurrentIds.put(str, mNextLong++);
-            }
-            long id = mCurrentIds.get(str);
-            int index = mUsedIds.getOrDefault(str, 0);
-            mUsedIds.put(str, index + 1);
-            return id + index * 10000;
-        }
-
-        private String genString(SliceItem item) {
-            StringBuilder builder = new StringBuilder();
-            SliceQuery.stream(item).forEach(i -> {
-                builder.append(i.getType());
-                i.removeHint(Slice.HINT_SELECTED);
-                builder.append(i.getHints());
-                switch (i.getType()) {
-                    case SliceItem.TYPE_REMOTE_VIEW:
-                        builder.append(i.getRemoteView());
-                        break;
-                    case SliceItem.TYPE_IMAGE:
-                        builder.append(i.getIcon());
-                        break;
-                    case SliceItem.TYPE_TEXT:
-                        builder.append(i.getText());
-                        break;
-                    case SliceItem.TYPE_COLOR:
-                        builder.append(i.getColor());
-                        break;
-                }
-            });
-            return builder.toString();
-        }
-
-        public void resetUsage() {
-            mUsedIds.clear();
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/LargeTemplateView.java b/core/java/android/app/slice/widget/LargeTemplateView.java
deleted file mode 100644
index 788f6fb..0000000
--- a/core/java/android/app/slice/widget/LargeTemplateView.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.util.TypedValue;
-
-import com.android.internal.widget.LinearLayoutManager;
-import com.android.internal.widget.RecyclerView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class LargeTemplateView extends SliceModeView {
-
-    private final LargeSliceAdapter mAdapter;
-    private final RecyclerView mRecyclerView;
-    private final int mDefaultHeight;
-    private final int mMaxHeight;
-    private Slice mSlice;
-    private boolean mIsScrollable;
-
-    public LargeTemplateView(Context context) {
-        super(context);
-
-        mRecyclerView = new RecyclerView(getContext());
-        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
-        mAdapter = new LargeSliceAdapter(context);
-        mRecyclerView.setAdapter(mAdapter);
-        addView(mRecyclerView);
-        mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
-                getResources().getDisplayMetrics());
-        mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
-                getResources().getDisplayMetrics());
-    }
-
-    @Override
-    public String getMode() {
-        return SliceView.MODE_LARGE;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mRecyclerView.getMeasuredHeight() > mMaxHeight
-                || (mSlice != null && mSlice.hasHint(Slice.HINT_PARTIAL))) {
-            mRecyclerView.getLayoutParams().height = mDefaultHeight;
-        } else {
-            mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    public void setSlice(Slice slice) {
-        SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        mSlice = slice;
-        List<SliceItem> items = new ArrayList<>();
-        boolean[] hasHeader = new boolean[1];
-        if (slice.hasHint(Slice.HINT_LIST)) {
-            addList(slice, items);
-        } else {
-            slice.getItems().forEach(item -> {
-                if (item.hasHint(Slice.HINT_HIDDEN)) {
-                    // If it's hidden we don't show it
-                    return;
-                } else if (item.hasHint(Slice.HINT_ACTIONS)) {
-                    // Action groups don't show in lists
-                    return;
-                } else if (item.getType() == SliceItem.TYPE_COLOR) {
-                    // A color is not a list item
-                    return;
-                } else if (item.getType() == SliceItem.TYPE_SLICE
-                        && item.hasHint(Slice.HINT_LIST)) {
-                    addList(item.getSlice(), items);
-                } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
-                    items.add(item);
-                } else if (!hasHeader[0]) {
-                    hasHeader[0] = true;
-                    items.add(0, item);
-                } else {
-                    item.addHint(Slice.HINT_LIST_ITEM);
-                    items.add(item);
-                }
-            });
-        }
-        mAdapter.setSliceItems(items, color);
-    }
-
-    private void addList(Slice slice, List<SliceItem> items) {
-        List<SliceItem> sliceItems = slice.getItems();
-        sliceItems.forEach(i -> {
-            if (!i.hasHint(Slice.HINT_HIDDEN) && i.getType() != SliceItem.TYPE_COLOR) {
-                i.addHint(Slice.HINT_LIST_ITEM);
-                items.add(i);
-            }
-        });
-    }
-
-    /**
-     * Whether or not the content in this template should be scrollable.
-     */
-    public void setScrollable(boolean isScrollable) {
-        // TODO -- restrict / enable how much this view can show
-        mIsScrollable = isScrollable;
-    }
-}
diff --git a/core/java/android/app/slice/widget/MessageView.java b/core/java/android/app/slice/widget/MessageView.java
deleted file mode 100644
index 3124398..0000000
--- a/core/java/android/app/slice/widget/MessageView.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.text.SpannableStringBuilder;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class MessageView extends LinearLayout implements SliceListView {
-
-    private TextView mDetails;
-    private ImageView mIcon;
-
-    public MessageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDetails = findViewById(android.R.id.summary);
-        mIcon = findViewById(android.R.id.icon);
-    }
-
-    @Override
-    public void setSliceItem(SliceItem slice) {
-        SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null);
-        if (source != null) {
-            final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    24, getContext().getResources().getDisplayMetrics());
-            // TODO try and turn this into a drawable
-            Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
-            Canvas iconCanvas = new Canvas(iconBm);
-            Drawable d = source.getIcon().loadDrawable(getContext());
-            d.setBounds(0, 0, iconSize, iconSize);
-            d.draw(iconCanvas);
-            mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
-        }
-        SpannableStringBuilder builder = new SpannableStringBuilder();
-        SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> {
-            if (builder.length() != 0) {
-                builder.append('\n');
-            }
-            builder.append(text.getText());
-        });
-        mDetails.setText(builder.toString());
-    }
-
-}
diff --git a/core/java/android/app/slice/widget/RemoteInputView.java b/core/java/android/app/slice/widget/RemoteInputView.java
deleted file mode 100644
index 6eff5af..0000000
--- a/core/java/android/app/slice/widget/RemoteInputView.java
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.animation.Animator;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutManager;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.R;
-
-/**
- * Host for the remote input.
- *
- * @hide
- */
-// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
-public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
-
-    private static final String TAG = "RemoteInput";
-
-    /**
-     * A marker object that let's us easily find views of this class.
-     */
-    public static final Object VIEW_TAG = new Object();
-
-    private RemoteEditText mEditText;
-    private ImageButton mSendButton;
-    private ProgressBar mProgressBar;
-    private PendingIntent mPendingIntent;
-    private RemoteInput[] mRemoteInputs;
-    private RemoteInput mRemoteInput;
-
-    private int mRevealCx;
-    private int mRevealCy;
-    private int mRevealR;
-    private boolean mResetting;
-
-    public RemoteInputView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mProgressBar = findViewById(R.id.remote_input_progress);
-        mSendButton = findViewById(R.id.remote_input_send);
-        mSendButton.setOnClickListener(this);
-
-        mEditText = (RemoteEditText) getChildAt(0);
-        mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-            @Override
-            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-                final boolean isSoftImeEvent = event == null
-                        && (actionId == EditorInfo.IME_ACTION_DONE
-                                || actionId == EditorInfo.IME_ACTION_NEXT
-                                || actionId == EditorInfo.IME_ACTION_SEND);
-                final boolean isKeyboardEnterKey = event != null
-                        && KeyEvent.isConfirmKey(event.getKeyCode())
-                        && event.getAction() == KeyEvent.ACTION_DOWN;
-
-                if (isSoftImeEvent || isKeyboardEnterKey) {
-                    if (mEditText.length() > 0) {
-                        sendRemoteInput();
-                    }
-                    // Consume action to prevent IME from closing.
-                    return true;
-                }
-                return false;
-            }
-        });
-        mEditText.addTextChangedListener(this);
-        mEditText.setInnerFocusable(false);
-        mEditText.mRemoteInputView = this;
-    }
-
-    private void sendRemoteInput() {
-        Bundle results = new Bundle();
-        results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
-        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
-                results);
-
-        mEditText.setEnabled(false);
-        mSendButton.setVisibility(INVISIBLE);
-        mProgressBar.setVisibility(VISIBLE);
-        mEditText.mShowImeOnInputConnection = false;
-
-        // Tell ShortcutManager that this package has been "activated".  ShortcutManager
-        // will reset the throttling for this package.
-        // Strictly speaking, the intent receiver may be different from the intent creator,
-        // but that's an edge case, and also because we can't always know which package will receive
-        // an intent, so we just reset for the creator.
-        getContext().getSystemService(ShortcutManager.class).onApplicationActive(
-                mPendingIntent.getCreatorPackage(),
-                getContext().getUserId());
-
-        try {
-            mPendingIntent.send(mContext, 0, fillInIntent);
-            reset();
-        } catch (PendingIntent.CanceledException e) {
-            Log.i(TAG, "Unable to send remote input result", e);
-            Toast.makeText(mContext, "Failure sending pending intent for inline reply :(",
-                    Toast.LENGTH_SHORT).show();
-            reset();
-        }
-    }
-
-    /**
-     * Creates a remote input view.
-     */
-    public static RemoteInputView inflate(Context context, ViewGroup root) {
-        RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
-                R.layout.slice_remote_input, root, false);
-        v.setTag(VIEW_TAG);
-        return v;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mSendButton) {
-            sendRemoteInput();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        super.onTouchEvent(event);
-
-        // We never want for a touch to escape to an outer view or one we covered.
-        return true;
-    }
-
-    private void onDefocus() {
-        setVisibility(INVISIBLE);
-    }
-
-    /**
-     * Set the pending intent for remote input.
-     */
-    public void setPendingIntent(PendingIntent pendingIntent) {
-        mPendingIntent = pendingIntent;
-    }
-
-    /**
-     * Set the remote inputs for this view.
-     */
-    public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
-        mRemoteInputs = remoteInputs;
-        mRemoteInput = remoteInput;
-        mEditText.setHint(mRemoteInput.getLabel());
-    }
-
-    /**
-     * Focuses the remote input view.
-     */
-    public void focusAnimated() {
-        if (getVisibility() != VISIBLE) {
-            Animator animator = ViewAnimationUtils.createCircularReveal(
-                    this, mRevealCx, mRevealCy, 0, mRevealR);
-            animator.setDuration(200);
-            animator.start();
-        }
-        focus();
-    }
-
-    private void focus() {
-        setVisibility(VISIBLE);
-        mEditText.setInnerFocusable(true);
-        mEditText.mShowImeOnInputConnection = true;
-        mEditText.setSelection(mEditText.getText().length());
-        mEditText.requestFocus();
-        updateSendButton();
-    }
-
-    private void reset() {
-        mResetting = true;
-
-        mEditText.getText().clear();
-        mEditText.setEnabled(true);
-        mSendButton.setVisibility(VISIBLE);
-        mProgressBar.setVisibility(INVISIBLE);
-        updateSendButton();
-        onDefocus();
-
-        mResetting = false;
-    }
-
-    @Override
-    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
-        if (mResetting && child == mEditText) {
-            // Suppress text events if it happens during resetting. Ideally this would be
-            // suppressed by the text view not being shown, but that doesn't work here because it
-            // needs to stay visible for the animation.
-            return false;
-        }
-        return super.onRequestSendAccessibilityEvent(child, event);
-    }
-
-    private void updateSendButton() {
-        mSendButton.setEnabled(mEditText.getText().length() != 0);
-    }
-
-    @Override
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-    }
-
-    @Override
-    public void onTextChanged(CharSequence s, int start, int before, int count) {
-    }
-
-    @Override
-    public void afterTextChanged(Editable s) {
-        updateSendButton();
-    }
-
-    /**
-     * Tries to find an action that matches the current pending intent of this view and updates its
-     * state to that of the found action
-     *
-     * @return true if a matching action was found, false otherwise
-     */
-    public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
-        if (mPendingIntent == null || actions == null) {
-            return false;
-        }
-        Intent current = mPendingIntent.getIntent();
-        if (current == null) {
-            return false;
-        }
-
-        for (Notification.Action a : actions) {
-            RemoteInput[] inputs = a.getRemoteInputs();
-            if (a.actionIntent == null || inputs == null) {
-                continue;
-            }
-            Intent candidate = a.actionIntent.getIntent();
-            if (!current.filterEquals(candidate)) {
-                continue;
-            }
-
-            RemoteInput input = null;
-            for (RemoteInput i : inputs) {
-                if (i.getAllowFreeFormInput()) {
-                    input = i;
-                }
-            }
-            if (input == null) {
-                continue;
-            }
-            setPendingIntent(a.actionIntent);
-            setRemoteInput(inputs, input);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    public void setRevealParameters(int cx, int cy, int r) {
-        mRevealCx = cx;
-        mRevealCy = cy;
-        mRevealR = r;
-    }
-
-    @Override
-    public void dispatchStartTemporaryDetach() {
-        super.dispatchStartTemporaryDetach();
-        // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
-        // won't lose IME focus.
-        detachViewFromParent(mEditText);
-    }
-
-    @Override
-    public void dispatchFinishTemporaryDetach() {
-        if (isAttachedToWindow()) {
-            attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
-        } else {
-            removeDetachedView(mEditText, false /* animate */);
-        }
-        super.dispatchFinishTemporaryDetach();
-    }
-
-    /**
-     * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
-     * whenever the user navigates away from it or it becomes invisible.
-     */
-    public static class RemoteEditText extends EditText {
-
-        private final Drawable mBackground;
-        private RemoteInputView mRemoteInputView;
-        boolean mShowImeOnInputConnection;
-
-        public RemoteEditText(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            mBackground = getBackground();
-        }
-
-        private void defocusIfNeeded(boolean animate) {
-            if (mRemoteInputView != null || isTemporarilyDetached()) {
-                if (isTemporarilyDetached()) {
-                    // We might get reattached but then the other one of HUN / expanded might steal
-                    // our focus, so we'll need to save our text here.
-                }
-                return;
-            }
-            if (isFocusable() && isEnabled()) {
-                setInnerFocusable(false);
-                if (mRemoteInputView != null) {
-                    mRemoteInputView.onDefocus();
-                }
-                mShowImeOnInputConnection = false;
-            }
-        }
-
-        @Override
-        protected void onVisibilityChanged(View changedView, int visibility) {
-            super.onVisibilityChanged(changedView, visibility);
-
-            if (!isShown()) {
-                defocusIfNeeded(false /* animate */);
-            }
-        }
-
-        @Override
-        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-            super.onFocusChanged(focused, direction, previouslyFocusedRect);
-            if (!focused) {
-                defocusIfNeeded(true /* animate */);
-            }
-        }
-
-        @Override
-        public void getFocusedRect(Rect r) {
-            super.getFocusedRect(r);
-            r.top = mScrollY;
-            r.bottom = mScrollY + (mBottom - mTop);
-        }
-
-        @Override
-        public boolean onKeyDown(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                // Eat the DOWN event here to prevent any default behavior.
-                return true;
-            }
-            return super.onKeyDown(keyCode, event);
-        }
-
-        @Override
-        public boolean onKeyUp(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                defocusIfNeeded(true /* animate */);
-                return true;
-            }
-            return super.onKeyUp(keyCode, event);
-        }
-
-        @Override
-        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-            final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
-
-            if (mShowImeOnInputConnection && inputConnection != null) {
-                final InputMethodManager imm = InputMethodManager.getInstance();
-                if (imm != null) {
-                    // onCreateInputConnection is called by InputMethodManager in the middle of
-                    // setting up the connection to the IME; wait with requesting the IME until that
-                    // work has completed.
-                    post(new Runnable() {
-                        @Override
-                        public void run() {
-                            imm.viewClicked(RemoteEditText.this);
-                            imm.showSoftInput(RemoteEditText.this, 0);
-                        }
-                    });
-                }
-            }
-
-            return inputConnection;
-        }
-
-        @Override
-        public void onCommitCompletion(CompletionInfo text) {
-            clearComposingText();
-            setText(text.getText());
-            setSelection(getText().length());
-        }
-
-        void setInnerFocusable(boolean focusable) {
-            setFocusableInTouchMode(focusable);
-            setFocusable(focusable);
-            setCursorVisible(focusable);
-
-            if (focusable) {
-                requestFocus();
-                setBackground(mBackground);
-            } else {
-                setBackground(null);
-            }
-
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/ShortcutView.java b/core/java/android/app/slice/widget/ShortcutView.java
deleted file mode 100644
index 0b7ad0d..0000000
--- a/core/java/android/app/slice/widget/ShortcutView.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
-import android.net.Uri;
-
-import com.android.internal.R;
-
-import java.util.List;
-
-/**
- * @hide
- */
-public class ShortcutView extends SliceModeView {
-
-    private static final String TAG = "ShortcutView";
-
-    private Uri mUri;
-    private PendingIntent mAction;
-    private SliceItem mLabel;
-    private SliceItem mIcon;
-
-    private int mLargeIconSize;
-    private int mSmallIconSize;
-
-    public ShortcutView(Context context) {
-        super(context);
-        final Resources res = getResources();
-        mSmallIconSize = res.getDimensionPixelSize(R.dimen.slice_icon_size);
-        mLargeIconSize = res.getDimensionPixelSize(R.dimen.slice_shortcut_size);
-    }
-
-    @Override
-    public void setSlice(Slice slice) {
-        removeAllViews();
-        determineShortcutItems(getContext(), slice);
-        SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        if (colorItem == null) {
-            colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        }
-        // TODO: pick better default colour
-        final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
-        ShapeDrawable circle = new ShapeDrawable(new OvalShape());
-        circle.setTint(color);
-        setBackground(circle);
-        if (mIcon != null) {
-            final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE);
-            final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
-            SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
-                    isLarge, this /* parent */);
-            mUri = slice.getUri();
-            setClickable(true);
-        } else {
-            setClickable(false);
-        }
-    }
-
-    @Override
-    public String getMode() {
-        return SliceView.MODE_SHORTCUT;
-    }
-
-    @Override
-    public boolean performClick() {
-        if (!callOnClick()) {
-            try {
-                if (mAction != null) {
-                    mAction.send();
-                } else {
-                    Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    getContext().startActivity(intent);
-                }
-            } catch (CanceledException e) {
-                e.printStackTrace();
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Looks at the slice and determines which items are best to use to compose the shortcut.
-     */
-    private void determineShortcutItems(Context context, Slice slice) {
-        List<String> h = slice.getHints();
-        SliceItem sliceItem = new SliceItem(slice, SliceItem.TYPE_SLICE,
-                h.toArray(new String[h.size()]));
-        SliceItem titleItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION,
-                Slice.HINT_TITLE, null);
-
-        if (titleItem != null) {
-            // Preferred case: hinted action containing hinted image and text
-            mAction = titleItem.getAction();
-            mIcon = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
-                    null);
-            mLabel = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
-                    null);
-        } else {
-            // No hinted action; just use the first one
-            SliceItem actionItem = SliceQuery.find(sliceItem, SliceItem.TYPE_ACTION, (String) null,
-                    null);
-            mAction = (actionItem != null) ? actionItem.getAction() : null;
-        }
-        // First fallback: any hinted image and text
-        if (mIcon == null) {
-            mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
-                    null);
-        }
-        if (mLabel == null) {
-            mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
-                    null);
-        }
-        // Second fallback: first image and text
-        if (mIcon == null) {
-            mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, (String) null,
-                    null);
-        }
-        if (mLabel == null) {
-            mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, (String) null,
-                    null);
-        }
-        // Final fallback: use app info
-        if (mIcon == null || mLabel == null || mAction == null) {
-            PackageManager pm = context.getPackageManager();
-            ProviderInfo providerInfo = pm.resolveContentProvider(
-                    slice.getUri().getAuthority(), 0);
-            ApplicationInfo appInfo = providerInfo.applicationInfo;
-            if (appInfo != null) {
-                if (mIcon == null) {
-                    Drawable icon = appInfo.loadDefaultIcon(pm);
-                    mIcon = new SliceItem(SliceViewUtil.createIconFromDrawable(icon),
-                            SliceItem.TYPE_IMAGE, new String[] {Slice.HINT_LARGE});
-                }
-                if (mLabel == null) {
-                    mLabel = new SliceItem(pm.getApplicationLabel(appInfo),
-                            SliceItem.TYPE_TEXT, null);
-                }
-                if (mAction == null) {
-                    mAction = PendingIntent.getActivity(context, 0,
-                            pm.getLaunchIntentForPackage(appInfo.packageName), 0);
-                }
-            }
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/SliceView.java b/core/java/android/app/slice/widget/SliceView.java
deleted file mode 100644
index fa1b64c..0000000
--- a/core/java/android/app/slice/widget/SliceView.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.graphics.drawable.ColorDrawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * A view for displaying a {@link Slice} which is a piece of app content and actions. SliceView is
- * able to present slice content in a templated format outside of the associated app. The way this
- * content is displayed depends on the structure of the slice, the hints associated with the
- * content, and the mode that SliceView is configured for. The modes that SliceView supports are:
- * <ul>
- * <li><b>Shortcut</b>: A shortcut is presented as an icon and a text label representing the main
- * content or action associated with the slice.</li>
- * <li><b>Small</b>: The small format has a restricted height and can present a single
- * {@link SliceItem} or a limited collection of items.</li>
- * <li><b>Large</b>: The large format displays multiple small templates in a list, if scrolling is
- * not enabled (see {@link #setScrollable(boolean)}) the view will show as many items as it can
- * comfortably fit.</li>
- * </ul>
- * <p>
- * When constructing a slice, the contents of it can be annotated with hints, these provide the OS
- * with some information on how the content should be displayed. For example, text annotated with
- * {@link Slice#HINT_TITLE} would be placed in the title position of a template. A slice annotated
- * with {@link Slice#HINT_LIST} would present the child items of that slice in a list.
- * <p>
- * SliceView can be provided a slice via a uri {@link #setSlice(Uri)} in which case a content
- * observer will be set for that uri and the view will update if there are any changes to the slice.
- * To use this the app must have a special permission to bind to the slice (see
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Example usage:
- *
- * <pre class="prettyprint">
- * SliceView v = new SliceView(getContext());
- * v.setMode(desiredMode);
- * v.setSlice(sliceUri);
- * </pre>
- */
-public class SliceView extends ViewGroup {
-
-    private static final String TAG = "SliceView";
-
-    /**
-     * @hide
-     */
-    public abstract static class SliceModeView extends FrameLayout {
-
-        public SliceModeView(Context context) {
-            super(context);
-        }
-
-        /**
-         * @return the mode of the slice being presented.
-         */
-        public abstract String getMode();
-
-        /**
-         * @param slice the slice to show in this view.
-         */
-        public abstract void setSlice(Slice slice);
-    }
-
-    /**
-     * @hide
-     */
-    @StringDef({
-            MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
-    })
-    public @interface SliceMode {}
-
-    /**
-     * Mode indicating this slice should be presented in small template format.
-     */
-    public static final String MODE_SMALL       = "SLICE_SMALL";
-    /**
-     * Mode indicating this slice should be presented in large template format.
-     */
-    public static final String MODE_LARGE       = "SLICE_LARGE";
-    /**
-     * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
-     * icon, and label. This can be indicated by using {@link Slice#HINT_TITLE} on an action in a
-     * slice.
-     */
-    public static final String MODE_SHORTCUT    = "SLICE_ICON";
-
-    /**
-     * Will select the type of slice binding based on size of the View. TODO: Put in some info about
-     * that selection.
-     */
-    private static final String MODE_AUTO = "auto";
-
-    private String mMode = MODE_AUTO;
-    private SliceModeView mCurrentView;
-    private final ActionRow mActions;
-    private Slice mCurrentSlice;
-    private boolean mShowActions = true;
-    private boolean mIsScrollable;
-    private SliceObserver mObserver;
-    private final int mShortcutSize;
-
-    public SliceView(Context context) {
-        this(context, null);
-    }
-
-    public SliceView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
-        mActions = new ActionRow(mContext, true);
-        mActions.setBackground(new ColorDrawable(0xffeeeeee));
-        mCurrentView = new LargeTemplateView(mContext);
-        addView(mCurrentView, getChildLp(mCurrentView));
-        addView(mActions, getChildLp(mActions));
-        mShortcutSize = getContext().getResources()
-                .getDimensionPixelSize(R.dimen.slice_shortcut_size);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        measureChildren(widthMeasureSpec, heightMeasureSpec);
-        int actionHeight = mActions.getVisibility() != View.GONE
-                ? mActions.getMeasuredHeight()
-                : 0;
-        int newHeightSpec = MeasureSpec.makeMeasureSpec(
-                mCurrentView.getMeasuredHeight() + actionHeight, MeasureSpec.EXACTLY);
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        setMeasuredDimension(width, newHeightSpec);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        mCurrentView.layout(l, t, l + mCurrentView.getMeasuredWidth(),
-                t + mCurrentView.getMeasuredHeight());
-        if (mActions.getVisibility() != View.GONE) {
-            mActions.layout(l, mCurrentView.getMeasuredHeight(), l + mActions.getMeasuredWidth(),
-                    mCurrentView.getMeasuredHeight() + mActions.getMeasuredHeight());
-        }
-    }
-
-    /**
-     * Populates this view with the {@link Slice} associated with the provided {@link Intent}. To
-     * use this method your app must have the permission
-     * {@link android.Manifest.permission#BIND_SLICE}).
-     * <p>
-     * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
-     * updated with the slice identified by the provided intent changes. The lifecycle of this
-     * observer is handled by SliceView in {@link #onAttachedToWindow()} and
-     * {@link #onDetachedFromWindow()}. To unregister this observer outside of that you can call
-     * {@link #clearSlice}.
-     *
-     * @return true if a slice was found for the provided intent.
-     * @hide
-     */
-    public boolean setSlice(@Nullable Intent intent) {
-        Slice s = Slice.bindSlice(mContext, intent);
-        if (s != null) {
-            return setSlice(s.getUri());
-        }
-        return s != null;
-    }
-
-    /**
-     * Populates this view with the {@link Slice} associated with the provided {@link Uri}. To use
-     * this method your app must have the permission
-     * {@link android.Manifest.permission#BIND_SLICE}).
-     * <p>
-     * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
-     * updated when the slice identified by the provided URI changes. The lifecycle of this observer
-     * is handled by SliceView in {@link #onAttachedToWindow()} and {@link #onDetachedFromWindow()}.
-     * To unregister this observer outside of that you can call {@link #clearSlice}.
-     *
-     * @return true if a slice was found for the provided uri.
-     */
-    public boolean setSlice(@NonNull Uri sliceUri) {
-        Preconditions.checkNotNull(sliceUri,
-                "Uri cannot be null, to remove the slice use clearSlice()");
-        if (sliceUri == null) {
-            clearSlice();
-            return false;
-        }
-        validate(sliceUri);
-        Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri);
-        if (s != null) {
-            if (mObserver != null) {
-                getContext().getContentResolver().unregisterContentObserver(mObserver);
-            }
-            mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
-            if (isAttachedToWindow()) {
-                registerSlice(sliceUri);
-            }
-            mCurrentSlice = s;
-            reinflate();
-        }
-        return s != null;
-    }
-
-    /**
-     * Populates this view to the provided {@link Slice}.
-     * <p>
-     * This does not register a content observer on the URI that the slice is backed by so it will
-     * not update if the content changes. To have the view update when the content changes use
-     * {@link #setSlice(Uri)} instead. Unlike {@link #setSlice(Uri)}, this method does not require
-     * any special permissions.
-     */
-    public void showSlice(@NonNull Slice slice) {
-        Preconditions.checkNotNull(slice,
-                "Slice cannot be null, to remove the slice use clearSlice()");
-        clearSlice();
-        mCurrentSlice = slice;
-        reinflate();
-    }
-
-    /**
-     * Unregisters the change observer that is set when using {@link #setSlice}. Normally this is
-     * done automatically during {@link #onDetachedFromWindow()}.
-     * <p>
-     * It is safe to call this method multiple times.
-     */
-    public void clearSlice() {
-        mCurrentSlice = null;
-        if (mObserver != null) {
-            getContext().getContentResolver().unregisterContentObserver(mObserver);
-            mObserver = null;
-        }
-    }
-
-    /**
-     * Set the mode this view should present in.
-     */
-    public void setMode(@SliceMode String mode) {
-        setMode(mode, false /* animate */);
-    }
-
-    /**
-     * Set whether this view should allow scrollable content when presenting in {@link #MODE_LARGE}.
-     */
-    public void setScrollable(boolean isScrollable) {
-        mIsScrollable = isScrollable;
-        reinflate();
-    }
-
-    /**
-     * @hide
-     */
-    public void setMode(@SliceMode String mode, boolean animate) {
-        if (animate) {
-            Log.e(TAG, "Animation not supported yet");
-        }
-        mMode = mode;
-        reinflate();
-    }
-
-    /**
-     * @return the mode this view is presenting in.
-     */
-    public @SliceMode String getMode() {
-        if (mMode.equals(MODE_AUTO)) {
-            return MODE_LARGE;
-        }
-        return mMode;
-    }
-
-    /**
-     * @hide
-     *
-     * Whether this view should show a row of actions with it.
-     */
-    public void setShowActionRow(boolean show) {
-        mShowActions = show;
-        reinflate();
-    }
-
-    private SliceModeView createView(String mode) {
-        switch (mode) {
-            case MODE_SHORTCUT:
-                return new ShortcutView(getContext());
-            case MODE_SMALL:
-                return new SmallTemplateView(getContext());
-        }
-        return new LargeTemplateView(getContext());
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        registerSlice(mCurrentSlice != null ? mCurrentSlice.getUri() : null);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mObserver != null) {
-            getContext().getContentResolver().unregisterContentObserver(mObserver);
-            mObserver = null;
-        }
-    }
-
-    private void registerSlice(Uri sliceUri) {
-        if (sliceUri == null || mObserver == null) {
-            return;
-        }
-        mContext.getContentResolver().registerContentObserver(sliceUri,
-                false /* notifyForDescendants */, mObserver);
-    }
-
-    private void reinflate() {
-        if (mCurrentSlice == null) {
-            return;
-        }
-        // TODO: Smarter mapping here from one state to the next.
-        SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
-        List<SliceItem> items = mCurrentSlice.getItems();
-        SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
-                Slice.HINT_ACTIONS,
-                Slice.HINT_ALT);
-        String mode = getMode();
-        if (!mode.equals(mCurrentView.getMode())) {
-            removeAllViews();
-            mCurrentView = createView(mode);
-            addView(mCurrentView, getChildLp(mCurrentView));
-            addView(mActions, getChildLp(mActions));
-        }
-        if (mode.equals(MODE_LARGE)) {
-            ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
-        }
-        if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) {
-            mCurrentView.setVisibility(View.VISIBLE);
-            mCurrentView.setSlice(mCurrentSlice);
-        } else {
-            mCurrentView.setVisibility(View.GONE);
-        }
-
-        boolean showActions = mShowActions && actionRow != null
-                && !mode.equals(MODE_SHORTCUT);
-        if (showActions) {
-            mActions.setActions(actionRow, color);
-            mActions.setVisibility(View.VISIBLE);
-        } else {
-            mActions.setVisibility(View.GONE);
-        }
-    }
-
-    private LayoutParams getChildLp(View child) {
-        if (child instanceof ShortcutView) {
-            return new LayoutParams(mShortcutSize, mShortcutSize);
-        } else {
-            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-        }
-    }
-
-    private static void validate(Uri sliceUri) {
-        if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) {
-            throw new RuntimeException("Invalid uri " + sliceUri);
-        }
-        if (sliceUri.getPathSegments().size() == 0) {
-            throw new RuntimeException("Invalid uri " + sliceUri);
-        }
-    }
-
-    private class SliceObserver extends ContentObserver {
-        SliceObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            this.onChange(selfChange, null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            Slice s = Slice.bindSlice(mContext.getContentResolver(), uri);
-            mCurrentSlice = s;
-            reinflate();
-        }
-    }
-}
diff --git a/core/java/android/app/slice/widget/SliceViewUtil.java b/core/java/android/app/slice/widget/SliceViewUtil.java
deleted file mode 100644
index 1cf0055..0000000
--- a/core/java/android/app/slice/widget/SliceViewUtil.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.annotation.ColorInt;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * A bunch of utilities for slice UI.
- *
- * @hide
- */
-public class SliceViewUtil {
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getColorAccent(Context context) {
-        return getColorAttr(context, android.R.attr.colorAccent);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getColorError(Context context) {
-        return getColorAttr(context, android.R.attr.colorError);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getDefaultColor(Context context, int resId) {
-        final ColorStateList list = context.getResources().getColorStateList(resId,
-                context.getTheme());
-
-        return list.getDefaultColor();
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getDisabled(Context context, int inputColor) {
-        return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int applyAlphaAttr(Context context, int attr, int inputColor) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        float alpha = ta.getFloat(0, 0);
-        ta.recycle();
-        return applyAlpha(alpha, inputColor);
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int applyAlpha(float alpha, int inputColor) {
-        alpha *= Color.alpha(inputColor);
-        return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
-                Color.blue(inputColor));
-    }
-
-    /**
-     * @hide
-     */
-    @ColorInt
-    public static int getColorAttr(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        @ColorInt
-        int colorAccent = ta.getColor(0, 0);
-        ta.recycle();
-        return colorAccent;
-    }
-
-    /**
-     * @hide
-     */
-    public static int getThemeAttr(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        int theme = ta.getResourceId(0, 0);
-        ta.recycle();
-        return theme;
-    }
-
-    /**
-     * @hide
-     */
-    public static Drawable getDrawable(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[] {
-                attr
-        });
-        Drawable drawable = ta.getDrawable(0);
-        ta.recycle();
-        return drawable;
-    }
-
-    /**
-     * @hide
-     */
-    public static Icon createIconFromDrawable(Drawable d) {
-        if (d instanceof BitmapDrawable) {
-            return Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
-        }
-        Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(b);
-        d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-        d.draw(canvas);
-        return Icon.createWithBitmap(b);
-    }
-
-    /**
-     * @hide
-     */
-    public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
-            boolean isLarge, ViewGroup parent) {
-        ImageView v = new ImageView(context);
-        v.setImageIcon(icon);
-        parent.addView(v);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
-        if (isLarge) {
-            // XXX better way to convert from icon -> bitmap or crop an icon (?)
-            Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
-            Canvas iconCanvas = new Canvas(iconBm);
-            v.layout(0, 0, iconSize, iconSize);
-            v.draw(iconCanvas);
-            v.setImageBitmap(getCircularBitmap(iconBm));
-        } else {
-            v.setColorFilter(Color.WHITE);
-        }
-        lp.width = iconSize;
-        lp.height = iconSize;
-        lp.gravity = Gravity.CENTER;
-    }
-
-    /**
-     * @hide
-     */
-    public static Bitmap getCircularBitmap(Bitmap bitmap) {
-        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
-                bitmap.getHeight(), Config.ARGB_8888);
-        Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
-        paint.setAntiAlias(true);
-        canvas.drawARGB(0, 0, 0, 0);
-        canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
-                bitmap.getWidth() / 2, paint);
-        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
-        canvas.drawBitmap(bitmap, rect, rect, paint);
-        return output;
-    }
-}
diff --git a/core/java/android/app/slice/widget/SmallTemplateView.java b/core/java/android/app/slice/widget/SmallTemplateView.java
deleted file mode 100644
index 1c4c5df..0000000
--- a/core/java/android/app/slice/widget/SmallTemplateView.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.text.Format;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Small template is also used to construct list items for use with {@link LargeTemplateView}.
- *
- * @hide
- */
-public class SmallTemplateView extends SliceModeView implements SliceListView {
-
-    private static final String TAG = "SmallTemplateView";
-
-    private int mIconSize;
-    private int mPadding;
-
-    private LinearLayout mStartContainer;
-    private TextView mTitleText;
-    private TextView mSecondaryText;
-    private LinearLayout mEndContainer;
-
-    public SmallTemplateView(Context context) {
-        super(context);
-        inflate(context, R.layout.slice_small_template, this);
-        mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
-        mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding);
-
-        mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame);
-        mTitleText = (TextView) findViewById(android.R.id.title);
-        mSecondaryText = (TextView) findViewById(android.R.id.summary);
-        mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
-    }
-
-    @Override
-    public String getMode() {
-        return SliceView.MODE_SMALL;
-    }
-
-    @Override
-    public void setSliceItem(SliceItem slice) {
-        resetViews();
-        SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
-        int color = colorItem != null ? colorItem.getColor() : -1;
-
-        // Look for any title elements
-        List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE,
-                null);
-        boolean hasTitleText = false;
-        boolean hasTitleItem = false;
-        for (int i = 0; i < titleItems.size(); i++) {
-            SliceItem item = titleItems.get(i);
-            if (!hasTitleItem) {
-                // icon, action icon, or timestamp
-                if (item.getType() == SliceItem.TYPE_ACTION) {
-                    hasTitleItem = addIcon(item, color, mStartContainer);
-                } else if (item.getType() == SliceItem.TYPE_IMAGE) {
-                    addIcon(item, color, mStartContainer);
-                    hasTitleItem = true;
-                } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
-                    TextView tv = new TextView(getContext());
-                    tv.setText(convertTimeToString(item.getTimestamp()));
-                    hasTitleItem = true;
-                }
-            }
-            if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) {
-                mTitleText.setText(item.getText());
-                hasTitleText = true;
-            }
-            if (hasTitleText && hasTitleItem) {
-                break;
-            }
-        }
-        mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE);
-        mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE);
-
-        if (slice.getType() != SliceItem.TYPE_SLICE) {
-            return;
-        }
-
-        // Deal with remaining items
-        int itemCount = 0;
-        boolean hasSummary = false;
-        ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
-                slice.getSlice().getItems());
-        for (int i = 0; i < sliceItems.size(); i++) {
-            SliceItem item = sliceItems.get(i);
-            if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
-                    && !item.hasHint(Slice.HINT_TITLE)) {
-                // TODO -- Should combine all text items?
-                mSecondaryText.setText(item.getText());
-                hasSummary = true;
-            }
-            if (itemCount <= 3) {
-                if (item.getType() == SliceItem.TYPE_ACTION) {
-                    if (addIcon(item, color, mEndContainer)) {
-                        itemCount++;
-                    }
-                } else if (item.getType() == SliceItem.TYPE_IMAGE) {
-                    addIcon(item, color, mEndContainer);
-                    itemCount++;
-                } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
-                    TextView tv = new TextView(getContext());
-                    tv.setText(convertTimeToString(item.getTimestamp()));
-                    mEndContainer.addView(tv);
-                    itemCount++;
-                } else if (item.getType() == SliceItem.TYPE_SLICE) {
-                    List<SliceItem> subItems = item.getSlice().getItems();
-                    for (int j = 0; j < subItems.size(); j++) {
-                        sliceItems.add(subItems.get(j));
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void setSlice(Slice slice) {
-        setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE,
-                slice.getHints().toArray(new String[slice.getHints().size()])));
-    }
-
-    /**
-     * @return Whether an icon was added.
-     */
-    private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) {
-        SliceItem image = null;
-        SliceItem action = null;
-        if (sliceItem.getType() == SliceItem.TYPE_ACTION) {
-            image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE);
-            action = sliceItem;
-        } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) {
-            image = sliceItem;
-        }
-        if (image != null) {
-            ImageView iv = new ImageView(getContext());
-            iv.setImageIcon(image.getIcon());
-            if (action != null) {
-                final SliceItem sliceAction = action;
-                iv.setOnClickListener(v -> AsyncTask.execute(
-                        () -> {
-                            try {
-                                sliceAction.getAction().send();
-                            } catch (CanceledException e) {
-                                e.printStackTrace();
-                            }
-                        }));
-                iv.setBackground(SliceViewUtil.getDrawable(getContext(),
-                        android.R.attr.selectableItemBackground));
-            }
-            if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
-                iv.setColorFilter(color);
-            }
-            container.addView(iv);
-            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
-            lp.width = mIconSize;
-            lp.height = mIconSize;
-            lp.setMarginStart(mPadding);
-            return true;
-        }
-        return false;
-    }
-
-    private String convertTimeToString(long time) {
-        // TODO -- figure out what format(s) we support
-        Date date = new Date(time);
-        Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss");
-        return format.format(date);
-    }
-
-    private void resetViews() {
-        mStartContainer.removeAllViews();
-        mEndContainer.removeAllViews();
-        mTitleText.setText(null);
-        mSecondaryText.setText(null);
-    }
-}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1352bc2..64d33d5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -543,9 +543,10 @@
     void forceDexOpt(String packageName);
 
     /**
-     * Execute the background dexopt job immediately.
+     * Execute the background dexopt job immediately on packages in packageNames.
+     * If null, then execute on all packages.
      */
-    boolean runBackgroundDexoptJob();
+    boolean runBackgroundDexoptJob(in List<String> packageNames);
 
     /**
      * Reconcile the information we have about the secondary dex files belonging to
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 7fc7836..e1137aa 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -48,8 +48,7 @@
     private MemoryRegion[] mMemoryRegions;
 
     /*
-     * TODO(b/66965339): Deprecate this constructor and the setter methods, and mark private fields
-     * as final when the ContextHubService JNI code is removed.
+     * TODO(b/67734082): Deprecate this constructor and mark private fields as final.
      */
     public ContextHubInfo() {
     }
@@ -89,17 +88,6 @@
     }
 
     /**
-     * set the context hub unique identifer
-     *
-     * @param bytes - Maximum number of bytes per message
-     *
-     * @hide
-     */
-    public void setMaxPacketLenBytes(int bytes) {
-        mMaxPacketLengthBytes = bytes;
-    }
-
-    /**
      * get the context hub unique identifer
      *
      * @return int - unique system wide identifier
@@ -109,17 +97,6 @@
     }
 
     /**
-     * set the context hub unique identifer
-     *
-     * @param id - unique system wide identifier for the hub
-     *
-     * @hide
-     */
-    public void setId(int id) {
-        mId = id;
-    }
-
-    /**
      * get a string as a hub name
      *
      * @return String - a name for the hub
@@ -129,17 +106,6 @@
     }
 
     /**
-     * set a string as the hub name
-     *
-     * @param name - the name for the hub
-     *
-     * @hide
-     */
-    public void setName(String name) {
-        mName = name;
-    }
-
-    /**
      * get a string as the vendor name
      *
      * @return String - a name for the vendor
@@ -149,17 +115,6 @@
     }
 
     /**
-     * set a string as the vendor name
-     *
-     * @param vendor - a name for the vendor
-     *
-     * @hide
-     */
-    public void setVendor(String vendor) {
-        mVendor = vendor;
-    }
-
-    /**
      * get tool chain string
      *
      * @return String - description of the tool chain
@@ -169,17 +124,6 @@
     }
 
     /**
-     * set tool chain string
-     *
-     * @param toolchain - description of the tool chain
-     *
-     * @hide
-     */
-    public void setToolchain(String toolchain) {
-        mToolchain = toolchain;
-    }
-
-    /**
      * get platform version
      *
      * @return int - platform version number
@@ -189,17 +133,6 @@
     }
 
     /**
-     * set platform version
-     *
-     * @param platformVersion - platform version number
-     *
-     * @hide
-     */
-    public void setPlatformVersion(int platformVersion) {
-        mPlatformVersion = platformVersion;
-    }
-
-    /**
      * get static platform version number
      *
      * @return int - platform version number
@@ -209,15 +142,6 @@
     }
 
     /**
-     * set platform software version
-     *
-     * @param staticSwVersion - platform static s/w version number
-     *
-     * @hide
-     */
-    public void setStaticSwVersion(int staticSwVersion) {}
-
-    /**
      * get the tool chain version
      *
      * @return int - the tool chain version
@@ -227,17 +151,6 @@
     }
 
     /**
-     * set the tool chain version number
-     *
-     * @param toolchainVersion - tool chain version number
-     *
-     * @hide
-     */
-    public void setToolchainVersion(int toolchainVersion) {
-        mToolchainVersion = toolchainVersion;
-    }
-
-    /**
      * get the peak processing mips the hub can support
      *
      * @return float - peak MIPS that this hub can deliver
@@ -247,17 +160,6 @@
     }
 
     /**
-     * set the peak mips that this hub can support
-     *
-     * @param peakMips - peak mips this hub can deliver
-     *
-     * @hide
-     */
-    public void setPeakMips(float peakMips) {
-        mPeakMips = peakMips;
-    }
-
-    /**
      * get the stopped power draw in milliwatts
      * This assumes that the hub enter a stopped state - which is
      * different from the sleep state. Latencies on exiting the
@@ -271,17 +173,6 @@
     }
 
     /**
-     * Set the power consumed by the hub in stopped state
-     *
-     * @param stoppedPowerDrawMw - stopped power in milli watts
-     *
-     * @hide
-     */
-    public void setStoppedPowerDrawMw(float stoppedPowerDrawMw) {
-        mStoppedPowerDrawMw = stoppedPowerDrawMw;
-    }
-
-    /**
      * get the power draw of the hub in sleep mode. This assumes
      * that the hub supports a sleep mode in which the power draw is
      * lower than the power consumed when the hub is actively
@@ -297,17 +188,6 @@
     }
 
     /**
-     * Set the sleep power draw in milliwatts
-     *
-     * @param sleepPowerDrawMw - sleep power draw in milliwatts.
-     *
-     * @hide
-     */
-    public void setSleepPowerDrawMw(float sleepPowerDrawMw) {
-        mSleepPowerDrawMw = sleepPowerDrawMw;
-    }
-
-    /**
      * get the peak powe draw of the hub. This is the power consumed
      * by the hub at maximum load.
      *
@@ -318,18 +198,6 @@
     }
 
     /**
-     * set the peak power draw of the hub
-     *
-     * @param peakPowerDrawMw - peak power draw of the hub in
-     *                        milliwatts.
-     *
-     * @hide
-     */
-    public void setPeakPowerDrawMw(float peakPowerDrawMw) {
-        mPeakPowerDrawMw = peakPowerDrawMw;
-    }
-
-    /**
      * get the sensors supported by this hub
      *
      * @return int[] - all the supported sensors on this hub
@@ -352,30 +220,6 @@
     }
 
     /**
-     * set the supported sensors on this hub
-     *
-     * @param supportedSensors - supported sensors on this hub
-     *
-     * @hide
-     */
-    public void setSupportedSensors(int[] supportedSensors) {
-        mSupportedSensors = Arrays.copyOf(supportedSensors, supportedSensors.length);
-    }
-
-    /**
-     * set memory regions for this hub
-     *
-     * @param memoryRegions - memory regions information
-     *
-     * @see MemoryRegion
-     *
-     * @hide
-     */
-    public void setMemoryRegions(MemoryRegion[] memoryRegions) {
-        mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
-    }
-
-    /**
      * @return the CHRE platform ID as defined in chre/version.h
      *
      * TODO(b/67734082): Expose as public API
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 3aaeb50..18287fa 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -94,5 +94,17 @@
      */
     void setAnalogForced(boolean isForced);
 
+    /**
+     * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+     * @return Vendor-specific key-value pairs, must be Map<String, String>
+     */
+    Map setParameters(in Map parameters);
+
+    /**
+     * @param keys Parameter keys to fetch
+     * @return Vendor-specific key-value pairs, must be Map<String, String>
+     */
+    Map getParameters(in List<String> keys);
+
     boolean isAntennaConnected();
 }
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index 6ed171b..775e25c 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -30,4 +30,9 @@
     void onBackgroundScanAvailabilityChange(boolean isAvailable);
     void onBackgroundScanComplete();
     void onProgramListChanged();
+
+    /**
+     * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+     */
+    void onParametersUpdated(in Map parameters);
 }
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 6e8991a..e93fd5f 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -309,6 +309,58 @@
     public abstract void setAnalogForced(boolean isForced);
 
     /**
+     * Generic method for setting vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Framework does not make any assumptions on the keys or values, other than
+     * ones stated in VendorKeyValue documentation (a requirement of key
+     * prefixes).
+     *
+     * For each pair in the result map, the key will be one of the keys
+     * contained in the input (possibly with wildcards expanded), and the value
+     * will be a vendor-specific result status (such as "OK" or an error code).
+     * The implementation may choose to return an empty map, or only return
+     * a status for a subset of the provided inputs, at its discretion.
+     *
+     * Application and HAL must not use keys with unknown prefix. In particular,
+     * it must not place a key-value pair in results vector for unknown key from
+     * parameters vector - instead, an unknown key should simply be ignored.
+     * In other words, results vector may contain a subset of parameter keys
+     * (however, the framework doesn't enforce a strict subset - the only
+     * formal requirement is vendor domain prefix for keys).
+     *
+     * @param parameters Vendor-specific key-value pairs.
+     * @return Operation completion status for parameters being set.
+     * @hide FutureFeature
+     */
+    public abstract @NonNull Map<String, String>
+            setParameters(@NonNull Map<String, String> parameters);
+
+    /**
+     * Generic method for retrieving vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Framework does not cache set/get requests, so it's possible for
+     * getParameter to return a different value than previous setParameter call.
+     *
+     * The syntax and semantics of keys are up to the vendor (as long as prefix
+     * rules are obeyed). For instance, vendors may include some form of
+     * wildcard support. In such case, result vector may be of different size
+     * than requested keys vector. However, wildcards are not recognized by
+     * framework and they are passed as-is to the HAL implementation.
+     *
+     * Unknown keys must be ignored and not placed into results vector.
+     *
+     * @param keys Parameter keys to fetch.
+     * @return Vendor-specific key-value pairs.
+     * @hide FutureFeature
+     */
+    public abstract @NonNull Map<String, String>
+            getParameters(@NonNull List<String> keys);
+
+    /**
      * Get current antenna connection state for current configuration.
      * Only valid if a configuration has been applied.
      * @return {@code true} if the antenna is connected, {@code false} otherwise.
@@ -429,6 +481,22 @@
          * Use {@link RadioTuner#getProgramList(String)} to get an actual list.
          */
         public void onProgramListChanged() {}
+
+        /**
+         * Generic callback for passing updates to vendor-specific parameter values.
+         * The framework does not interpret the parameters, they are passed
+         * in an opaque manner between a vendor application and HAL.
+         *
+         * It's up to the HAL implementation if and how to implement this callback,
+         * as long as it obeys the prefix rule. In particular, only selected keys
+         * may be notified this way. However, setParameters must not trigger
+         * this callback, while an internal event can change parameters
+         * asynchronously.
+         *
+         * @param parameters Vendor-specific key-value pairs.
+         * @hide FutureFeature
+         */
+        public void onParametersUpdated(@NonNull Map<String, String> parameters) {}
     }
 
 }
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index b621969..864d17c 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -24,6 +24,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Implements the RadioTuner interface by forwarding calls to radio service.
@@ -251,6 +252,24 @@
     }
 
     @Override
+    public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+        try {
+            return mTuner.setParameters(Objects.requireNonNull(parameters));
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
+    public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+        try {
+            return mTuner.getParameters(Objects.requireNonNull(keys));
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
     public boolean isAntennaConnected() {
         try {
             return mTuner.isAntennaConnected();
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index ffd5b30f..a01f658 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -22,6 +22,8 @@
 import android.os.Looper;
 import android.util.Log;
 
+import java.util.Map;
+
 /**
  * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
  */
@@ -94,4 +96,9 @@
     public void onProgramListChanged() {
         mHandler.post(() -> mCallback.onProgramListChanged());
     }
+
+    @Override
+    public void onParametersUpdated(Map parameters) {
+        mHandler.post(() -> mCallback.onParametersUpdated(parameters));
+    }
 }
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index a6c3009..8e33b65 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -16,8 +16,6 @@
 
 package android.os;
 
-import android.os.IStatsCallbacks;
-
 /**
   * Binder interface to communicate with the statistics management service.
   * {@hide}
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 55b33a6..2bcd863 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -58,11 +58,12 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    throw new RuntimeException("StatsD service connection lost");
+                    Slog.d(TAG, "Failed to find statsd when adding configuration");
+                    return false;
                 }
                 return service.addConfiguration(configKey, config, pkg, cls);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when getting data");
+                Slog.d(TAG, "Failed to connect to statsd when adding configuration");
                 return false;
             }
         }
@@ -80,11 +81,12 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    throw new RuntimeException("StatsD service connection lost");
+                    Slog.d(TAG, "Failed to find statsd when removing configuration");
+                    return false;
                 }
                 return service.removeConfiguration(configKey);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when getting data");
+                Slog.d(TAG, "Failed to connect to statsd when removing configuration");
                 return false;
             }
         }
@@ -102,7 +104,8 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    throw new RuntimeException("StatsD service connection lost");
+                    Slog.d(TAG, "Failed to find statsd when getting data");
+                    return null;
                 }
                 return service.getData(configKey);
             } catch (RemoteException e) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5641009..aa2f1c1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -61,6 +61,8 @@
     private static native long nativeCreateTransaction();
     private static native long nativeGetNativeTransactionFinalizer();
     private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+    private static native void nativeMergeTransaction(long transactionObj,
+            long otherTransactionObj);
     private static native void nativeSetAnimationTransaction(long transactionObj);
 
     private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
@@ -654,6 +656,19 @@
         }
     }
 
+    /**
+     * Merge the supplied transaction in to the deprecated "global" transaction.
+     * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
+     * <p>
+     * This is a utility for interop with legacy-code and will go away with the Global Transaction.
+     */
+    @Deprecated
+    public static void mergeToGlobalTransaction(Transaction t) {
+        synchronized(sGlobalTransaction) {
+            sGlobalTransaction.merge(t);
+        }
+    }
+
     /** end a transaction */
     public static void closeTransaction() {
         closeTransaction(false);
@@ -1368,7 +1383,7 @@
          * Sets the security of the surface.  Setting the flag is equivalent to creating the
          * Surface with the {@link #SECURE} flag.
          */
-        Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+        public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
             sc.checkNotReleased();
             if (isSecure) {
                 nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
@@ -1449,5 +1464,14 @@
             nativeSetAnimationTransaction(mNativeObject);
             return this;
         }
+
+        /**
+         * Merge the other transaction into this transaction, clearing the
+         * other transaction as if it had been applied.
+         */
+        public Transaction merge(Transaction other) {
+            nativeMergeTransaction(mNativeObject, other.mNativeObject);
+            return this;
+        }
     }
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 534335b..c192f5c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1663,14 +1663,6 @@
     void writeToProto(ProtoOutputStream proto, long fieldId);
 
     /**
-     * Returns whether a given window type can be magnified.
-     *
-     * @param windowType The window type.
-     * @return True if the window can be magnified.
-     */
-    public boolean canMagnifyWindow(int windowType);
-
-    /**
      * Returns whether a given window type is considered a top level one.
      * A top level window does not have a container, i.e. attached window,
      * or if it has a container it is laid out as a top-level window, not
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d477ffd..d0c2d86 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -195,6 +195,27 @@
     private final boolean mHapticTextHandleEnabled;
 
     private final Magnifier mMagnifier;
+    private final Runnable mUpdateMagnifierRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mMagnifier.update();
+        }
+    };
+    // Update the magnifier contents whenever anything in the view hierarchy is updated.
+    // Note: this only captures UI thread-visible changes, so it's a known issue that an animating
+    // VectorDrawable or Ripple animation will not trigger capture, since they're owned by
+    // RenderThread.
+    private final ViewTreeObserver.OnDrawListener mMagnifierOnDrawListener =
+            new ViewTreeObserver.OnDrawListener() {
+        @Override
+        public void onDraw() {
+            if (mMagnifier != null) {
+                // Posting the method will ensure that updating the magnifier contents will
+                // happen right after the rendering of the current frame.
+                mTextView.post(mUpdateMagnifierRunnable);
+            }
+        }
+    };
 
     // Used to highlight a word when it is corrected by the IME
     private CorrectionHighlighter mCorrectionHighlighter;
@@ -415,15 +436,21 @@
         }
 
         final ViewTreeObserver observer = mTextView.getViewTreeObserver();
-        // No need to create the controller.
-        // The get method will add the listener on controller creation.
-        if (mInsertionPointCursorController != null) {
-            observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+        if (observer.isAlive()) {
+            // No need to create the controller.
+            // The get method will add the listener on controller creation.
+            if (mInsertionPointCursorController != null) {
+                observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+            }
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.resetTouchOffsets();
+                observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+            }
+            if (FLAG_USE_MAGNIFIER) {
+                observer.addOnDrawListener(mMagnifierOnDrawListener);
+            }
         }
-        if (mSelectionModifierCursorController != null) {
-            mSelectionModifierCursorController.resetTouchOffsets();
-            observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
-        }
+
         updateSpellCheckSpans(0, mTextView.getText().length(),
                 true /* create the spell checker if needed */);
 
@@ -472,6 +499,13 @@
             mSpellChecker = null;
         }
 
+        if (FLAG_USE_MAGNIFIER) {
+            final ViewTreeObserver observer = mTextView.getViewTreeObserver();
+            if (observer.isAlive()) {
+                observer.removeOnDrawListener(mMagnifierOnDrawListener);
+            }
+        }
+
         hideCursorAndSpanControllers();
         stopTextActionModeWithPreservingSelection();
     }
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index f6741c3..f79a7f5 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -18,34 +18,33 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UiThread;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.PixelCopy;
+import android.view.Surface;
+import android.view.SurfaceView;
 import android.view.View;
-import android.view.ViewRootImpl;
 import android.widget.ImageView;
 import android.widget.PopupWindow;
 
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
-import java.util.Timer;
-import java.util.TimerTask;
-
 /**
  * Android magnifier widget. Can be used by any view which is attached to window.
  */
 public final class Magnifier {
-    private static final String LOG_TAG = "magnifier";
-    private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
-    // The view for which this magnifier is attached.
+    // Use this to specify that a previous configuration value does not exist.
+    private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+    // The view to which this magnifier is attached.
     private final View mView;
     // The window containing the magnifier.
     private final PopupWindow mWindow;
@@ -64,8 +63,12 @@
     private final Handler mPixelCopyHandler = Handler.getMain();
     // Current magnification scale.
     private final float mZoomScale;
-    // Timer used to schedule the copy task.
-    private Timer mTimer;
+    // Variables holding previous states, used for detecting redundant calls and invalidation.
+    private final Point mPrevStartCoordsInSurface = new Point(
+            NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+    private final PointF mPrevPosInView = new PointF(
+            NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+    private final Rect mPixelCopyRequestRect = new Rect();
 
     /**
      * Initializes a magnifier.
@@ -91,8 +94,8 @@
         mWindow.setTouchable(false);
         mWindow.setBackgroundDrawable(null);
 
-        final int bitmapWidth = (int) (mWindowWidth / mZoomScale);
-        final int bitmapHeight = (int) (mWindowHeight / mZoomScale);
+        final int bitmapWidth = Math.round(mWindowWidth / mZoomScale);
+        final int bitmapHeight = Math.round(mWindowHeight / mZoomScale);
         mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
         getImageView().setImageBitmap(mBitmap);
     }
@@ -106,32 +109,29 @@
      *        relative to the view. The lower end is clamped to 0
      */
     public void show(@FloatRange(from=0) float xPosInView, @FloatRange(from=0) float yPosInView) {
-        if (xPosInView < 0) {
-            xPosInView = 0;
-        }
-
-        if (yPosInView < 0) {
-            yPosInView = 0;
-        }
+        xPosInView = Math.max(0, xPosInView);
+        yPosInView = Math.max(0, yPosInView);
 
         configureCoordinates(xPosInView, yPosInView);
 
-        if (mTimer == null) {
-            mTimer = new Timer();
-            mTimer.schedule(new TimerTask() {
-                @Override
-                public void run() {
-                    performPixelCopy();
-                }
-            }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
-        }
+        // Clamp startX value to avoid distorting the rendering of the magnifier content.
+        final int startX = Math.max(0, Math.min(
+                mCenterZoomCoords.x - mBitmap.getWidth() / 2,
+                mView.getWidth() - mBitmap.getWidth()));
+        final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
 
-        if (mWindow.isShowing()) {
-            mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
-                    mWindow.getHeight());
-        } else {
-            mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
-                    mWindowCoords.x, mWindowCoords.y);
+        if (startX != mPrevStartCoordsInSurface.x || startY != mPrevStartCoordsInSurface.y) {
+            performPixelCopy(startX, startY);
+
+            mPrevPosInView.x = xPosInView;
+            mPrevPosInView.y = yPosInView;
+
+            if (mWindow.isShowing()) {
+                mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
+                        mWindow.getHeight());
+            } else {
+                mWindow.showAtLocation(mView, Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y);
+            }
         }
     }
 
@@ -140,11 +140,18 @@
      */
     public void dismiss() {
         mWindow.dismiss();
+    }
 
-        if (mTimer != null) {
-            mTimer.cancel();
-            mTimer.purge();
-            mTimer = null;
+    /**
+     * Forces the magnifier to update its content. It uses the previous coordinates passed to
+     * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
+     *
+     * @hide
+     */
+    public void update() {
+        if (mWindow.isShowing()) {
+            // Update the contents shown in the magnifier.
+            performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y);
         }
     }
 
@@ -170,13 +177,22 @@
     }
 
     private void configureCoordinates(float xPosInView, float yPosInView) {
-        final int[] coordinatesOnScreen = new int[2];
-        mView.getLocationOnScreen(coordinatesOnScreen);
-        final float posXOnScreen = xPosInView + coordinatesOnScreen[0];
-        final float posYOnScreen = yPosInView + coordinatesOnScreen[1];
+        final float posX;
+        final float posY;
 
-        mCenterZoomCoords.x = (int) posXOnScreen;
-        mCenterZoomCoords.y = (int) posYOnScreen;
+        if (mView instanceof SurfaceView) {
+            // No offset required if the backing Surface matches the size of the SurfaceView.
+            posX = xPosInView;
+            posY = yPosInView;
+        } else {
+            final int[] coordinatesInSurface = new int[2];
+            mView.getLocationInSurface(coordinatesInSurface);
+            posX = xPosInView + coordinatesInSurface[0];
+            posY = yPosInView + coordinatesInSurface[1];
+        }
+
+        mCenterZoomCoords.x = Math.round(posX);
+        mCenterZoomCoords.y = Math.round(posY);
 
         final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
                 R.dimen.magnifier_offset);
@@ -184,34 +200,36 @@
         mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
     }
 
-    private void performPixelCopy() {
-        final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
-        int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
+    private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
+        final Surface surface = getValidViewSurface();
+        if (surface != null) {
+            mPixelCopyRequestRect.set(startXInSurface, startYInSurface,
+                    startXInSurface + mBitmap.getWidth(), startYInSurface + mBitmap.getHeight());
 
-        // Clamp startX value to avoid distorting the rendering of the magnifier content.
-        if (rawStartX < 0) {
-            rawStartX = 0;
-        } else if (rawStartX + mBitmap.getWidth() > mView.getWidth()) {
-            rawStartX = mView.getWidth() - mBitmap.getWidth();
-        }
-
-        final int startX = rawStartX;
-        final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
-
-        if (viewRootImpl != null && viewRootImpl.mSurface != null
-                && viewRootImpl.mSurface.isValid()) {
-            PixelCopy.request(
-                    viewRootImpl.mSurface,
-                    new Rect(startX, startY, startX + mBitmap.getWidth(),
-                            startY + mBitmap.getHeight()),
-                    mBitmap,
-                    result -> getImageView().invalidate(),
+            PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
+                    result -> {
+                        getImageView().invalidate();
+                        mPrevStartCoordsInSurface.x = startXInSurface;
+                        mPrevStartCoordsInSurface.y = startYInSurface;
+                    },
                     mPixelCopyHandler);
-        } else {
-            Log.d(LOG_TAG, "Could not perform PixelCopy request");
         }
     }
 
+    @Nullable
+    private Surface getValidViewSurface() {
+        final Surface surface;
+        if (mView instanceof SurfaceView) {
+            surface = ((SurfaceView) mView).getHolder().getSurface();
+        } else if (mView.getViewRootImpl() != null) {
+            surface = mView.getViewRootImpl().mSurface;
+        } else {
+            surface = null;
+        }
+
+        return (surface != null && surface.isValid()) ? surface : null;
+    }
+
     private ImageView getImageView() {
         return mWindow.getContentView().findViewById(R.id.magnifier_image);
     }
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 1a19a40..dd3e6f0 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,10 +44,10 @@
 
 struct NativeFamilyBuilder {
     NativeFamilyBuilder(uint32_t langId, int variant)
-        : langId(langId), variant(static_cast<minikin::FontVariant>(variant)),
+        : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
           allowUnsupportedFont(false) {}
     uint32_t langId;
-    minikin::FontVariant variant;
+    minikin::FontFamily::Variant variant;
     bool allowUnsupportedFont;
     std::vector<minikin::Font> fonts;
     std::vector<minikin::FontVariation> axes;
@@ -141,7 +141,7 @@
     }
 
     builder->fonts.push_back(minikin::Font(minikinFont,
-            minikin::FontStyle(weight, static_cast<minikin::FontSlant>(italic))));
+            minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
     builder->axes.clear();
     return true;
 }
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 5f32d37..1e7f5f5 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -581,7 +581,7 @@
         // restore the original settings.
         paint->setTextSkewX(saveSkewX);
         paint->setFakeBoldText(savefakeBold);
-        if (paint->getFontVariant() == minikin::FontVariant::ELEGANT) {
+        if (paint->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT) {
             SkScalar size = paint->getTextSize();
             metrics->fTop = -size * kElegantTop / 2048;
             metrics->fBottom = -size * kElegantBottom / 2048;
@@ -880,12 +880,13 @@
 
     static jboolean isElegantTextHeight(jlong paintHandle) {
         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        return obj->getFontVariant() == minikin::FontVariant::ELEGANT;
+        return obj->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT;
     }
 
     static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        obj->setFontVariant(aa ? minikin::FontVariant::ELEGANT : minikin::FontVariant::DEFAULT);
+        obj->setFamilyVariant(
+                aa ? minikin::FontFamily::Variant::ELEGANT : minikin::FontFamily::Variant::DEFAULT);
     }
 
     static jfloat getTextSize(jlong paintHandle) {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 3e4073f..d67c0b0 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -83,7 +83,7 @@
 
 static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
     Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
-    return face->fStyle.weight;
+    return face->fStyle.weight();
 }
 
 static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cfeba83..bb1bfad 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -311,6 +311,14 @@
     transaction->apply(sync);
 }
 
+static void nativeMergeTransaction(JNIEnv* env, jclass clazz,
+        jlong transactionObj, jlong otherTransactionObj) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    auto otherTransaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(
+            otherTransactionObj);
+    transaction->merge(std::move(*otherTransaction));
+}
+
 static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz, jlong transactionObj) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     transaction->setAnimationTransaction();
@@ -882,6 +890,8 @@
             (void*)nativeApplyTransaction },
     {"nativeGetNativeTransactionFinalizer", "()J",
             (void*)nativeGetNativeTransactionFinalizer },
+    {"nativeMergeTransaction", "(JJ)V",
+            (void*)nativeMergeTransaction },
     {"nativeSetAnimationTransaction", "(J)V",
             (void*)nativeSetAnimationTransaction },
     {"nativeSetLayer", "(JJI)V",
diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto
new file mode 100644
index 0000000..a8da0bf
--- /dev/null
+++ b/core/proto/android/os/cpufreq.proto
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuFreqProto";
+
+package android.os;
+
+// cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
+message CpuFreq {
+
+    optional int32 jiffy_hz = 1; // obtain by system config.
+
+    repeated CpuFreqStats cpu_freqs = 2;
+}
+
+// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
+message CpuFreqStats {
+
+    optional string cpu_name = 1;
+
+    message TimeInState {
+        optional int32 state_khz = 1;  // cpu frequency
+        optional int64 time_jiffy = 2; // number of jiffies
+    }
+    repeated TimeInState times = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 6c5206c..ecdabcf 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
+import "frameworks/base/core/proto/android/os/cpufreq.proto";
 import "frameworks/base/core/proto/android/os/cpuinfo.proto";
 import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -78,6 +79,10 @@
         (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
     ];
 
+    optional CpuFreq cpu_freq = 2004 [
+        (section).type = SECTION_FILE,
+        (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
+    ];
 
     // System Services
     optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 81164d5..76eb4e6 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -119,6 +119,22 @@
 CHECK_EMOJI := true
 endif
 
+fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp
+
 .PHONY: fontchain_lint
-fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img
+fontchain_lint: $(fontchain_lint_timestamp)
+
+fontchain_lint_deps := \
+    external/unicode/DerivedAge.txt \
+    external/unicode/emoji-data.txt \
+    external/unicode/emoji-sequences.txt \
+    external/unicode/emoji-variation-sequences.txt \
+    external/unicode/emoji-zwj-sequences.txt \
+    external/unicode/additions/emoji-data.txt \
+    external/unicode/additions/emoji-sequences.txt \
+    external/unicode/additions/emoji-zwj-sequences.txt \
+
+$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps)
+	@echo Running fontchain lint
 	$(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+	touch $@
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 90fe0b7..bad766c 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -29,12 +29,6 @@
 minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
                                                         const Typeface* typeface) {
     const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
-    minikin::FontStyle resolved = resolvedFace->fStyle;
-
-    const minikin::FontVariant minikinVariant =
-            (paint->getFontVariant() == minikin::FontVariant::ELEGANT)
-                    ? minikin::FontVariant::ELEGANT
-                    : minikin::FontVariant::COMPACT;
 
     minikin::MinikinPaint minikinPaint;
     /* Prepare minikin Paint */
@@ -46,7 +40,8 @@
     minikinPaint.wordSpacing = paint->getWordSpacing();
     minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
     minikinPaint.localeListId = paint->getMinikinLocaleListId();
-    minikinPaint.fontStyle = minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant);
+    minikinPaint.familyVariant = paint->getFamilyVariant();
+    minikinPaint.fontStyle = resolvedFace->fStyle;
     minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
     minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
     return minikinPaint;
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 76beb11..002f759 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -73,9 +73,9 @@
 
     uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; }
 
-    void setFontVariant(minikin::FontVariant variant) { mFontVariant = variant; }
+    void setFamilyVariant(minikin::FontFamily::Variant variant) { mFamilyVariant = variant; }
 
-    minikin::FontVariant getFontVariant() const { return mFontVariant; }
+    minikin::FontFamily::Variant getFamilyVariant() const { return mFamilyVariant; }
 
     void setHyphenEdit(uint32_t hyphen) { mHyphenEdit = hyphen; }
 
@@ -90,7 +90,7 @@
     float mWordSpacing = 0;
     std::string mFontFeatureSettings;
     uint32_t mMinikinLocaleListId;
-    minikin::FontVariant mFontVariant;
+    minikin::FontFamily::Variant mFamilyVariant;
     uint32_t mHyphenEdit = 0;
     // The native Typeface object has the same lifetime of the Java Typeface
     // object. The Java Paint object holds a strong reference to the Java Typeface
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 94492c5..ae9c475 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -24,7 +24,7 @@
         , mWordSpacing(0)
         , mFontFeatureSettings()
         , mMinikinLocaleListId(0)
-        , mFontVariant(minikin::FontVariant::DEFAULT) {}
+        , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
 
 Paint::Paint(const Paint& paint)
         : SkPaint(paint)
@@ -32,7 +32,7 @@
         , mWordSpacing(paint.mWordSpacing)
         , mFontFeatureSettings(paint.mFontFeatureSettings)
         , mMinikinLocaleListId(paint.mMinikinLocaleListId)
-        , mFontVariant(paint.mFontVariant)
+        , mFamilyVariant(paint.mFamilyVariant)
         , mHyphenEdit(paint.mHyphenEdit)
         , mTypeface(paint.mTypeface) {}
 
@@ -42,7 +42,7 @@
         , mWordSpacing(0)
         , mFontFeatureSettings()
         , mMinikinLocaleListId(0)
-        , mFontVariant(minikin::FontVariant::DEFAULT) {}
+        , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
 
 Paint::~Paint() {}
 
@@ -52,7 +52,7 @@
     mWordSpacing = other.mWordSpacing;
     mFontFeatureSettings = other.mFontFeatureSettings;
     mMinikinLocaleListId = other.mMinikinLocaleListId;
-    mFontVariant = other.mFontVariant;
+    mFamilyVariant = other.mFamilyVariant;
     mHyphenEdit = other.mHyphenEdit;
     mTypeface = other.mTypeface;
     return *this;
@@ -62,7 +62,8 @@
     return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) &&
            a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing &&
            a.mFontFeatureSettings == b.mFontFeatureSettings &&
-           a.mMinikinLocaleListId == b.mMinikinLocaleListId && a.mFontVariant == b.mFontVariant &&
-           a.mHyphenEdit == b.mHyphenEdit && a.mTypeface == b.mTypeface;
+           a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
+           a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
+           a.mTypeface == b.mTypeface;
 }
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index e527adc..ebc14c8 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -45,7 +45,7 @@
 
 static minikin::FontStyle computeMinikinStyle(int weight, bool italic) {
     return minikin::FontStyle(uirenderer::MathUtils::clamp(weight, 1, 1000),
-                              static_cast<minikin::FontSlant>(italic));
+                              static_cast<minikin::FontStyle::Slant>(italic));
 }
 
 // Resolve the relative weight from the baseWeight and target style.
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 1fcc028..66d6f52 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -81,39 +81,39 @@
 
 TEST(TypefaceTest, createWithDifferentBaseWeight) {
     std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle);
 
     std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
-    EXPECT_EQ(300, light->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, light->fStyle.slant);
+    EXPECT_EQ(300, light->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, light->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, light->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_fromRegular) {
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(Typeface::createRelative(nullptr, Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -123,30 +123,30 @@
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(700, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(700, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(1000, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(1000, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(700, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(700, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"),
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(1000, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(1000, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -156,30 +156,30 @@
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(300, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(300, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(600, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(600, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.ITLIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(300, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(300, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"),
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(600, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(600, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -189,22 +189,22 @@
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
     // Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
     // Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -212,8 +212,8 @@
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -224,23 +224,23 @@
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
     // Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT,
     // Typeface.ITALIC), Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
     // Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -248,8 +248,8 @@
     // Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -261,8 +261,8 @@
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.NORMAL);
     std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
-    EXPECT_EQ(400, normal->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+    EXPECT_EQ(400, normal->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java,
@@ -270,8 +270,8 @@
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.BOLD);
     std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
@@ -279,8 +279,8 @@
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.ITALIC);
     std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -289,8 +289,8 @@
     // Typeface.create(typeface, Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createRelative(base.get(), Typeface::kBoldItalic));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
@@ -300,8 +300,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false)
     //     .build();
     std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
-    EXPECT_EQ(400, regular->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+    EXPECT_EQ(400, regular->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java,
@@ -309,8 +309,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
     //     .build();
     std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
@@ -318,8 +318,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
     //     .build();
     std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -327,8 +327,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
     //     .build();
     std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 
     // In Java,
@@ -336,8 +336,8 @@
     // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
     //     .build();
     std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
-    EXPECT_EQ(1000, over1000->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+    EXPECT_EQ(1000, over1000->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
 }
 
@@ -346,24 +346,24 @@
     // Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
     std::unique_ptr<Typeface> regular(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
-    EXPECT_EQ(400, regular->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+    EXPECT_EQ(400, regular->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java, new
     // Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
     std::unique_ptr<Typeface> bold(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, new
     // Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
     std::unique_ptr<Typeface> italic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -371,8 +371,8 @@
     // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
@@ -380,8 +380,8 @@
     // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
     std::unique_ptr<Typeface> over1000(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
-    EXPECT_EQ(1000, over1000->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+    EXPECT_EQ(1000, over1000->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
 }
 
@@ -389,30 +389,30 @@
     // In Java, new Typeface.Builder("Roboto-Regular.ttf").build();
     std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
             makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(400, regular->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+    EXPECT_EQ(400, regular->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
     EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
     std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
             makeSingleFamlyVector(kRobotoBold), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(700, bold->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+    EXPECT_EQ(700, bold->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
     EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
     std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
             makeSingleFamlyVector(kRobotoItalic), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(400, italic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+    EXPECT_EQ(400, italic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
     std::unique_ptr<Typeface> boldItalic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic),
                                          RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(700, boldItalic->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+    EXPECT_EQ(700, boldItalic->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
     EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 }
 
@@ -422,8 +422,8 @@
             buildFamily(kRobotoBoldItalic)};
     std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
             std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(400, typeface->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+    EXPECT_EQ(400, typeface->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
 }
 
 TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
@@ -431,8 +431,8 @@
             buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)};
     std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
             std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
-    EXPECT_EQ(700, typeface->fStyle.weight);
-    EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+    EXPECT_EQ(700, typeface->fStyle.weight());
+    EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
 }
 
 }  // namespace
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ec34253..e0a979d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -24,7 +24,7 @@
     <string name="wifi_security_none" msgid="7985461072596594400">"لا شيء"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"معطلة"</string>
-    <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏أخفقت تهيئة عنوان IP"</string>
+    <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏تعذّرت تهيئة عنوان IP"</string>
     <string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string>
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏تعذّر اتصال WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index bb5d2d8..1b78d30 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un NIP ou d\'une clé d\'accès incorrects."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Association refusée par <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordinateur"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Écouteurs"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Téléphone"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imagerie"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Écouteurs"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Périphérique d\'entrée"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi désactivé."</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi déconnecté."</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi : une barre."</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec audio Bluetooth LDAC : qualité de lecture"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Sélectionner le codec audio Bluetooth LDAC :\nQualité de lecture"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Diffusion : <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"DNS par TLS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"Si cette option est activée, essayez le DNS par TLS sur le port 853."</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionnez le mode DNS privé"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportuniste"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Entrez le nom d\'hôte du fournisseur DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si cette option est activée, le passage du Wi-Fi aux données cellulaires est forcé lorsque le signal Wi-Fi est faible"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 308e97d8..b66f95f 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -48,7 +48,7 @@
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
     <string name="speed_label_medium" msgid="3175763313268941953">"普通"</string>
     <string name="speed_label_fast" msgid="7715732164050975057">"速い"</string>
-    <string name="speed_label_very_fast" msgid="2265363430784523409">"とても速い"</string>
+    <string name="speed_label_very_fast" msgid="2265363430784523409">"非常に速い"</string>
     <string name="preference_summary_default_combination" msgid="8532964268242666060">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="6557104142667339895">"切断"</string>
     <string name="bluetooth_disconnecting" msgid="8913264760027764974">"切断中..."</string>
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PINまたはパスキーが正しくないため、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>をペアに設定できませんでした。"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>と通信できません。"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ペア設定が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に拒否されました。"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"コンピュータ"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ヘッドセット"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"スマートフォン"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"画像"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ヘッドフォン"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"入力用周辺機器"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-FiはOFFです。"</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fiが切断されました。"</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fiはレベル1です。"</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth オーディオ LDAC コーデック: 再生音質"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth オーディオ LDAC コーデックを選択:\n再生音質"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ストリーミング: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"この設定を有効にした場合、ポート 853 で DNS over TLS を試行します。"</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"プライベート DNS"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"プライベート DNS モードを選択"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"OFF"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"日和見"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"プライベート DNS プロバイダのホスト名"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS プロバイダのホスト名を入力"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fiログレベルを上げて、Wi-Fi選択ツールでSSID RSSIごとに表示します"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ON にすると、Wi-Fi の電波強度が弱い場合は強制的にモバイルデータ接続に切り替わるようになります"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 4e3bc33..5a9bbc5 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN 또는 패스키가 잘못되어 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 페어링하지 못했습니다."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 통신할 수 없습니다."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 페어링을 거부했습니다."</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"컴퓨터"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"헤드셋"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"전화"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"이미징"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"헤드폰"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"입력 주변기기"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"블루투스"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi가 꺼져 있습니다."</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 연결이 끊어졌습니다."</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 신호 막대가 한 개입니다."</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"블루투스 오디오 LDAC 코덱: 재생 품질"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"블루투스 오디오 LDAC 코덱 선택:\n재생 품질"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"스트리밍: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"TLS를 통한 DNS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"사용 설정한 경우, 포트 853에서 TLS를 통해 DNS를 시도합니다."</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"비공개 DNS"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"비공개 DNS 모드 선택"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"사용 안함"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"기회주의적"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"비공개 DNS 제공업체 호스트 이름"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS 제공업체의 호스트 이름 입력"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시합니다."</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"사용 설정하면 Wi-Fi 신호가 약할 때 데이터 연결을 Wi-Fi에서 모바일 네트워크로 더욱 적극적으로 핸드오버합니다."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index a1beef6..2b31bf1 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -316,7 +316,7 @@
     <string name="enable_freeform_support_summary" msgid="8247310463288834487">"Ruhusu uwezo wa kutumia madirisha ya majaribio yenye muundo huru."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
-    <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gonga ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
+    <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
     <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nenosiri jipya la hifadhi rudufu limewekwa"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
@@ -331,8 +331,8 @@
     <item msgid="5363960654009010371">"Rangi zilizoboreshwa kwa ajili ya maudhui dijitali"</item>
   </string-array>
     <string name="inactive_apps_title" msgid="1317817863508274533">"Programu zilizozimwa"</string>
-    <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gonga ili ugeuze."</string>
-    <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gonga ili ugeuze."</string>
+    <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gusa ili ugeuze."</string>
+    <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gusa ili ugeuze."</string>
     <string name="runningservices_settings_title" msgid="8097287939865165213">"Huduma zinazoendeshwa"</string>
     <string name="runningservices_settings_summary" msgid="854608995821032748">"Onyesha na dhibiti huduma zinazoendeshwa kwa sasa"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5ff39b6..2db6178 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ไม่สามารถจับคู่กับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ได้เพราะ PIN หรือรหัสผ่านไม่ถูกต้อง"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ไม่สามารถเชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ปฏิเสธการจับคู่อุปกรณ์"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"คอมพิวเตอร์"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ชุดหูฟัง"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"โทรศัพท์"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"การถ่ายภาพ"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"หูฟัง"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"อุปกรณ์อินพุต"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"บลูทูธ"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ปิดอยู่"</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"สัญญาณ Wi-Fi 1 ขีด"</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"เลือกตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC:\nคุณภาพการเล่น"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"สตรีมมิง: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"DNS ผ่าน TLS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"หากเปิดใช้แล้ว ให้ลองใช้ DNS ผ่าน TLS บนพอร์ต 853"</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS ส่วนตัว"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"เลือกโหมด DNS ส่วนตัว"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"ปิด"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ที่ใช้โอกาส"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"ชื่อโฮสต์ของผู้ให้บริการ DNS ส่วนตัว"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ป้อนชื่อโฮสต์ของผู้ให้บริการ DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"เมื่อเปิดใช้แล้ว Wi-Fi จะส่งผ่านการเชื่อมต่อข้อมูลไปยังเครือข่ายมือถือเมื่อสัญญาณ Wi-Fi อ่อน"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2554388..2215fe5 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -99,6 +99,13 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
+    <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"電腦"</string>
+    <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"免持耳機"</string>
+    <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"電話"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"顯像裝置"</string>
+    <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"頭戴式耳機"</string>
+    <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"周邊輸入裝置"</string>
+    <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"藍牙"</string>
     <string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 連線已中斷。"</string>
     <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 訊號強度一格。"</string>
@@ -209,8 +216,12 @@
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"藍牙音訊 LDAC 轉碼器:播放品質"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"選取藍牙音訊 LDAC 轉碼器:\n播放品質"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"串流中:<xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
-    <string name="dns_tls" msgid="6773814174391131955">"透過傳輸層安全標準 (TLS) 執行 DNS"</string>
-    <string name="dns_tls_summary" msgid="3692494150251071380">"如果啟用這個選項,即可在通訊埠 853 上嘗試透過傳輸層安全標準 (TLS) 執行 DNS。"</string>
+    <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"私人 DNS"</string>
+    <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"選取私人 DNS 模式"</string>
+    <string name="private_dns_mode_off" msgid="8236575187318721684">"停用"</string>
+    <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"隨機"</string>
+    <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 供應商主機名稱"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商的主機名稱"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"啟用時,Wi-Fi 連線在訊號不穩的情況下會更積極轉換成行動數據連線"</string>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 3f3dd2a..e4dcbee 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -24,10 +24,10 @@
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"Tafadhali subiri…"</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Tutatuma ripoti ya hitilafu kwenye simu baada ya muda mfupi"</string>
     <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Chagua kushiriki ripoti ya hitilafu"</string>
-    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gonga ili ushiriki ripoti yako ya hitilafu"</string>
+    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Chagua kushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
     <string name="bugreport_confirm" msgid="5917407234515812495">"Ripoti za hitilafu zinajumuisha data kutoka faili za kumbukumbu mbalimbali zilizo kwenye mfumo, ambazo huenda zinajumuisha data ambayo unachukulia kuwa nyeti (kama vile matumizi ya programu na maelezo kuhusu data ilipo). Shiriki ripoti za hitilafu na watu na programu unazoamini pekee."</string>
     <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Usionyeshe tena"</string>
     <string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index b585bdf..f451fda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
@@ -75,17 +76,20 @@
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
     private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+    private final OnSettingsClickListener mOnSettingsClickListener;
     private String mKeyToRemoveOnGutsClosed;
 
     public NotificationGutsManager(
             NotificationPresenter presenter,
             NotificationStackScrollLayout stackScroller,
             NotificationInfo.CheckSaveListener checkSaveListener,
-            Context context) {
+            Context context,
+            OnSettingsClickListener onSettingsClickListener) {
         mPresenter = presenter;
         mStackScroller = stackScroller;
         mCheckSaveListener = checkSaveListener;
         mContext = context;
+        mOnSettingsClickListener = onSettingsClickListener;
         Resources res = context.getResources();
 
         mNonBlockablePkgs = new HashSet<>();
@@ -189,6 +193,7 @@
                 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
                     mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
                     guts.resetFalsingCheck();
+                    mOnSettingsClickListener.onClick(sbn.getKey());
                     startAppNotificationSettingsActivity(pkg, appUid, channel);
                 };
             }
@@ -352,4 +357,8 @@
         pw.print("mKeyToRemoveOnGutsClosed: ");
         pw.println(mKeyToRemoveOnGutsClosed);
     }
+
+    public interface OnSettingsClickListener {
+        void onClick(String key);
+    }
 }
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 6775615..1bbb94a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -43,6 +43,7 @@
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -919,7 +920,14 @@
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
         mGutsManager = new NotificationGutsManager(this, mStackScroller,
-                mCheckSaveListener, mContext);
+                mCheckSaveListener, mContext,
+                key -> {
+                    try {
+                        mBarService.onNotificationSettingsViewed(key);
+                    } catch (RemoteException e) {
+                        // if we're here we're dead
+                    }
+                });
         mNotificationPanel.setStatusBar(this);
         mNotificationPanel.setGroupManager(mGroupManager);
         mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1470,6 +1478,11 @@
                         }
                     }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
                 }
+                try {
+                    mBarService.onNotificationDirectReplied(entry.key);
+                } catch (RemoteException e) {
+                    // system process is dead if we're here.
+                }
             }
         });
 
@@ -1785,9 +1798,14 @@
         final int id = n.getId();
         final int userId = n.getUserId();
         try {
-            // TODO: record actual dismissal surface
+            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+            if (isHeadsUp(n.getKey())) {
+                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+            } else if (mStackScroller.hasPulsingNotifications()) {
+                dismissalSurface = NotificationStats.DISMISSAL_AOD;
+            }
             mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
-                    NotificationStats.DISMISSAL_OTHER);
+                    dismissalSurface);
             if (FORCE_REMOTE_INPUT_HISTORY
                     && mKeysKeptForRemoteInput.contains(n.getKey())) {
                 mKeysKeptForRemoteInput.remove(n.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4b8f581..0d41e20 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -207,6 +207,7 @@
         } else {
             addExistingRows();
         }
+        updateRowsH(getActiveRow());
     }
 
     private ColorStateList loadColorStateList(int colorResId) {
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 633bb3e..3d81baf 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -43,7 +43,8 @@
     android.hardware.oemlock-V1.0-java \
     android.hardware.tetheroffload.control-V1.0-java \
     android.hardware.vibrator-V1.0-java \
-    android.hardware.configstore-V1.0-java
+    android.hardware.configstore-V1.0-java \
+    android.hardware.contexthub-V1.0-java
 
 ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/Tuner.java
index e6ae320..2ea4271 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/Tuner.java
@@ -27,8 +27,10 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 class Tuner extends ITuner.Stub {
     private static final String TAG = "BroadcastRadioService.Tuner";
@@ -96,6 +98,10 @@
     private native boolean nativeIsAnalogForced(long nativeContext);
     private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
 
+    private native Map<String, String> nativeSetParameters(long nativeContext,
+            Map<String, String> parameters);
+    private native Map<String, String> nativeGetParameters(long nativeContext, List<String> keys);
+
     private native boolean nativeIsAntennaConnected(long nativeContext);
 
     @Override
@@ -273,6 +279,31 @@
     }
 
     @Override
+    public Map setParameters(Map parameters) {
+        Map<String, String> results;
+        synchronized (mLock) {
+            checkNotClosedLocked();
+            results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
+        }
+        if (results == null) return Collections.emptyMap();
+        return results;
+    }
+
+    @Override
+    public Map getParameters(List<String> keys) {
+        if (keys == null) {
+            throw new IllegalArgumentException("The argument must not be a null pointer");
+        }
+        Map<String, String> results;
+        synchronized (mLock) {
+            checkNotClosedLocked();
+            results = nativeGetParameters(mNativeContext, keys);
+        }
+        if (results == null) return Collections.emptyMap();
+        return results;
+    }
+
+    @Override
     public boolean isAntennaConnected() {
         synchronized (mLock) {
             checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
index a87ae8d..2460c67 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
@@ -26,6 +26,9 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.util.List;
+import java.util.Map;
+
 class TunerCallback implements ITunerCallback {
     private static final String TAG = "BroadcastRadioService.TunerCallback";
 
@@ -121,6 +124,11 @@
     }
 
     @Override
+    public void onParametersUpdated(Map parameters) {
+        dispatch(() -> mClientCallback.onParametersUpdated(parameters));
+    }
+
+    @Override
     public IBinder asBinder() {
         throw new RuntimeException("Not a binder");
     }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 5e9f355..da481a8 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -18,15 +18,25 @@
 
 import android.Manifest;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.contexthub.V1_0.AsyncEventType;
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.IContexthubCallback;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
 import android.hardware.location.ContextHubInfo;
-import android.hardware.location.ContextHubManager;
 import android.hardware.location.ContextHubMessage;
-import android.hardware.location.IContextHubService;
 import android.hardware.location.IContextHubCallback;
-import android.hardware.location.NanoAppFilter;
+import android.hardware.location.IContextHubService;
+import android.hardware.location.IContextHubTransactionCallback;
 import android.hardware.location.NanoApp;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppFilter;
 import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppState;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Log;
@@ -38,8 +48,10 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.HashMap;
 
 /**
  * @hide
@@ -48,55 +60,152 @@
     private static final String TAG = "ContextHubService";
     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
-        + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
-    public static final int ANY_HUB             = -1;
-    public static final int MSG_LOAD_NANO_APP   = 3;
+    /*
+     * Constants for the type of transaction that is defined by ContextHubService.
+     * This is used to report the transaction callback to clients, and is different from
+     * ContextHubTransaction.Type.
+     */
+    public static final int MSG_ENABLE_NANO_APP = 1;
+    public static final int MSG_DISABLE_NANO_APP = 2;
+    public static final int MSG_LOAD_NANO_APP = 3;
     public static final int MSG_UNLOAD_NANO_APP = 4;
+    public static final int MSG_QUERY_NANO_APPS = 5;
+    public static final int MSG_QUERY_MEMORY = 6;
+    public static final int MSG_HUB_RESET = 7;
 
     private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
     private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
     private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
     private static final int PRE_LOADED_APP_MEM_REQ = 0;
 
-    private static final int MSG_HEADER_SIZE = 4;
-    private static final int HEADER_FIELD_MSG_TYPE = 0;
-    private static final int HEADER_FIELD_MSG_VERSION = 1;
-    private static final int HEADER_FIELD_HUB_HANDLE = 2;
-    private static final int HEADER_FIELD_APP_INSTANCE = 3;
-
-    private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
-    private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
-    private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2;
-
     private static final int OS_APP_INSTANCE = -1;
 
     private final Context mContext;
+
+    // TODO(b/69270990): Remove once old ContextHubManager API is deprecated
+    // Service cache maintaining of instance ID to nanoapp infos
     private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
             new ConcurrentHashMap<>();
+    // The next available instance ID (managed by the service) to assign to a nanoapp
+    private int mNextAvailableInstanceId = 0;
+    // A map of the long nanoapp ID to instance ID managed by the service
+    private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap =
+            new ConcurrentHashMap<>();
+
     private final ContextHubInfo[] mContextHubInfo;
     private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
             new RemoteCallbackList<>();
 
-    private native int nativeSendMessage(int[] header, byte[] data);
-    private native ContextHubInfo[] nativeInitialize();
+    // Proxy object to communicate with the Context Hub HAL
+    private final IContexthub mContextHubProxy;
+
+    // The manager for transaction queue
+    private final ContextHubTransactionManager mTransactionManager;
+
+    /**
+     * Class extending the callback to register with a Context Hub.
+     */
+    private class ContextHubServiceCallback extends IContexthubCallback.Stub {
+        private final int mContextHubId;
+
+        ContextHubServiceCallback(int contextHubId) {
+            mContextHubId = contextHubId;
+        }
+
+        @Override
+        public void handleClientMsg(ContextHubMsg message) {
+            handleClientMessageCallback(mContextHubId, message);
+        }
+
+        @Override
+        public void handleTxnResult(int transactionId, int result) {
+            handleTransactionResultCallback(mContextHubId, transactionId, result);
+        }
+
+        @Override
+        public void handleHubEvent(int eventType) {
+            handleHubEventCallback(mContextHubId, eventType);
+        }
+
+        @Override
+        public void handleAppAbort(long nanoAppId, int abortCode) {
+            handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
+        }
+
+        @Override
+        public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) {
+            handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
+        }
+    }
 
     public ContextHubService(Context context) {
         mContext = context;
-        mContextHubInfo = nativeInitialize();
+
+        mContextHubProxy = getContextHubProxy();
+        if (mContextHubProxy == null) {
+            mTransactionManager = null;
+            mContextHubInfo = new ContextHubInfo[0];
+            return;
+        }
+
+        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+
+        List<ContextHub> hubList;
+        try {
+            hubList = mContextHubProxy.getHubs();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while getting Context Hub info");
+            hubList = Collections.emptyList();
+        }
+        mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
+
+        for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+            int contextHubId = contextHubInfo.getId();
+            try {
+                mContextHubProxy.registerCallback(
+                        contextHubId, new ContextHubServiceCallback(contextHubId));
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
+                        + contextHubId + ")");
+            }
+        }
+
+        // Do a query to initialize the service cache list of nanoapps
+        // TODO(b/69270990): Remove this when old API is deprecated
+        for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+            queryNanoAppsInternal(contextHubInfo.getId());
+        }
 
         for (int i = 0; i < mContextHubInfo.length; i++) {
             Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
-                  + ", name:  " + mContextHubInfo[i].getName());
+                    + ", name:  " + mContextHubInfo[i].getName());
         }
     }
 
+    /**
+     * @return the IContexthub proxy interface
+     */
+    private IContexthub getContextHubProxy() {
+        IContexthub proxy = null;
+        try {
+            proxy = IContexthub.getService(true /* retry */);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+        } catch (NoSuchElementException e) {
+            Log.i(TAG, "Context Hub HAL service not found");
+        }
+
+        return proxy;
+    }
+
     @Override
     public int registerCallback(IContextHubCallback callback) throws RemoteException {
         checkPermissions();
         mCallbacksList.register(callback);
         Log.d(TAG, "Added callback, total callbacks " +
-              mCallbacksList.getRegisteredCallbackCount());
+                mCallbacksList.getRegisteredCallbackCount());
         return 0;
     }
 
@@ -109,29 +218,112 @@
         for (int i = 0; i < returnArray.length; ++i) {
             returnArray[i] = i;
             Log.d(TAG, String.format("Hub %s is mapped to %d",
-                                     mContextHubInfo[i].getName(), returnArray[i]));
+                    mContextHubInfo[i].getName(), returnArray[i]));
         }
 
         return returnArray;
     }
 
     @Override
-    public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+    public ContextHubInfo getContextHubInfo(int contextHubId) throws RemoteException {
         checkPermissions();
-        if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
-            Log.e(TAG, "Invalid context hub handle " + contextHubHandle);
+        if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+            Log.e(TAG, "Invalid context hub handle " + contextHubId);
             return null; // null means fail
         }
 
-        return mContextHubInfo[contextHubHandle];
+        return mContextHubInfo[contextHubId];
+    }
+
+    /**
+     * Creates an internal load transaction callback to be used for old API clients
+     *
+     * @param contextHubId  the ID of the hub to load the binary
+     * @param nanoAppBinary the binary to load
+     * @return the callback interface
+     */
+    private IContextHubTransactionCallback createLoadTransactionCallback(
+            int contextHubId, NanoAppBinary nanoAppBinary) {
+        return new IContextHubTransactionCallback.Stub() {
+            @Override
+            public void onTransactionComplete(int result) {
+                handleLoadResponseOldApi(contextHubId, result, nanoAppBinary);
+            }
+
+            @Override
+            public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+            }
+        };
+    }
+
+    /**
+     * Creates an internal unload transaction callback to be used for old API clients
+     *
+     * @param contextHubId the ID of the hub to unload the nanoapp
+     * @param nanoAppId    the ID of the nanoapp to unload
+     * @return the callback interface
+     */
+    private IContextHubTransactionCallback createUnloadTransactionCallback(
+            int contextHubId, long nanoAppId) {
+        return new IContextHubTransactionCallback.Stub() {
+            @Override
+            public void onTransactionComplete(int result) {
+                handleUnloadResponseOldApi(contextHubId, result, nanoAppId);
+            }
+
+            @Override
+            public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+            }
+        };
+    }
+
+    /**
+     * Creates an internal query transaction callback to be used for old API clients
+     *
+     * @param contextHubId the ID of the hub to query
+     * @return the callback interface
+     */
+    private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) {
+        return new IContextHubTransactionCallback.Stub() {
+            @Override
+            public void onTransactionComplete(int result) {
+            }
+
+            @Override
+            public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+                byte[] data = {(byte) result};
+                onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+            }
+        };
+    }
+
+    /**
+     * Adds a new transaction to the transaction manager queue
+     *
+     * @param transaction the transaction to add
+     * @return the result of adding the transaction
+     */
+    private int addTransaction(ContextHubServiceTransaction transaction) {
+        int result = Result.OK;
+        try {
+            mTransactionManager.addTransaction(transaction);
+        } catch (IllegalStateException e) {
+            Log.e(TAG, e.getMessage());
+            result = Result.TRANSACTION_PENDING; /* failed */
+        }
+
+        return result;
     }
 
     @Override
-    public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
+    public int loadNanoApp(int contextHubId, NanoApp app) throws RemoteException {
         checkPermissions();
+        if (mContextHubProxy == null) {
+            return -1;
+        }
 
-        if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
-            Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle);
+        if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+            Log.e(TAG, "Invalid contextHubhandle " + contextHubId);
             return -1;
         }
         if (app == null) {
@@ -139,20 +331,17 @@
             return -1;
         }
 
-        int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE];
-        msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle;
-        msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
-        msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
-        msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
+        // Create an internal IContextHubTransactionCallback for the old API clients
+        NanoAppBinary nanoAppBinary = new NanoAppBinary(app.getAppBinary());
+        IContextHubTransactionCallback onCompleteCallback =
+                createLoadTransactionCallback(contextHubId, nanoAppBinary);
 
-        long appId = app.getAppId();
+        ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
+                contextHubId, nanoAppBinary, onCompleteCallback);
 
-        msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);
-        msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF);
-
-        int errVal = nativeSendMessage(msgHeader, app.getAppBinary());
-        if (errVal != 0) {
-            Log.e(TAG, "Send Message returns error" + contextHubHandle);
+        int result = addTransaction(transaction);
+        if (result != Result.OK) {
+            Log.e(TAG, "Failed to load nanoapp with error code " + result);
             return -1;
         }
 
@@ -163,23 +352,26 @@
     @Override
     public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException {
         checkPermissions();
+        if (mContextHubProxy == null) {
+            return -1;
+        }
+
         NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
         if (info == null) {
             Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle);
             return -1; //means failed
         }
 
-        // Call Native interface here
-        int[] msgHeader = new int[MSG_HEADER_SIZE];
-        msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB;
-        msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle;
-        msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
-        msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP;
+        int contextHubId = info.getContexthubId();
+        long nanoAppId = info.getAppId();
+        IContextHubTransactionCallback onCompleteCallback =
+                createUnloadTransactionCallback(contextHubId, nanoAppId);
+        ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
+                contextHubId, nanoAppId, onCompleteCallback);
 
-        byte msg[] = new byte[0];
-
-        if (nativeSendMessage(msgHeader, msg) != 0) {
-            Log.e(TAG, "native send message fails");
+        int result = addTransaction(transaction);
+        if (result != Result.OK) {
+            Log.e(TAG, "Failed to unload nanoapp with error code " + result);
             return -1;
         }
 
@@ -189,7 +381,7 @@
 
     @Override
     public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
-                                                      throws RemoteException {
+            throws RemoteException {
         checkPermissions();
         // This assumes that all the nanoAppInfo is current. This is reasonable
         // for the use cases for tightly controlled nanoApps.
@@ -206,7 +398,7 @@
         checkPermissions();
         ArrayList<Integer> foundInstances = new ArrayList<Integer>();
 
-        for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+        for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
             NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
 
             if (filter.testMatch(info)) {
@@ -223,23 +415,230 @@
         return retArray;
     }
 
-    @Override
-    public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
-                           throws RemoteException {
-        checkPermissions();
+    /**
+     * Performs a query at the specified hub.
+     *
+     * This method should only be invoked internally by the service, either to update the service
+     * cache or as a result of an explicit query requested by a client through the sendMessage API.
+     *
+     * @param contextHubId the ID of the hub to do the query
+     * @return the result of the query
+     */
+    private int queryNanoAppsInternal(int contextHubId) {
+        if (mContextHubProxy == null) {
+            return Result.UNKNOWN_FAILURE;
+        }
 
-        if (msg == null || msg.getData() == null) {
-            Log.w(TAG, "null ptr");
+        IContextHubTransactionCallback onCompleteCallback =
+                createQueryTransactionCallback(contextHubId);
+        ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+                contextHubId, onCompleteCallback);
+
+        return addTransaction(transaction);
+    }
+
+    @Override
+    public int sendMessage(
+            int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException {
+        checkPermissions();
+        if (mContextHubProxy == null) {
+            return -1;
+        }
+        if (msg == null) {
+            Log.e(TAG, "ContextHubMessage cannot be null");
+            return -1;
+        }
+        if (msg.getData() == null) {
+            Log.w(TAG, "ContextHubMessage message body cannot be null");
             return -1;
         }
 
-        int[] msgHeader = new int[MSG_HEADER_SIZE];
-        msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle;
-        msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle;
-        msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion();
-        msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType();
+        int result;
+        if (nanoAppHandle == OS_APP_INSTANCE) {
+            if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
+                result = queryNanoAppsInternal(hubHandle);
+            } else {
+                Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
+                result = Result.BAD_PARAMS;
+            }
+        } else {
+            NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
+            if (info != null) {
+                ContextHubMsg hubMessage = new ContextHubMsg();
+                hubMessage.appName = info.getAppId();
+                hubMessage.msgType = msg.getMsgType();
+                hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
+                ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
 
-        return nativeSendMessage(msgHeader, msg.getData());
+                try {
+                    result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to send nanoapp message - RemoteException");
+                    result = Result.UNKNOWN_FAILURE;
+                }
+            } else {
+                Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
+                        + nanoAppHandle + " does not exist.");
+                result = Result.BAD_PARAMS;
+            }
+        }
+
+        return (result == Result.OK ? 0 : -1);
+    }
+
+    /**
+     * Handles a unicast or broadcast message from a nanoapp.
+     *
+     * @param contextHubId the ID of the hub the message came from
+     * @param message      the message contents
+     */
+    private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
+        // TODO(b/67734082): Send to new API clients
+        byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
+
+        int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
+                mNanoAppIdToInstanceMap.get(message.appName) : -1;
+        onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+    }
+
+    /**
+     * A helper function to handle a load response from the Context Hub for the old API.
+     *
+     * TODO(b/69270990): Remove this once the old APIs are obsolete.
+     */
+    private void handleLoadResponseOldApi(
+            int contextHubId, int result, NanoAppBinary nanoAppBinary) {
+        if (nanoAppBinary == null) {
+            Log.e(TAG, "Nanoapp binary field was null for a load transaction");
+            return;
+        }
+
+        // NOTE: The legacy JNI code used to do a query right after a load success
+        // to synchronize the service cache. Instead store the binary that was requested to
+        // load to update the cache later without doing a query.
+        int instanceId = 0;
+        long nanoAppId = nanoAppBinary.getNanoAppId();
+        int nanoAppVersion = nanoAppBinary.getNanoAppVersion();
+        if (result == TransactionResult.SUCCESS) {
+            if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+                instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+            } else {
+                instanceId = mNextAvailableInstanceId++;
+                mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+            }
+
+            addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion);
+        }
+
+        byte[] data = new byte[5];
+        data[0] = (byte) result;
+        ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
+
+        onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+    }
+
+    /**
+     * A helper function to handle an unload response from the Context Hub for the old API.
+     *
+     * TODO(b/69270990): Remove this once the old APIs are obsolete.
+     */
+    private void handleUnloadResponseOldApi(
+            int contextHubId, int result, long nanoAppId) {
+        if (result == TransactionResult.SUCCESS) {
+            int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+            deleteAppInstance(instanceId);
+            mNanoAppIdToInstanceMap.remove(nanoAppId);
+        }
+
+        byte[] data = new byte[1];
+        data[0] = (byte) result;
+        onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+    }
+
+    /**
+     * Handles a transaction response from a Context Hub.
+     *
+     * @param contextHubId  the ID of the hub the response came from
+     * @param transactionId the ID of the transaction
+     * @param result        the result of the transaction reported by the hub
+     */
+    private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) {
+        mTransactionManager.onTransactionResponse(transactionId, result);
+    }
+
+    /**
+     * Handles an asynchronous event from a Context Hub.
+     *
+     * @param contextHubId the ID of the hub the response came from
+     * @param eventType    the type of the event as defined in Context Hub HAL AsyncEventType
+     */
+    private void handleHubEventCallback(int contextHubId, int eventType) {
+        if (eventType == AsyncEventType.RESTARTED) {
+            mTransactionManager.onHubReset();
+            queryNanoAppsInternal(contextHubId);
+
+            byte[] data = {TransactionResult.SUCCESS};
+            onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+        } else {
+            Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
+                    + eventType + ")");
+        }
+    }
+
+    /**
+     * Handles an asynchronous abort event of a nanoapp.
+     *
+     * @param contextHubId the ID of the hub that the nanoapp aborted in
+     * @param nanoAppId    the ID of the aborted nanoapp
+     * @param abortCode    the nanoapp-specific abort code
+     */
+    private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
+        // TODO(b/31049861): Implement this
+    }
+
+    /**
+     * Handles a query response from a Context Hub.
+     *
+     * @param contextHubId    the ID of the hub of the response
+     * @param nanoAppInfoList the list of loaded nanoapps
+     */
+    private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+        List<NanoAppState> nanoAppStateList =
+                ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
+
+        updateServiceCache(contextHubId, nanoAppInfoList);
+        mTransactionManager.onQueryResponse(nanoAppStateList);
+    }
+
+    /**
+     * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response.
+     *
+     * TODO(b/69270990): Remove this when the old API functionality is removed.
+     *
+     * @param contextHubId    the ID of the hub the response came from
+     * @param nanoAppInfoList the list of loaded nanoapps
+     */
+    private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+        synchronized (mNanoAppHash) {
+            for (int instanceId : mNanoAppHash.keySet()) {
+                if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) {
+                    deleteAppInstance(instanceId);
+                }
+            }
+
+            for (HubAppInfo appInfo : nanoAppInfoList) {
+                int instanceId;
+                long nanoAppId = appInfo.appId;
+                if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+                    instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+                } else {
+                    instanceId = mNextAvailableInstanceId++;
+                    mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+                }
+
+                addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version);
+            }
+        }
     }
 
     @Override
@@ -257,7 +656,7 @@
         pw.println("");
         pw.println("=================== NANOAPPS ====================");
         // Dump nanoAppHash
-        for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+        for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
             pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
         }
 
@@ -268,19 +667,15 @@
         mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
     }
 
-    private int onMessageReceipt(int[] header, byte[] data) {
-        if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
-            return  -1;
+    private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+        if (data == null) {
+            return -1;
         }
 
+        int msgVersion = 0;
         int callbacksCount = mCallbacksList.beginBroadcast();
-        int msgType = header[HEADER_FIELD_MSG_TYPE];
-        int msgVersion = header[HEADER_FIELD_MSG_VERSION];
-        int hubHandle = header[HEADER_FIELD_HUB_HANDLE];
-        int appInstance = header[HEADER_FIELD_APP_INSTANCE];
-
         Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
-              hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
+                hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
 
         if (callbacksCount < 1) {
             Log.v(TAG, "No message callbacks registered.");
@@ -323,8 +718,8 @@
         }
 
         mNanoAppHash.put(appInstanceHandle, appInfo);
-        Log.d(TAG, action + " app instance " + appInstanceHandle + " with id "
-              + appId + " version " + appVersion);
+        Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x"
+                + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion));
 
         return 0;
     }
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
new file mode 100644
index 0000000..66145bb
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppState;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An abstract class representing transactions requested to the Context Hub Service.
+ *
+ * @hide
+ */
+/* package */ abstract class ContextHubServiceTransaction {
+    private final int mTransactionId;
+    @ContextHubTransaction.Type
+    private final int mTransactionType;
+
+    /*
+     * true if the transaction has already completed, false otherwise
+     */
+    private boolean mIsComplete = false;
+
+    /* package */ ContextHubServiceTransaction(int id, int type) {
+        mTransactionId = id;
+        mTransactionType = type;
+    }
+
+    /**
+     * Starts this transaction with a Context Hub.
+     *
+     * All instances of this class must implement this method by making an asynchronous request to
+     * a hub.
+     *
+     * @return the synchronous error code of the transaction start
+     */
+    /* package */
+    abstract int onTransact();
+
+    /**
+     * A function to invoke when a transaction times out.
+     *
+     * All instances of this class must implement this method by reporting the timeout to the
+     * client.
+     */
+    /* package */
+    abstract void onTimeout();
+
+    /**
+     * A function to invoke when the transaction completes.
+     *
+     * Only relevant for load, unload, enable, or disable transactions.
+     *
+     * @param result the result of the transaction
+     */
+    /* package */ void onTransactionComplete(int result) {
+    }
+
+    /**
+     * A function to invoke when a query transaction completes.
+     *
+     * Only relevant for query transactions.
+     *
+     * @param result           the result of the query
+     * @param nanoAppStateList the list of nanoapps given by the query response
+     */
+    /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+    }
+
+    /**
+     * @return the ID of this transaction
+     */
+    /* package */ int getTransactionId() {
+        return mTransactionId;
+    }
+
+    /**
+     * @return the type of this transaction
+     * @see ContextHubTransaction.Type
+     */
+    @ContextHubTransaction.Type
+    /* package */ int getTransactionType() {
+        return mTransactionType;
+    }
+
+    /**
+     * Gets the timeout period as defined in IContexthub.hal
+     *
+     * @return the timeout of this transaction in the specified time unit
+     */
+    /* package */ long getTimeout(TimeUnit unit) {
+        switch (mTransactionType) {
+            case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+                return unit.convert(30L, TimeUnit.SECONDS);
+            case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+            case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+            case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+            case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+                // Note: query timeout is not specified at the HAL
+            default: /* fall through */
+                return unit.convert(5L, TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * Marks the transaction as complete.
+     *
+     * Should only be called as a result of a response from a Context Hub callback
+     */
+    /* package */ void setComplete() {
+        mIsComplete = true;
+    }
+
+    /**
+     * @return true if the transaction has already completed, false otherwise
+     */
+    /* package */ boolean isComplete() {
+        return mIsComplete;
+    }
+
+    /**
+     * @return the human-readable string of this transaction's type
+     */
+    private String getTransactionTypeString() {
+        switch (mTransactionType) {
+            case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+                return "Load";
+            case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+                return "Unload";
+            case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+                return "Enable";
+            case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+                return "Disable";
+            case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+                return "Query";
+            default:
+                return "Unknown";
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getTransactionTypeString() + " transaction (ID = " + mTransactionId + ")";
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
new file mode 100644
index 0000000..ddbaf86
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A class encapsulating helper functions used by the ContextHubService class
+ */
+/* package */ class ContextHubServiceUtil {
+    private static final String TAG = "ContextHubServiceUtil";
+
+    /**
+     * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
+     *
+     * @param hubList the ContextHub ArrayList
+     * @return the ContextHubInfo array
+     */
+    /* package */
+    static ContextHubInfo[] createContextHubInfoArray(List<ContextHub> hubList) {
+        ContextHubInfo[] contextHubInfoList = new ContextHubInfo[hubList.size()];
+        for (int i = 0; i < hubList.size(); i++) {
+            contextHubInfoList[i] = new ContextHubInfo(hubList.get(i));
+        }
+
+        return contextHubInfoList;
+    }
+
+    /**
+     * Copies a primitive byte array to a ArrayList<Byte>.
+     *
+     * @param inputArray  the primitive byte array
+     * @param outputArray the ArrayList<Byte> array to append
+     */
+    /* package */
+    static void copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray) {
+        outputArray.clear();
+        outputArray.ensureCapacity(inputArray.length);
+        for (byte element : inputArray) {
+            outputArray.add(element);
+        }
+    }
+
+    /**
+     * Creates a byte array given a ArrayList<Byte> and copies its contents.
+     *
+     * @param array the ArrayList<Byte> object
+     * @return the byte array
+     */
+    /* package */
+    static byte[] createPrimitiveByteArray(ArrayList<Byte> array) {
+        byte[] primitiveArray = new byte[array.size()];
+        for (int i = 0; i < array.size(); i++) {
+            primitiveArray[i] = array.get(i);
+        }
+
+        return primitiveArray;
+    }
+
+    /**
+     * Generates the Context Hub HAL's NanoAppBinary object from the client-facing
+     * android.hardware.location.NanoAppBinary object.
+     *
+     * @param nanoAppBinary the client-facing NanoAppBinary object
+     * @return the Context Hub HAL's NanoAppBinary object
+     */
+    /* package */
+    static android.hardware.contexthub.V1_0.NanoAppBinary createHidlNanoAppBinary(
+            NanoAppBinary nanoAppBinary) {
+        android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+                new android.hardware.contexthub.V1_0.NanoAppBinary();
+
+        hidlNanoAppBinary.appId = nanoAppBinary.getNanoAppId();
+        hidlNanoAppBinary.appVersion = nanoAppBinary.getNanoAppVersion();
+        hidlNanoAppBinary.flags = nanoAppBinary.getFlags();
+        hidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion();
+        hidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion();
+
+        // Log exceptions while processing the binary, but continue to pass down the binary
+        // since the error checking is deferred to the Context Hub.
+        try {
+            copyToByteArrayList(nanoAppBinary.getBinaryNoHeader(), hidlNanoAppBinary.customBinary);
+        } catch (IndexOutOfBoundsException e) {
+            Log.w(TAG, e.getMessage());
+        } catch (NullPointerException e) {
+            Log.w(TAG, "NanoApp binary was null");
+        }
+
+        return hidlNanoAppBinary;
+    }
+
+    /**
+     * Generates a client-facing NanoAppState array from a HAL HubAppInfo array.
+     *
+     * @param nanoAppInfoList the array of HubAppInfo objects
+     * @return the corresponding array of NanoAppState objects
+     */
+    /* package */
+    static List<NanoAppState> createNanoAppStateList(
+            List<HubAppInfo> nanoAppInfoList) {
+        ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
+        for (HubAppInfo appInfo : nanoAppInfoList) {
+            nanoAppStateList.add(
+                    new NanoAppState(appInfo.appId, appInfo.version, appInfo.enabled));
+        }
+
+        return nanoAppStateList;
+    }
+
+    /**
+     * Creates a HIDL ContextHubMsg object to send to a nanoapp.
+     *
+     * @param hostEndPoint the ID of the client sending the message
+     * @param message      the client-facing NanoAppMessage object describing the message
+     * @return the HIDL ContextHubMsg object
+     */
+    /* package */
+    static ContextHubMsg createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message) {
+        ContextHubMsg hidlMessage = new ContextHubMsg();
+
+        hidlMessage.appName = message.getNanoAppId();
+        hidlMessage.hostEndPoint = hostEndPoint;
+        hidlMessage.msgType = message.getMessageType();
+        copyToByteArrayList(message.getMessageBody(), hidlMessage.msg);
+
+        return hidlMessage;
+    }
+
+    /**
+     * Creates a client-facing NanoAppMessage object to send to a client.
+     *
+     * @param message the HIDL ContextHubMsg object from a nanoapp
+     * @return the NanoAppMessage object
+     */
+    /* package */
+    static NanoAppMessage createNanoAppMessage(ContextHubMsg message) {
+        byte[] messageArray = createPrimitiveByteArray(message.msg);
+
+        return NanoAppMessage.createMessageFromNanoApp(
+                message.appName, message.msgType, messageArray,
+                message.hostEndPoint == HostEndPoint.BROADCAST);
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
new file mode 100644
index 0000000..898b76c
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppState;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages transactions at the Context Hub Service.
+ *
+ * This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ * and executes them through the Context Hub. At any point in time, either the transaction queue is
+ * empty, or there is a pending transaction that is waiting for an asynchronous response from the
+ * hub. This class also handles synchronous errors and timeouts of each transaction.
+ *
+ * @hide
+ */
+/* package */ class ContextHubTransactionManager {
+    private static final String TAG = "ContextHubTransactionManager";
+
+    /*
+     * Maximum number of transaction requests that can be pending at a time
+     */
+    private static final int MAX_PENDING_REQUESTS = 10;
+
+    /*
+     * The proxy to talk to the Context Hub
+     */
+    private final IContexthub mContextHubProxy;
+
+    /*
+     * A queue containing the current transactions
+     */
+    private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
+
+    /*
+     * The next available transaction ID
+     */
+    private final AtomicInteger mNextAvailableId = new AtomicInteger();
+
+    /*
+     * An executor and the future object for scheduling timeout timers
+     */
+    private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
+    private ScheduledFuture<?> mTimeoutFuture = null;
+
+    /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+        mContextHubProxy = contextHubProxy;
+    }
+
+    /**
+     * Creates a transaction for loading a nanoapp.
+     *
+     * @param contextHubId       the ID of the hub to load the nanoapp to
+     * @param nanoAppBinary      the binary of the nanoapp to load
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createLoadTransaction(
+            int contextHubId, NanoAppBinary nanoAppBinary,
+            IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP) {
+            @Override
+            /* package */ int onTransact() {
+                android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+                        ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
+                try {
+                    return mContextHubProxy.loadNanoApp(
+                            contextHubId, hidlNanoAppBinary, this.getTransactionId());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to load nanoapp with ID 0x" +
+                            Long.toHexString(nanoAppBinary.getNanoAppId()));
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTimeout() {
+                onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(int result) {
+                try {
+                    onCompleteCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a transaction for unloading a nanoapp.
+     *
+     * @param contextHubId       the ID of the hub to load the nanoapp to
+     * @param nanoAppId          the ID of the nanoapp to unload
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createUnloadTransaction(
+            int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    return mContextHubProxy.unloadNanoApp(
+                            contextHubId, nanoAppId, this.getTransactionId());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to unload nanoapp with ID 0x" +
+                            Long.toHexString(nanoAppId));
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTimeout() {
+                onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(int result) {
+                try {
+                    onCompleteCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a transaction for querying for a list of nanoapps.
+     *
+     * @param contextHubId       the ID of the hub to query
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createQueryTransaction(
+            int contextHubId, IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    return mContextHubProxy.queryApps(contextHubId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to query for nanoapps");
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTimeout() {
+                onQueryResponse(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT,
+                        Collections.emptyList());
+            }
+
+            @Override
+            /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+                try {
+                    onCompleteCallback.onQueryResponse(result, nanoAppStateList);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onQueryComplete");
+                }
+            }
+        };
+    }
+
+    /**
+     * Adds a new transaction to the queue.
+     *
+     * If there was no pending transaction at the time, the transaction that was added will be
+     * started in this method.
+     *
+     * @param transaction the transaction to add
+     * @throws IllegalStateException if the queue is full
+     */
+    /* package */
+    synchronized void addTransaction(
+            ContextHubServiceTransaction transaction) throws IllegalStateException {
+        if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
+            throw new IllegalStateException("Transaction transaction queue is full (capacity = "
+                    + MAX_PENDING_REQUESTS + ")");
+        }
+        mTransactionQueue.add(transaction);
+
+        if (mTransactionQueue.size() == 1) {
+            startNextTransaction();
+        }
+    }
+
+    /**
+     * Handles a transaction response from a Context Hub.
+     *
+     * @param transactionId the transaction ID of the response
+     * @param result        the result of the transaction
+     */
+    /* package */
+    synchronized void onTransactionResponse(int transactionId, int result) {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+            return;
+        }
+        if (transaction.getTransactionId() != transactionId) {
+            Log.w(TAG, "Received unexpected transaction response (expected ID = "
+                    + transaction.getTransactionId() + ", received ID = " + transactionId + ")");
+            return;
+        }
+
+        transaction.onTransactionComplete(result);
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Handles a query response from a Context Hub.
+     *
+     * @param nanoAppStateList the list of nanoapps included in the response
+     */
+    /* package */
+    synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            Log.w(TAG, "Received unexpected query response (no transaction pending)");
+            return;
+        }
+        if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+            Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+            return;
+        }
+
+        transaction.onQueryResponse(TransactionResult.SUCCESS, nanoAppStateList);
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Handles a hub reset event by stopping a pending transaction and starting the next.
+     */
+    /* package */
+    synchronized void onHubReset() {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            return;
+        }
+
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Pops the front transaction from the queue and starts the next pending transaction request.
+     *
+     * Removing elements from the transaction queue must only be done through this method. When a
+     * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
+     * complete.
+     *
+     * It is assumed that the transaction queue is non-empty when this method is invoked, and that
+     * the caller has obtained a lock on this ContextHubTransactionManager object.
+     */
+    private void removeTransactionAndStartNext() {
+        mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+
+        ContextHubServiceTransaction transaction = mTransactionQueue.remove();
+        transaction.setComplete();
+
+        if (!mTransactionQueue.isEmpty()) {
+            startNextTransaction();
+        }
+    }
+
+    /**
+     * Starts the next pending transaction request.
+     *
+     * Starting new transactions must only be done through this method. This method continues to
+     * process the transaction queue as long as there are pending requests, and no transaction is
+     * pending.
+     *
+     * It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
+     * object.
+     */
+    private void startNextTransaction() {
+        int result = Result.UNKNOWN_FAILURE;
+        while (result != Result.OK && !mTransactionQueue.isEmpty()) {
+            ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+            result = transaction.onTransact();
+
+            if (result == Result.OK) {
+                Runnable onTimeoutFunc = () -> {
+                    synchronized (this) {
+                        if (!transaction.isComplete()) {
+                            Log.d(TAG, transaction + " timed out");
+                            transaction.onTimeout();
+
+                            removeTransactionAndStartNext();
+                        }
+                    }
+                };
+
+                long timeoutSeconds = transaction.getTimeout(TimeUnit.SECONDS);
+                mTimeoutFuture = mTimeoutExecutor.schedule(onTimeoutFunc, timeoutSeconds,
+                        TimeUnit.SECONDS);
+            } else {
+                mTransactionQueue.remove();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index eb94414..6ba1d8d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -771,7 +771,7 @@
                                 .setType(expanded ? MetricsEvent.TYPE_DETAIL
                                         : MetricsEvent.TYPE_COLLAPSE));
                     }
-                    if (expanded) {
+                    if (expanded && userAction) {
                         r.recordExpanded();
                     }
                     EventLogTags.writeNotificationExpansion(key,
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 679250c..8591304 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 
+import android.annotation.Nullable;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -40,6 +41,7 @@
 import com.android.server.pm.dex.DexoptOptions;
 
 import java.io.File;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.TimeUnit;
@@ -402,14 +404,22 @@
     }
 
     /**
-     * Execute the idle optimizations immediately.
+     * Execute idle optimizations immediately on packages in packageNames. If packageNames is null,
+     * then execute on all packages.
      */
-    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
+    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context,
+            @Nullable List<String> packageNames) {
         // Create a new object to make sure we don't interfere with the scheduled jobs.
         // Note that this may still run at the same time with the job scheduled by the
         // JobScheduler but the scheduler will not be able to cancel it.
         BackgroundDexOptService bdos = new BackgroundDexOptService();
-        int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
+        ArraySet<String> packagesToOptimize;
+        if (packageNames == null) {
+            packagesToOptimize = pm.getOptimizablePackages();
+        } else {
+            packagesToOptimize = new ArraySet<>(packageNames);
+        }
+        int result = bdos.idleOptimization(pm, packagesToOptimize, context);
         return result == OPTIMIZE_PROCESSED;
     }
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 210eb13..6a06be2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -486,6 +486,16 @@
         }
     }
 
+    public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
+            @Nullable String volumeUuid, int flags) throws InstallerException {
+        if (!checkBeforeRemote()) return new byte[0];
+        try {
+            return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void invalidateMounts() throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 83cffe5..bbe59eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -293,6 +293,7 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.DexLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
@@ -2380,7 +2381,10 @@
 
         mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                 "*dexopt*");
-        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
+        DexManager.Listener dexManagerListener = DexLogger.getListener(this,
+                installer, mInstallLock);
+        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
+                dexManagerListener);
         mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
 
         mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -8985,11 +8989,11 @@
      * Execute the background dexopt job immediately.
      */
     @Override
-    public boolean runBackgroundDexoptJob() {
+    public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return false;
         }
-        return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
+        return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
     }
 
     List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 807eb1a..ee773a5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1219,7 +1219,13 @@
     }
 
     private int runDexoptJob() throws RemoteException {
-        boolean result = mInterface.runBackgroundDexoptJob();
+        String arg;
+        List<String> packageNames = new ArrayList<>();
+        while ((arg = getNextArg()) != null) {
+            packageNames.add(arg);
+        }
+        boolean result = mInterface.runBackgroundDexoptJob(packageNames.isEmpty() ? null :
+                packageNames);
         return result ? 0 : -1;
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
new file mode 100644
index 0000000..c7bbf1c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+
+import android.util.ArraySet;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+
+import java.io.File;
+import java.util.Set;
+
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+
+/**
+ * This class is responsible for logging data about secondary dex files.
+ * The data logged includes hashes of the name and content of each file.
+ */
+public class DexLogger implements DexManager.Listener {
+    private static final String TAG = "DexLogger";
+
+    // Event log tag & subtag used for SafetyNet logging of dynamic
+    // code loading (DCL) - see b/63927552.
+    private static final int SNET_TAG = 0x534e4554;
+    private static final String DCL_SUBTAG = "dcl";
+
+    private final IPackageManager mPackageManager;
+    private final Object mInstallLock;
+    @GuardedBy("mInstallLock")
+    private final Installer mInstaller;
+
+    public static DexManager.Listener getListener(IPackageManager pms,
+            Installer installer, Object installLock) {
+        return new DexLogger(pms, installer, installLock);
+    }
+
+    private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+      mPackageManager = pms;
+      mInstaller = installer;
+      mInstallLock = installLock;
+    }
+
+    /**
+     * Compute and log hashes of the name and content of a secondary dex file.
+     */
+    @Override
+    public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+            String dexPath, int storageFlags) {
+        int ownerUid = appInfo.uid;
+
+        byte[] hash = null;
+        synchronized(mInstallLock) {
+            try {
+                hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
+                        ownerUid, appInfo.volumeUuid, storageFlags);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
+                        " : " + e.getMessage());
+            }
+        }
+        if (hash == null) {
+            return;
+        }
+
+        String dexFileName = new File(dexPath).getName();
+        String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
+        // Valid SHA256 will be 256 bits, 32 bytes.
+        if (hash.length == 32) {
+            message = message + ' ' + ByteStringUtils.toHexString(hash);
+        }
+
+        EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);
+
+        if (dexUseInfo.isUsedByOtherApps()) {
+            Set<String> otherPackages = dexUseInfo.getLoadingPackages();
+            Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
+            for (String otherPackageName : otherPackages) {
+                try {
+                    int otherUid = mPackageManager.getPackageUid(
+                        otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
+                    if (otherUid != -1 && otherUid != ownerUid) {
+                        otherUids.add(otherUid);
+                    }
+                } catch (RemoteException ignore) {
+                    // Can't happen, we're local.
+                }
+            }
+            for (int otherUid : otherUids) {
+                EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6274754..0e2730c 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -76,6 +76,7 @@
     private final Object mInstallLock;
     @GuardedBy("mInstallLock")
     private final Installer mInstaller;
+    private final Listener mListener;
 
     // Possible outcomes of a dex search.
     private static int DEX_SEARCH_NOT_FOUND = 0;  // dex file not found
@@ -96,14 +97,24 @@
      */
     private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
 
+    public interface Listener {
+        /**
+         * Invoked just before the secondary dex file {@code dexPath} for the specified application
+         * is reconciled.
+         */
+        void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+                String dexPath, int storageFlags);
+    }
+
     public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
-            Installer installer, Object installLock) {
+            Installer installer, Object installLock, Listener listener) {
       mPackageCodeLocationsCache = new HashMap<>();
       mPackageDexUsage = new PackageDexUsage();
       mPackageManager = pms;
       mPackageDexOptimizer = pdo;
       mInstaller = installer;
       mInstallLock = installLock;
+      mListener = listener;
     }
 
     /**
@@ -389,7 +400,7 @@
                 : mPackageDexOptimizer;
         String packageName = options.getPackageName();
         PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
-        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+        if (useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
             }
@@ -433,7 +444,7 @@
      */
     public void reconcileSecondaryDexFiles(String packageName) {
         PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
-        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+        if (useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
             }
@@ -481,12 +492,16 @@
                 continue;
             }
 
+            if (mListener != null) {
+                mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
+            }
+
             boolean dexStillExists = true;
             synchronized(mInstallLock) {
                 try {
                     String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
                     dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
-                            pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
+                            info.uid, isas, info.volumeUuid, flags);
                 } catch (InstallerException e) {
                     Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
                             " : " + e.getMessage());
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9162a97..7748ae4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -8062,19 +8062,6 @@
     }
 
     @Override
-    public boolean canMagnifyWindow(int windowType) {
-        switch (windowType) {
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
-            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
-            case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
                 && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index de4fd7cd..88d1e55 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -292,6 +292,8 @@
         public void setMagnificationSpecLocked(MagnificationSpec spec) {
             mMagnifedViewport.updateMagnificationSpecLocked(spec);
             mMagnifedViewport.recomputeBoundsLocked();
+
+            mService.applyMagnificationSpec(spec);
             mService.scheduleAnimationLocked();
         }
 
@@ -421,7 +423,7 @@
         public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
             if (spec != null && !spec.isNop()) {
-                if (!mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                if (!windowState.shouldMagnify()) {
                     return null;
                 }
             }
@@ -476,6 +478,7 @@
             private final ViewportWindow mWindow;
 
             private boolean mFullRedrawNeeded;
+            private int mTempLayer = 0;
 
             public MagnifiedViewport() {
                 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
@@ -565,7 +568,7 @@
                     portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
                     windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
 
-                    if (mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                    if (windowState.shouldMagnify()) {
                         mMagnificationRegion.op(windowBounds, Region.Op.UNION);
                         mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
                     } else {
@@ -676,10 +679,12 @@
 
             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
                 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+                mTempLayer = 0;
                 dc.forAllWindows((w) -> {
                     if (w.isOnScreen() && w.isVisibleLw()
                             && !w.mWinAnimator.mEnterAnimationPending) {
-                        outWindows.put(w.mLayer, w);
+                        mTempLayer++;
+                        outWindows.put(mTempLayer, w);
                     }
                 }, false /* traverseTopToBottom */ );
             }
@@ -705,7 +710,7 @@
                     SurfaceControl surfaceControl = null;
                     try {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+                        surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
                                 .setName(SURFACE_TITLE)
                                 .setSize(mTempPoint.x, mTempPoint.y) // not a typo
                                 .setFormat(PixelFormat.TRANSLUCENT)
@@ -714,8 +719,6 @@
                         /* ignore */
                     }
                     mSurfaceControl = surfaceControl;
-                    mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
-                            .getLayerStack());
                     mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
                             TYPE_MAGNIFICATION_OVERLAY)
                             * WindowManagerService.TYPE_LAYER_MULTIPLIER);
@@ -1005,6 +1008,8 @@
 
         private final long mRecurringAccessibilityEventsIntervalMillis;
 
+        private int mTempLayer = 0;
+
         public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
                 WindowsForAccessibilityCallback callback) {
             mContext = windowManagerService.mContext;
@@ -1090,6 +1095,7 @@
                     if (isReportedWindowType(windowState.mAttrs.type)) {
                         // Add the window to the ones to be reported.
                         WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
+                        window.layer = addedWindows.size();
                         addedWindows.add(window.token);
                         windows.add(window);
                         if (windowState.isFocused()) {
@@ -1323,9 +1329,10 @@
 
         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
             final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+            mTempLayer = 0;
             dc.forAllWindows((w) -> {
                 if (w.isVisibleLw()) {
-                    outWindows.put(w.mLayer, w);
+                    outWindows.put(mTempLayer++, w);
                 }
             }, false /* traverseTopToBottom */ );
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2ef7f25..accfc65 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -253,7 +253,6 @@
 
     private void updateLayers() {
         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
-        updateThumbnailLayer();
     }
 
     private void stepThumbnailAnimation(long currentTime) {
@@ -283,27 +282,12 @@
                 + "][" + tmpFloats[Matrix.MSKEW_X]
                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
-        updateThumbnailLayer();
         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
     }
 
     /**
-     * Updates the thumbnail layer z order to just above the highest animation layer if changed
-     */
-    void updateThumbnailLayer() {
-        if (thumbnail != null) {
-            final int layer = mAppToken.getHighestAnimLayer();
-            if (DEBUG_LAYERS) Slog.v(TAG,
-                    "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
-            thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
-                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
-            mThumbnailLayer = layer;
-        }
-    }
-
-    /**
      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
      * and keep producing the first frame of the animation.
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9729e50..f19cd0f 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -30,7 +30,6 @@
 import android.util.Slog;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 /**
  * Four black surfaces put together to make a black frame.
@@ -42,22 +41,22 @@
         final int layer;
         final SurfaceControl surface;
 
-        BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
-                throws OutOfResourcesException {
+        BlackSurface(int layer,
+                int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
             left = l;
             top = t;
             this.layer = layer;
             int w = r-l;
             int h = b-t;
 
-            surface = new SurfaceControl.Builder(session)
+            surface = dc.makeOverlay()
                     .setName("BlackSurface")
                     .setSize(w, h)
                     .setColorLayer(true)
+                    .setParent(null) // TODO: Work-around for b/69259549
                     .build();
 
             surface.setAlpha(1);
-            surface.setLayerStack(layerStack);
             surface.setLayer(layer);
             surface.show();
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
@@ -114,30 +113,32 @@
         }
     }
 
-    public BlackFrame(SurfaceSession session, Rect outer, Rect inner, int layer, int layerStack,
+    public BlackFrame(Rect outer, Rect inner, int layer, DisplayContent dc,
             boolean forceDefaultOrientation) throws OutOfResourcesException {
         boolean success = false;
 
         mForceDefaultOrientation = forceDefaultOrientation;
 
+        // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
+        // b/68253229
         mOuterRect = new Rect(outer);
         mInnerRect = new Rect(inner);
         try {
             if (outer.top < inner.top) {
-                mBlackSurfaces[0] = new BlackSurface(session, layer,
-                        outer.left, outer.top, inner.right, inner.top, layerStack);
+                mBlackSurfaces[0] = new BlackSurface(layer,
+                        outer.left, outer.top, inner.right, inner.top, dc);
             }
             if (outer.left < inner.left) {
-                mBlackSurfaces[1] = new BlackSurface(session, layer,
-                        outer.left, inner.top, inner.left, outer.bottom, layerStack);
+                mBlackSurfaces[1] = new BlackSurface(layer,
+                        outer.left, inner.top, inner.left, outer.bottom, dc);
             }
             if (outer.bottom > inner.bottom) {
-                mBlackSurfaces[2] = new BlackSurface(session, layer,
-                        inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
+                mBlackSurfaces[2] = new BlackSurface(layer,
+                        inner.left, inner.bottom, outer.right, outer.bottom, dc);
             }
             if (outer.right > inner.right) {
-                mBlackSurfaces[3] = new BlackSurface(session, layer,
-                        inner.right, outer.top, outer.right, inner.bottom, layerStack);
+                mBlackSurfaces[3] = new BlackSurface(layer,
+                        inner.right, outer.top, outer.right, inner.bottom, dc);
             }
             success = true;
         } finally {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2d5d1b2..2a216ab 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -33,7 +33,6 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class CircularDisplayMask {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
@@ -54,8 +53,10 @@
     private boolean mDimensionsUnequal = false;
     private int mMaskThickness;
 
-    public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+    public CircularDisplayMask(DisplayContent dc, int zOrder,
             int screenOffset, int maskThickness) {
+        final Display display = dc.getDisplay();
+
         mScreenSize = new Point();
         display.getSize(mScreenSize);
         if (mScreenSize.x != mScreenSize.y + screenOffset) {
@@ -66,7 +67,7 @@
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("CircularDisplayMask")
                     .setSize(mScreenSize.x, mScreenSize.y) // not a typo
                     .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index cc94807..b534b8a 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -375,6 +375,10 @@
         return toString();
     }
 
+    boolean isAlwaysOnTop() {
+        return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
+    }
+
     abstract protected int getChildCount();
 
     abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
deleted file mode 100644
index 8fb2be8..0000000
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-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.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
-
-import java.io.PrintWriter;
-
-public class DimLayer {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
-    private final WindowManagerService mService;
-
-    /** Actual surface that dims */
-    private SurfaceControl mDimSurface;
-
-    /** Last value passed to mDimSurface.setAlpha() */
-    private float mAlpha = 0;
-
-    /** Last value passed to mDimSurface.setLayer() */
-    private int mLayer = -1;
-
-    /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
-    private final Rect mBounds = new Rect();
-
-    /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
-    private final Rect mLastBounds = new Rect();
-
-    /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
-    private boolean mShowing = false;
-
-    /** Value of mAlpha when beginning transition to mTargetAlpha */
-    private float mStartAlpha = 0;
-
-    /** Final value of mAlpha following transition */
-    private float mTargetAlpha = 0;
-
-    /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
-    private long mStartTime;
-
-    /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
-    private long mDuration;
-
-    private boolean mDestroyed = false;
-
-    private final int mDisplayId;
-
-
-    /** Interface implemented by users of the dim layer */
-    interface DimLayerUser {
-        /** Returns true if the  dim should be fullscreen. */
-        boolean dimFullscreen();
-        /** Returns the display info. of the dim layer user. */
-        DisplayInfo getDisplayInfo();
-        /** Returns true if the dim layer user is currently attached to a display */
-        boolean isAttachedToDisplay();
-        /** Gets the bounds of the dim layer user. */
-        void getDimBounds(Rect outBounds);
-        /** Returns the layer to place a dim layer. */
-        default int getLayerForDim(WindowStateAnimator animator, int layerOffset,
-                int defaultLayer) {
-            return defaultLayer;
-        }
-
-        String toShortString();
-    }
-    /** The user of this dim layer. */
-    private final DimLayerUser mUser;
-
-    private final String mName;
-
-    DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
-        mUser = user;
-        mDisplayId = displayId;
-        mService = service;
-        mName = name;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
-    }
-
-    private void constructSurface(WindowManagerService service) {
-        service.openSurfaceTransaction();
-        try {
-            mDimSurface = new SurfaceControl.Builder(service.mFxSession)
-                    .setName(mName)
-                    .setSize(16, 16)
-                    .setColorLayer(true)
-                    .build();
-
-            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
-                    "  DIM " + mDimSurface + ": CREATE");
-            mDimSurface.setLayerStack(mDisplayId);
-            adjustBounds();
-            adjustAlpha(mAlpha);
-            adjustLayer(mLayer);
-        } catch (Exception e) {
-            Slog.e(TAG_WM, "Exception creating Dim surface", e);
-        } finally {
-            service.closeSurfaceTransaction("DimLayer.constructSurface");
-        }
-    }
-
-    /** Return true if dim layer is showing */
-    boolean isDimming() {
-        return mTargetAlpha != 0;
-    }
-
-    /** Return true if in a transition period */
-    boolean isAnimating() {
-        return mTargetAlpha != mAlpha;
-    }
-
-    float getTargetAlpha() {
-        return mTargetAlpha;
-    }
-
-    void setLayer(int layer) {
-        if (mLayer == layer) {
-            return;
-        }
-        mLayer = layer;
-        adjustLayer(layer);
-    }
-
-    private void adjustLayer(int layer) {
-        if (mDimSurface != null) {
-            mDimSurface.setLayer(layer);
-        }
-    }
-
-    int getLayer() {
-        return mLayer;
-    }
-
-    private void setAlpha(float alpha) {
-        if (mAlpha == alpha) {
-            return;
-        }
-        mAlpha = alpha;
-        adjustAlpha(alpha);
-    }
-
-    private void adjustAlpha(float alpha) {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
-        try {
-            if (mDimSurface != null) {
-                mDimSurface.setAlpha(alpha);
-            }
-            if (alpha == 0 && mShowing) {
-                if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
-                if (mDimSurface != null) {
-                    mDimSurface.hide();
-                    mShowing = false;
-                }
-            } else if (alpha > 0 && !mShowing) {
-                if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
-                if (mDimSurface != null) {
-                    mDimSurface.show();
-                    mShowing = true;
-                }
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure setting alpha immediately", e);
-        }
-    }
-
-    /**
-     * NOTE: Must be called with Surface transaction open.
-     */
-    private void adjustBounds() {
-        if (mUser.dimFullscreen()) {
-            getBoundsForFullscreen(mBounds);
-        }
-
-        if (mDimSurface != null) {
-            mDimSurface.setPosition(mBounds.left, mBounds.top);
-            mDimSurface.setSize(mBounds.width(), mBounds.height());
-            if (DEBUG_DIM_LAYER) Slog.v(TAG,
-                    "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
-        }
-
-        mLastBounds.set(mBounds);
-    }
-
-    private void getBoundsForFullscreen(Rect outBounds) {
-        final int dw, dh;
-        final float xPos, yPos;
-        // Set surface size to screen size.
-        final DisplayInfo info = mUser.getDisplayInfo();
-        // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
-        // a corner.
-        dw = (int) (info.logicalWidth * 1.5);
-        dh = (int) (info.logicalHeight * 1.5);
-        // back off position so 1/4 of Surface is before and 1/4 is after.
-        xPos = -1 * dw / 6;
-        yPos = -1 * dh / 6;
-        outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
-    }
-
-    void setBoundsForFullscreen() {
-        getBoundsForFullscreen(mBounds);
-        setBounds(mBounds);
-    }
-
-    /** @param bounds The new bounds to set */
-    void setBounds(Rect bounds) {
-        mBounds.set(bounds);
-        if (isDimming() && !mLastBounds.equals(bounds)) {
-            try {
-                mService.openSurfaceTransaction();
-                adjustBounds();
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Failure setting size", e);
-            } finally {
-                mService.closeSurfaceTransaction("DimLayer.setBounds");
-            }
-        }
-    }
-
-    /**
-     * @param duration The time to test.
-     * @return True if the duration would lead to an earlier end to the current animation.
-     */
-    private boolean durationEndsEarlier(long duration) {
-        return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
-    }
-
-    /** Jump to the end of the animation.
-     * NOTE: Must be called with Surface transaction open. */
-    void show() {
-        if (isAnimating()) {
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
-            show(mLayer, mTargetAlpha, 0);
-        }
-    }
-
-    /**
-     * Begin an animation to a new dim value.
-     * NOTE: Must be called with Surface transaction open.
-     *
-     * @param layer The layer to set the surface to.
-     * @param alpha The dim value to end at.
-     * @param duration How long to take to get there in milliseconds.
-     */
-    void show(int layer, float alpha, long duration) {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
-                + " duration=" + duration + ", mDestroyed=" + mDestroyed);
-        if (mDestroyed) {
-            Slog.e(TAG, "show: no Surface");
-            // Make sure isAnimating() returns false.
-            mTargetAlpha = mAlpha = 0;
-            return;
-        }
-
-        if (mDimSurface == null) {
-            constructSurface(mService);
-        }
-
-        if (!mLastBounds.equals(mBounds)) {
-            adjustBounds();
-        }
-        setLayer(layer);
-
-        long curTime = SystemClock.uptimeMillis();
-        final boolean animating = isAnimating();
-        if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
-                || (!animating && mAlpha != alpha)) {
-            if (duration <= 0) {
-                // No animation required, just set values.
-                setAlpha(alpha);
-            } else {
-                // Start or continue animation with new parameters.
-                mStartAlpha = mAlpha;
-                mStartTime = curTime;
-                mDuration = duration;
-            }
-        }
-        mTargetAlpha = alpha;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
-                + mStartTime + " mTargetAlpha=" + mTargetAlpha);
-    }
-
-    /** Immediate hide.
-     * NOTE: Must be called with Surface transaction open. */
-    void hide() {
-        if (mShowing) {
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
-            hide(0);
-        }
-    }
-
-    /**
-     * Gradually fade to transparent.
-     * NOTE: Must be called with Surface transaction open.
-     *
-     * @param duration Time to fade in milliseconds.
-     */
-    void hide(long duration) {
-        if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
-            show(mLayer, 0, duration);
-        }
-    }
-
-    /**
-     * Advance the dimming per the last #show(int, float, long) call.
-     * NOTE: Must be called with Surface transaction open.
-     *
-     * @return True if animation is still required after this step.
-     */
-    boolean stepAnimation() {
-        if (mDestroyed) {
-            Slog.e(TAG, "stepAnimation: surface destroyed");
-            // Ensure that isAnimating() returns false;
-            mTargetAlpha = mAlpha = 0;
-            return false;
-        }
-        if (isAnimating()) {
-            final long curTime = SystemClock.uptimeMillis();
-            final float alphaDelta = mTargetAlpha - mStartAlpha;
-            float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
-            if (alphaDelta > 0 && alpha > mTargetAlpha ||
-                    alphaDelta < 0 && alpha < mTargetAlpha) {
-                // Don't exceed limits.
-                alpha = mTargetAlpha;
-            }
-            if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
-            setAlpha(alpha);
-        }
-
-        return isAnimating();
-    }
-
-    /** Cleanup */
-    void destroySurface() {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
-        if (mDimSurface != null) {
-            mDimSurface.destroy();
-            mDimSurface = null;
-        }
-        mDestroyed = true;
-    }
-
-    public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
-                pw.print(" mLayer="); pw.print(mLayer);
-                pw.print(" mAlpha="); pw.println(mAlpha);
-        pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
-                pw.print(" mBounds="); pw.println(mBounds.toShortString());
-        pw.print(prefix); pw.print("Last animation: ");
-                pw.print(" mDuration="); pw.print(mDuration);
-                pw.print(" mStartTime="); pw.print(mStartTime);
-                pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
-        pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
-                pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
-    }
-}
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
deleted file mode 100644
index 6f9e45a..0000000
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ /dev/null
@@ -1,403 +0,0 @@
-package com.android.server.wm;
-
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-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.LAYER_OFFSET_DIM;
-
-import android.graphics.Rect;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
-
-import java.io.PrintWriter;
-
-/**
- * Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
- * as well as other use cases (such as dimming above a dead window).
- */
-class DimLayerController {
-    private static final String TAG_LOCAL = "DimLayerController";
-    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
-    /** Amount of time in milliseconds to animate the dim surface from one value to another,
-     * when no window animation is driving it. */
-    private static final int DEFAULT_DIM_DURATION = 200;
-
-    /**
-     * The default amount of dim applied over a dead window
-     */
-    private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
-
-    // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
-    // instead of creating a new object per fullscreen task on a display.
-    private DimLayer mSharedFullScreenDimLayer;
-
-    private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
-
-    private DisplayContent mDisplayContent;
-
-    private Rect mTmpBounds = new Rect();
-
-    DimLayerController(DisplayContent displayContent) {
-        mDisplayContent = displayContent;
-    }
-
-    /** Updates the dim layer bounds, recreating it if needed. */
-    void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
-        final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
-        final boolean previousFullscreen = state.dimLayer != null
-                && state.dimLayer == mSharedFullScreenDimLayer;
-        DimLayer newDimLayer;
-        final int displayId = mDisplayContent.getDisplayId();
-        if (dimLayerUser.dimFullscreen()) {
-            if (previousFullscreen && mSharedFullScreenDimLayer != null) {
-                // Update the bounds for fullscreen in case of rotation.
-                mSharedFullScreenDimLayer.setBoundsForFullscreen();
-                return;
-            }
-            // Use shared fullscreen dim layer
-            newDimLayer = mSharedFullScreenDimLayer;
-            if (newDimLayer == null) {
-                if (state.dimLayer != null) {
-                    // Re-purpose the previous dim layer.
-                    newDimLayer = state.dimLayer;
-                } else {
-                    // Create new full screen dim layer.
-                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
-                            getDimLayerTag(dimLayerUser));
-                }
-                dimLayerUser.getDimBounds(mTmpBounds);
-                newDimLayer.setBounds(mTmpBounds);
-                mSharedFullScreenDimLayer = newDimLayer;
-            } else if (state.dimLayer != null) {
-                state.dimLayer.destroySurface();
-            }
-        } else {
-            newDimLayer = (state.dimLayer == null || previousFullscreen)
-                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
-                            getDimLayerTag(dimLayerUser))
-                    : state.dimLayer;
-            dimLayerUser.getDimBounds(mTmpBounds);
-            newDimLayer.setBounds(mTmpBounds);
-        }
-        state.dimLayer = newDimLayer;
-    }
-
-    private static String getDimLayerTag(DimLayerUser dimLayerUser) {
-        return TAG_LOCAL + "/" + dimLayerUser.toShortString();
-    }
-
-    private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
-                + dimLayerUser.toShortString());
-        DimLayerState state = mState.get(dimLayerUser);
-        if (state == null) {
-            state = new DimLayerState();
-            mState.put(dimLayerUser, state);
-        }
-        return state;
-    }
-
-    private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        if (state == null) {
-            if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
-                    + dimLayerUser.toShortString());
-            return;
-        }
-        state.continueDimming = true;
-    }
-
-    boolean isDimming() {
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayerState state = mState.valueAt(i);
-            if (state.dimLayer != null && state.dimLayer.isDimming()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void resetDimming() {
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            mState.valueAt(i).continueDimming = false;
-        }
-    }
-
-    private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        return state != null && state.continueDimming;
-    }
-
-    void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
-            WindowStateAnimator newWinAnimator, boolean aboveApp) {
-        // Only set dim params on the highest dimmed layer.
-        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
-        DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
-        state.dimAbove = aboveApp;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
-                + " dimLayerUser=" + dimLayerUser.toShortString()
-                + " newWinAnimator=" + newWinAnimator
-                + " state.animator=" + state.animator);
-        if (newWinAnimator.getShown() && (state.animator == null
-                || !state.animator.getShown()
-                || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
-            state.animator = newWinAnimator;
-            if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
-                // Dim should cover the entire screen for system windows.
-                mDisplayContent.getLogicalDisplayRect(mTmpBounds);
-            } else {
-                dimLayerUser.getDimBounds(mTmpBounds);
-            }
-            state.dimLayer.setBounds(mTmpBounds);
-        }
-    }
-
-    void stopDimmingIfNeeded() {
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
-            stopDimmingIfNeeded(dimLayerUser);
-        }
-    }
-
-    private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
-        // No need to check if state is null, we know the key has a value.
-        DimLayerState state = mState.get(dimLayerUser);
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
-                + " dimLayerUser=" + dimLayerUser.toShortString()
-                + " state.continueDimming=" + state.continueDimming
-                + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
-        if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
-            return;
-        }
-
-        if (!state.continueDimming && state.dimLayer.isDimming()) {
-            state.animator = null;
-            dimLayerUser.getDimBounds(mTmpBounds);
-            state.dimLayer.setBounds(mTmpBounds);
-        }
-    }
-
-    boolean animateDimLayers() {
-        int fullScreen = -1;
-        int fullScreenAndDimming = -1;
-        int topFullScreenUserLayer = 0;
-        boolean result = false;
-
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            final DimLayer.DimLayerUser user = mState.keyAt(i);
-            final DimLayerState state = mState.valueAt(i);
-
-            if (!user.isAttachedToDisplay()) {
-                // Leaked dim user that is no longer attached to the display. Go ahead and clean it
-                // clean-up and log what happened.
-                // TODO: This is a work around for b/34395537 as the dim user should have cleaned-up
-                // it self when it was detached from the display. Need to investigate how the dim
-                // user is leaking...
-                //Slog.wtfStack(TAG_WM, "Leaked dim user=" + user.toShortString()
-                //        + " state=" + state);
-                Slog.w(TAG_WM, "Leaked dim user=" + user.toShortString() + " state=" + state);
-                removeDimLayerUser(user);
-                continue;
-            }
-
-            // We have to check that we are actually the shared fullscreen layer
-            // for this path. If we began as non fullscreen and became fullscreen
-            // (e.g. Docked stack closing), then we may not be the shared layer
-            // and we have to make sure we always animate the layer.
-            if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
-                fullScreen = i;
-                if (!state.continueDimming) {
-                    continue;
-                }
-
-                // When choosing which user to assign the shared fullscreen layer to
-                // we need to look at Z-order.
-                if (topFullScreenUserLayer == 0 ||
-                        (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) {
-                    fullScreenAndDimming = i;
-                    if (state.animator != null) {
-                        topFullScreenUserLayer = state.animator.mAnimLayer;
-                    }
-                }
-            } else {
-                // We always want to animate the non fullscreen windows, they don't share their
-                // dim layers.
-                result |= animateDimLayers(user);
-            }
-        }
-        // For the shared, full screen dim layer, we prefer the animation that is causing it to
-        // appear.
-        if (fullScreenAndDimming != -1) {
-            result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
-        } else if (fullScreen != -1) {
-            // If there is no animation for the full screen dim layer to appear, we can use any of
-            // the animators that will cause it to disappear.
-            result |= animateDimLayers(mState.keyAt(fullScreen));
-        }
-        return result;
-    }
-
-    private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
-                + " dimLayerUser=" + dimLayerUser.toShortString()
-                + " state.animator=" + state.animator
-                + " state.continueDimming=" + state.continueDimming);
-        final int dimLayer;
-        final float dimAmount;
-        if (state.animator == null) {
-            dimLayer = state.dimLayer.getLayer();
-            dimAmount = 0;
-        } else {
-            if (state.dimAbove) {
-                dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
-                dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
-            } else {
-                dimLayer = dimLayerUser.getLayerForDim(state.animator, LAYER_OFFSET_DIM,
-                        state.animator.mAnimLayer - LAYER_OFFSET_DIM);
-                dimAmount = state.animator.mWin.mAttrs.dimAmount;
-            }
-        }
-        final float targetAlpha = state.dimLayer.getTargetAlpha();
-        if (targetAlpha != dimAmount) {
-            if (state.animator == null) {
-                state.dimLayer.hide(DEFAULT_DIM_DURATION);
-            } else {
-                long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
-                        ? state.animator.mAnimation.computeDurationHint()
-                        : DEFAULT_DIM_DURATION;
-                if (targetAlpha > dimAmount) {
-                    duration = getDimLayerFadeDuration(duration);
-                }
-                state.dimLayer.show(dimLayer, dimAmount, duration);
-
-                // If we showed a dim layer, make sure to redo the layout because some things depend
-                // on whether a dim layer is showing or not.
-                if (targetAlpha == 0) {
-                    mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
-                    mDisplayContent.setLayoutNeeded();
-                }
-            }
-        } else if (state.dimLayer.getLayer() != dimLayer) {
-            state.dimLayer.setLayer(dimLayer);
-        }
-        if (state.dimLayer.isAnimating()) {
-            if (!mDisplayContent.okToAnimate()) {
-                // Jump to the end of the animation.
-                state.dimLayer.show();
-            } else {
-                return state.dimLayer.stepAnimation();
-            }
-        }
-        return false;
-    }
-
-    boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
-        DimLayerState state = mState.get(dimLayerUser);
-        return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
-    }
-
-    private long getDimLayerFadeDuration(long duration) {
-        TypedValue tv = new TypedValue();
-        mDisplayContent.mService.mContext.getResources().getValue(
-                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
-        if (tv.type == TypedValue.TYPE_FRACTION) {
-            duration = (long) tv.getFraction(duration, duration);
-        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
-            duration = tv.data;
-        }
-        return duration;
-    }
-
-    void close() {
-        for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayerState state = mState.valueAt(i);
-            state.dimLayer.destroySurface();
-        }
-        mState.clear();
-        mSharedFullScreenDimLayer = null;
-    }
-
-    void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = mState.get(dimLayerUser);
-        if (state != null) {
-            // Destroy the surface, unless it's the shared fullscreen dim.
-            if (state.dimLayer != mSharedFullScreenDimLayer) {
-                state.dimLayer.destroySurface();
-            }
-            mState.remove(dimLayerUser);
-        }
-        if (mState.isEmpty()) {
-            mSharedFullScreenDimLayer = null;
-        }
-    }
-
-    @VisibleForTesting
-    boolean hasDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
-        return mState.containsKey(dimLayerUser);
-    }
-
-    @VisibleForTesting
-    boolean hasSharedFullScreenDimLayer() {
-        return mSharedFullScreenDimLayer != null;
-    }
-
-    void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
-        applyDim(dimLayerUser, animator, false /* aboveApp */);
-    }
-
-    void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
-        applyDim(dimLayerUser, animator, true /* aboveApp */);
-    }
-
-    void applyDim(
-            DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
-        if (dimLayerUser == null) {
-            Slog.e(TAG, "Trying to apply dim layer for: " + this
-                    + ", but no dim layer user found.");
-            return;
-        }
-        if (!getContinueDimming(dimLayerUser)) {
-            setContinueDimming(dimLayerUser);
-            if (!isDimming(dimLayerUser, animator)) {
-                if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
-                startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
-            }
-        }
-    }
-
-    private static class DimLayerState {
-        // The particular window requesting a dim layer. If null, hide dimLayer.
-        WindowStateAnimator animator;
-        // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
-        // end then stop any dimming.
-        boolean continueDimming;
-        DimLayer dimLayer;
-        boolean dimAbove;
-    }
-
-    void dump(String prefix, PrintWriter pw) {
-        pw.println(prefix + "DimLayerController");
-        final String doubleSpace = "  ";
-        final String prefixPlusDoubleSpace = prefix + doubleSpace;
-
-        for (int i = 0, n = mState.size(); i < n; i++) {
-            pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
-            DimLayerState state = mState.valueAt(i);
-            pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
-                    + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
-                    + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
-            if (state.dimLayer != null) {
-                state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
new file mode 100644
index 0000000..9fe16ae
--- /dev/null
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.graphics.Rect;
+
+/**
+ * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
+ * black layers of varying opacity at various Z-levels which create the effect of a Dim.
+ */
+class Dimmer {
+    private static final String TAG = "WindowManager";
+
+    private class DimState {
+        SurfaceControl mSurfaceControl;
+        boolean mDimming;
+
+        /**
+         * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+         * details on Dim lifecycle.
+         */
+        boolean mDontReset;
+
+        DimState(SurfaceControl ctl) {
+            mSurfaceControl = ctl;
+            mDimming = true;
+        }
+    };
+
+    private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+
+    /**
+     * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
+     * host, some controller of it, or one of the hosts children.
+     */
+    private WindowContainer mHost;
+
+    Dimmer(WindowContainer host) {
+        mHost = host;
+    }
+
+    SurfaceControl makeDimLayer() {
+        final SurfaceControl control = mHost.makeChildSurface(null)
+                .setParent(mHost.getSurfaceControl())
+                .setColorLayer(true)
+                .setName("Dim Layer for - " + mHost.getName())
+                .build();
+        return control;
+    }
+
+    /**
+     * Retreive the DimState for a given child of the host.
+     */
+    DimState getDimState(WindowContainer container) {
+        DimState state = mDimLayerUsers.get(container);
+        if (state == null) {
+            final SurfaceControl ctl = makeDimLayer();
+            state = new DimState(ctl);
+            /**
+             * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
+             * via state resetting for Dim's with containers.
+             */
+            if (container == null) {
+                state.mDontReset = true;
+            }
+            mDimLayerUsers.put(container, state);
+        }
+        return state;
+    }
+
+    private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
+            float alpha) {
+        final DimState d = getDimState(container);
+        t.show(d.mSurfaceControl);
+        if (container != null) {
+            t.setRelativeLayer(d.mSurfaceControl,
+                    container.getSurfaceControl(), relativeLayer);
+        } else {
+            t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+        }
+        t.setAlpha(d.mSurfaceControl, alpha);
+
+        d.mDimming = true;
+    }
+
+    /**
+     * Finish a dim started by dimAbove in the case there was no call to dimAbove.
+     *
+     * @param t A Transaction in which to finish the dim.
+     */
+    void stopDim(SurfaceControl.Transaction t) {
+        DimState d = getDimState(null);
+        t.hide(d.mSurfaceControl);
+        d.mDontReset = false;
+    }
+    /**
+     * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
+     * remove this effect. If the Dim can be assosciated with a particular child of the host
+     * consider using the other variant of dimAbove which ties the Dim lifetime to the child
+     * lifetime more explicitly.
+     *
+     * @param t A transaction in which to apply the Dim.
+     * @param alpha The alpha at which to Dim.
+     */
+    void dimAbove(SurfaceControl.Transaction t, float alpha) {
+        dim(t, null, 1, alpha);
+    }
+
+    /**
+     * Place a dim above the given container, which should be a child of the host container.
+     * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
+     * and the child should call dimAbove again to request the Dim to continue.
+     *
+     * @param t A transaction in which to apply the Dim.
+     * @param container The container which to dim above. Should be a child of our host.
+     * @param alpha The alpha at which to Dim.
+     */
+    void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+        dim(t, container, 1, alpha);
+    }
+
+    /**
+     * Like {@link #dimAbove} but places the dim below the given container.
+     *
+     * @param t A transaction in which to apply the Dim.
+     * @param container The container which to dim below. Should be a child of our host.
+     * @param alpha The alpha at which to Dim.
+     */
+
+    void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+        dim(t, container, -1, alpha);
+    }
+
+    /**
+     * Mark all dims as pending completion on the next call to {@link #updateDims}
+     *
+     * This is intended for us by the host container, to be called at the beginning of
+     * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
+     * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
+     * a chance to request dims to continue.
+     */
+    void resetDimStates() {
+        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+            final DimState state = mDimLayerUsers.valueAt(i);
+            if (state.mDontReset == false) {
+                state.mDimming = false;
+            }
+        }
+    }
+
+    /**
+     * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
+     * described in {@link #resetDimStates}.
+     *
+     * @param t A transaction in which to update the dims.
+     * @param bounds The bounds at which to dim.
+     * @return true if any Dims were updated.
+     */
+    boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
+        boolean didSomething = false;
+        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+            DimState state = mDimLayerUsers.valueAt(i);
+            // TODO: We want to animate the addition and removal of Dim's instead of immediately
+            // acting. When we do this we need to take care to account for the "Replacing Windows"
+            // case (and seamless dim transfer).
+            if (state.mDimming == false) {
+                mDimLayerUsers.removeAt(i);
+                state.mSurfaceControl.destroy();
+            } else {
+                didSomething = true;
+                // TODO: Once we use geometry from hierarchy this falls away.
+                t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
+                t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+            }
+        }
+        return didSomething;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4d839d0..17312b2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -142,8 +142,10 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.InputDevice;
+import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -321,8 +323,6 @@
     final DockedStackDividerController mDividerControllerLocked;
     final PinnedStackController mPinnedStackControllerLocked;
 
-    DimLayerController mDimLayerController;
-
     final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
 
     private boolean mHaveBootMsg = false;
@@ -346,10 +346,37 @@
     // {@code false} if this display is in the processing of being created.
     private boolean mDisplayReady = false;
 
-    private final WindowLayersController mLayersController;
     WallpaperController mWallpaperController;
     int mInputMethodAnimLayerAdjustment;
 
+    private final SurfaceSession mSession = new SurfaceSession();
+
+    /**
+     * We organize all top-level Surfaces in to the following layers.
+     * mOverlayLayer contains a few Surfaces which are always on top of others
+     * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+     * {@link #mWindowingLayer} contains everything else.
+     */
+    private SurfaceControl mOverlayLayer;
+
+    /**
+     * See {@link #mOverlayLayer}
+     */
+    private SurfaceControl mWindowingLayer;
+
+    /**
+     * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
+     * <p>
+     * For these surfaces currently we use a surface based on the larger of width or height so we
+     * don't have to resize when rotating the display.
+     */
+    private int mSurfaceSize;
+
+    /**
+     * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+     */
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         if (winAnimator.hasSurface()) {
@@ -503,9 +530,6 @@
         return true;
     };
 
-    private final Consumer<WindowState> mPrepareWindowSurfaces =
-            w -> w.mWinAnimator.prepareSurfaceLocked(true);
-
     private final Consumer<WindowState> mPerformLayout = w -> {
         // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
         // wasting time and funky changes while a window is animating away.
@@ -558,12 +582,6 @@
                     w.updateLastInsetValues();
                 }
 
-                // Window frames may have changed. Update dim layer with the new bounds.
-                final Task task = w.getTask();
-                if (task != null) {
-                    mDimLayerController.updateDimLayer(task);
-                }
-
                 if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.mFrame
                         + " mContainingFrame=" + w.mContainingFrame
                         + " mDisplayFrame=" + w.mDisplayFrame);
@@ -657,8 +675,6 @@
             }
         }
 
-        w.applyDimLayerIfNeeded();
-
         if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
                 && mWallpaperController.isWallpaperTarget(w)) {
             // This is the wallpaper target and its obscured state changed... make sure the
@@ -741,13 +757,11 @@
      * initialize direct children.
      * @param display May not be null.
      * @param service You know.
-     * @param layersController window layer controller used to assign layer to the windows on this
-     *                         display.
      * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
      *                            wallpaper windows in the window list.
      */
     DisplayContent(Display display, WindowManagerService service,
-            WindowLayersController layersController, WallpaperController wallpaperController) {
+            WallpaperController wallpaperController) {
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -756,7 +770,6 @@
 
         mDisplay = display;
         mDisplayId = display.getDisplayId();
-        mLayersController = layersController;
         mWallpaperController = wallpaperController;
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
@@ -766,7 +779,22 @@
         initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
-        mDimLayerController = new DimLayerController(this);
+
+        mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
+                .setSize(mSurfaceSize, mSurfaceSize)
+                .setOpaque(true);
+        mWindowingLayer = b.setName("Display Root").build();
+        mOverlayLayer = b.setName("Display Overlays").build();
+
+        getPendingTransaction().setLayer(mWindowingLayer, 0)
+                .setLayerStack(mWindowingLayer, mDisplayId)
+                .show(mWindowingLayer)
+                .setLayer(mOverlayLayer, 1)
+                .setLayerStack(mOverlayLayer, mDisplayId)
+                .show(mOverlayLayer);
+        getPendingTransaction().apply();
 
         // These are the only direct children we should ever have and they are permanent.
         super.addChild(mBelowAppWindowsContainers, null);
@@ -1030,11 +1058,7 @@
 
         setLayoutNeeded();
         final int[] anim = new int[2];
-        if (isDimming()) {
-            anim[0] = anim[1] = 0;
-        } else {
-            mService.mPolicy.selectRotationAnimationLw(anim);
-        }
+        mService.mPolicy.selectRotationAnimationLw(anim);
 
         if (!rotateSeamlessly) {
             mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1], this);
@@ -1071,8 +1095,7 @@
             //       it doesn't support hardware OpenGL emulation yet.
             if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                     && screenRotationAnimation.hasScreenshot()) {
-                if (screenRotationAnimation.setRotationInTransaction(
-                        rotation, mService.mFxSession,
+                if (screenRotationAnimation.setRotationInTransaction(rotation,
                         MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
                         mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
                     mService.scheduleAnimationLocked();
@@ -1907,22 +1930,6 @@
         }
     }
 
-    boolean animateDimLayers() {
-        return mDimLayerController.animateDimLayers();
-    }
-
-    private void resetDimming() {
-        mDimLayerController.resetDimming();
-    }
-
-    boolean isDimming() {
-        return mDimLayerController.isDimming();
-    }
-
-    private void stopDimmingIfNeeded() {
-        mDimLayerController.stopDimmingIfNeeded();
-    }
-
     @Override
     void removeIfPossible() {
         if (isAnimating()) {
@@ -1938,7 +1945,6 @@
         try {
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
-            mDimLayerController.close();
             if (mService.canDispatchPointerEvents()) {
                 if (mTapDetector != null) {
                     mService.unregisterPointerEventListener(mTapDetector);
@@ -1947,6 +1953,9 @@
                     mService.unregisterPointerEventListener(mService.mMousePositionTracker);
                 }
             }
+            // The pending transaction won't be applied so we should
+            // just clean up any surfaces pending destruction.
+            onPendingTransactionApplied();
         } finally {
             mRemovingDisplay = false;
         }
@@ -2228,8 +2237,7 @@
                 token.dump(pw, "    ");
             }
         }
-        pw.println();
-        mDimLayerController.dump(prefix, pw);
+
         pw.println();
 
         // Dump stack references
@@ -2342,10 +2350,16 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
-        mLayersController.assignWindowLayers(this);
+        assignChildLayers(getPendingTransaction());
         if (setLayoutNeeded) {
             setLayoutNeeded();
         }
+
+        // We accumlate the layer changes in-to "getPendingTransaction()" but we defer
+        // the application of this transaction until the animation pass triggers
+        // prepareSurfaces. This allows us to synchronize Z-ordering changes with
+        // the hiding and showing of surfaces.
+        scheduleAnimation();
     }
 
     // TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -2701,10 +2715,6 @@
         }
     }
 
-    void prepareWindowSurfaces() {
-        forAllWindows(mPrepareWindowSurfaces, false /* traverseTopToBottom */);
-    }
-
     boolean inputMethodClientHasFocus(IInputMethodClient client) {
         final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
         if (imFocus == null) {
@@ -2846,7 +2856,6 @@
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
-        resetDimming();
 
         mTmpRecoveringMemory = recoveringMemory;
         forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
@@ -2857,8 +2866,6 @@
                 mTmpApplySurfaceChangesTransactionState.preferredModeId,
                 true /* inTraversal, must call performTraversalInTrans... below */);
 
-        stopDimmingIfNeeded();
-
         final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
         if (wallpaperVisible != mLastWallpaperVisible) {
             mLastWallpaperVisible = wallpaperVisible;
@@ -3062,13 +3069,6 @@
                 // Include this window.
 
                 final WindowStateAnimator winAnim = w.mWinAnimator;
-                int layer = winAnim.mSurfaceController.getLayer();
-                if (mScreenshotApplicationState.maxLayer < layer) {
-                    mScreenshotApplicationState.maxLayer = layer;
-                }
-                if (mScreenshotApplicationState.minLayer > layer) {
-                    mScreenshotApplicationState.minLayer = layer;
-                }
 
                 // Don't include wallpaper in bounds calculation
                 if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
@@ -3112,8 +3112,6 @@
 
             final WindowState appWin = mScreenshotApplicationState.appWin;
             final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
-            final int maxLayer = mScreenshotApplicationState.maxLayer;
-            final int minLayer = mScreenshotApplicationState.minLayer;
 
             if (appToken != null && appWin == null) {
                 // Can't find a window to snapshot.
@@ -3134,11 +3132,6 @@
             // because we don't want to release the mWindowMap lock until the screenshot is
             // taken.
 
-            if (maxLayer == 0) {
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                        + ": returning null maxLayer=" + maxLayer);
-                return null;
-            }
 
             if (!mutableIncludeFullDisplay.value) {
                 // Constrain frame to the screen size.
@@ -3183,8 +3176,6 @@
             convertCropForSurfaceFlinger(crop, rot, dw, dh);
 
             if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
-                        + maxLayer + " appToken=" + appToken);
                 forAllWindows(w -> {
                     final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
                     Slog.i(TAG_WM, w + ": " + w.mLayer
@@ -3206,11 +3197,13 @@
             SurfaceControl.openTransaction();
             SurfaceControl.closeTransactionSync();
 
-            bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
+            // TODO(b/68392460): We should screenshot Task controls directly
+            // but it's difficult at the moment as the Task doesn't have the
+            // correct size set.
+            bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
                     inRotation, rot);
             if (bitmap == null) {
-                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
-                        + ") to layer " + maxLayer);
+                Slog.w(TAG_WM, "Failed to take screenshot");
                 return null;
             }
         }
@@ -3366,6 +3359,10 @@
      * I.e Activities.
      */
     private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+        /**
+         * A control placed at the appropriate level for transitions to occur.
+         */
+        SurfaceControl mAnimationLayer = null;
 
         // Cached reference to some special stacks we tend to get a lot so we don't need to loop
         // through the list to find them.
@@ -3677,6 +3674,50 @@
             // to prevent freezing/unfreezing the display too early.
             return mLastOrientation;
         }
+
+        @Override
+        void assignChildLayers(SurfaceControl.Transaction t) {
+            final int NORMAL_STACK_STATE = 0;
+            final int BOOSTED_STATE = 1;
+            final int ALWAYS_ON_TOP_STATE = 2;
+
+            // We allow stacks to change visual order from the AM specified order due to
+            // Z-boosting during animations. However we must take care to ensure TaskStacks
+            // which are marked as alwaysOnTop remain that way.
+            int layer = 0;
+            for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+                for (int i = 0; i < mChildren.size(); i++) {
+                    final TaskStack s = mChildren.get(i);
+                    layer++;
+                    if (state == NORMAL_STACK_STATE) {
+                        s.assignLayer(t, layer);
+                    } else if (state == BOOSTED_STATE && s.needsZBoost()) {
+                        s.assignLayer(t, layer);
+                    } else if (state == ALWAYS_ON_TOP_STATE &&
+                            s.isAlwaysOnTop()) {
+                        s.assignLayer(t, layer);
+                    }
+                    s.assignChildLayers(t);
+                }
+                // The appropriate place for App-Transitions to occur is right
+                // above all other animations but still below things in the Picture-and-Picture
+                // windowing mode.
+                if (state == BOOSTED_STATE && mAnimationLayer != null) {
+                    t.setLayer(mAnimationLayer, layer + 1);
+                }
+            }
+        }
+
+        @Override
+        void onParentSet() {
+            super.onParentSet();
+            if (getParent() != null) {
+                mAnimationLayer = makeSurface().build();
+            } else {
+                mAnimationLayer.destroy();
+                mAnimationLayer = null;
+            }
+        }
     }
 
     /**
@@ -3760,4 +3801,119 @@
         E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
                 boolean useIdentityTransform, int rotation);
     }
+
+    SurfaceControl.Builder makeSurface(SurfaceSession s) {
+        return mService.makeSurfaceBuilder(s)
+                .setParent(mWindowingLayer);
+    }
+
+    @Override
+    SurfaceSession getSession() {
+        return mSession;
+    }
+
+    @Override
+    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+        SurfaceSession s = child != null ? child.getSession() : getSession();
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
+        b.setSize(mSurfaceSize, mSurfaceSize);
+
+        if (child == null) {
+            return b;
+        }
+
+        b.setName(child.getName());
+        if (child.isScreenOverlay()) {
+            return b.setParent(mOverlayLayer);
+        } else {
+            return b.setParent(mWindowingLayer);
+        }
+    }
+
+    /**
+     * The makeSurface variants are for use by the window-container
+     * hierarchy. makeOverlay here is a function for various non windowing
+     * overlays like the ScreenRotation screenshot, the Strict Mode Flash
+     * and other potpourii.
+     */
+    SurfaceControl.Builder makeOverlay() {
+        return mService.makeSurfaceBuilder(mSession)
+            .setParent(mOverlayLayer);
+    }
+
+    void applyMagnificationSpec(MagnificationSpec spec) {
+        applyMagnificationSpec(getPendingTransaction(), spec);
+        getPendingTransaction().apply();
+    }
+
+    @Override
+    void onParentSet() {
+        // Since we are the top of the SurfaceControl hierarchy here
+        // we create the root surfaces explicitly rather than chaining
+        // up as the default implementation in onParentSet does. So we
+        // explicitly do NOT call super here.
+    }
+
+    @Override
+    void assignChildLayers(SurfaceControl.Transaction t) {
+        t.setLayer(mOverlayLayer, 1)
+                .setLayer(mWindowingLayer, 0);
+
+        // These are layers as children of "mWindowingLayer"
+        mBelowAppWindowsContainers.assignLayer(t, 0);
+        mTaskStackContainers.assignLayer(t, 1);
+        mAboveAppWindowsContainers.assignLayer(t, 2);
+
+        WindowState imeTarget = mService.mInputMethodTarget;
+        if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) {
+            // In split-screen windowing mode we can't layer the
+            // IME relative to the IME target because it needs to
+            // go over the docked divider, so instead we place it on top
+            // of everything and use relative layering of windows which need
+            // to go above it (see special logic in WindowState#assignLayer)
+            mImeWindowsContainers.assignLayer(t, 3);
+        } else {
+            t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
+                    imeTarget.getSurfaceControl(),
+                    // TODO: We need to use an extra level on the app surface to ensure
+                    // this is always above SurfaceView but always below attached window.
+                    1);
+        }
+
+        // Above we have assigned layers to our children, now we ask them to assign
+        // layers to their children.
+        mBelowAppWindowsContainers.assignChildLayers(t);
+        mTaskStackContainers.assignChildLayers(t);
+        mAboveAppWindowsContainers.assignChildLayers(t);
+        mImeWindowsContainers.assignChildLayers(t);
+    }
+
+    /**
+     * Here we satisfy an unfortunate special case of the IME in split-screen mode. Imagine
+     * that the IME target is one of the docked applications. We'd like the docked divider to be
+     * above both of the applications, and we'd like the IME to be above the docked divider.
+     * However we need child windows of the applications to be above the IME (Text drag handles).
+     * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
+     * somehow. We do this by relatively ordering children of the target to the IME in cooperation
+     * with {@link #WindowState#assignLayer}
+     */
+    void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
+        t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+    }
+
+    @Override
+    void destroyAfterPendingTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterTransaction}.
+     */
+    void onPendingTransactionApplied() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index d79ba89..8308417 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -54,7 +54,6 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.LocalServices;
-import com.android.server.wm.DimLayer.DimLayerUser;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
@@ -62,7 +61,7 @@
 /**
  * Keeps information about the docked stack divider.
  */
-public class DockedStackDividerController implements DimLayerUser {
+public class DockedStackDividerController {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
 
@@ -114,7 +113,6 @@
     private boolean mLastVisibility = false;
     private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
             = new RemoteCallbackList<>();
-    private final DimLayer mDimLayer;
 
     private boolean mMinimizedDock;
     private int mOriginalDockedSide = DOCKED_INVALID;
@@ -141,13 +139,12 @@
     private boolean mImeHideRequested;
     private final Rect mLastDimLayerRect = new Rect();
     private float mLastDimLayerAlpha;
+    private TaskStack mDimmedStack;
 
     DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
         final Context context = service.mContext;
-        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
-                "DockedStackDim");
         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                 context, android.R.interpolator.fast_out_slow_in);
         loadDimens();
@@ -463,6 +460,11 @@
         }
         mOriginalDockedSide = DOCKED_INVALID;
         setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
+
+        if (mDimmedStack != null) {
+            mDimmedStack.stopDimming();
+            mDimmedStack = null;
+        }
     }
 
     /**
@@ -564,34 +566,12 @@
         final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
         if (visibleAndValid) {
-            stack.getDimBounds(mTmpRect);
-            if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
-                if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
-                    try {
-                        // TODO: This should use the regular animation transaction - here and below
-                        mService.openSurfaceTransaction();
-                        mDimLayer.setBounds(mTmpRect);
-                        mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
-                    } finally {
-                        mService.closeSurfaceTransaction("setResizeDimLayer");
-                    }
-                }
-                mLastDimLayerRect.set(mTmpRect);
-                mLastDimLayerAlpha = alpha;
-            } else {
-                visibleAndValid = false;
-            }
+            mDimmedStack = stack;
+            stack.dim(alpha);
         }
-        if (!visibleAndValid) {
-            if (mLastDimLayerAlpha != 0f) {
-                try {
-                    mService.openSurfaceTransaction();
-                    mDimLayer.hide();
-                } finally {
-                    mService.closeSurfaceTransaction("setResizeDimLayer");
-                }
-            }
-            mLastDimLayerAlpha = 0f;
+        if (!visibleAndValid && stack != null) {
+            mDimmedStack = null;
+            stack.stopDimming();
         }
     }
 
@@ -829,12 +809,8 @@
             return animateForMinimizedDockedStack(now);
         } else if (mAnimatingForIme) {
             return animateForIme(now);
-        } else {
-            if (mDimLayer != null && mDimLayer.isDimming()) {
-                mDimLayer.setLayer(getResizeDimLayer());
-            }
-            return false;
         }
+        return false;
     }
 
     private boolean animateForIme(long now) {
@@ -942,27 +918,6 @@
                 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
     }
 
-    @Override
-    public boolean dimFullscreen() {
-        return false;
-    }
-
-    @Override
-    public DisplayInfo getDisplayInfo() {
-        return mDisplayContent.getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return mDisplayContent != null;
-    }
-
-    @Override
-    public void getDimBounds(Rect outBounds) {
-        // This dim layer user doesn't need this.
-    }
-
-    @Override
     public String toShortString() {
         return TAG;
     }
@@ -977,10 +932,6 @@
         pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
         pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
         pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
-        if (mDimLayer.isDimming()) {
-            pw.println(prefix + "  Dim layer is dimming: ");
-            mDimLayer.printTo(prefix + "    ", pw);
-        }
     }
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 8bec8d7..fddf6ca 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -49,19 +49,19 @@
     private int mRotation;
     private boolean mVisible;
 
-    public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+    public EmulatorDisplayOverlay(Context context, DisplayContent dc,
             int zOrder) {
+        final Display display = dc.getDisplay();
         mScreenSize = new Point();
         display.getSize(mScreenSize);
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("EmulatorDisplayOverlay")
                     .setSize(mScreenSize.x, mScreenSize.y)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(zOrder);
             ctrl.setPosition(0, 0);
             ctrl.show();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f541926..43dfccc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -138,7 +138,6 @@
     ParcelFileDescriptor mSurfaceTraceFd;
     RemoteEventTrace mRemoteEventTrace;
 
-    private final WindowLayersController mLayersController;
     final WallpaperController mWallpaperController;
 
     private final Handler mHandler;
@@ -163,7 +162,6 @@
     RootWindowContainer(WindowManagerService service) {
         mService = service;
         mHandler = new MyHandler(service.mH.getLooper());
-        mLayersController = new WindowLayersController(mService);
         mWallpaperController = new WallpaperController(mService);
     }
 
@@ -231,7 +229,7 @@
     }
 
     private DisplayContent createDisplayContent(final Display display) {
-        final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+        final DisplayContent dc = new DisplayContent(display, mService,
                 mWallpaperController);
         final int displayId = display.getDisplayId();
 
@@ -1103,4 +1101,9 @@
     String getName() {
         return "ROOT";
     }
+
+    @Override
+    void scheduleAnimation() {
+        mService.scheduleAnimationLocked();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 3350fea..70bf15c 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -225,7 +225,7 @@
     }
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
-            SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
+            boolean inTransaction, boolean forceDefaultOrientation,
             boolean isSecure, WindowManagerService service) {
         mService = service;
         mContext = context;
@@ -269,7 +269,7 @@
 
         try {
             try {
-                mSurfaceControl = new SurfaceControl.Builder(session)
+                mSurfaceControl = displayContent.makeOverlay()
                         .setName("ScreenshotSurface")
                         .setSize(mWidth, mHeight)
                         .setSecure(isSecure)
@@ -281,7 +281,6 @@
                 // TODO(multidisplay): we should use the proper display
                 SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
                         SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
-                mSurfaceControl.setLayerStack(display.getLayerStack());
                 mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
                 mSurfaceControl.setAlpha(0);
                 mSurfaceControl.show();
@@ -370,11 +369,11 @@
     }
 
     // Must be called while in a transaction.
-    public boolean setRotationInTransaction(int rotation, SurfaceSession session,
+    public boolean setRotationInTransaction(int rotation,
             long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
         setRotationInTransaction(rotation);
         if (TWO_PHASE_ANIMATION) {
-            return startAnimation(session, maxAnimationDuration, animationScale,
+            return startAnimation(maxAnimationDuration, animationScale,
                     finalWidth, finalHeight, false, 0, 0);
         }
 
@@ -385,7 +384,7 @@
     /**
      * Returns true if animating.
      */
-    private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+    private boolean startAnimation(long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, boolean dismissing,
             int exitAnim, int enterAnim) {
         if (mSurfaceControl == null) {
@@ -561,8 +560,8 @@
                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
                         mOriginalWidth*2, mOriginalHeight*2);
                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
-                mCustomBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
+                mCustomBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
                 mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -601,8 +600,8 @@
                             mOriginalWidth*2, mOriginalHeight*2);
                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
                 }
-                mExitingBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
+                mExitingBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
                 mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -624,8 +623,8 @@
                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
                         finalWidth*2, finalHeight*2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
-                mEnteringBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
+                mEnteringBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
@@ -642,7 +641,7 @@
     /**
      * Returns true if animating.
      */
-    public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+    public boolean dismiss(long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
         if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
         if (mSurfaceControl == null) {
@@ -650,7 +649,7 @@
             return false;
         }
         if (!mStarted) {
-            startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+            startAnimation(maxAnimationDuration, animationScale, finalWidth, finalHeight,
                     true, exitAnim, enterAnim);
         }
         if (!mStarted) {
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index eb8ee69..f51a6a9 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -41,15 +41,14 @@
     private boolean mDrawNeeded;
     private final int mThickness = 20;
 
-    public StrictModeFlash(Display display, SurfaceSession session) {
+    public StrictModeFlash(DisplayContent dc) {
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("StrictModeFlash")
                     .setSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
             ctrl.setPosition(0, 0);
             ctrl.show();
diff --git a/core/java/android/os/IStatsCallbacks.aidl b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
similarity index 62%
rename from core/java/android/os/IStatsCallbacks.aidl
rename to services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
index 02e7cd3..5390e5a 100644
--- a/core/java/android/os/IStatsCallbacks.aidl
+++ b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.os;
+package com.android.server.wm;
 
-/**
-  * Callback for Statsd to allow binder calls to clients.
-  * {@hide}
-  */
-interface IStatsCallbacks {
-    void onReceiveLogs(out byte[] log);
-}
+import android.view.SurfaceSession;
+import android.view.SurfaceControl;
+
+interface SurfaceBuilderFactory {
+    SurfaceControl.Builder make(SurfaceSession s);
+};
+
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 13435d7..f70845e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -51,7 +51,7 @@
 import java.io.PrintWriter;
 import java.util.function.Consumer;
 
-class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerUser {
+class Task extends WindowContainer<AppWindowToken> {
     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
     // Return value from {@link setBounds} indicating no change was made to the Task bounds.
     private static final int BOUNDS_CHANGE_NONE = 0;
@@ -105,6 +105,9 @@
     // stack moves and we in fact do so when moving from full screen to pinned.
     private boolean mPreserveNonFloatingState = false;
 
+    private Dimmer mDimmer = new Dimmer(this);
+    private final Rect mTmpDimBoundsRect = new Rect();
+
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
             int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
             TaskWindowContainerController controller) {
@@ -188,12 +191,6 @@
         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
         mDeferRemoval = false;
 
-        // Make sure to remove dim layer user first before removing task its from parent.
-        DisplayContent content = getDisplayContent();
-        if (content != null) {
-            content.mDimLayerController.removeDimLayerUser(this);
-        }
-
         super.removeImmediately();
     }
 
@@ -237,6 +234,8 @@
 
     @Override
     void onParentSet() {
+        super.onParentSet();
+
         // Update task bounds if needed.
         updateDisplayInfo(getDisplayContent());
 
@@ -312,9 +311,7 @@
         mBounds.set(bounds);
 
         mRotation = rotation;
-        if (displayContent != null) {
-            displayContent.mDimLayerController.updateDimLayer(this);
-        }
+
         onOverrideConfigurationChanged(overrideConfig);
         return boundsChange;
     }
@@ -482,7 +479,6 @@
     }
 
     /** Bounds of the task to be used for dimming, as well as touch related tests. */
-    @Override
     public void getDimBounds(Rect out) {
         final DisplayContent displayContent = mStack.getDisplayContent();
         // It doesn't matter if we in particular are part of the resize, since we couldn't have
@@ -634,23 +630,6 @@
         return null;
     }
 
-    @Override
-    public boolean dimFullscreen() {
-        return isFullscreen();
-    }
-
-    @Override
-    public int getLayerForDim(WindowStateAnimator animator, int layerOffset, int defaultLayer) {
-        // If the dim layer is for a starting window, move the dim layer back in the z-order behind
-        // the lowest activity window to ensure it does not occlude the main window if it is
-        // translucent
-        final AppWindowToken appToken = animator.mWin.mAppToken;
-        if (animator.mAttrType == TYPE_APPLICATION_STARTING && hasChild(appToken) ) {
-            return Math.min(defaultLayer, appToken.getLowestAnimLayer() - layerOffset);
-        }
-        return defaultLayer;
-    }
-
     boolean isFullscreen() {
         if (useCurrentBounds()) {
             return mFillsParent;
@@ -661,16 +640,6 @@
         return true;
     }
 
-    @Override
-    public DisplayInfo getDisplayInfo() {
-        return getDisplayContent().getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return getDisplayContent() != null;
-    }
-
     void forceWindowsScaleable(boolean force) {
         mService.openSurfaceTransaction();
         try {
@@ -718,9 +687,18 @@
         mPreserveNonFloatingState = false;
     }
 
+    Dimmer getDimmer() {
+        return mDimmer;
+    }
+
     @Override
-    public String toShortString() {
-        return "Task=" + mTaskId;
+    void prepareSurfaces() {
+        mDimmer.resetDimStates();
+        super.prepareSurfaces();
+        getDimBounds(mTmpDimBoundsRect);
+        if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+            scheduleAnimation();
+        }
     }
 
     @CallSuper
@@ -757,4 +735,8 @@
             wtoken.dump(pw, triplePrefix);
         }
     }
+
+    String toShortString() {
+        return "Task=" + mTaskId;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 12f6b5a..5d4ba09 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -59,7 +59,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-class TaskPositioner implements DimLayer.DimLayerUser {
+class TaskPositioner {
     private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
     private static final String TAG_LOCAL = "TaskPositioner";
     private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
@@ -99,9 +99,6 @@
     private WindowPositionerEventReceiver mInputEventReceiver;
     private Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
-    private DimLayer mDimLayer;
-    @CtrlType
-    private int mCurrentDimSide;
     private Rect mTmpRect = new Rect();
     private int mSideMargin;
     private int mMinVisibleWidth;
@@ -207,15 +204,6 @@
                             mService.mActivityManager.resizeTask(
                                     mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
                         }
-
-                        if (mCurrentDimSide != CTRL_NONE) {
-                            final int createMode = mCurrentDimSide == CTRL_LEFT
-                                    ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                                    : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-                            mService.mActivityManager.setTaskWindowingModeSplitScreenPrimary(
-                                    mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
-                                    null /* initialBounds */);
-                        }
                     } catch(RemoteException e) {}
 
                     // Post back to WM to handle clean-ups. We still need the input
@@ -243,7 +231,9 @@
     /**
      * @param display The Display that the window being dragged is on.
      */
-    void register(Display display) {
+    void register(DisplayContent displayContent) {
+        final Display display = displayContent.getDisplay();
+
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "Registering task positioner");
         }
@@ -305,7 +295,6 @@
         }
         mService.pauseRotationLocked();
 
-        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
@@ -336,12 +325,6 @@
         mDragWindowHandle = null;
         mDragApplicationHandle = null;
         mDisplay = null;
-
-        if (mDimLayer != null) {
-            mDimLayer.destroySurface();
-            mDimLayer = null;
-        }
-        mCurrentDimSide = CTRL_NONE;
         mDragEnded = true;
 
         // Resume rotations after a drag.
@@ -434,7 +417,6 @@
         }
 
         updateWindowDragBounds(nX, nY, mTmpRect);
-        updateDimLayerVisibility(nX);
         return false;
     }
 
@@ -621,88 +603,6 @@
                 "updateWindowDragBounds: " + mWindowDragBounds);
     }
 
-    private void updateDimLayerVisibility(int x) {
-        @CtrlType
-        int dimSide = getDimSide(x);
-        if (dimSide == mCurrentDimSide) {
-            return;
-        }
-
-        mCurrentDimSide = dimSide;
-
-        if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
-        mService.openSurfaceTransaction();
-        if (mCurrentDimSide == CTRL_NONE) {
-            mDimLayer.hide();
-        } else {
-            showDimLayer();
-        }
-        mService.closeSurfaceTransaction("updateDimLayerVisibility");
-    }
-
-    /**
-     * Returns the side of the screen the dim layer should be shown.
-     * @param x horizontal coordinate used to determine if the dim layer should be shown
-     * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
-     * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
-     * shouldn't be shown.
-     */
-    private int getDimSide(int x) {
-        if (!mTask.mStack.inFreeformWindowingMode()
-                || !mTask.mStack.fillsParent()
-                || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
-            return CTRL_NONE;
-        }
-
-        mTask.mStack.getDimBounds(mTmpRect);
-        if (x - mSideMargin <= mTmpRect.left) {
-            return CTRL_LEFT;
-        }
-        if (x + mSideMargin >= mTmpRect.right) {
-            return CTRL_RIGHT;
-        }
-
-        return CTRL_NONE;
-    }
-
-    private void showDimLayer() {
-        mTask.mStack.getDimBounds(mTmpRect);
-        if (mCurrentDimSide == CTRL_LEFT) {
-            mTmpRect.right = mTmpRect.centerX();
-        } else if (mCurrentDimSide == CTRL_RIGHT) {
-            mTmpRect.left = mTmpRect.centerX();
-        }
-
-        mDimLayer.setBounds(mTmpRect);
-        mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
-                RESIZING_HINT_DURATION_MS);
-    }
-
-    @Override /** {@link DimLayer.DimLayerUser} */
-    public boolean dimFullscreen() {
-        return isFullscreen();
-    }
-
-    boolean isFullscreen() {
-        return false;
-    }
-
-    @Override /** {@link DimLayer.DimLayerUser} */
-    public DisplayInfo getDisplayInfo() {
-        return mTask.mStack.getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return mTask != null && mTask.getDisplayContent() != null;
-    }
-
-    @Override
-    public void getDimBounds(Rect out) {
-        // This dim layer user doesn't need this.
-    }
-
-    @Override
     public String toShortString() {
         return TAG;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 053fb47..f9062a8 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -55,6 +55,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.Surface;
+import android.view.SurfaceControl;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -63,7 +64,7 @@
 
 import java.io.PrintWriter;
 
-public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
+public class TaskStack extends WindowContainer<Task> implements
         BoundsAnimationTarget {
     /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
      * restrict IME adjustment so that a min portion of top stack remains visible.*/
@@ -108,8 +109,8 @@
     /** Density as of last time {@link #mBounds} was set. */
     private int mDensity;
 
-    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
-    private DimLayer mAnimationBackgroundSurface;
+    private SurfaceControl mAnimationBackgroundSurface;
+    private boolean mAnimationBackgroundSurfaceIsShown = false;
 
     /** The particular window with an Animation with non-zero background color. */
     private WindowStateAnimator mAnimationBackgroundAnimator;
@@ -149,6 +150,13 @@
 
     Rect mPreAnimationBounds = new Rect();
 
+    private Dimmer mDimmer = new Dimmer(this);
+
+    /**
+     * For {@link #prepareSurfaces}.
+     */
+    final Rect mTmpDimBoundsRect = new Rect();
+
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
         mService = service;
         mStackId = stackId;
@@ -245,6 +253,35 @@
         }
     }
 
+    private void setAnimationBackgroundBounds(Rect bounds) {
+        if (mAnimationBackgroundSurface == null) {
+            return;
+        }
+        getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height())
+                .setPosition(mAnimationBackgroundSurface, 0, 0);
+        scheduleAnimation();
+    }
+
+    private void hideAnimationSurface() {
+        if (mAnimationBackgroundSurface == null) {
+            return;
+        }
+        getPendingTransaction().hide(mAnimationBackgroundSurface);
+        mAnimationBackgroundSurfaceIsShown = false;
+        scheduleAnimation();
+    }
+
+    private void showAnimationSurface(float alpha) {
+        if (mAnimationBackgroundSurface == null) {
+            return;
+        }
+        getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
+                .setAlpha(mAnimationBackgroundSurface, alpha)
+                .show(mAnimationBackgroundSurface);
+        mAnimationBackgroundSurfaceIsShown = true;
+        scheduleAnimation();
+    }
+
     private boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFillsParent;
         int rotation = Surface.ROTATION_0;
@@ -267,10 +304,7 @@
             return false;
         }
 
-        if (mDisplayContent != null) {
-            mDisplayContent.mDimLayerController.updateDimLayer(this);
-            mAnimationBackgroundSurface.setBounds(bounds);
-        }
+        setAnimationBackgroundBounds(bounds);
 
         mBounds.set(bounds);
         mRotation = rotation;
@@ -368,7 +402,6 @@
     }
 
     /** Bounds of the stack with other system factors taken into consideration. */
-    @Override
     public void getDimBounds(Rect out) {
         getBounds(out);
     }
@@ -700,9 +733,12 @@
         }
 
         mDisplayContent = dc;
-        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
-                "animation background stackId=" + mStackId);
+
         updateBoundsForWindowModeChange();
+        mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
+            .setName("animation background stackId=" + mStackId)
+            .build();
+
         super.onDisplayChanged(dc);
     }
 
@@ -914,16 +950,16 @@
 
     @Override
     void onParentSet() {
+        super.onParentSet();
+
         if (getParent() != null || mDisplayContent == null) {
             return;
         }
 
-        // Looks like the stack was removed from the display. Go ahead and clean things up.
-        mDisplayContent.mDimLayerController.removeDimLayerUser(this);
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
         if (mAnimationBackgroundSurface != null) {
-            mAnimationBackgroundSurface.destroySurface();
+            mAnimationBackgroundSurface.destroy();
             mAnimationBackgroundSurface = null;
         }
 
@@ -933,9 +969,7 @@
 
     void resetAnimationBackgroundAnimator() {
         mAnimationBackgroundAnimator = null;
-        if (mAnimationBackgroundSurface != null) {
-            mAnimationBackgroundSurface.hide();
-        }
+        hideAnimationSurface();
     }
 
     void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
@@ -944,8 +978,7 @@
                 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
             mAnimationBackgroundAnimator = winAnimator;
             animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
-            mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
-                    ((color >> 24) & 0xff) / 255f, 0);
+            showAnimationSurface(((color >> 24) & 0xff) / 255f);
         }
     }
 
@@ -1250,7 +1283,7 @@
         }
         proto.write(FILLS_PARENT, mFillsParent);
         mBounds.writeToProto(proto, BOUNDS);
-        proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+        proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
         proto.end(token);
     }
 
@@ -1273,9 +1306,8 @@
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
             mChildren.get(taskNdx).dump(prefix + "  ", pw);
         }
-        if (mAnimationBackgroundSurface.isDimming()) {
-            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
-            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
+        if (mAnimationBackgroundSurfaceIsShown) {
+            pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
         }
         if (!mExitingAppTokens.isEmpty()) {
             pw.println();
@@ -1299,11 +1331,6 @@
     }
 
     @Override
-    public boolean dimFullscreen() {
-        return !isActivityTypeStandard() || fillsParent();
-    }
-
-    @Override
     boolean fillsParent() {
         if (useCurrentBounds()) {
             return mFillsParent;
@@ -1315,16 +1342,6 @@
     }
 
     @Override
-    public DisplayInfo getDisplayInfo() {
-        return mDisplayContent.getDisplayInfo();
-    }
-
-    @Override
-    public boolean isAttachedToDisplay() {
-        return mDisplayContent != null;
-    }
-
-    @Override
     public String toString() {
         return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
     }
@@ -1333,7 +1350,6 @@
         return toShortString();
     }
 
-    @Override
     public String toShortString() {
         return "Stack=" + mStackId;
     }
@@ -1691,4 +1707,32 @@
                 || activityType == ACTIVITY_TYPE_RECENTS
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
+
+    Dimmer getDimmer() {
+        return mDimmer;
+    }
+
+    @Override
+    void prepareSurfaces() {
+        mDimmer.resetDimStates();
+        super.prepareSurfaces();
+        getDimBounds(mTmpDimBoundsRect);
+        if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+            scheduleAnimation();
+        }
+    }
+
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayContent.getDisplayInfo();
+    }
+
+    void dim(float alpha) {
+        mDimmer.dimAbove(getPendingTransaction(), alpha);
+        scheduleAnimation();
+    }
+
+    void stopDimming() {
+        mDimmer.stopDim(getPendingTransaction());
+        scheduleAnimation();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index d97aaac..9216b66 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -53,7 +53,7 @@
     private int mLastDH;
     private boolean mDrawNeeded;
 
-    Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+    Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
         if (false) {
             Log.i(TAG_WM, "*********************** WATERMARK");
             for (int i=0; i<tokens.length; i++) {
@@ -61,7 +61,7 @@
             }
         }
 
-        mDisplay = display;
+        mDisplay = dc.getDisplay();
         mTokens = tokens;
 
         StringBuilder builder = new StringBuilder(32);
@@ -114,7 +114,7 @@
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("WatermarkSurface")
                     .setSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 1912095..20bade67 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -200,7 +200,7 @@
                     ++mAnimTransactionSequence;
                     dc.updateWindowsForAnimator(this);
                     dc.updateWallpaperForAnimator(this);
-                    dc.prepareWindowSurfaces();
+                    dc.prepareSurfaces();
                 }
 
                 for (int i = 0; i < numDisplays; i++) {
@@ -214,8 +214,6 @@
                     if (screenRotationAnimation != null) {
                         screenRotationAnimation.updateSurfacesInTransaction();
                     }
-
-                    orAnimating(dc.animateDimLayers());
                     orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
                     //TODO (multidisplay): Magnification is supported only for the default display.
                     if (accessibilityController != null && dc.isDefaultDisplay) {
@@ -237,6 +235,13 @@
                 if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
             }
 
+            final int numDisplays = mDisplayContentsAnimators.size();
+            for (int i = 0; i < numDisplays; i++) {
+                final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+                dc.onPendingTransactionApplied();
+            }
+
             boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
             boolean doRequest = false;
             if (mBulkUpdateParams != 0) {
@@ -271,6 +276,7 @@
             mService.destroyPreservedSurfaceLocked();
             mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
+
             if (DEBUG_WINDOW_TRACE) {
                 Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
                         + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8f4b897..a5e6288 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,9 +21,13 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static android.view.SurfaceControl.Transaction;
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.util.Pools;
 
 import android.util.proto.ProtoOutputStream;
@@ -64,7 +68,14 @@
             new Pools.SynchronizedPool<>(3);
 
     // The owner/creator for this container. No controller if null.
-    private WindowContainerController mController;
+     WindowContainerController mController;
+
+    protected SurfaceControl mSurfaceControl;
+
+    /**
+     * Applied as part of the animation pass in "prepareSurfaces".
+     */
+    private Transaction mPendingTransaction = new Transaction();
 
     @Override
     final protected WindowContainer getParent() {
@@ -101,7 +112,22 @@
      * Supposed to be overridden and contain actions that should be executed after parent was set.
      */
     void onParentSet() {
-        // Do nothing by default.
+        if (mParent == null) {
+            return;
+        }
+        if (mSurfaceControl == null) {
+            // If we don't yet have a surface, but we now have a parent, we should
+            // build a surface.
+            mSurfaceControl = makeSurface().build();
+            getPendingTransaction().show(mSurfaceControl);
+        } else {
+            // If we have a surface but a new parent, we just need to perform a reparent.
+            getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+        }
+
+        // Either way we need to ask the parent to assign us a Z-order.
+        mParent.assignChildLayers();
+        scheduleAnimation();
     }
 
     // Temp. holders for a chain of containers we are currently processing.
@@ -188,6 +214,11 @@
             mChildren.remove(child);
         }
 
+        if (mSurfaceControl != null) {
+            destroyAfterPendingTransaction(mSurfaceControl);
+            mSurfaceControl = null;
+        }
+
         if (mParent != null) {
             mParent.removeChild(this);
         }
@@ -195,6 +226,7 @@
         if (mController != null) {
             setController(null);
         }
+
     }
 
     /**
@@ -407,7 +439,7 @@
     }
 
     /**
-a     * Returns whether this child is on top of the window hierarchy.
+     * @return Whether this child is on top of the window hierarchy.
      */
     boolean isOnTop() {
         return getParent().getTopChild() == this && getParent().isOnTop();
@@ -673,6 +705,103 @@
         mController = controller;
     }
 
+    SurfaceControl.Builder makeSurface() {
+        final WindowContainer p = getParent();
+        return p.makeChildSurface(this);
+    }
+
+    /**
+     * @param child The WindowContainer this child surface is for, or null if the Surface
+     *              is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
+     */
+    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+        final WindowContainer p = getParent();
+        // Give the parent a chance to set properties. In hierarchy v1 we rely
+        // on this to set full-screen dimensions on all our Surface-less Layers.
+        final SurfaceControl.Builder b = p.makeChildSurface(child);
+        if (child != null && child.isScreenOverlay()) {
+            // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
+            // WindowContainer hierarchy vs the SurfaceControl hierarchy)
+            // and we shouldn't set ourselves as the parent.
+            return b;
+        } else {
+            return b.setParent(mSurfaceControl);
+        }
+    }
+
+    /**
+     * There are various layers which require promotion from the WindowContainer
+     * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
+     * for the particular usage.
+     *
+     * TODO: Perhaps this should be eliminated, either through modifying
+     * the window container hierarchy or through modifying the way we express these overlay
+     * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
+     * Flash and not actually use a WindowState).
+     */
+    boolean isScreenOverlay() {
+        return false;
+    }
+
+    /**
+     * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
+     */
+    boolean shouldMagnify() {
+        for (int i = 0; i < mChildren.size(); i++) {
+            if (!mChildren.get(i).shouldMagnify()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    SurfaceSession getSession() {
+        if (getParent() != null) {
+            return getParent().getSession();
+        }
+        return null;
+    }
+
+    void assignLayer(Transaction t, int layer) {
+        if (mSurfaceControl != null) {
+            t.setLayer(mSurfaceControl, layer);
+        }
+    }
+
+    void assignChildLayers(Transaction t) {
+        int layer = 0;
+        boolean boosting = false;
+
+        // We use two passes as a way to promote children which
+        // need Z-boosting to the end of the list.
+        for (int i = 0; i < 2; i++ ) {
+            for (int j = 0; j < mChildren.size(); ++j) {
+                final WindowContainer wc = mChildren.get(j);
+                if (wc.needsZBoost() && !boosting) {
+                    continue;
+                }
+                wc.assignLayer(t, layer);
+                wc.assignChildLayers(t);
+
+                layer++;
+            }
+            boosting = true;
+        }
+    }
+
+    void assignChildLayers() {
+        assignChildLayers(getPendingTransaction());
+    }
+
+    boolean needsZBoost() {
+        for (int i = 0; i < mChildren.size(); i++) {
+            if (mChildren.get(i).needsZBoost()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
      * {@link com.android.server.wm.proto.WindowContainerProto}.
@@ -719,4 +848,59 @@
             mConsumerWrapperPool.release(this);
         }
     }
+
+    // TODO(b/68336570): Should this really be on WindowContainer since it
+    // can only be used on the top-level nodes that aren't animated?
+    // (otherwise we would be fighting other callers of setMatrix).
+    void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
+        if (shouldMagnify()) {
+            t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
+                    .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
+        } else {
+            for (int i = 0; i < mChildren.size(); i++) {
+                mChildren.get(i).applyMagnificationSpec(t, spec);
+            }
+        }
+    }
+
+    /**
+     * TODO: Once we totally eliminate global transaction we will pass transaction in here
+     * rather than merging to global.
+     */
+    void prepareSurfaces() {
+        SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
+        for (int i = 0; i < mChildren.size(); i++) {
+            mChildren.get(i).prepareSurfaces();
+        }
+    }
+
+    /**
+     * Trigger a call to prepareSurfaces from the animation thread, such that
+     * mPendingTransaction will be applied.
+     */
+    void scheduleAnimation() {
+        if (mParent != null) {
+            mParent.scheduleAnimation();
+        }
+    }
+
+    SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
+    /**
+     * Destroy a given surface after executing mPendingTransaction. This is
+     * largely a workaround for destroy not being part of transactions
+     * rather than an intentional design, so please take care when
+     * expanding use.
+     */
+    void destroyAfterPendingTransaction(SurfaceControl surface) {
+        if (mParent != null) {
+            mParent.destroyAfterPendingTransaction(surface);
+        }
+    }
+    
+    Transaction getPendingTransaction() {
+        return mPendingTransaction;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
deleted file mode 100644
index 7caf2fe..0000000
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.util.Slog;
-
-import java.util.ArrayDeque;
-import java.util.function.Consumer;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
-
-/**
- * Controller for assigning layers to windows on the display.
- *
- * This class encapsulates general algorithm for assigning layers and special rules that we need to
- * apply on top. The general algorithm goes through windows from bottom to the top and the higher
- * the window is, the higher layer is assigned. The final layer is equal to base layer +
- * adjustment from the order. This means that the window list is assumed to be ordered roughly by
- * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
- * handled with care, because they break the algorithm).
- *
- * On top of the general algorithm we add special rules, that govern such amazing things as:
- * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
- * <li>docked/pinned windows (that need to be lifted above other application windows, including
- * animations)
- * <li>dock divider (which needs to live above applications, but below IME)</li>
- * <li>replaced windows, which need to live above their normal level, because they anticipate
- * an animation</li>.
- */
-class WindowLayersController {
-    private final WindowManagerService mService;
-
-    WindowLayersController(WindowManagerService service) {
-        mService = service;
-    }
-
-    private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
-    private WindowState mDockDivider = null;
-    private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
-    private int mCurBaseLayer;
-    private int mCurLayer;
-    private boolean mAnyLayerChanged;
-    private int mHighestApplicationLayer;
-    private int mHighestDockedAffectedLayer;
-    private int mHighestLayerInImeTargetBaseLayer;
-    private WindowState mImeTarget;
-    private boolean mAboveImeTarget;
-    private ArrayDeque<WindowState> mAboveImeTargetAppWindows = new ArrayDeque();
-
-    private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
-        boolean layerChanged = false;
-
-        int oldLayer = w.mLayer;
-        if (w.mBaseLayer == mCurBaseLayer) {
-            mCurLayer += WINDOW_LAYER_MULTIPLIER;
-        } else {
-            mCurBaseLayer = mCurLayer = w.mBaseLayer;
-        }
-        assignAnimLayer(w, mCurLayer);
-
-        // TODO: Preserved old behavior of code here but not sure comparing oldLayer to
-        // mAnimLayer and mLayer makes sense...though the worst case would be unintentional
-        // layer reassignment.
-        if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
-            layerChanged = true;
-            mAnyLayerChanged = true;
-        }
-
-        if (w.mAppToken != null) {
-            mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-        if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
-            mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-        if (w.getAppToken() != null && w.inSplitScreenSecondaryWindowingMode()) {
-            mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-
-        collectSpecialWindows(w);
-
-        if (layerChanged) {
-            w.scheduleAnimationIfDimming();
-        }
-    };
-
-    final void assignWindowLayers(DisplayContent dc) {
-        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based",
-                new RuntimeException("here").fillInStackTrace());
-
-        reset();
-        dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
-
-        adjustSpecialWindows();
-
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && mAnyLayerChanged
-                && dc.getDisplayId() == DEFAULT_DISPLAY) {
-            mService.mAccessibilityController.onWindowLayersChangedLocked();
-        }
-
-        if (DEBUG_LAYERS) logDebugLayers(dc);
-    }
-
-    private void logDebugLayers(DisplayContent dc) {
-        dc.forAllWindows((w) -> {
-            final WindowStateAnimator winAnimator = w.mWinAnimator;
-            Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
-                    + " mLayer=" + w.mLayer + (w.mAppToken == null
-                    ? "" : " mAppLayer=" + w.mAppToken.getAnimLayerAdjustment())
-                    + " =mAnimLayer=" + winAnimator.mAnimLayer);
-        }, false /* traverseTopToBottom */);
-    }
-
-    private void reset() {
-        mPinnedWindows.clear();
-        mInputMethodWindows.clear();
-        mDockedWindows.clear();
-        mAssistantWindows.clear();
-        mReplacingWindows.clear();
-        mDockDivider = null;
-
-        mCurBaseLayer = 0;
-        mCurLayer = 0;
-        mAnyLayerChanged = false;
-
-        mHighestApplicationLayer = 0;
-        mHighestDockedAffectedLayer = 0;
-        mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
-        mImeTarget = mService.mInputMethodTarget;
-        mAboveImeTarget = false;
-        mAboveImeTargetAppWindows.clear();
-    }
-
-    private void collectSpecialWindows(WindowState w) {
-        if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
-            mDockDivider = w;
-            return;
-        }
-        if (w.mWillReplaceWindow) {
-            mReplacingWindows.add(w);
-        }
-        if (w.mIsImWindow) {
-            mInputMethodWindows.add(w);
-            return;
-        }
-        if (mImeTarget != null) {
-            if (w.getParentWindow() == mImeTarget && w.mSubLayer > 0) {
-                // Child windows of the ime target with a positive sub-layer should be placed above
-                // the IME.
-                mAboveImeTargetAppWindows.add(w);
-            } else if (mAboveImeTarget && w.mAppToken != null) {
-                // windows of apps above the IME target should be placed above the IME.
-                mAboveImeTargetAppWindows.add(w);
-            }
-            if (w == mImeTarget) {
-                mAboveImeTarget = true;
-            }
-        }
-
-        final int windowingMode = w.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            mPinnedWindows.add(w);
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mDockedWindows.add(w);
-        }
-        if (w.isActivityTypeAssistant()) {
-            mAssistantWindows.add(w);
-        }
-    }
-
-    private void adjustSpecialWindows() {
-        // The following adjustments are beyond the highest docked-affected layer
-        int layer = mHighestDockedAffectedLayer +  TYPE_LAYER_OFFSET;
-
-        // Adjust the docked stack windows and dock divider above only the windows that are affected
-        // by the docked stack. When this happens, also boost the assistant window layers, otherwise
-        // the docked stack windows & divider would be promoted above the assistant.
-        if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
-            while (!mDockedWindows.isEmpty()) {
-                final WindowState window = mDockedWindows.remove();
-                layer = assignAndIncreaseLayerIfNeeded(window, layer);
-            }
-
-            layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
-
-            while (!mAssistantWindows.isEmpty()) {
-                final WindowState window = mAssistantWindows.remove();
-                if (window.mLayer > mHighestDockedAffectedLayer) {
-                    layer = assignAndIncreaseLayerIfNeeded(window, layer);
-                }
-            }
-        }
-
-        // The following adjustments are beyond the highest app layer or boosted layer
-        layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
-
-        // We know that we will be animating a relaunching window in the near future, which will
-        // receive a z-order increase. We want the replaced window to immediately receive the same
-        // treatment, e.g. to be above the dock divider.
-        while (!mReplacingWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
-        }
-
-        while (!mPinnedWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
-        }
-
-        // Make sure IME is the highest window in the base layer of it's target.
-        if (mImeTarget != null) {
-            if (mImeTarget.mAppToken == null) {
-                // For non-app ime targets adjust the layer we start from to match what we found
-                // when assigning layers. Otherwise, just use the highest app layer we have some far.
-                layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER;
-            }
-
-            while (!mInputMethodWindows.isEmpty()) {
-                layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
-            }
-
-            // Adjust app windows the should be displayed above the IME since they are above the IME
-            // target.
-            while (!mAboveImeTargetAppWindows.isEmpty()) {
-                layer = assignAndIncreaseLayerIfNeeded(mAboveImeTargetAppWindows.remove(), layer);
-            }
-        }
-
-    }
-
-    private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
-        if (win != null) {
-            assignAnimLayer(win, layer);
-            // Make sure we leave space in-between normal windows for dims and such.
-            layer += WINDOW_LAYER_MULTIPLIER;
-        }
-        return layer;
-    }
-
-    private void assignAnimLayer(WindowState w, int layer) {
-        w.mLayer = layer;
-        w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
-                + w.getSpecialWindowAnimLayerAdjustment();
-        if (w.mAppToken != null) {
-            w.mAppToken.mAppAnimator.updateThumbnailLayer();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ce34306..4656539 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -529,7 +529,6 @@
 
     AccessibilityController mAccessibilityController;
 
-    final SurfaceSession mFxSession;
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
     CircularDisplayMask mCircularDisplayMask;
@@ -804,6 +803,13 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
+    class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
+        public SurfaceControl.Builder make(SurfaceSession s) {
+            return new SurfaceControl.Builder(s);
+        }
+    };
+    SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+
     static void boostPriorityForLockedSection() {
         sThreadPriorityBooster.boost();
     }
@@ -992,7 +998,6 @@
             mPointerEventDispatcher = null;
         }
 
-        mFxSession = new SurfaceSession();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mDisplays = mDisplayManager.getDisplays();
         for (Display display : mDisplays) {
@@ -2478,11 +2483,8 @@
                 mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
-                if (displayContent.isDimming()) {
-                    anim[0] = anim[1] = 0;
-                } else {
-                    mPolicy.selectRotationAnimationLw(anim);
-                }
+                mPolicy.selectRotationAnimationLw(anim);
+
                 startFreezingDisplayLocked(false, anim[0], anim[1], displayContent);
                 config = new Configuration(mTempConfiguration);
             }
@@ -3592,8 +3594,7 @@
                                 com.android.internal.R.dimen.circular_display_mask_thickness);
 
                         mCircularDisplayMask = new CircularDisplayMask(
-                                getDefaultDisplayContentLocked().getDisplay(),
-                                mFxSession,
+                                getDefaultDisplayContentLocked(),
                                 mPolicy.getWindowLayerFromTypeLw(
                                         WindowManager.LayoutParams.TYPE_POINTER)
                                         * TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
@@ -3621,8 +3622,7 @@
                 if (mEmulatorDisplayOverlay == null) {
                     mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
                             mContext,
-                            getDefaultDisplayContentLocked().getDisplay(),
-                            mFxSession,
+                            getDefaultDisplayContentLocked(),
                             mPolicy.getWindowLayerFromTypeLw(
                                     WindowManager.LayoutParams.TYPE_POINTER)
                                     * TYPE_LAYER_MULTIPLIER + 10);
@@ -3670,7 +3670,7 @@
                 // TODO(multi-display): support multiple displays
                 if (mStrictModeFlash == null) {
                     mStrictModeFlash = new StrictModeFlash(
-                            getDefaultDisplayContentLocked().getDisplay(), mFxSession);
+                            getDefaultDisplayContentLocked());
                 }
                 mStrictModeFlash.setVisibility(on);
             } finally {
@@ -4522,7 +4522,7 @@
 
         Display display = displayContent.getDisplay();
         mTaskPositioner = new TaskPositioner(this);
-        mTaskPositioner.register(display);
+        mTaskPositioner.register(displayContent);
         mInputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We need to grab the touch focus so that the touch events during the
@@ -5892,7 +5892,7 @@
 
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
+                    inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
                     this);
             mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                     screenRotationAnimation);
@@ -5952,11 +5952,10 @@
             // TODO(multidisplay): rotation on main screen only.
             DisplayInfo displayInfo = displayContent.getDisplayInfo();
             // Get rotation animation again, with new top window
-            boolean isDimming = displayContent.isDimming();
-            if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
+            if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
                 mExitAnimId = mEnterAnimId = 0;
             }
-            if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+            if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
                     getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                         displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                 scheduleAnimationLocked();
@@ -6040,8 +6039,8 @@
                 if (toks != null && toks.length > 0) {
                     // TODO(multi-display): Show watermarks on secondary displays.
                     final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                    mWatermark = new Watermark(displayContent.getDisplay(),
-                            displayContent.mRealDisplayMetrics, mFxSession, toks);
+                    mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+                            toks);
                 }
             }
         } catch (FileNotFoundException e) {
@@ -7557,4 +7556,13 @@
             w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
         }, false /* traverseTopToBottom */);
     }
+
+    public void applyMagnificationSpec(MagnificationSpec spec) {
+        getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+    }
+
+    SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
+        return mSurfaceBuilderFactory.make(s);
+    }
 }
+
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6b1932d..9f77886 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -23,6 +23,7 @@
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -41,6 +42,7 @@
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -54,6 +56,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -147,6 +152,8 @@
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
@@ -602,6 +609,14 @@
                 };
             };
 
+    /**
+     * Indicates whether we have requested a Dim (in the sense of {@link Dimmer}) from our host
+     * container.
+     */
+    private boolean mIsDimming = false;
+
+    private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
@@ -2034,23 +2049,6 @@
         return isVisibleOrAdding();
     }
 
-    void scheduleAnimationIfDimming() {
-        final DisplayContent dc = getDisplayContent();
-        if (dc == null) {
-            return;
-        }
-
-        // If layout is currently deferred, we want to hold of with updating the layers.
-        if (mService.mWindowPlacerLocked.isLayoutDeferred()) {
-            return;
-        }
-        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-        if (dimLayerUser != null && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator)) {
-            // Force an animation pass just to update the mDimLayer layer.
-            mService.scheduleAnimationLocked();
-        }
-    }
-
     private final class DeadWindowEventReceiver extends InputEventReceiver {
         DeadWindowEventReceiver(InputChannel inputChannel) {
             super(inputChannel, mService.mH.getLooper());
@@ -2106,31 +2104,12 @@
         mInputWindowHandle.inputChannel = null;
     }
 
-    void applyDimLayerIfNeeded() {
-        // When the app is terminated (eg. from Recents), the task might have already been
-        // removed with the window pending removal. Don't apply dim in such cases, as there
-        // will be no more updateDimLayer() calls, which leaves the dimlayer invalid.
-        final AppWindowToken token = mAppToken;
-        if (token != null && token.removed) {
-            return;
-        }
-
-        final DisplayContent dc = getDisplayContent();
-        if (!mAnimatingExit && mAppDied) {
-            // If app died visible, apply a dim over the window to indicate that it's inactive
-            dc.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
-        } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
-                && dc != null && !mAnimatingExit && isVisible()) {
-            dc.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
-        }
-    }
-
-    private DimLayer.DimLayerUser getDimLayerUser() {
+    private Dimmer getDimmer() {
         Task task = getTask();
         if (task != null) {
-            return task;
+            return task.getDimmer();
         }
-        return getStack();
+        return getStack().getDimmer();
     }
 
     /** Returns true if the replacement window was removed. */
@@ -2152,9 +2131,6 @@
 
     private void removeReplacedWindow() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
-        if (isDimming()) {
-            transferDimToReplacement();
-        }
         mWillReplaceWindow = false;
         mAnimateReplacingWindow = false;
         mReplacingRemoveRequested = false;
@@ -2217,11 +2193,11 @@
             // need to intercept touches outside of that window. The dim layer user
             // associated with the window (task or stack) will give us the good bounds, as
             // they would be used to display the dim layer.
-            final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-            if (dimLayerUser != null) {
-                dimLayerUser.getDimBounds(mTmpRect);
+            final Task task = getTask();
+            if (task != null) {
+                task.getDimBounds(mTmpRect);
             } else {
-                getVisibleBounds(mTmpRect);
+                getStack().getDimBounds(mTmpRect);
             }
             if (inFreeformWindowingMode()) {
                 // For freeform windows we the touch region to include the whole surface for the
@@ -2729,14 +2705,6 @@
         return displayContent.isDefaultDisplay;
     }
 
-    @Override
-    public boolean isDimming() {
-        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-        final DisplayContent dc = getDisplayContent();
-        return dimLayerUser != null && dc != null
-                && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
-    }
-
     void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
         mShowToOwnerOnly = showToOwnerOnly;
     }
@@ -3591,15 +3559,6 @@
         return winY;
     }
 
-    private void transferDimToReplacement() {
-        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-        final DisplayContent dc = getDisplayContent();
-        if (dimLayerUser != null && dc != null) {
-            dc.mDimLayerController.applyDim(dimLayerUser,
-                    mReplacementWindow.mWinAnimator, (mAttrs.flags & FLAG_DIM_BEHIND) != 0);
-        }
-    }
-
     // During activity relaunch due to resize, we sometimes use window replacement
     // for only child windows (as the main window is handled by window preservation)
     // and the big surface.
@@ -4388,4 +4347,80 @@
             return false;
         }
     }
+
+    @Override
+    boolean shouldMagnify() {
+        if (mAttrs.type == TYPE_INPUT_METHOD ||
+                mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+            return false;
+        } else if (isScreenOverlay()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    boolean isScreenOverlay() {
+        // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+        // worry not: it's a fake TYPE_NAVIGATION_BAR.
+        if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+                mAttrs.type == TYPE_NAVIGATION_BAR ||
+                mAttrs.type == TYPE_STATUS_BAR) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    SurfaceSession getSession() {
+        if (mSession.mSurfaceSession != null) {
+            return mSession.mSurfaceSession;
+        } else {
+            return getParent().getSession();
+        }
+    }
+
+    @Override
+    boolean needsZBoost() {
+        return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
+    }
+
+    @Override
+    SurfaceControl.Builder makeSurface() {
+        return mToken.makeChildSurface(this);
+    }
+
+
+    @Override
+    void prepareSurfaces() {
+        mIsDimming = false;
+        if (!mAnimatingExit && mAppDied) {
+            mIsDimming = true;
+            getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+        } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+                && !mAnimatingExit && isVisible()) {
+            mIsDimming = true;
+            getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+        }
+
+        mWinAnimator.prepareSurfaceLocked(true);
+        super.prepareSurfaces();
+    }
+
+    @Override
+    void assignLayer(Transaction t, int layer) {
+        // See comment in assignRelativeLayerForImeTargetChild
+        if (!isChildWindow()
+                || (mService.mInputMethodTarget != getParentWindow())
+                || !inSplitScreenWindowingMode()) {
+            super.assignLayer(t, layer);
+            return;
+        }
+        getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+    }
+
+    @Override
+    public boolean isDimming() {
+        return mIsDimming;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86397ae..840cc40 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -60,7 +60,6 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.view.MagnificationSpec;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -559,7 +558,10 @@
         }
         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
         if (mSurfaceController != null) {
-            mSurfaceController.setLayer(mAnimLayer + 1);
+            // Our SurfaceControl is always at layer 0 within the parent Surface managed by
+            // window-state. We want this old Surface to stay on top of the new one
+            // until we do the swap, so we place it at layer 1.
+            mSurfaceController.mSurfaceControl.setLayer(1);
         }
         mDestroyPreservedSurfaceUponRedraw = true;
         mSurfaceDestroyDeferred = true;
@@ -730,7 +732,6 @@
         try {
             mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
             mSurfaceController.setLayerStackInTransaction(getLayerStack());
-            mSurfaceController.setLayer(mAnimLayer);
         } finally {
             mService.closeSurfaceTransaction("createSurfaceLocked");
         }
@@ -867,22 +868,6 @@
         mPendingDestroySurface = null;
     }
 
-    void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
-        final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
-        final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
-
-        if (spec != null && !spec.isNop()) {
-            float scale = spec.scale;
-            transform.postScale(scale, scale);
-            transform.postTranslate(spec.offsetX, spec.offsetY);
-
-            // As we are scaling the whole surface, to keep the content
-            // in the same position we will also have to scale the surfaceInsets.
-            transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
-                    -(surfaceInsetTop*scale - surfaceInsetTop));
-        }
-    }
-
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
@@ -969,11 +954,6 @@
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
 
-            MagnificationSpec spec = getMagnificationSpec();
-            if (spec != null) {
-                applyMagnificationSpec(spec, tmpMatrix);
-            }
-
             // "convert" it into SurfaceFlinger's format
             // (a 2x2 matrix + an offset)
             // Here we must not transform the position of the surface
@@ -1057,49 +1037,16 @@
                 TAG, "computeShownFrameLocked: " + this +
                 " not attached, mAlpha=" + mAlpha);
 
-        MagnificationSpec spec = getMagnificationSpec();
-        if (spec != null) {
-            final Rect frame = mWin.mFrame;
-            final float tmpFloats[] = mService.mTmpFloats;
-            final Matrix tmpMatrix = mWin.mTmpMatrix;
-
-            tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
-            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
-
-            applyMagnificationSpec(spec, tmpMatrix);
-
-            tmpMatrix.getValues(tmpFloats);
-
-            mHaveMatrix = true;
-            mDsDx = tmpFloats[Matrix.MSCALE_X];
-            mDtDx = tmpFloats[Matrix.MSKEW_Y];
-            mDtDy = tmpFloats[Matrix.MSKEW_X];
-            mDsDy = tmpFloats[Matrix.MSCALE_Y];
-            float x = tmpFloats[Matrix.MTRANS_X];
-            float y = tmpFloats[Matrix.MTRANS_Y];
-            mWin.mShownPosition.set(Math.round(x), Math.round(y));
-
-            mShownAlpha = mAlpha;
-        } else {
-            mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
-            if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
-                mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
-            }
-            mShownAlpha = mAlpha;
-            mHaveMatrix = false;
-            mDsDx = mWin.mGlobalScale;
-            mDtDx = 0;
-            mDtDy = 0;
-            mDsDy = mWin.mGlobalScale;
+        mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
+        if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+            mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
         }
-    }
-
-    private MagnificationSpec getMagnificationSpec() {
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && mWin.getDisplayId() == DEFAULT_DISPLAY) {
-            return mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
-        }
-        return null;
+        mShownAlpha = mAlpha;
+        mHaveMatrix = false;
+        mDsDx = mWin.mGlobalScale;
+        mDtDx = 0;
+        mDtDy = 0;
+        mDsDy = mWin.mGlobalScale;
     }
 
     /**
@@ -1140,26 +1087,6 @@
             w.expandForSurfaceInsets(finalClipRect);
         }
 
-        // We may be applying a magnification spec to all windows,
-        // simulating a transformation in screen space, in which case
-        // we need to transform all other screen space values...including
-        // the final crop. This is kind of messed up and we should look
-        // in to actually transforming screen-space via a parent-layer.
-        // b/38322835
-        MagnificationSpec spec = getMagnificationSpec();
-        if (spec != null && !spec.isNop()) {
-            Matrix transform = mWin.mTmpMatrix;
-            RectF finalCrop = mService.mTmpRectF;
-            transform.reset();
-            transform.postScale(spec.scale, spec.scale);
-            transform.postTranslate(-spec.offsetX, -spec.offsetY);
-            transform.mapRect(finalCrop);
-            finalClipRect.top = (int) finalCrop.top;
-            finalClipRect.left = (int) finalCrop.left;
-            finalClipRect.right = (int) finalCrop.right;
-            finalClipRect.bottom = (int) finalCrop.bottom;
-        }
-
         return true;
     }
 
@@ -1517,7 +1444,6 @@
             mReportSurfaceResized = true;
             mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                     WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-            w.applyDimLayerIfNeeded();
         }
     }
 
@@ -1615,7 +1541,6 @@
                         mDtDy * w.mHScale * mExtraHScale,
                         mDsDy * w.mVScale * mExtraVScale,
                         recoveringMemory);
-            mSurfaceController.setLayer(mAnimLayer);
 
             if (prepared && mDrawState == HAS_DRAWN) {
                 if (mLastHidden) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index a214523..6746754 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@
 
     final WindowStateAnimator mAnimator;
 
-    private SurfaceControlWithBackground mSurfaceControl;
+    SurfaceControlWithBackground mSurfaceControl;
 
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
@@ -101,7 +101,8 @@
         mWindowSession = win.mSession;
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
-        final SurfaceControl.Builder b = new SurfaceControl.Builder(s)
+        final SurfaceControl.Builder b = win.makeSurface()
+                .setParent(win.getSurfaceControl())
                 .setName(name)
                 .setSize(w, h)
                 .setFormat(format)
@@ -245,25 +246,6 @@
         }
     }
 
-    void setLayer(int layer) {
-        if (mSurfaceControl != null) {
-            mService.openSurfaceTransaction();
-            try {
-                if (mAnimator.mWin.usesRelativeZOrdering()) {
-                    mSurfaceControl.setRelativeLayer(
-                            mAnimator.mWin.getParentWindow()
-                            .mWinAnimator.mSurfaceController.mSurfaceControl,
-                            -1);
-                } else {
-                    mSurfaceLayer = layer;
-                    mSurfaceControl.setLayer(layer);
-                }
-            } finally {
-                mService.closeSurfaceTransaction("setLayer");
-            }
-        }
-    }
-
     void setLayerStackInTransaction(int layerStack) {
         if (mSurfaceControl != null) {
             mSurfaceControl.setLayerStack(layerStack);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index cd5e475..5081868 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -704,7 +704,7 @@
 
             // Create a new surface for the thumbnail
             WindowState window = appToken.findMainWindow();
-            final SurfaceControl surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+            final SurfaceControl surfaceControl = appToken.makeSurface()
                     .setName("thumbnail anim")
                     .setSize(dirty.width(), dirty.height())
                     .setFormat(PixelFormat.TRANSLUCENT)
@@ -712,7 +712,6 @@
                             window != null ? window.mOwnerUid : Binder.getCallingUid())
                     .build();
 
-            surfaceControl.setLayerStack(display.getLayerStack());
             if (SHOW_TRANSACTIONS) {
                 Slog.i(TAG, "  THUMBNAIL " + surfaceControl + ": CREATE");
             }
@@ -750,10 +749,13 @@
             anim.restrictDuration(MAX_ANIMATION_DURATION);
             anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
 
-            openingAppAnimator.updateThumbnailLayer();
             openingAppAnimator.thumbnail = surfaceControl;
             openingAppAnimator.thumbnailAnimation = anim;
             mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+
+            // We parent the thumbnail to the app token, and just place it
+            // on top of anything else in the app token.
+            surfaceControl.setLayer(Integer.MAX_VALUE);
         } catch (Surface.OutOfResourcesException e) {
             Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
                     + dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 5657f6c..6aa5101 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -67,6 +67,7 @@
             mWriteQueue.clear();
             mTraceFile.delete();
             try (OutputStream os = new FileOutputStream(mTraceFile)) {
+                mTraceFile.setReadable(true, false);
                 ProtoOutputStream proto = new ProtoOutputStream(os);
                 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
                 proto.flush();
@@ -147,7 +148,7 @@
     }
 
     static WindowTracing createDefaultAndStartLooper(Context context) {
-        File file = new File("/data/system/window_trace.proto");
+        File file = new File("/data/misc/wmtrace/wm_trace.pb");
         WindowTracing windowTracing = new WindowTracing(file);
         new Thread(windowTracing::loop, "window_tracing").start();
         return windowTracing;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 36e9e7f..4a2da37 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -23,7 +23,6 @@
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_ContextHubService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
     $(LOCAL_REL_DIR)/com_android_server_locksettings_SyntheticPasswordManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 6403a5a..df53fee 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -51,8 +51,9 @@
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
-using V1_2::ITunerCallback;
 using V1_1::ProgramListResult;
+using V1_1::VendorKeyValue;
+using V1_2::ITunerCallback;
 using utils::HalRevision;
 
 static mutex gContextMutex;
@@ -93,6 +94,7 @@
     wp<V1_1::IBroadcastRadio> mHalModule11;
     sp<V1_0::ITuner> mHalTuner;
     sp<V1_1::ITuner> mHalTuner11;
+    sp<V1_2::ITuner> mHalTuner12;
     sp<HalDeathRecipient> mHalDeathRecipient;
 
 private:
@@ -179,8 +181,11 @@
 
     ctx.mHalTuner = halTuner;
     ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
+    ctx.mHalTuner12 = V1_2::ITuner::castFrom(halTuner).withDefault(nullptr);
     ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
             "Provided tuner does not implement 1.1 HAL");
+    ALOGW_IF(ctx.mHalRev >= HalRevision::V1_2 && ctx.mHalTuner12 == nullptr,
+            "Provided tuner does not implement 1.2 HAL");
 
     ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner));
     halTuner->linkToDeath(ctx.mHalDeathRecipient, 0);
@@ -194,16 +199,21 @@
     return tuner;
 }
 
-sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
+static sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
     lock_guard<mutex> lk(gContextMutex);
     return getHalTuner(getNativeContext(nativeContext));
 }
 
-sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
+static sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
     lock_guard<mutex> lk(gContextMutex);
     return getNativeContext(nativeContext).mHalTuner11;
 }
 
+static sp<V1_2::ITuner> getHalTuner12(jlong nativeContext) {
+    lock_guard<mutex> lk(gContextMutex);
+    return getNativeContext(nativeContext).mHalTuner12;
+}
+
 sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
     return TunerCallback::getNativeCallback(env,
             env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
@@ -233,6 +243,7 @@
     ctx.mHalDeathRecipient = nullptr;
 
     ctx.mHalTuner11 = nullptr;
+    ctx.mHalTuner12 = nullptr;
     ctx.mHalTuner = nullptr;
 }
 
@@ -488,6 +499,48 @@
     convert::ThrowIfFailed(env, halResult);
 }
 
+static jobject nativeSetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jParameters) {
+    ALOGV("%s", __func__);
+
+    auto halTuner = getHalTuner12(nativeContext);
+    if (halTuner == nullptr) {
+        ALOGI("Parameters are not supported with HAL < 1.2");
+        return nullptr;
+    }
+
+    JavaRef<jobject> jResults = nullptr;
+    auto parameters = convert::VendorInfoToHal(env, jParameters);
+    auto hidlResult = halTuner->setParameters(parameters,
+            [&](const hidl_vec<VendorKeyValue> results) {
+        jResults = convert::VendorInfoFromHal(env, results);
+    });
+
+    if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+    return jResults.release();
+}
+
+static jobject nativeGetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jKeys) {
+    ALOGV("%s", __func__);
+
+    auto halTuner = getHalTuner12(nativeContext);
+    if (halTuner == nullptr) {
+        ALOGI("Parameters are not supported with HAL < 1.2");
+        return nullptr;
+    }
+
+    JavaRef<jobject> jResults = nullptr;
+    auto keys = convert::StringListToHal(env, jKeys);
+    auto hidlResult = halTuner->getParameters(keys,
+            [&](const hidl_vec<VendorKeyValue> parameters) {
+        jResults = convert::VendorInfoFromHal(env, parameters);
+    });
+
+    if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+    return jResults.release();
+}
+
 static bool nativeIsAntennaConnected(JNIEnv *env, jobject obj, jlong nativeContext) {
     ALOGV("%s", __func__);
     auto halTuner = getHalTuner(nativeContext);
@@ -525,6 +578,8 @@
     { "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
     { "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
     { "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
+    { "nativeSetParameters", "(JLjava/util/Map;)Ljava/util/Map;", (void*)nativeSetParameters },
+    { "nativeGetParameters", "(JLjava/util/List;)Ljava/util/Map;", (void*)nativeGetParameters },
     { "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected },
 };
 
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index ed7c9c4..d624df6 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -70,6 +70,7 @@
         jmethodID onBackgroundScanAvailabilityChange;
         jmethodID onBackgroundScanComplete;
         jmethodID onProgramListChanged;
+        jmethodID onParametersUpdated;
     } TunerCallback;
 } gjni;
 
@@ -346,7 +347,10 @@
 Return<void> NativeCallback::parametersUpdated(const hidl_vec<VendorKeyValue>& parameters) {
     ALOGV("%s", __func__);
 
-    // TODO(b/65862441): pass this callback to the front-end
+    mCallbackThread.enqueue([this, parameters](JNIEnv *env) {
+        auto jParameters = convert::VendorInfoFromHal(env, parameters);
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onParametersUpdated, jParameters.get());
+    });
 
     return {};
 }
@@ -437,6 +441,8 @@
             "onBackgroundScanComplete", "()V");
     gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass,
             "onProgramListChanged", "()V");
+    gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass,
+            "onParametersUpdated", "(Ljava/util/Map;)V");
 
     auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback",
             gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 8dfa14f..734ce79 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -34,6 +34,7 @@
 namespace utils = hardware::broadcastradio::utils;
 
 using hardware::Return;
+using hardware::hidl_string;
 using hardware::hidl_vec;
 using regions::RegionalBandConfig;
 
@@ -98,6 +99,11 @@
     } HashMap;
 
     struct {
+        jmethodID get;
+        jmethodID size;
+    } List;
+
+    struct {
         jmethodID put;
     } Map;
 
@@ -145,8 +151,21 @@
         jclass clazz;
         jmethodID cstor;
     } ParcelableException;
+
+    struct {
+        jclass clazz;
+    } String;
 } gjni;
 
+static jstring CastToString(JNIEnv *env, jobject obj) {
+    if (env->IsInstanceOf(obj, gjni.String.clazz)) {
+        return static_cast<jstring>(obj);
+    } else {
+        ALOGE("Cast failed, object is not a string");
+        return nullptr;
+    }
+}
+
 template <>
 bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
     return __ThrowIfFailedHidl(env, hidlResult);
@@ -250,12 +269,26 @@
 }
 
 static std::string StringFromJava(JNIEnv *env, JavaRef<jstring> &jStr) {
-    auto cstr = (jStr == nullptr) ? nullptr : env->GetStringUTFChars(jStr.get(), nullptr);
+    if (jStr == nullptr) return {};
+    auto cstr = env->GetStringUTFChars(jStr.get(), nullptr);
     std::string str(cstr);
     env->ReleaseStringUTFChars(jStr.get(), cstr);
     return str;
 }
 
+hidl_vec<hidl_string> StringListToHal(JNIEnv *env, jobject jList) {
+    auto len = (jList == nullptr) ? 0 : env->CallIntMethod(jList, gjni.List.size);
+    hidl_vec<hidl_string> list(len);
+
+    for (decltype(len) i = 0; i < len; i++) {
+        auto jString = make_javaref(env, CastToString(env, env->CallObjectMethod(
+                jList, gjni.List.get, i)));
+        list[i] = StringFromJava(env, jString);
+    }
+
+    return list;
+}
+
 JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hidl_vec<VendorKeyValue> &info) {
     ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
 
@@ -275,7 +308,10 @@
 
     auto jInfoArr = make_javaref(env, static_cast<jobjectArray>(env->CallStaticObjectMethod(
             gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo)));
-    LOG_FATAL_IF(jInfoArr == nullptr, "Converted array is null");
+    if (jInfoArr == nullptr) {
+        ALOGE("Converted array is null");
+        return {};
+    }
 
     auto len = env->GetArrayLength(jInfoArr.get());
     hidl_vec<VendorKeyValue> vec;
@@ -651,6 +687,10 @@
     gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass);
     gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "<init>", "()V");
 
+    auto listClass = FindClassOrDie(env, "java/util/List");
+    gjni.List.get = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+    gjni.List.size = GetMethodIDOrDie(env, listClass, "size", "()I");
+
     auto mapClass = FindClassOrDie(env, "java/util/Map");
     gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put",
             "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
@@ -713,6 +753,9 @@
     gjni.ParcelableException.clazz = MakeGlobalRefOrDie(env, parcelableExcClass);
     gjni.ParcelableException.cstor = GetMethodIDOrDie(env, parcelableExcClass, "<init>",
             "(Ljava/lang/Throwable;)V");
+
+    auto stringClass = FindClassOrDie(env, "java/lang/String");
+    gjni.String.clazz = MakeGlobalRefOrDie(env, stringClass);
 }
 
 } // namespace android
diff --git a/services/core/jni/BroadcastRadio/convert.h b/services/core/jni/BroadcastRadio/convert.h
index 1fc75f0..b8c55c1 100644
--- a/services/core/jni/BroadcastRadio/convert.h
+++ b/services/core/jni/BroadcastRadio/convert.h
@@ -35,6 +35,8 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+hardware::hidl_vec<hardware::hidl_string> StringListToHal(JNIEnv *env, jobject jList);
+
 JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hardware::hidl_vec<V1_1::VendorKeyValue> &info);
 hardware::hidl_vec<V1_1::VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo);
 
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
deleted file mode 100644
index ad372de..0000000
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ /dev/null
@@ -1,1201 +0,0 @@
-/*
- * Copyright 2016, 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.
- */
-
-#undef LOG_NDEBUG
-#undef LOG_TAG
-#define LOG_NDEBUG 0
-#define LOG_TAG "ContextHubService"
-
-#include <inttypes.h>
-#include <jni.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/endian.h>
-
-#include <chrono>
-#include <mutex>
-#include <queue>
-#include <unordered_map>
-#include <utility>
-
-#include <android-base/macros.h>
-#include <android/hardware/contexthub/1.0/IContexthub.h>
-#include <cutils/log.h>
-
-#include "core_jni_helpers.h"
-#include <nativehelper/JNIHelp.h>
-
-using android::hardware::contexthub::V1_0::AsyncEventType;
-using android::hardware::contexthub::V1_0::ContextHub;
-using android::hardware::contexthub::V1_0::ContextHubMsg;
-using android::hardware::contexthub::V1_0::HubAppInfo;
-using android::hardware::contexthub::V1_0::IContexthub;
-using android::hardware::contexthub::V1_0::IContexthubCallback;
-using android::hardware::contexthub::V1_0::NanoAppBinary;
-using android::hardware::contexthub::V1_0::Result;
-using android::hardware::contexthub::V1_0::TransactionResult;
-
-using android::hardware::Return;
-
-using std::chrono::steady_clock;
-
-// If a transaction takes longer than this, we'll allow it to be
-// canceled by a new transaction.  Note we do _not_ automatically
-// cancel a transaction after this much time.  We can have a
-// legal transaction which takes longer than this amount of time,
-// as long as no other new transactions are attempted after this
-// time has expired.
-constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29);
-
-namespace android {
-
-constexpr uint32_t kNanoAppBinaryHeaderVersion = 1;
-
-// Important: this header is explicitly defined as little endian byte order, and
-// therefore may not match host endianness
-struct NanoAppBinaryHeader {
-    uint32_t headerVersion;        // 0x1 for this version
-    uint32_t magic;                // "NANO" (see NANOAPP_MAGIC in context_hub.h)
-    uint64_t appId;                // App Id, contains vendor id
-    uint32_t appVersion;           // Version of the app
-    uint32_t flags;                // Signed, encrypted
-    uint64_t hwHubType;            // Which hub type is this compiled for
-    uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for
-    uint8_t targetChreApiMinorVersion;
-    uint8_t reserved[6];
-} __attribute__((packed));
-
-enum HubMessageType {
-    CONTEXT_HUB_APPS_ENABLE  = 1, // Enables loaded nano-app(s)
-    CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s)
-    CONTEXT_HUB_LOAD_APP     = 3, // Load a supplied app
-    CONTEXT_HUB_UNLOAD_APP   = 4, // Unload a specified app
-    CONTEXT_HUB_QUERY_APPS   = 5, // Query for app(s) info on hub
-    CONTEXT_HUB_QUERY_MEMORY = 6, // Query for memory info
-    CONTEXT_HUB_OS_REBOOT    = 7, // Request to reboot context HUB OS
-};
-
-constexpr jint OS_APP_ID = -1;
-constexpr jint INVALID_APP_ID = -2;
-
-constexpr jint MIN_APP_ID = 1;
-constexpr jint MAX_APP_ID = 128;
-
-constexpr size_t MSG_HEADER_SIZE = 4;
-constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
-constexpr size_t HEADER_FIELD_MSG_VERSION = 1;
-constexpr size_t HEADER_FIELD_HUB_HANDLE = 2;
-constexpr size_t HEADER_FIELD_APP_INSTANCE = 3;
-
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
-constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
-
-jint getAppInstanceForAppId(uint64_t app_id);
-int onMessageReceipt(const uint32_t *header,
-                     size_t headerLen,
-                     const char *msg,
-                     size_t msgLen);
-void onHubReset(uint32_t hubId);
-void queryHubForApps(uint32_t hubId);
-void passOnOsResponse(uint32_t hubHandle,
-                      uint32_t msgType,
-                      TransactionResult result,
-                      const int8_t *additionalData,
-                      size_t additionalDataLen);
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle);
-void closeUnloadTxn(bool success);
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
-                               uint32_t hubHandle);
-
-struct JniInfo {
-    JavaVM *vm;
-    jclass contextHubInfoClass;
-    jclass contextHubServiceClass;
-    jclass memoryRegionsClass;
-
-    jobject jContextHubService;
-
-    jmethodID msgReceiptCallBack;
-
-    jmethodID contextHubInfoCtor;
-    jmethodID contextHubInfoSetId;
-    jmethodID contextHubInfoSetName;
-    jmethodID contextHubInfoSetVendor;
-    jmethodID contextHubInfoSetToolchain;
-    jmethodID contextHubInfoSetPlatformVersion;
-    jmethodID contextHubInfoSetStaticSwVersion;
-    jmethodID contextHubInfoSetToolchainVersion;
-    jmethodID contextHubInfoSetPeakMips;
-    jmethodID contextHubInfoSetStoppedPowerDrawMw;
-    jmethodID contextHubInfoSetSleepPowerDrawMw;
-    jmethodID contextHubInfoSetPeakPowerDrawMw;
-    jmethodID contextHubInfoSetSupportedSensors;
-    jmethodID contextHubInfoSetMemoryRegions;
-    jmethodID contextHubInfoSetMaxPacketLenBytes;
-
-    jmethodID contextHubServiceMsgReceiptCallback;
-    jmethodID contextHubServiceAddAppInstance;
-    jmethodID contextHubServiceDeleteAppInstance;
-};
-
-
-
-class TxnManager {
-public:
-    TxnManager() {
-        mData = nullptr;
-        mIsPending = false;
-    }
-
-    ~TxnManager() {
-        closeTxn();
-    }
-
-    int addTxn(HubMessageType txnIdentifier, void *txnData) {
-        std::lock_guard<std::mutex>lock(mLock);
-        if (mIsPending) {
-            ALOGW("Transaction already found pending when trying to add a new one.");
-            return -1;
-        }
-        mIsPending = true;
-        mFirstTimeTxnCanBeCanceled = steady_clock::now() + kMinTransactionCancelTime;
-        mData = txnData;
-        mIdentifier = txnIdentifier;
-
-        return 0;
-    }
-
-    int closeTxn() {
-        std::lock_guard<std::mutex>lock(mLock);
-        closeTxnUnlocked();
-        return 0;
-    }
-
-    bool isTxnPending() {
-        std::lock_guard<std::mutex>lock(mLock);
-        return mIsPending;
-    }
-
-    void closeAnyStaleTxns() {
-        std::lock_guard<std::mutex>lock(mLock);
-        if (mIsPending && steady_clock::now() >= mFirstTimeTxnCanBeCanceled) {
-            ALOGW("Stale transaction canceled");
-            closeTxnUnlocked();
-        }
-    }
-
-    int fetchTxnData(HubMessageType *id, void **data) {
-        if (id == nullptr || data == nullptr) {
-            ALOGW("Null Params isNull{id, data} {%d, %d}",
-                  id == nullptr ? 1 : 0,
-                  data == nullptr ? 1 : 0);
-            return -1;
-        }
-
-        std::lock_guard<std::mutex>lock(mLock);
-        if (!mIsPending) {
-            ALOGW("No Transactions pending");
-            return -1;
-        }
-
-        *id = mIdentifier;
-        *data = mData;
-        return 0;
-    }
-
- private:
-    bool mIsPending;            // Is a transaction pending
-    std::mutex mLock;           // mutex for manager
-    HubMessageType mIdentifier; // What are we doing
-    void *mData;                // Details
-    steady_clock::time_point mFirstTimeTxnCanBeCanceled;
-
-    // Only call this if you hold the lock.
-    void closeTxnUnlocked() {
-        mIsPending = false;
-        free(mData);
-        mData = nullptr;
-    }
-};
-
-
-struct ContextHubServiceCallback : IContexthubCallback {
-    uint32_t mContextHubId;
-
-    ContextHubServiceCallback(uint32_t hubId) {
-        mContextHubId = hubId;
-    }
-
-    virtual Return<void> handleClientMsg(const ContextHubMsg &msg) {
-        jint appHandle = getAppInstanceForAppId(msg.appName);
-        if (appHandle < 0) {
-            ALOGE("Filtering out message due to invalid App Instance.");
-        } else {
-            uint32_t msgHeader[MSG_HEADER_SIZE] = {};
-            msgHeader[HEADER_FIELD_MSG_TYPE] = msg.msgType;
-            msgHeader[HEADER_FIELD_HUB_HANDLE] = mContextHubId;
-            msgHeader[HEADER_FIELD_APP_INSTANCE] = appHandle;
-            onMessageReceipt(msgHeader,
-                             MSG_HEADER_SIZE,
-                             reinterpret_cast<const char *>(msg.msg.data()),
-                             msg.msg.size());
-        }
-
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleHubEvent(AsyncEventType evt) {
-        if (evt == AsyncEventType::RESTARTED) {
-            ALOGW("Context Hub handle %d restarted", mContextHubId);
-            onHubReset(mContextHubId);
-        } else {
-            ALOGW("Cannot handle event %u from hub %d", evt, mContextHubId);
-        }
-
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleTxnResult(uint32_t txnId,
-                                         TransactionResult result) {
-        ALOGI("Handle transaction result , hubId %" PRIu32 ", txnId %" PRIu32 ", result %" PRIu32,
-              mContextHubId,
-              txnId,
-              result);
-
-        switch(txnId) {
-            case CONTEXT_HUB_APPS_ENABLE:
-            case CONTEXT_HUB_APPS_DISABLE:
-                passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
-                break;
-
-            case CONTEXT_HUB_UNLOAD_APP:
-                closeUnloadTxn(result == TransactionResult::SUCCESS);
-                passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
-                break;
-
-            case CONTEXT_HUB_LOAD_APP:
-                {
-                    jint appInstanceHandle = INVALID_APP_ID;
-                    bool appRunningOnHub = (result == TransactionResult::SUCCESS);
-                    if (!(closeLoadTxn(appRunningOnHub, &appInstanceHandle))) {
-                        if (appRunningOnHub) {
-                            // Now we're in an odd situation.  Our nanoapp
-                            // is up and running on the Context Hub.  However,
-                            // something went wrong in our Service code so that
-                            // we're not able to properly track this nanoapp
-                            // in our Service code.  If we tell the Java layer
-                            // things are good, it's a lie because the handle
-                            // we give them will fail when used with the Service.
-                            // If we tell the Java layer this failed, it's kind
-                            // of a lie as well, since this nanoapp is running.
-                            //
-                            // We leave a more robust fix for later, and for
-                            // now just tell the user things have failed.
-                            //
-                            // TODO(b/30835981): Make this situation better.
-                            result = TransactionResult::FAILURE;
-                        }
-                    }
-
-                    passOnOsResponse(mContextHubId,
-                                     txnId,
-                                     result,
-                                     reinterpret_cast<int8_t *>(&appInstanceHandle),
-                                     sizeof(appInstanceHandle));
-                    break;
-                }
-
-            default:
-                ALOGI("unrecognized transction id %" PRIu32, txnId);
-                break;
-        }
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleAppsInfo(
-            const android::hardware::hidl_vec<HubAppInfo>& apps) {
-        TransactionResult result = TransactionResult::SUCCESS;
-        handleQueryAppsResponse(apps,mContextHubId);
-        passOnOsResponse(mContextHubId, CONTEXT_HUB_QUERY_APPS, result, nullptr, 0);
-        return android::hardware::Void();
-    }
-
-    virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) {
-        ALOGI("Handle app aport called from %" PRIx64 " with abort code %" PRIu32,
-            appId,
-            abortCode);
-
-        // TODO: Plumb this to the clients interested in this app
-        return android::hardware::Void();
-    }
-
-    void setContextHubId(uint32_t id) {
-        mContextHubId = id;
-    }
-
-    uint32_t getContextHubId() {
-        return(mContextHubId);
-    }
-};
-
-struct AppInstanceInfo {
-    HubAppInfo appInfo;          // returned from the HAL
-    uint64_t truncName;          // Possibly truncated name for logging
-    uint32_t hubHandle;          // Id of the hub this app is on
-    jint instanceId;             // system wide unique instance id - assigned
-};
-
-struct ContextHubInfo {
-    int numHubs;
-    Vector<ContextHub> hubs;
-    sp<IContexthub> contextHub;
-};
-
-struct ContextHubServiceDb {
-    int initialized;
-    ContextHubInfo hubInfo;
-    JniInfo jniInfo;
-    std::queue<jint> freeIds;
-    std::unordered_map<jint, AppInstanceInfo> appInstances;
-    TxnManager txnManager;
-    std::vector<ContextHubServiceCallback *> regCallBacks;
-};
-
-ContextHubServiceDb db;
-
-bool getHubIdForHubHandle(int hubHandle, uint32_t *hubId) {
-    if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs || hubId == nullptr) {
-        return false;
-    } else {
-        *hubId = db.hubInfo.hubs[hubHandle].hubId;
-        return true;
-    }
-}
-
-int getHubHandleForAppInstance(jint id) {
-    if (!db.appInstances.count(id)) {
-        ALOGD("%s: Cannot find app for app instance %" PRId32,
-              __FUNCTION__,
-              id);
-        return -1;
-    }
-
-    return db.appInstances[id].hubHandle;
-}
-
-jint getAppInstanceForAppId(uint64_t app_id) {
-    auto end = db.appInstances.end();
-    for (auto current = db.appInstances.begin(); current != end; ++current) {
-        if (current->second.appInfo.appId == app_id) {
-            return current->first;
-        }
-    }
-    ALOGD("Cannot find app for app id %" PRIu64 ".", app_id);
-    return -1;
-}
-
-uint64_t getAppIdForAppInstance(jint id) {
-    if (!db.appInstances.count(id)) {
-        return INVALID_APP_ID;
-    }
-    return db.appInstances[id].appInfo.appId;
-}
-
-void queryHubForApps(uint32_t hubId) {
-    Result r = db.hubInfo.contextHub->queryApps(hubId);
-    ALOGD("Sent query for apps to hub %" PRIu32 " with result %" PRIu32, hubId, r);
-}
-
-void sendQueryForApps() {
-    for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
-        queryHubForApps(db.hubInfo.hubs[i].hubId);
-    }
-}
-
-int returnId(jint id) {
-    // Note : This method is not thread safe.
-    // id returned is guaranteed to be in use
-    if (id >= 0) {
-        db.freeIds.push(id);
-        return 0;
-    }
-
-    return -1;
-}
-
-jint generateId() {
-    // Note : This method is not thread safe.
-    jint retVal = -1;
-
-    if (!db.freeIds.empty()) {
-        retVal = db.freeIds.front();
-        db.freeIds.pop();
-    }
-
-    return retVal;
-}
-
-jint addAppInstance(const HubAppInfo *appInfo, uint32_t hubHandle,
-        jint appInstanceHandle, JNIEnv *env) {
-    // Not checking if the apps are indeed distinct
-    AppInstanceInfo entry;
-    assert(appInfo);
-
-
-    entry.appInfo = *appInfo;
-
-    entry.instanceId = appInstanceHandle;
-    entry.truncName = appInfo->appId;
-    entry.hubHandle = hubHandle;
-    db.appInstances[appInstanceHandle] = entry;
-    // Finally - let the service know of this app instance, to populate
-    // the Java cache.
-    env->CallIntMethod(db.jniInfo.jContextHubService,
-                       db.jniInfo.contextHubServiceAddAppInstance,
-                       hubHandle, entry.instanceId,
-                       entry.truncName,
-                       entry.appInfo.version);
-
-    const char *action = (db.appInstances.count(appInstanceHandle) == 0) ? "Added" : "Updated";
-    ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32
-          " as appInstance %" PRId32, action, entry.truncName,
-          entry.hubHandle, appInstanceHandle);
-
-    return appInstanceHandle;
-}
-
-int deleteAppInstance(jint id, JNIEnv *env) {
-    bool fullyDeleted = true;
-
-    if (db.appInstances.count(id)) {
-        db.appInstances.erase(id);
-    } else {
-        ALOGW("Cannot delete App id (%" PRId32 ") from the JNI C++ cache", id);
-        fullyDeleted = false;
-    }
-    returnId(id);
-
-    if ((env == nullptr) ||
-        (env->CallIntMethod(db.jniInfo.jContextHubService,
-                       db.jniInfo.contextHubServiceDeleteAppInstance,
-                       id) != 0)) {
-        ALOGW("Cannot delete App id (%" PRId32 ") from Java cache", id);
-        fullyDeleted = false;
-    }
-
-    if (fullyDeleted) {
-        ALOGI("Deleted App id : %" PRId32, id);
-        return 0;
-    }
-    return -1;
-}
-
-int startLoadAppTxn(uint64_t appId, int hubHandle) {
-    AppInstanceInfo *txnInfo = new AppInstanceInfo();
-    jint instanceId = generateId();
-
-    if (!txnInfo || instanceId < 0) {
-        returnId(instanceId);
-        delete txnInfo;
-        return -1;
-    }
-
-    txnInfo->truncName = appId;
-    txnInfo->hubHandle = hubHandle;
-    txnInfo->instanceId = instanceId;
-
-    txnInfo->appInfo.appId = appId;
-    txnInfo->appInfo.version = -1; // Awaited
-
-    if (db.txnManager.addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) {
-        returnId(instanceId);
-        delete txnInfo;
-        return -1;
-    }
-
-    return 0;
-}
-
-int startUnloadAppTxn(jint appInstanceHandle) {
-    jint *txnData = new(jint);
-    if (!txnData) {
-        ALOGW("Cannot allocate memory to start unload transaction");
-        return -1;
-    }
-
-    *txnData = appInstanceHandle;
-
-    if (db.txnManager.addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) {
-        delete txnData;
-        ALOGW("Cannot start transaction to unload app");
-        return -1;
-    }
-
-    return 0;
-}
-
-void getHubsCb(const ::android::hardware::hidl_vec<ContextHub>& hubs)  {
-    for (size_t i = 0; i < hubs.size(); i++) {
-        db.hubInfo.hubs.push_back(hubs[i]);
-    }
-}
-
-void initContextHubService() {
-    db.hubInfo.numHubs = 0;
-
-    db.hubInfo.contextHub = IContexthub::getService();
-
-    if (db.hubInfo.contextHub == nullptr) {
-        ALOGE("Could not load context hub hal");
-    } else {
-        ALOGI("Loaded context hub hal, isRemote %s", db.hubInfo.contextHub->isRemote() ? "TRUE" : "FALSE");
-    }
-
-    // Prep for storing app info
-    for (jint i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
-        db.freeIds.push(i);
-    }
-
-    if (db.hubInfo.contextHub != nullptr) {
-        std::function<void(const ::android::hardware::hidl_vec<ContextHub>& hubs)> f = getHubsCb;
-        if(!db.hubInfo.contextHub->getHubs(f).isOk()) {
-            ALOGW("GetHubs Failed! transport error.");
-            return;
-        };
-
-        int retNumHubs = db.hubInfo.hubs.size();
-        ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
-        db.hubInfo.numHubs = retNumHubs;
-
-        for (int i = 0; i < db.hubInfo.numHubs; i++) {
-            ALOGI("Subscribing to hubHandle %d", i);
-
-            ContextHubServiceCallback *callBackPtr =
-                new ContextHubServiceCallback(db.hubInfo.hubs[i].hubId);
-            db.hubInfo.contextHub->registerCallback(db.hubInfo.hubs[i].hubId,
-                                                    callBackPtr);
-            db.regCallBacks.push_back(callBackPtr);
-        }
-
-        sendQueryForApps();
-
-    } else {
-        ALOGW("No Context Hub Module present");
-    }
-}
-
-void onHubReset(uint32_t hubId) {
-    TransactionResult result = TransactionResult::SUCCESS;
-    db.txnManager.closeTxn();
-    // TODO : Expose this through an api
-    passOnOsResponse(hubId, CONTEXT_HUB_OS_REBOOT, result, nullptr, 0);
-    queryHubForApps(hubId);
-}
-
-int onMessageReceipt(const uint32_t *header,
-                     size_t headerLen,
-                     const char *msg,
-                     size_t msgLen) {
-    JNIEnv *env;
-
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-      return -1;
-    }
-
-    jbyteArray jmsg = env->NewByteArray(msgLen);
-    if (jmsg == nullptr) {
-        ALOGW("Can't allocate %zu byte array", msgLen);
-        return -1;
-    }
-    jintArray jheader = env->NewIntArray(headerLen);
-    if (jheader == nullptr) {
-        env->DeleteLocalRef(jmsg);
-        ALOGW("Can't allocate %zu int array", headerLen);
-        return -1;
-    }
-
-    env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<const jbyte *>(msg));
-    env->SetIntArrayRegion(jheader, 0, headerLen, reinterpret_cast<const jint *>(header));
-
-    int ret = (env->CallIntMethod(db.jniInfo.jContextHubService,
-                                  db.jniInfo.contextHubServiceMsgReceiptCallback,
-                                  jheader,
-                                  jmsg) != 0);
-    env->DeleteLocalRef(jmsg);
-    env->DeleteLocalRef(jheader);
-
-    return ret;
-}
-
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
-                               uint32_t hubHandle) {
-    JNIEnv *env;
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-            return -1;
-    }
-
-    int numApps = apps.size();
-
-    // We use this information to sync our JNI and Java caches of nanoapp info.
-    // We want to accomplish two things here:
-    // 1) Remove entries from our caches which are stale, and pertained to
-    //    apps no longer running on Context Hub.
-    // 2) Populate our caches with the latest information of all these apps.
-
-    // We make a couple of assumptions here:
-    // A) The JNI and Java caches are in sync with each other (this isn't
-    //    necessarily true; any failure of a single call into Java land to
-    //    update its cache will leave that cache in a bad state.  For NYC,
-    //    we're willing to tolerate this for now).
-    // B) The total number of apps is relatively small, so horribly inefficent
-    //    algorithms aren't too painful.
-    // C) We're going to call this relatively infrequently, so its inefficency
-    //    isn't a big impact.
-
-
-    // (1).  Looking for stale cache entries.  Yes, this is O(N^2).  See
-    // assumption (B).  Per assumption (A), it is sufficient to iterate
-    // over just the JNI cache.
-    auto end = db.appInstances.end();
-    for (auto current = db.appInstances.begin(); current != end; ) {
-        AppInstanceInfo cacheEntry = current->second;
-        // We perform our iteration here because if we call
-        // delete_app_instance() below, it will erase() this entry.
-        current++;
-        bool entryIsStale = true;
-        for (int i = 0; i < numApps; i++) {
-            if (apps[i].appId == cacheEntry.appInfo.appId) {
-                // We found a match; this entry is current.
-                entryIsStale = false;
-                break;
-            }
-        }
-
-        if (entryIsStale) {
-            deleteAppInstance(cacheEntry.instanceId, env);
-        }
-    }
-
-    // (2).  Update our caches with the latest.
-    for (int i = 0; i < numApps; i++) {
-        // We will only have one instance of the app
-        // TODO : Change this logic once we support multiple instances of the same app
-        jint appInstance = getAppInstanceForAppId(apps[i].appId);
-        if (appInstance == -1) {
-            // This is a previously unknown app, let's allocate an "id" for it.
-            appInstance = generateId();
-        }
-        addAppInstance(&apps[i], hubHandle, appInstance, env);
-    }
-    return 0;
-}
-
-// TODO(b/30807327): Do not use raw bytes for additional data.  Use the
-//     JNI interfaces for the appropriate types.
-void passOnOsResponse(uint32_t hubHandle,
-                      uint32_t msgType,
-                      TransactionResult result,
-                      const int8_t *additionalData,
-                      size_t additionalDataLen) {
-    JNIEnv *env;
-
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-        ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32,
-              msgType);
-        return;
-    }
-
-    uint32_t header[MSG_HEADER_SIZE];
-    memset(header, 0, sizeof(header));
-
-    if (!additionalData) {
-        additionalDataLen = 0; // clamp
-    }
-    int msgLen = 1 + additionalDataLen;
-
-    int8_t *msg = new int8_t[msgLen];
-
-    if (!msg) {
-        ALOGW("Unexpected : Ran out of memory, cannot send response");
-        return;
-    }
-
-    header[HEADER_FIELD_MSG_TYPE] = msgType;
-    header[HEADER_FIELD_MSG_VERSION] = 0;
-    header[HEADER_FIELD_HUB_HANDLE] = hubHandle;
-    header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID;
-
-    // Due to API constraints, at the moment we can't change the fact that
-    // we're changing our 4-byte response to a 1-byte value.  But we can prevent
-    // the possible change in sign (and thus meaning) that would happen from
-    // a naive cast.  Further, we can log when we're losing part of the value.
-    // TODO(b/30918279): Don't truncate this result.
-    int8_t truncatedResult;
-    truncatedResult = static_cast<int8_t>(result);
-    msg[0] = truncatedResult;
-
-    if (additionalData) {
-        memcpy(&msg[1], additionalData, additionalDataLen);
-    }
-
-    jbyteArray jmsg = env->NewByteArray(msgLen);
-    jintArray jheader = env->NewIntArray(arraysize(header));
-
-    env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<jbyte *>(msg));
-    env->SetIntArrayRegion(jheader, 0, arraysize(header), reinterpret_cast<jint *>(header));
-
-    ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
-          header[HEADER_FIELD_MSG_TYPE],
-          header[HEADER_FIELD_APP_INSTANCE],
-          header[HEADER_FIELD_HUB_HANDLE]);
-
-    env->CallIntMethod(db.jniInfo.jContextHubService,
-                       db.jniInfo.contextHubServiceMsgReceiptCallback,
-                       jheader,
-                       jmsg);
-
-    env->DeleteLocalRef(jmsg);
-    env->DeleteLocalRef(jheader);
-
-    delete[] msg;
-}
-
-void closeUnloadTxn(bool success) {
-    void *txnData = nullptr;
-    HubMessageType txnId;
-
-    if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
-        txnId == CONTEXT_HUB_UNLOAD_APP) {
-        JNIEnv *env;
-        if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-            ALOGW("Could not attach to JVM !");
-            env = nullptr;
-        }
-        jint handle = *reinterpret_cast<jint *>(txnData);
-        deleteAppInstance(handle, env);
-    } else {
-        ALOGW("Could not unload the app successfully ! success %d, txnData %p",
-              success,
-              txnData);
-    }
-
-    db.txnManager.closeTxn();
-}
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle) {
-    void *txnData;
-    HubMessageType txnId;
-
-    if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
-        txnId == CONTEXT_HUB_LOAD_APP) {
-        AppInstanceInfo *info = static_cast<AppInstanceInfo *>(txnData);
-        *appInstanceHandle = info->instanceId;
-
-        JNIEnv *env;
-        if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) {
-            addAppInstance(&info->appInfo, info->hubHandle, info->instanceId, env);
-        } else {
-            ALOGW("Could not attach to JVM !");
-            success = false;
-        }
-        // While we just called addAppInstance above, our info->appInfo was
-        // incomplete (for example, the 'version' is hardcoded to -1).  So we
-        // trigger an additional query to the CHRE, so we'll be able to get
-        // all the app "info", and have our JNI and Java caches with the
-        // full information.
-        sendQueryForApps();
-    } else {
-        ALOGW("Could not load the app successfully ! Unexpected failure");
-        *appInstanceHandle = INVALID_APP_ID;
-        success = false;
-    }
-
-    db.txnManager.closeTxn();
-    return success;
-}
-
-int initJni(JNIEnv *env, jobject instance) {
-    if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
-        return -1;
-    }
-
-    db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
-
-    db.jniInfo.contextHubInfoClass =
-            env->FindClass("android/hardware/location/ContextHubInfo");
-    db.jniInfo.contextHubServiceClass =
-            env->FindClass("com/android/server/location/ContextHubService");
-
-    db.jniInfo.memoryRegionsClass =
-            env->FindClass("android/hardware/location/MemoryRegion");
-
-    db.jniInfo.contextHubInfoCtor =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
-    db.jniInfo.contextHubInfoSetId =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass, "setId", "(I)V");
-    db.jniInfo.contextHubInfoSetName =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubInfoSetVendor =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setVendor",
-                             "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubInfoSetToolchain =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setToolchain",
-                             "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubInfoSetPlatformVersion =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setPlatformVersion",
-                             "(I)V");
-    db.jniInfo.contextHubInfoSetStaticSwVersion =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setStaticSwVersion",
-                             "(I)V");
-    db.jniInfo.contextHubInfoSetToolchainVersion =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setToolchainVersion",
-                             "(I)V");
-    db.jniInfo.contextHubInfoSetPeakMips =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setPeakMips",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetStoppedPowerDrawMw =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setStoppedPowerDrawMw",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetSleepPowerDrawMw =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setSleepPowerDrawMw",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetPeakPowerDrawMw =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setPeakPowerDrawMw",
-                             "(F)V");
-    db.jniInfo.contextHubInfoSetSupportedSensors =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setSupportedSensors",
-                             "([I)V");
-    db.jniInfo.contextHubInfoSetMemoryRegions =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setMemoryRegions",
-                             "([Landroid/hardware/location/MemoryRegion;)V");
-    db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
-             env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                              "setMaxPacketLenBytes",
-                              "(I)V");
-    db.jniInfo.contextHubServiceMsgReceiptCallback =
-            env->GetMethodID(db.jniInfo.contextHubServiceClass,
-                             "onMessageReceipt",
-                             "([I[B)I");
-    db.jniInfo.contextHubInfoSetName =
-            env->GetMethodID(db.jniInfo.contextHubInfoClass,
-                             "setName",
-                             "(Ljava/lang/String;)V");
-    db.jniInfo.contextHubServiceAddAppInstance =
-                 env->GetMethodID(db.jniInfo.contextHubServiceClass,
-                                  "addAppInstance",
-                                  "(IIJI)I");
-    db.jniInfo.contextHubServiceDeleteAppInstance =
-                 env->GetMethodID(db.jniInfo.contextHubServiceClass,
-                                  "deleteAppInstance",
-                                  "(I)I");
-
-    return 0;
-}
-
-jobject constructJContextHubInfo(JNIEnv *env, const ContextHub &hub) {
-    jstring jstrBuf;
-    jintArray jintBuf;
-    jobjectArray jmemBuf;
-
-    jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass,
-                                  db.jniInfo.contextHubInfoCtor);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId);
-
-    jstrBuf = env->NewStringUTF(hub.name.c_str());
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf);
-    env->DeleteLocalRef(jstrBuf);
-
-    jstrBuf = env->NewStringUTF(hub.vendor.c_str());
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf);
-    env->DeleteLocalRef(jstrBuf);
-
-    jstrBuf = env->NewStringUTF(hub.toolchain.c_str());
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf);
-    env->DeleteLocalRef(jstrBuf);
-
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub.platformVersion);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub.toolchainVersion);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub.peakMips);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
-                        hub.stoppedPowerDrawMw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
-                        hub.sleepPowerDrawMw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
-                        hub.peakPowerDrawMw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
-                        hub.maxSupportedMsgLen);
-
-
-    jintBuf = env->NewIntArray(hub.connectedSensors.size());
-    int *connectedSensors = new int[hub.connectedSensors.size()];
-
-    if (!connectedSensors) {
-      ALOGW("Cannot allocate memory! Unexpected");
-      assert(false);
-    } else {
-      for (unsigned int i = 0; i < hub.connectedSensors.size(); i++) {
-        // TODO :: Populate connected sensors.
-        //connectedSensors[i] = hub.connectedSensors[i].sensorType;
-        connectedSensors[i] = 0;
-      }
-    }
-
-    env->SetIntArrayRegion(jintBuf, 0, hub.connectedSensors.size(),
-                           connectedSensors);
-
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
-    env->DeleteLocalRef(jintBuf);
-
-    // We are not getting the memory regions from the CH Hal - change this when it is available
-    jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
-    // Note the zero size above. We do not need to set any elements
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
-    env->DeleteLocalRef(jmemBuf);
-
-
-    delete[] connectedSensors;
-    return jHub;
-}
-
-jobjectArray nativeInitialize(JNIEnv *env, jobject instance) {
-    jobject hub;
-    jobjectArray retArray;
-
-    if (initJni(env, instance) < 0) {
-        return nullptr;
-    }
-
-    initContextHubService();
-
-    if (db.hubInfo.numHubs > 1) {
-        ALOGW("Clamping the number of hubs to 1");
-        db.hubInfo.numHubs = 1;
-    }
-
-    retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
-
-    for(int i = 0; i < db.hubInfo.numHubs; i++) {
-        hub = constructJContextHubInfo(env, db.hubInfo.hubs[i]);
-        env->SetObjectArrayElement(retArray, i, hub);
-    }
-
-    return retArray;
-}
-
-Result sendLoadNanoAppRequest(uint32_t hubId,
-                              jbyte *data,
-                              size_t dataBufferLength) {
-    auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data);
-    Result result;
-
-    if (dataBufferLength < sizeof(NanoAppBinaryHeader)) {
-        ALOGE("Got short NanoApp, length %zu", dataBufferLength);
-        result = Result::BAD_PARAMS;
-    } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) {
-        ALOGE("Got unexpected NanoApp header version %" PRIu32,
-              letoh32(header->headerVersion));
-        result = Result::BAD_PARAMS;
-    } else {
-        NanoAppBinary nanoapp;
-
-        // Data from the common nanoapp header goes into explicit fields
-        nanoapp.appId      = letoh64(header->appId);
-        nanoapp.appVersion = letoh32(header->appVersion);
-        nanoapp.flags      = letoh32(header->flags);
-        nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion;
-        nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion;
-
-        // Everything past the header goes in customBinary
-        auto dataBytes = reinterpret_cast<const uint8_t *>(data);
-        std::vector<uint8_t> customBinary(
-            dataBytes + sizeof(NanoAppBinaryHeader),
-            dataBytes + dataBufferLength);
-        nanoapp.customBinary = std::move(customBinary);
-
-        ALOGW("Calling Load NanoApp on hub %d", hubId);
-        result = db.hubInfo.contextHub->loadNanoApp(hubId,
-                                                    nanoapp,
-                                                    CONTEXT_HUB_LOAD_APP);
-    }
-
-    return result;
-}
-
-jint nativeSendMessage(JNIEnv *env,
-                       jobject instance,
-                       jintArray header_,
-                       jbyteArray data_) {
-    // With the new binderized HAL definition, this function can be made much simpler.
-    // All the magic can be removed. This is not however needed for the default implementation
-    // TODO :: Change the JNI interface to conform to the new HAL interface and clean up this
-    // function
-    jint retVal = -1; // Default to failure
-
-    jint *header = env->GetIntArrayElements(header_, 0);
-    size_t numHeaderElements = env->GetArrayLength(header_);
-    jbyte *data = env->GetByteArrayElements(data_, 0);
-    size_t dataBufferLength = env->GetArrayLength(data_);
-
-    if (numHeaderElements < MSG_HEADER_SIZE) {
-        ALOGW("Malformed header len");
-        return -1;
-    }
-
-    jint appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
-    uint32_t msgType = header[HEADER_FIELD_MSG_TYPE];
-    int hubHandle = -1;
-    uint64_t appId;
-
-    if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-        hubHandle = getHubHandleForAppInstance(appInstanceHandle);
-    } else if (msgType == CONTEXT_HUB_LOAD_APP) {
-        if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) {
-            return -1;
-        }
-        uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO];
-        uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI];
-        appId = appIdHi << 32 | appIdLo;
-
-        hubHandle = header[HEADER_FIELD_HUB_HANDLE];
-    } else {
-        hubHandle = header[HEADER_FIELD_HUB_HANDLE];
-    }
-
-    uint32_t hubId = -1;
-    if (!getHubIdForHubHandle(hubHandle, &hubId)) {
-        ALOGD("Invalid hub Handle %d", hubHandle);
-        return -1;
-    }
-
-    if (msgType == CONTEXT_HUB_LOAD_APP ||
-        msgType == CONTEXT_HUB_UNLOAD_APP) {
-
-        db.txnManager.closeAnyStaleTxns();
-
-        if (db.txnManager.isTxnPending()) {
-            // TODO : There is a race conditio
-            ALOGW("Cannot load or unload app while a transaction is pending !");
-            return -1;
-        } else if (msgType == CONTEXT_HUB_LOAD_APP) {
-            if (startLoadAppTxn(appId, hubHandle) != 0) {
-                ALOGW("Cannot Start Load Transaction");
-                return -1;
-            }
-        } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-            if (startUnloadAppTxn(appInstanceHandle) != 0) {
-                ALOGW("Cannot Start UnLoad Transaction");
-                return -1;
-            }
-        }
-    }
-
-    Result result;
-
-    if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-        ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %" PRIu32,
-              db.appInstances[appInstanceHandle].appInfo.appId,
-              hubId);
-        result = db.hubInfo.contextHub->unloadNanoApp(
-                hubId, db.appInstances[appInstanceHandle].appInfo.appId, CONTEXT_HUB_UNLOAD_APP);
-    } else {
-        if (appInstanceHandle == OS_APP_ID) {
-            if (msgType == CONTEXT_HUB_LOAD_APP) {
-                result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
-            } else if (msgType == CONTEXT_HUB_QUERY_APPS) {
-                result = db.hubInfo.contextHub->queryApps(hubId);
-            } else {
-                ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
-                result = Result::BAD_PARAMS;
-            }
-        } else {
-            appId = getAppIdForAppInstance(appInstanceHandle);
-            if (appId == static_cast<uint64_t>(INVALID_APP_ID)) {
-                ALOGD("Cannot find application instance %d", appInstanceHandle);
-                result = Result::BAD_PARAMS;
-            } else if (hubHandle != getHubHandleForAppInstance(appInstanceHandle)) {
-                ALOGE("Given hubHandle (%d) doesn't match expected for app instance (%d)",
-                      hubHandle,
-                      getHubHandleForAppInstance(appInstanceHandle));
-                result = Result::BAD_PARAMS;
-            } else {
-                ContextHubMsg msg;
-                msg.appName = appId;
-                msg.msgType = msgType;
-                msg.msg.setToExternal((unsigned char *)data, dataBufferLength);
-
-                ALOGW("Sending msg of type %" PRIu32 " len %zu to app %" PRIx64 " on hub %" PRIu32,
-                       msgType,
-                       dataBufferLength,
-                       appId,
-                       hubId);
-                result = db.hubInfo.contextHub->sendMessageToHub(hubId, msg);
-            }
-        }
-    }
-
-    if (result != Result::OK) {
-        ALOGD("Send Message failure - %d", result);
-        if (msgType == CONTEXT_HUB_LOAD_APP) {
-            jint ignored;
-            closeLoadTxn(false, &ignored);
-        } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-            closeUnloadTxn(false);
-        }
-    } else {
-        retVal = 0;
-    }
-
-    env->ReleaseIntArrayElements(header_, header, 0);
-    env->ReleaseByteArrayElements(data_, data, 0);
-
-    return retVal;
-}
-
-//--------------------------------------------------------------------------------------------------
-//
-const JNINativeMethod gContextHubServiceMethods[] = {
-    {"nativeInitialize",
-            "()[Landroid/hardware/location/ContextHubInfo;",
-            reinterpret_cast<void*>(nativeInitialize)},
-    {"nativeSendMessage",
-            "([I[B)I",
-            reinterpret_cast<void*>(nativeSendMessage)}
-};
-
-int register_android_server_location_ContextHubService(JNIEnv *env)
-{
-    RegisterMethodsOrDie(env, "com/android/server/location/ContextHubService",
-            gContextHubServiceMethods, NELEM(gContextHubServiceMethods));
-
-    return 0;
-}
-
-}//namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 2f45181..46d5043 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,6 @@
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_vr_VrManagerService(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_location_ContextHubService(JNIEnv* env);
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
@@ -82,7 +81,6 @@
     register_android_server_vr_VrManagerService(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
-    register_android_server_location_ContextHubService(env);
     register_android_server_location_GnssLocationProvider(env);
     register_android_server_connectivity_Vpn(env);
     register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 66f5770..c145e82 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1872,8 +1872,8 @@
     }
 
     @Test
-    public void testStats_updatedOnExpansion() throws Exception {
-        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+    public void testStats_updatedOnUserExpansion() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
@@ -1883,6 +1883,17 @@
     }
 
     @Test
+    public void testStats_notUpdatedOnAutoExpansion() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+        assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
+        assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+    }
+
+    @Test
     public void testStats_updatedOnViewSettings() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f1e76ab..9a5ebed 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
     <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 4db9a30..36d0c8b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -17,12 +17,15 @@
 package com.android.server.pm.dex;
 
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.Build;
 import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.server.pm.Installer;
+
 import dalvik.system.DelegateLastClassLoader;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
@@ -36,8 +39,13 @@
 import java.util.Map;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -45,6 +53,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
@@ -56,6 +70,12 @@
     private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
             DelegateLastClassLoader.class.getName();
 
+    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+    @Mock Installer mInstaller;
+    @Mock IPackageManager mPM;
+    private final Object mInstallLock = new Object();
+    @Mock DexManager.Listener mListener;
+
     private DexManager mDexManager;
 
     private TestData mFooUser0;
@@ -90,7 +110,8 @@
         mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
                 DELEGATE_LAST_CLASS_LOADER_NAME);
 
-        mDexManager = new DexManager(null, null, null, null);
+        mDexManager = new DexManager(
+            mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);
 
         // Foo and Bar are available to user0.
         // Only Bar is available to user1;
@@ -440,6 +461,20 @@
 
     }
 
+    @Test
+    public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+        when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
+                .thenReturn(mFooUser0.mPackageInfo);
+
+        mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
+
+        verify(mListener, times(fooSecondaries.size()))
+                .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
+                        any(DexUseInfo.class), anyString(), anyInt());
+    }
 
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
@@ -492,12 +527,12 @@
     }
 
     private PackageUseInfo getPackageUseInfo(TestData testData) {
-        assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
-        return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+        assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
+        return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
     }
 
     private void assertNoUseInfo(TestData testData) {
-        assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+        assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
     }
 
     private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -555,8 +590,8 @@
 
         List<String> getSecondaryDexPathsFromProtectedDirs() {
             List<String> paths = new ArrayList<>();
-            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
-            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+            paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
+            paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
             return paths;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index baa3e05..40edfd2 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -70,6 +70,10 @@
     private static final long HOUR_MS = 60 * MINUTE_MS;
     private static final long DAY_MS = 24 * HOUR_MS;
 
+    private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
+    private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
+    private static final long RARE_THRESHOLD = 48 * HOUR_MS;
+
     private MyInjector mInjector;
 
     static class MyContextWrapper extends ContextWrapper {
@@ -171,6 +175,14 @@
             return packageName != null && packageName.equals(mBoundWidgetPackage);
         }
 
+        @Override
+        String getAppIdleSettings() {
+            return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
+                    + WORKING_SET_THRESHOLD + "/"
+                    + FREQUENT_THRESHOLD + "/"
+                    + RARE_THRESHOLD;
+        }
+
         // Internal methods
 
         void setDisplayOn(boolean on) {
@@ -228,12 +240,12 @@
         AppStandbyController controller = setupController();
 
         setChargingState(controller, true);
-        mInjector.mElapsedRealtime = 8 * DAY_MS;
+        mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
         assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
                 mInjector.mElapsedRealtime, false));
 
         setChargingState(controller, false);
-        mInjector.mElapsedRealtime = 16 * DAY_MS;
+        mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
         controller.checkIdleStates(USER_ID);
         assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
                 mInjector.mElapsedRealtime, false));
@@ -273,26 +285,26 @@
         reportEvent(controller, USER_INTERACTION, 0);
 
         // ACTIVE bucket
-        assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
 
         // WORKING_SET bucket
-        assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
 
         // WORKING_SET bucket
-        assertTimeout(controller, 47 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
 
         // FREQUENT bucket
-        assertTimeout(controller, 4 * DAY_MS, STANDBY_BUCKET_FREQUENT);
+        assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
 
         // RARE bucket
-        assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
+        assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
 
-        reportEvent(controller, USER_INTERACTION, 9 * DAY_MS);
+        reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1);
 
-        assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
 
         // RARE bucket
-        assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+        assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
     }
 
     @Test
@@ -305,18 +317,18 @@
         reportEvent(controller, USER_INTERACTION, 0);
 
         // ACTIVE bucket
-        assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
 
         // WORKING_SET bucket
-        assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
 
         // RARE bucket, should fail because the screen wasn't ON.
-        mInjector.mElapsedRealtime = 9 * DAY_MS;
+        mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
         controller.checkIdleStates(USER_ID);
         assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
 
         mInjector.setDisplayOn(true);
-        assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+        assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 6060881..b55c79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -129,6 +129,9 @@
             controller.removeStartingWindow();
             waitUntilHandlersIdle();
             assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+            controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+            mDisplayContent.onPendingTransactionApplied();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index bb88264..d9ab5c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,71 +51,77 @@
 @RunWith(AndroidJUnit4.class)
 public class AppWindowTokenTests extends WindowTestsBase {
 
+    TaskStack mStack;
+    Task mTask;
+    WindowTestUtils.TestAppWindowToken mToken;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mStack = createTaskStackOnDisplay(mDisplayContent);
+        mTask = createTaskInStack(mStack, 0 /* userId */);
+        mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+        mTask.addChild(mToken, 0);
+    }
+
     @Test
     @Presubmit
     public void testAddWindow_Order() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertEquals(0, mToken.getWindowsCount());
 
-        assertEquals(0, token.getWindowsCount());
-
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
-        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+        final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
                 "startingWin");
-        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
-        final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+        final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
 
         // Should not contain the windows that were added above.
-        assertEquals(4, token.getWindowsCount());
-        assertTrue(token.hasWindow(win1));
-        assertTrue(token.hasWindow(startingWin));
-        assertTrue(token.hasWindow(baseWin));
-        assertTrue(token.hasWindow(win4));
+        assertEquals(4, mToken.getWindowsCount());
+        assertTrue(mToken.hasWindow(win1));
+        assertTrue(mToken.hasWindow(startingWin));
+        assertTrue(mToken.hasWindow(baseWin));
+        assertTrue(mToken.hasWindow(win4));
 
         // The starting window should be on-top of all other windows.
-        assertEquals(startingWin, token.getLastChild());
+        assertEquals(startingWin, mToken.getLastChild());
 
         // The base application window should be below all other windows.
-        assertEquals(baseWin, token.getFirstChild());
-        token.removeImmediately();
+        assertEquals(baseWin, mToken.getFirstChild());
+        mToken.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testFindMainWindow() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertNull(mToken.findMainWindow());
 
-        assertNull(token.findMainWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        assertEquals(window1, token.findMainWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+        assertEquals(window1, mToken.findMainWindow());
         window1.mAnimatingExit = true;
-        assertEquals(window1, token.findMainWindow());
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
-        assertEquals(window2, token.findMainWindow());
-        token.removeImmediately();
+        assertEquals(window1, mToken.findMainWindow());
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+        assertEquals(window2, mToken.findMainWindow());
+        mToken.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testGetTopFullscreenWindow() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertNull(mToken.getTopFullscreenWindow());
 
-        assertNull(token.getTopFullscreenWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
-        final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
-        assertEquals(window12, token.getTopFullscreenWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+        final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+        final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+        assertEquals(window12, mToken.getTopFullscreenWindow());
         window12.mAttrs.width = 500;
-        assertEquals(window11, token.getTopFullscreenWindow());
+        assertEquals(window11, mToken.getTopFullscreenWindow());
         window11.mAttrs.width = 500;
-        assertEquals(window1, token.getTopFullscreenWindow());
-        token.removeImmediately();
+        assertEquals(window1, mToken.getTopFullscreenWindow());
+        mToken.removeImmediately();
     }
 
     @Test
@@ -124,27 +130,21 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
-        // Create an app window with token on a display.
-        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final WindowTestUtils.TestAppWindowToken appWindowToken =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-        task.addChild(appWindowToken, 0);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
-        appWindowToken.addWindow(appWindow);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+        mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.resizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
-        appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
         sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
@@ -159,18 +159,11 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
-        // Create an app window with token on a display.
-        final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
-        final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final WindowTestUtils.TestAppWindowToken appWindowToken =
-                new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
-        task.addChild(appWindowToken, 0);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
-        appWindowToken.addWindow(appWindow);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+        mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
         performRotation(Surface.ROTATION_90);
@@ -193,53 +186,49 @@
     @Test
     @Presubmit
     public void testGetOrientation() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-        token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        token.setFillsParent(false);
+        mToken.setFillsParent(false);
         // Can specify orientation if app doesn't fill parent.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
 
-        token.setFillsParent(true);
-        token.hidden = true;
-        token.sendingToBottom = true;
+        mToken.setFillsParent(true);
+        mToken.hidden = true;
+        mToken.sendingToBottom = true;
         // Can not specify orientation if app isn't visible even though it fills parent.
-        assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
 
-        token.sendingToBottom = false;
-        token.setIsOnTop(true);
-        // Allow for token to provide orientation hidden if on top and not being sent to bottom.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+        mToken.sendingToBottom = false;
+        mToken.setIsOnTop(true);
+        // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
     }
 
     @Test
     @Presubmit
     public void testKeyguardFlagsDuringRelaunch() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
 
         // Add window with show when locked flag
-        token.addWindow(appWindow);
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.addWindow(appWindow);
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Start relaunching
-        token.startRelaunching();
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.startRelaunching();
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Remove window and make sure that we still report back flag
-        token.removeChild(appWindow);
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.removeChild(appWindow);
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Finish relaunching and ensure flag is now not reported
-        token.finishRelaunching();
-        assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+        mToken.finishRelaunching();
+        assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
deleted file mode 100644
index c3a471a..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DimLayerController} class.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
- */
-@SmallTest
-@Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
-public class DimLayerControllerTests extends WindowTestsBase {
-
-    /**
-     * This tests if shared fullscreen dim layer is added when stack is added to display
-     * and is removed when the only stack on the display is removed.
-     */
-    @Test
-    public void testSharedFullScreenDimLayer() throws Exception {
-        // Create a display.
-        final DisplayContent dc = createNewDisplay();
-        assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
-        // Add stack with activity.
-        final TaskStack stack = createTaskStackOnDisplay(dc);
-        assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
-        assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
-        // Remove the only stack on the display and check if the shared dim layer clears.
-        stack.removeImmediately();
-        assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
-        assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
new file mode 100644
index 0000000..f069d49
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+
+/**
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DimmerTests extends WindowTestsBase {
+    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+        final SurfaceControl mControl = mock(SurfaceControl.class);
+        final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+
+        @Override
+        SurfaceControl getSurfaceControl() {
+            return mControl;
+        }
+
+        @Override
+        SurfaceControl.Transaction getPendingTransaction() {
+            return mTransaction;
+        }
+    }
+
+    private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+        final SurfaceSession mSession = new SurfaceSession();
+        SurfaceControl mBuiltSurface = null;
+        final SurfaceControl mHostControl = mock(SurfaceControl.class);
+        final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+
+        class MockSurfaceBuilder extends SurfaceControl.Builder {
+            MockSurfaceBuilder(SurfaceSession ss) {
+                super(ss);
+            }
+
+            @Override
+            public SurfaceControl build() {
+                SurfaceControl sc = mock(SurfaceControl.class);
+                mBuiltSurface = sc;
+                return sc;
+            }
+        }
+
+        @Override
+        SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+            return new MockSurfaceBuilder(mSession);
+        }
+
+        @Override
+        SurfaceControl getSurfaceControl() {
+            return mHostControl;
+        }
+
+        @Override
+        SurfaceControl.Transaction getPendingTransaction() {
+            return mHostTransaction;
+        }
+    }
+
+    MockSurfaceBuildingContainer mHost;
+    Dimmer mDimmer;
+    SurfaceControl.Transaction mTransaction;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mHost = new MockSurfaceBuildingContainer();
+        mTransaction = mock(SurfaceControl.Transaction.class);
+        mDimmer = new Dimmer(mHost);
+    }
+
+    @Test
+    public void testDimAboveNoChildCreatesSurface() throws Exception {
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, alpha);
+        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+        verify(mTransaction).show(mHost.mBuiltSurface);
+        verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+    }
+
+    @Test
+    public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+        float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, alpha);
+        final SurfaceControl firstSurface = mHost.mBuiltSurface;
+
+        alpha = 0.9f;
+        mDimmer.dimAbove(mTransaction, alpha);
+
+        assertEquals(firstSurface, mHost.mBuiltSurface);
+        verify(mTransaction).setAlpha(firstSurface, 0.9f);
+    }
+
+    @Test
+    public void testUpdateDimsAppliesSize() throws Exception {
+        mDimmer.dimAbove(mTransaction, 0.8f);
+
+        int width = 100;
+        int height = 300;
+        Rect bounds = new Rect(0, 0, width, height);
+        mDimmer.updateDims(mTransaction, bounds);
+        verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+    }
+
+    @Test
+    public void testDimAboveNoChildNotReset() throws Exception {
+        mDimmer.dimAbove(mTransaction, 0.8f);
+        mDimmer.resetDimStates();
+
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mHost.mBuiltSurface, never()).destroy();
+    }
+
+    @Test
+    public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, child, alpha);
+        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+        verify(mTransaction).show(mHost.mBuiltSurface);
+        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+    }
+
+    @Test
+    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimBelow(mTransaction, child, alpha);
+        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+        verify(mTransaction).show(mHost.mBuiltSurface);
+        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+    }
+
+    @Test
+    public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, child, alpha);
+        mDimmer.resetDimStates();
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mHost.mBuiltSurface).destroy();
+    }
+
+    @Test
+    public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        final float alpha = 0.8f;
+        mDimmer.dimAbove(mTransaction, child, alpha);
+        mDimmer.resetDimStates();
+        mDimmer.dimAbove(mTransaction, child, alpha);
+
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mHost.mBuiltSurface, never()).destroy();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 887def7..873a01b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -66,7 +66,7 @@
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
 
         mPositioner = new TaskPositioner(sWm);
-        mPositioner.register(display);
+        mPositioner.register(mDisplayContent);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
index b846fd0..0ef78f4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -105,14 +105,10 @@
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
         assertEquals(stack, task.mStack);
-        assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
-        assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
 
         // Remove stack and check if its child is also removed.
         stack.removeImmediately();
         assertNull(stack.getDisplayContent());
         assertNull(task.mStack);
-        assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
-        assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
     }
 }
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 53d0bfb..a45695f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -568,11 +568,6 @@
     }
 
     @Override
-    public boolean canMagnifyWindow(int windowType) {
-        return false;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         return false;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
deleted file mode 100644
index 3c3514f..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
-        sWm.mInputMethodTarget = null;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // The Ime has an higher base layer than app windows and lower base layer than system
-        // windows, so it should be above app windows and below system windows if there isn't an IME
-        // target.
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows and below system windows if it is targeting an app
-        // window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
-                "imeAppTargetChildAboveWindow");
-        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
-                "imeAppTargetChildBelowWindow");
-
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows except for child windows that are z-ordered above it
-        // and below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
-        final WindowState appBelowImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState appAboveImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows except for non-fullscreen app window above it and
-        // below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
-        assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
-        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
-                mDisplayContent, "imeSystemOverlayTarget",
-                true /* ownerCanAddInternalSystemWindow */);
-
-        sWm.mInputMethodTarget = imeSystemOverlayTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // The IME target base layer is higher than all window except for the nav bar window, so the
-        // IME should be above all windows except for the nav bar.
-        assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testStackLayers() throws Exception {
-        WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
-        WindowState dockedStackWindow = createWindowOnStack(null,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
-                mDisplayContent, "dockedStackWindow");
-        WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
-                mDisplayContent, "assistantStackWindow");
-
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
-        assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
-        assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
-    }
-
-    private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
-            throws Exception {
-        assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
-    }
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 0980f7e..4c5e291 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -73,7 +73,6 @@
     private static boolean sOneTimeSetupDone = false;
     DisplayContent mDisplayContent;
     DisplayInfo mDisplayInfo = new DisplayInfo();
-    WindowLayersController mLayersController;
     WindowState mWallpaperWindow;
     WindowState mImeWindow;
     WindowState mImeDialogWindow;
@@ -98,8 +97,9 @@
 
         final Context context = InstrumentationRegistry.getTargetContext();
         AttributeCache.init(context);
+
         sWm = TestWindowManagerPolicy.getWindowManagerService(context);
-        mLayersController = new WindowLayersController(sWm);
+        beforeCreateDisplay();
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);
         mDisplayContent = createNewDisplay();
@@ -126,6 +126,10 @@
         waitUntilHandlersIdle();
     }
 
+    void beforeCreateDisplay() {
+        // Called before display is created.
+    }
+
     @After
     public void tearDown() throws Exception {
         final LinkedList<WindowState> nonCommonWindows = new LinkedList();
@@ -149,6 +153,14 @@
         waitUntilHandlersIdle();
     }
 
+    /**
+     * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+     *         set-up (or null).
+     */
+    SurfaceBuilderFactory getSurfaceBuilderFactory() {
+        return null;
+    }
+
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
         final WindowState win = createWindow(parent, type, name);
         mCommonWindows.add(win);
@@ -162,6 +174,11 @@
         Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
     }
 
+    /** Asserts that the first entry is greater than the second entry. */
+    void assertLessThan(int first, int second) throws Exception {
+        Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+    }
+
     /**
      * Waits until the main handler for WM has processed all messages.
      */
@@ -264,7 +281,7 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+        return new DisplayContent(display, sWm, new WallpaperController(sWm));
     }
 
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 692e08b..7219104 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,43 +94,6 @@
         assertEquals(null, dc.getWindowToken(token.token));
     }
 
-    @Test
-    public void testAdjustAnimLayer() throws Exception {
-        final WindowTestUtils.TestWindowToken token =
-                new WindowTestUtils.TestWindowToken(0, mDisplayContent);
-        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
-        final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
-        window2.mLayer = 100;
-        window3.mLayer = 200;
-
-        // We assign layers once, to get the base values computed by
-        // the controller.
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
-        final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
-        final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
-        final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
-        final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
-        // Then we set an adjustment, and assign them again, they should
-        // be offset.
-        int adj = token.adj = 50;
-        mLayersController.assignWindowLayers(mDisplayContent);
-        final int highestLayer = token.getHighestAnimLayer();
-
-        assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
-        assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
-        assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
-        assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
-        assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
-        assertEquals(window3StartLayer + adj, highestLayer);
-    }
-
     /**
      * Test that a window token isn't orphaned by the system when it is requested to be removed.
      * Tokens should only be removed from the system when all their windows are gone.
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
new file mode 100644
index 0000000..f7c4b1f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+    private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+        HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+        HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+        @Override
+        public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+            mRelativeLayersForControl.remove(sc);
+            mLayersForControl.put(sc, layer);
+            return super.setLayer(sc, layer);
+        }
+
+        @Override
+        public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+                SurfaceControl relativeTo,
+                int layer) {
+            mRelativeLayersForControl.put(sc, relativeTo);
+            mLayersForControl.put(sc, layer);
+            return super.setRelativeLayer(sc, relativeTo, layer);
+        }
+
+        int getLayer(SurfaceControl sc) {
+            return mLayersForControl.get(sc);
+        }
+
+        SurfaceControl getRelativeLayer(SurfaceControl sc) {
+            return mRelativeLayersForControl.get(sc);
+        }
+    };
+
+    // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+    // such that we can keep track of the parents of Surfaces as they are constructed.
+    private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+    private class HierarchyRecorder extends SurfaceControl.Builder {
+        SurfaceControl mPendingParent;
+
+        HierarchyRecorder(SurfaceSession s) {
+            super(s);
+        }
+
+        public SurfaceControl.Builder setParent(SurfaceControl sc) {
+            mPendingParent = sc;
+            return super.setParent(sc);
+        }
+        public SurfaceControl build() {
+            SurfaceControl sc = super.build();
+            mParentFor.put(sc, mPendingParent);
+            mPendingParent = null;
+            return sc;
+        }
+    };
+
+    class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+        public SurfaceControl.Builder make(SurfaceSession s) {
+            return new HierarchyRecorder(s);
+        }
+    };
+
+    private LayerRecordingTransaction mTransaction;
+
+    @Override
+    void beforeCreateDisplay() {
+        // We can't use @Before here because it may happen after WindowTestsBase @Before
+        // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+        // would miss construction of the top-level layers.
+        mTransaction = new LayerRecordingTransaction();
+        sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+    }
+
+    @After
+    public void after() {
+        mTransaction.close();
+        mParentFor.clear();
+    }
+
+    LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+        LinkedList<SurfaceControl> p = new LinkedList();
+        SurfaceControl current = sc;
+        do {
+            p.addLast(current);
+
+            SurfaceControl rs = t.getRelativeLayer(current);
+            if (rs != null) {
+                current = rs;
+            } else {
+                current = mParentFor.get(current);
+            }
+        } while (current != null);
+        return p;
+    }
+
+    void assertZOrderGreaterThan(LayerRecordingTransaction t,
+            SurfaceControl left, SurfaceControl right) throws Exception {
+        final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+        final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+        SurfaceControl commonAncestor = null;
+        SurfaceControl leftTop = leftParentChain.peekLast();
+        SurfaceControl rightTop = rightParentChain.peekLast();
+        while (leftTop != null && rightTop != null && leftTop == rightTop) {
+            commonAncestor = leftParentChain.removeLast();
+            rightParentChain.removeLast();
+            leftTop = leftParentChain.peekLast();
+            rightTop = rightParentChain.peekLast();
+        }
+
+        if (rightTop == null) { // right is the parent of left.
+            assertGreaterThan(t.getLayer(leftTop), 0);
+        } else if (leftTop == null) { // left is the parent of right.
+            assertGreaterThan(0, t.getLayer(rightTop));
+        } else {
+            assertGreaterThan(t.getLayer(leftTop),
+                    t.getLayer(rightTop));
+        }
+    }
+
+    void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+            WindowState left, WindowState right) throws Exception {
+        assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+        sWm.mInputMethodTarget = null;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // The Ime has an higher base layer than app windows and lower base layer than system
+        // windows, so it should be above app windows and below system windows if there isn't an IME
+        // target.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows and below system windows if it is targeting an app
+        // window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+                "imeAppTargetChildAboveWindow");
+        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+                "imeAppTargetChildBelowWindow");
+
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows except for child windows that are z-ordered above it
+        // and below system windows if it is targeting an app window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+        final WindowState appBelowImeTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState appAboveImeTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows except for non-fullscreen app window above it and
+        // below system windows if it is targeting an app window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+        assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+                mDisplayContent, "imeSystemOverlayTarget",
+                true /* ownerCanAddInternalSystemWindow */);
+
+        sWm.mInputMethodTarget = imeSystemOverlayTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // The IME target base layer is higher than all window except for the nav bar window, so the
+        // IME should be above all windows except for the nav bar.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+        // The IME has a higher base layer than the status bar so we may expect it to go
+        // above the status bar once they are both in the Non-App layer, as past versions of this
+        // test enforced. However this seems like the wrong behavior unless the status bar is the
+        // IME target.
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+        sWm.mInputMethodTarget = mStatusBarWindow;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testStackLayers() throws Exception {
+        final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+                "pinnedStackWindow");
+        final WindowState dockedStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+                mDisplayContent, "dockedStackWindow");
+        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+                mDisplayContent, "assistantStackWindow");
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+        assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 1e5eb05..7ca17af 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -91,8 +91,6 @@
     private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
     private long mScreenOnDuration; // Total screen on duration since device was "born"
 
-    private long mElapsedTimeThreshold;
-    private long mScreenOnTimeThreshold;
     private final File mStorageDir;
 
     private boolean mScreenOn;
@@ -113,11 +111,6 @@
         readScreenOnTime();
     }
 
-    public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
-        mElapsedTimeThreshold = elapsedTimeThreshold;
-        mScreenOnTimeThreshold = screenOnTimeThreshold;
-    }
-
     public void updateDisplay(boolean screenOn, long elapsedRealtime) {
         if (screenOn == mScreenOn) return;
 
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 97f7de2..cd0fce6 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -91,14 +91,14 @@
             0,
             0,
             COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
-            COMPRESS_TIME ? 240 * 1000 : 8 * ONE_HOUR
+            COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
     };
 
     static final long[] ELAPSED_TIME_THRESHOLDS = {
             0,
             COMPRESS_TIME ?  1 * ONE_MINUTE : 12 * ONE_HOUR,
-            COMPRESS_TIME ?  4 * ONE_MINUTE :  2 * ONE_DAY,
-            COMPRESS_TIME ? 16 * ONE_MINUTE :  8 * ONE_DAY
+            COMPRESS_TIME ?  4 * ONE_MINUTE : 24 * ONE_HOUR,
+            COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
     };
 
     static final int[] THRESHOLD_BUCKETS = {
@@ -140,9 +140,7 @@
     static final int MSG_PAROLE_STATE_CHANGED = 9;
     static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
 
-    long mAppIdleScreenThresholdMillis;
     long mCheckIdleIntervalMillis;
-    long mAppIdleWallclockThresholdMillis;
     long mAppIdleParoleIntervalMillis;
     long mAppIdleParoleDurationMillis;
     long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
@@ -912,14 +910,6 @@
         pw.println();
         pw.println("Settings:");
 
-        pw.print("  mAppIdleDurationMillis=");
-        TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
-        pw.println();
-
-        pw.print("  mAppIdleWallclockThresholdMillis=");
-        TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
-        pw.println();
-
         pw.print("  mCheckIdleIntervalMillis=");
         TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
         pw.println();
@@ -1049,6 +1039,11 @@
                 int userId) {
             return appWidgetManager.isBoundWidgetPackage(packageName, userId);
         }
+
+        String getAppIdleSettings() {
+            return Settings.Global.getString(mContext.getContentResolver(),
+                    Settings.Global.APP_IDLE_CONSTANTS);
+        }
     }
 
     class AppStandbyHandler extends Handler {
@@ -1181,31 +1176,18 @@
                 // Look at global settings for this.
                 // TODO: Maybe apply different thresholds for different users.
                 try {
-                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
-                            Settings.Global.APP_IDLE_CONSTANTS));
+                    mParser.setString(mInjector.getAppIdleSettings());
                 } catch (IllegalArgumentException e) {
                     Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
                     // fallthrough, mParser is empty and all defaults will be returned.
                 }
 
-                // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
-                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
-                mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
-                        COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
-                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
-                        COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
-
                 // Default: 24 hours between paroles
                 mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
                         COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
 
                 mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
-                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
-                        mAppIdleScreenThresholdMillis);
 
                 String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
                 mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
@@ -1215,6 +1197,9 @@
                         null);
                 mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
                         ELAPSED_TIME_THRESHOLDS);
+                mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
+                        COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
+
             }
         }
 
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index dc897a2..c968406 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -101,9 +101,6 @@
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
-     * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
-     * not a 2 or 3-digit code.
-     *
      * @hide
      */
     public CellIdentityGsm (int lac, int cid, int arfcn, int bsic, String mccStr,
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index f234b0a..825dcc3 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -102,9 +102,6 @@
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
-     * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
-     * not a 2 or 3-digit code.
-     *
      * @hide
      */
     public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 1a461f2..e74b570 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -102,9 +102,6 @@
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
-     * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
-     * not a 2 or 3-digit code.
-     *
      * @hide
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 64cc7c1..1e6abf2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -362,6 +362,9 @@
 
     /**
      * TelephonyProvider column name for enable Volte.
+     *
+     * If this setting is not initialized (set to -1)  then we use the Carrier Config value
+     * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
      *@hide
      */
     public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index c9f3199..fd42033 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -72,6 +72,9 @@
 
         self.ident = self.raw.replace(" deprecated ", " ")
 
+    def __hash__(self):
+        return hash(self.raw)
+
     def __repr__(self):
         return self.raw
 
@@ -110,6 +113,9 @@
             ident = ident[:ident.index(" throws ")]
         self.ident = ident
 
+    def __hash__(self):
+        return hash(self.raw)
+
     def __repr__(self):
         return self.raw
 
@@ -145,6 +151,9 @@
 
         self.name = self.fullname[self.fullname.rindex(".")+1:]
 
+    def __hash__(self):
+        return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
+
     def __repr__(self):
         return self.raw
 
@@ -256,6 +265,14 @@
     _fail(clazz, detail, True, rule, msg)
 
 
+noticed = {}
+
+def notice(clazz):
+    global noticed
+
+    noticed[clazz.fullname] = hash(clazz)
+
+
 def verify_constants(clazz):
     """All static final constants must be FOO_NAME style."""
     if re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -1203,6 +1220,9 @@
 
 def examine_clazz(clazz):
     """Find all style issues in the given class."""
+
+    notice(clazz)
+
     if clazz.pkg.name.startswith("java"): return
     if clazz.pkg.name.startswith("junit"): return
     if clazz.pkg.name.startswith("org.apache"): return
@@ -1258,10 +1278,11 @@
 
 def examine_stream(stream):
     """Find all style issues in the given API stream."""
-    global failures
+    global failures, noticed
     failures = {}
+    noticed = {}
     _parse_stream(stream, examine_clazz)
-    return failures
+    return (failures, noticed)
 
 
 def examine_api(api):
@@ -1338,6 +1359,8 @@
             help="Disable terminal colors")
     parser.add_argument("--allow-google", action='store_const', const=True,
             help="Allow references to Google")
+    parser.add_argument("--show-noticed", action='store_const', const=True,
+            help="Show API changes noticed")
     args = vars(parser.parse_args())
 
     if args['no_color']:
@@ -1350,16 +1373,21 @@
     previous_file = args['previous.txt']
 
     with current_file as f:
-        cur_fail = examine_stream(f)
+        cur_fail, cur_noticed = examine_stream(f)
     if not previous_file is None:
         with previous_file as f:
-            prev_fail = examine_stream(f)
+            prev_fail, prev_noticed = examine_stream(f)
 
         # ignore errors from previous API level
         for p in prev_fail:
             if p in cur_fail:
                 del cur_fail[p]
 
+        # ignore classes unchanged from previous API level
+        for k, v in prev_noticed.iteritems():
+            if k in cur_noticed and v == cur_noticed[k]:
+                del cur_noticed[k]
+
         """
         # NOTE: disabled because of memory pressure
         # look for compatibility issues
@@ -1371,6 +1399,12 @@
             print
         """
 
+    if args['show_noticed'] and len(cur_noticed) != 0:
+        print "%s API changes noticed %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+        for f in sorted(cur_noticed.keys()):
+            print f
+        print
+
     if len(cur_fail) != 0:
         print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
         for f in sorted(cur_fail):
diff --git a/vr/Android.mk b/vr/Android.mk
index 5b65d3f..73e9f23 100644
--- a/vr/Android.mk
+++ b/vr/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_MODULE := libdvr_loader
 LOCAL_MODULE_OWNER := google
 LOCAL_SRC_FILES := dvr_library_loader.cpp
+LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_SHARED_LIBRARY)
 
 # Java platform library for vr stuff.
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 115b86d..aa2c268 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -113,6 +113,32 @@
     }
 
     /**
+     * Called when a discovery (publish or subscribe) operation results in a
+     * service discovery. Called when a Subscribe service was configured with a range requirement
+     * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or
+     * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared
+     * (i.e. this callback called) if the range of the publisher is within the specified distance
+     * constraints.
+     *
+     * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     * @param serviceSpecificInfo The service specific information (arbitrary
+     *            byte array) provided by the peer as part of its discovery
+     *            configuration.
+     * @param matchFilter The filter which resulted in this service discovery. For
+     * {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED},
+     * {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE} discovery sessions this is the publisher's
+     *                    match filter. For {@link PublishConfig#PUBLISH_TYPE_SOLICITED},
+     *                    {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} discovery sessions this
+     *                    is the subscriber's match filter.
+     * @param distanceMm The measured distance to the Publisher in mm.
+     * @hide
+     */
+    public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
+        byte[] serviceSpecificInfo, List<byte[]> matchFilter, int distanceMm) {
+        /* empty */
+    }
+
+    /**
      * Called in response to
      * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}
      * when a message is transmitted successfully - i.e. when it was received successfully by the
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index 8ff3842..421a8af 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -29,6 +29,8 @@
     void onSessionTerminated(int reason);
 
     void onMatch(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter);
+    void onMatchWithDistance(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter,
+            int distanceMm);
 
     void onMessageSendSuccess(int messageId);
     void onMessageSendFail(int messageId, int reason);
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index d018620..e60f52f 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -29,6 +29,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Defines the configuration of a Aware publish session. Built using
@@ -81,14 +82,19 @@
     public final boolean mEnableTerminateNotification;
 
     /** @hide */
+    public final boolean mEnableRanging;
+
+    /** @hide */
     public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
-            int publishType, int ttlSec, boolean enableTerminateNotification) {
+            int publishType, int ttlSec, boolean enableTerminateNotification,
+            boolean enableRanging) {
         mServiceName = serviceName;
         mServiceSpecificInfo = serviceSpecificInfo;
         mMatchFilter = matchFilter;
         mPublishType = publishType;
         mTtlSec = ttlSec;
         mEnableTerminateNotification = enableTerminateNotification;
+        mEnableRanging = enableRanging;
     }
 
     @Override
@@ -103,7 +109,8 @@
                 + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
                 + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
                 + ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec
-                + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+                + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+                + ", mEnableRanging=" + mEnableRanging + "]";
     }
 
     @Override
@@ -119,6 +126,7 @@
         dest.writeInt(mPublishType);
         dest.writeInt(mTtlSec);
         dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+        dest.writeInt(mEnableRanging ? 1 : 0);
     }
 
     public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
@@ -135,9 +143,10 @@
             int publishType = in.readInt();
             int ttlSec = in.readInt();
             boolean enableTerminateNotification = in.readInt() != 0;
+            boolean enableRanging = in.readInt() != 0;
 
             return new PublishConfig(serviceName, ssi, matchFilter, publishType,
-                    ttlSec, enableTerminateNotification);
+                    ttlSec, enableTerminateNotification, enableRanging);
         }
     };
 
@@ -157,21 +166,14 @@
                 lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
                 && mPublishType == lhs.mPublishType
                 && mTtlSec == lhs.mTtlSec
-                && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+                && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+                && mEnableRanging == lhs.mEnableRanging;
     }
 
     @Override
     public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + Arrays.hashCode(mServiceName);
-        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + Arrays.hashCode(mMatchFilter);
-        result = 31 * result + mPublishType;
-        result = 31 * result + mTtlSec;
-        result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
-
-        return result;
+        return Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec,
+                mEnableTerminateNotification, mEnableRanging);
     }
 
     /**
@@ -226,6 +228,7 @@
         private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
         private int mTtlSec = 0;
         private boolean mEnableTerminateNotification = true;
+        private boolean mEnableRanging = false;
 
         /**
          * Specify the service name of the publish session. The actual on-air
@@ -352,12 +355,35 @@
         }
 
         /**
+         * Configure whether the publish discovery session supports ranging and allows peers to
+         * measure distance to it. This API is used in conjunction with
+         * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and
+         * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
+         * maximum distance at which discovery will be triggered.
+         * <p>
+         * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
+         * device will be refused. If the peer has ranging enabled (using the
+         * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+         *
+         * @param enable If true, ranging is supported on request of the peer.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide
+         */
+        public Builder setRangingEnabled(boolean enable) {
+            mEnableRanging = enable;
+            return this;
+        }
+
+        /**
          * Build {@link PublishConfig} given the current requests made on the
          * builder.
          */
         public PublishConfig build() {
             return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
-                    mTtlSec, mEnableTerminateNotification);
+                    mTtlSec, mEnableTerminateNotification, mEnableRanging);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 4bf2fb6..f6552a7 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -29,6 +29,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Defines the configuration of a Aware subscribe session. Built using
@@ -79,15 +80,32 @@
     public final boolean mEnableTerminateNotification;
 
     /** @hide */
+    public final boolean mMinDistanceMmSet;
+
+    /** @hide */
+    public final int mMinDistanceMm;
+
+    /** @hide */
+    public final boolean mMaxDistanceMmSet;
+
+    /** @hide */
+    public final int mMaxDistanceMm;
+
+    /** @hide */
     public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
-            int subscribeType, int ttlSec,
-            boolean enableTerminateNotification) {
+            int subscribeType, int ttlSec, boolean enableTerminateNotification,
+            boolean minDistanceMmSet, int minDistanceMm, boolean maxDistanceMmSet,
+            int maxDistanceMm) {
         mServiceName = serviceName;
         mServiceSpecificInfo = serviceSpecificInfo;
         mMatchFilter = matchFilter;
         mSubscribeType = subscribeType;
         mTtlSec = ttlSec;
         mEnableTerminateNotification = enableTerminateNotification;
+        mMinDistanceMm = minDistanceMm;
+        mMinDistanceMmSet = minDistanceMmSet;
+        mMaxDistanceMm = maxDistanceMm;
+        mMaxDistanceMmSet = maxDistanceMmSet;
     }
 
     @Override
@@ -102,7 +120,11 @@
                 + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
                 + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
                 + ", mSubscribeType=" + mSubscribeType + ", mTtlSec=" + mTtlSec
-                + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+                + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+                + ", mMinDistanceMm=" + mMinDistanceMm
+                + ", mMinDistanceMmSet=" + mMinDistanceMmSet
+                + ", mMaxDistanceMm=" + mMaxDistanceMm
+                + ", mMaxDistanceMmSet=" + mMaxDistanceMmSet + "]";
     }
 
     @Override
@@ -118,6 +140,10 @@
         dest.writeInt(mSubscribeType);
         dest.writeInt(mTtlSec);
         dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+        dest.writeInt(mMinDistanceMm);
+        dest.writeInt(mMinDistanceMmSet ? 1 : 0);
+        dest.writeInt(mMaxDistanceMm);
+        dest.writeInt(mMaxDistanceMmSet ? 1 : 0);
     }
 
     public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() {
@@ -134,9 +160,14 @@
             int subscribeType = in.readInt();
             int ttlSec = in.readInt();
             boolean enableTerminateNotification = in.readInt() != 0;
+            int minDistanceMm = in.readInt();
+            boolean minDistanceMmSet = in.readInt() != 0;
+            int maxDistanceMm = in.readInt();
+            boolean maxDistanceMmSet = in.readInt() != 0;
 
-            return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType,
-                    ttlSec, enableTerminateNotification);
+            return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, ttlSec,
+                    enableTerminateNotification, minDistanceMmSet, minDistanceMm, maxDistanceMmSet,
+                    maxDistanceMm);
         }
     };
 
@@ -152,23 +183,37 @@
 
         SubscribeConfig lhs = (SubscribeConfig) o;
 
-        return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
-                lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
-                && mSubscribeType == lhs.mSubscribeType
-                && mTtlSec == lhs.mTtlSec
-                && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+        if (!(Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(
+                mServiceSpecificInfo, lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter,
+                lhs.mMatchFilter) && mSubscribeType == lhs.mSubscribeType && mTtlSec == lhs.mTtlSec
+                && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+                && mMinDistanceMmSet == lhs.mMinDistanceMmSet
+                && mMaxDistanceMmSet == lhs.mMaxDistanceMmSet)) {
+            return false;
+        }
+
+        if (mMinDistanceMmSet && mMinDistanceMm != lhs.mMinDistanceMm) {
+            return false;
+        }
+
+        if (mMaxDistanceMmSet && mMaxDistanceMm != lhs.mMaxDistanceMm) {
+            return false;
+        }
+
+        return true;
     }
 
     @Override
     public int hashCode() {
-        int result = 17;
+        int result = Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mSubscribeType,
+                mTtlSec, mEnableTerminateNotification, mMinDistanceMmSet, mMaxDistanceMmSet);
 
-        result = 31 * result + Arrays.hashCode(mServiceName);
-        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + Arrays.hashCode(mMatchFilter);
-        result = 31 * result + mSubscribeType;
-        result = 31 * result + mTtlSec;
-        result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
+        if (mMinDistanceMmSet) {
+            result = Objects.hash(result, mMinDistanceMm);
+        }
+        if (mMaxDistanceMmSet) {
+            result = Objects.hash(result, mMaxDistanceMm);
+        }
 
         return result;
     }
@@ -213,6 +258,17 @@
                         "Match filter longer than supported by device characteristics");
             }
         }
+
+        if (mMinDistanceMmSet && mMinDistanceMm < 0) {
+            throw new IllegalArgumentException("Minimum distance must be non-negative");
+        }
+        if (mMaxDistanceMmSet && mMaxDistanceMm < 0) {
+            throw new IllegalArgumentException("Maximum distance must be non-negative");
+        }
+        if (mMinDistanceMmSet && mMaxDistanceMmSet && mMaxDistanceMm <= mMinDistanceMm) {
+            throw new IllegalArgumentException(
+                    "Maximum distance must be greater than minimum distance");
+        }
     }
 
     /**
@@ -225,6 +281,10 @@
         private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
         private int mTtlSec = 0;
         private boolean mEnableTerminateNotification = true;
+        private boolean mMinDistanceMmSet = false;
+        private int mMinDistanceMm;
+        private boolean mMaxDistanceMmSet = false;
+        private int mMaxDistanceMm;
 
         /**
          * Specify the service name of the subscribe session. The actual on-air
@@ -350,13 +410,69 @@
         }
 
         /**
+         * Configure the minimum distance to a discovered publisher at which to trigger a discovery
+         * notification. I.e. discovery will only be triggered if we've found a matching publisher
+         * (based on the other criteria in this configuration) <b>and</b> the distance to the
+         * publisher is > the value specified in this API.
+         * <p>
+         * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence,
+         * i.e. discovery with min < distance < max.
+         * <p>
+         * If this API is called, the subscriber requires ranging. In such a case, the publisher
+         * peer must enable ranging using
+         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+         * never be triggered.
+         *
+         * @param minDistanceMm Minimum distance, in mm, to the publisher above which to trigger
+         *                      discovery.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide
+         */
+        public Builder setMinDistanceMm(int minDistanceMm) {
+            mMinDistanceMm = minDistanceMm;
+            mMinDistanceMmSet = true;
+            return this;
+        }
+
+        /**
+         * Configure the maximum distance to a discovered publisher at which to trigger a discovery
+         * notification. I.e. discovery will only be triggered if we've found a matching publisher
+         * (based on the other criteria in this configuration) <b>and</b> the distance to the
+         * publisher is < the value specified in this API.
+         * <p>
+         * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence,
+         * i.e. discovery with min < distance < max.
+         * <p>
+         * If this API is called, the subscriber requires ranging. In such a case, the publisher
+         * peer must enable ranging using
+         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+         * never be triggered.
+         *
+         * @param maxDistanceMm Maximum distance, in mm, to the publisher below which to trigger
+         *                      discovery.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide
+         */
+        public Builder setMaxDistanceMm(int maxDistanceMm) {
+            mMaxDistanceMm = maxDistanceMm;
+            mMaxDistanceMmSet = true;
+            return this;
+        }
+
+        /**
          * Build {@link SubscribeConfig} given the current requests made on the
          * builder.
          */
         public SubscribeConfig build() {
             return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter,
-                    mSubscribeType, mTtlSec,
-                    mEnableTerminateNotification);
+                    mSubscribeType, mTtlSec, mEnableTerminateNotification,
+                    mMinDistanceMmSet, mMinDistanceMm, mMaxDistanceMmSet, mMaxDistanceMm);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index ed6804d..166da48 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -20,8 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
-import android.annotation.SystemService;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkRequest;
@@ -564,6 +564,7 @@
         private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5;
         private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
         private static final int CALLBACK_MESSAGE_RECEIVED = 7;
+        private static final int CALLBACK_MATCH_WITH_DISTANCE = 8;
 
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
@@ -618,7 +619,9 @@
                         case CALLBACK_SESSION_TERMINATED:
                             onProxySessionTerminated(msg.arg1);
                             break;
-                        case CALLBACK_MATCH: {
+                        case CALLBACK_MATCH:
+                        case CALLBACK_MATCH_WITH_DISTANCE:
+                            {
                             List<byte[]> matchFilter = null;
                             byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2);
                             try {
@@ -629,9 +632,16 @@
                                         + new String(HexEncoding.encode(arg))
                                         + "' - cannot be parsed: e=" + e);
                             }
-                            mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
-                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
-                                    matchFilter);
+                            if (msg.what == CALLBACK_MATCH) {
+                                mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
+                                        msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+                                        matchFilter);
+                            } else {
+                                mOriginalCallback.onServiceDiscoveredWithinRange(
+                                        new PeerHandle(msg.arg1),
+                                        msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+                                        matchFilter, msg.arg2);
+                            }
                             break;
                         }
                         case CALLBACK_MESSAGE_SEND_SUCCESS:
@@ -684,21 +694,38 @@
             mHandler.sendMessage(msg);
         }
 
-        @Override
-        public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
-            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
-
+        private void onMatchCommon(int messageType, int peerId, byte[] serviceSpecificInfo,
+                byte[] matchFilter, int distanceMm) {
             Bundle data = new Bundle();
             data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
             data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
 
-            Message msg = mHandler.obtainMessage(CALLBACK_MATCH);
+            Message msg = mHandler.obtainMessage(messageType);
             msg.arg1 = peerId;
+            msg.arg2 = distanceMm;
             msg.setData(data);
             mHandler.sendMessage(msg);
         }
 
         @Override
+        public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
+            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+            onMatchCommon(CALLBACK_MATCH, peerId, serviceSpecificInfo, matchFilter, 0);
+        }
+
+        @Override
+        public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter,
+                int distanceMm) {
+            if (VDBG) {
+                Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm);
+            }
+
+            onMatchCommon(CALLBACK_MATCH_WITH_DISTANCE, peerId, serviceSpecificInfo, matchFilter,
+                    distanceMm);
+        }
+
+        @Override
         public void onMessageSendSuccess(int messageId) {
             if (VDBG) Log.v(TAG, "onMessageSendSuccess");
 
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 1aeeee3..653fcff 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -19,20 +19,17 @@
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.net.wifi.RttManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
-import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -407,6 +404,7 @@
         final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
         final int messageId = 2123;
         final int reason = AWARE_STATUS_ERROR;
+        final int distanceMm = 100;
 
         InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
                 mockSubscribeSession);
@@ -442,6 +440,8 @@
         // (3) ...
         subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
         sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+        sessionProxyCallback.getValue().onMatchWithDistance(peerHandle.peerId, string1.getBytes(),
+                matchFilter, distanceMm);
         sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
         sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
         sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
@@ -450,7 +450,9 @@
         inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
                 eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
         inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
-                eq(string1.getBytes()), (List<byte[]>) isNull());
+                eq(string1.getBytes()), isNull());
+        inOrder.verify(mockSessionCallback).onServiceDiscoveredWithinRange(peerIdCaptor.capture(),
+                eq(string1.getBytes()), isNull(), eq(distanceMm));
         assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
         inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
                 eq(string1.getBytes()));
@@ -685,11 +687,18 @@
         SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
 
         collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+        collector.checkThat("mServiceSpecificInfo", subscribeConfig.mServiceSpecificInfo,
+                equalTo(null));
+        collector.checkThat("mMatchFilter", subscribeConfig.mMatchFilter, equalTo(null));
         collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
                 equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
         collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
         collector.checkThat("mEnableTerminateNotification",
                 subscribeConfig.mEnableTerminateNotification, equalTo(true));
+        collector.checkThat("mMinDistanceCmSet", subscribeConfig.mMinDistanceMmSet, equalTo(false));
+        collector.checkThat("mMinDistanceMm", subscribeConfig.mMinDistanceMm, equalTo(0));
+        collector.checkThat("mMaxDistanceMmSet", subscribeConfig.mMaxDistanceMmSet, equalTo(false));
+        collector.checkThat("mMaxDistanceMm", subscribeConfig.mMaxDistanceMm, equalTo(0));
     }
 
     @Test
@@ -698,16 +707,19 @@
         final String serviceSpecificInfo = "long arbitrary string with some info";
         final byte[] matchFilter = { 1, 16, 1, 22 };
         final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
-        final int subscribeCount = 10;
         final int subscribeTtl = 15;
         final boolean enableTerminateNotification = false;
+        final int minDistance = 10;
+        final int maxDistance = 50;
 
         SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
-                        new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+                    new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setSubscribeType(subscribeType)
                 .setTtlSec(subscribeTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setMinDistanceMm(minDistance)
+                .setMaxDistanceMm(maxDistance).build();
 
         collector.checkThat("mServiceName", serviceName.getBytes(),
                 equalTo(subscribeConfig.mServiceName));
@@ -719,6 +731,10 @@
         collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
         collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
                 equalTo(subscribeConfig.mEnableTerminateNotification));
+        collector.checkThat("mMinDistanceMmSet", true, equalTo(subscribeConfig.mMinDistanceMmSet));
+        collector.checkThat("mMinDistanceMm", minDistance, equalTo(subscribeConfig.mMinDistanceMm));
+        collector.checkThat("mMaxDistanceMmSet", true, equalTo(subscribeConfig.mMaxDistanceMmSet));
+        collector.checkThat("mMaxDistanceMm", maxDistance, equalTo(subscribeConfig.mMaxDistanceMm));
     }
 
     @Test
@@ -729,13 +745,17 @@
         final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
         final int subscribeTtl = 15;
         final boolean enableTerminateNotification = true;
+        final int minDistance = 10;
+        final int maxDistance = 50;
 
         SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
                         new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setSubscribeType(subscribeType)
                 .setTtlSec(subscribeTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setMinDistanceMm(minDistance)
+                .setMaxDistanceMm(maxDistance).build();
 
         Parcel parcelW = Parcel.obtain();
         subscribeConfig.writeToParcel(parcelW, 0);
@@ -769,11 +789,15 @@
         PublishConfig publishConfig = new PublishConfig.Builder().build();
 
         collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+        collector.checkThat("mServiceSpecificInfo", publishConfig.mServiceSpecificInfo,
+                equalTo(null));
+        collector.checkThat("mMatchFilter", publishConfig.mMatchFilter, equalTo(null));
         collector.checkThat("mPublishType", publishConfig.mPublishType,
                 equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
         collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
         collector.checkThat("mEnableTerminateNotification",
                 publishConfig.mEnableTerminateNotification, equalTo(true));
+        collector.checkThat("mEnableRanging", publishConfig.mEnableRanging, equalTo(false));
     }
 
     @Test
@@ -782,16 +806,17 @@
         final String serviceSpecificInfo = "long arbitrary string with some info";
         final byte[] matchFilter = { 1, 16, 1, 22 };
         final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
-        final int publishCount = 10;
         final int publishTtl = 15;
         final boolean enableTerminateNotification = false;
+        final boolean enableRanging = true;
 
         PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
                         new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setPublishType(publishType)
                 .setTtlSec(publishTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setRangingEnabled(enableRanging).build();
 
         collector.checkThat("mServiceName", serviceName.getBytes(),
                 equalTo(publishConfig.mServiceName));
@@ -802,6 +827,7 @@
         collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
         collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
                 equalTo(publishConfig.mEnableTerminateNotification));
+        collector.checkThat("mEnableRanging", enableRanging, equalTo(publishConfig.mEnableRanging));
     }
 
     @Test
@@ -810,16 +836,17 @@
         final String serviceSpecificInfo = "long arbitrary string with some info";
         final byte[] matchFilter = { 1, 16, 1, 22 };
         final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
-        final int publishCount = 10;
         final int publishTtl = 15;
         final boolean enableTerminateNotification = false;
+        final boolean enableRanging = true;
 
         PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
                         new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setPublishType(publishType)
                 .setTtlSec(publishTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setRangingEnabled(enableRanging).build();
 
         Parcel parcelW = Parcel.obtain();
         publishConfig.writeToParcel(parcelW, 0);