Merge "Fix regression to drawBitmapArray" into pi-dev
diff --git a/api/current.txt b/api/current.txt
index d11a27a..3b18217 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1605,17 +1605,17 @@
     field public static final int holo_purple = 17170458; // 0x106001a
     field public static final int holo_red_dark = 17170455; // 0x1060017
     field public static final int holo_red_light = 17170454; // 0x1060016
-    field public static final int primary_text_dark = 17170433; // 0x1060001
-    field public static final int primary_text_dark_nodisable = 17170434; // 0x1060002
-    field public static final int primary_text_light = 17170435; // 0x1060003
-    field public static final int primary_text_light_nodisable = 17170436; // 0x1060004
-    field public static final int secondary_text_dark = 17170437; // 0x1060005
-    field public static final int secondary_text_dark_nodisable = 17170438; // 0x1060006
-    field public static final int secondary_text_light = 17170439; // 0x1060007
-    field public static final int secondary_text_light_nodisable = 17170440; // 0x1060008
+    field public static final deprecated int primary_text_dark = 17170433; // 0x1060001
+    field public static final deprecated int primary_text_dark_nodisable = 17170434; // 0x1060002
+    field public static final deprecated int primary_text_light = 17170435; // 0x1060003
+    field public static final deprecated int primary_text_light_nodisable = 17170436; // 0x1060004
+    field public static final deprecated int secondary_text_dark = 17170437; // 0x1060005
+    field public static final deprecated int secondary_text_dark_nodisable = 17170438; // 0x1060006
+    field public static final deprecated int secondary_text_light = 17170439; // 0x1060007
+    field public static final deprecated int secondary_text_light_nodisable = 17170440; // 0x1060008
     field public static final int tab_indicator_text = 17170441; // 0x1060009
-    field public static final int tertiary_text_dark = 17170448; // 0x1060010
-    field public static final int tertiary_text_light = 17170449; // 0x1060011
+    field public static final deprecated int tertiary_text_dark = 17170448; // 0x1060010
+    field public static final deprecated int tertiary_text_light = 17170449; // 0x1060011
     field public static final int transparent = 17170445; // 0x106000d
     field public static final int white = 17170443; // 0x106000b
     field public static final int widget_edittext_dark = 17170442; // 0x106000a
@@ -7304,6 +7304,7 @@
   }
 
   public abstract class SliceProvider extends android.content.ContentProvider {
+    ctor public SliceProvider(java.lang.String...);
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
@@ -13649,17 +13650,17 @@
     method public boolean isDecodeAsAlphaMaskEnabled();
     method public boolean isMutableRequired();
     method public boolean isUnpremultipliedRequired();
-    method public android.graphics.ImageDecoder setAllocator(int);
-    method public android.graphics.ImageDecoder setConserveMemory(boolean);
-    method public android.graphics.ImageDecoder setCrop(android.graphics.Rect);
-    method public android.graphics.ImageDecoder setDecodeAsAlphaMaskEnabled(boolean);
-    method public android.graphics.ImageDecoder setMutableRequired(boolean);
-    method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
-    method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor);
-    method public android.graphics.ImageDecoder setTargetColorSpace(android.graphics.ColorSpace);
-    method public android.graphics.ImageDecoder setTargetSampleSize(int);
-    method public android.graphics.ImageDecoder setTargetSize(int, int);
-    method public android.graphics.ImageDecoder setUnpremultipliedRequired(boolean);
+    method public void setAllocator(int);
+    method public void setConserveMemory(boolean);
+    method public void setCrop(android.graphics.Rect);
+    method public void setDecodeAsAlphaMaskEnabled(boolean);
+    method public void setMutableRequired(boolean);
+    method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
+    method public void setPostProcessor(android.graphics.PostProcessor);
+    method public void setTargetColorSpace(android.graphics.ColorSpace);
+    method public void setTargetSampleSize(int);
+    method public void setTargetSize(int, int);
+    method public void setUnpremultipliedRequired(boolean);
     field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
     field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
     field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
@@ -27012,8 +27013,8 @@
 
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
     method public void close() throws java.io.IOException;
+    method public java.io.FileDescriptor getFileDescriptor();
     method public int getPort();
-    method public java.io.FileDescriptor getSocket();
   }
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -38566,6 +38567,7 @@
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isStrongBoxBacked();
     method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38593,6 +38595,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -38685,6 +38688,7 @@
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38704,6 +38708,7 @@
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1284de88..165c6ae 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5556,6 +5556,7 @@
 package android.telephony.ims {
 
   public final class ImsCallForwardInfo implements android.os.Parcelable {
+    ctor public ImsCallForwardInfo(int, int, int, int, java.lang.String, int);
     method public int describeContents();
     method public int getCondition();
     method public java.lang.String getNumber();
@@ -5870,7 +5871,7 @@
   }
 
   public final class ImsSsData implements android.os.Parcelable {
-    ctor public ImsSsData();
+    ctor public ImsSsData(int, int, int, int, int);
     method public int describeContents();
     method public boolean isTypeBarring();
     method public boolean isTypeCf();
@@ -5921,7 +5922,7 @@
   }
 
   public final class ImsSsInfo implements android.os.Parcelable {
-    ctor public ImsSsInfo();
+    ctor public ImsSsInfo(int, java.lang.String);
     method public int describeContents();
     method public java.lang.String getIcbNum();
     method public int getStatus();
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 2b85ec0..c6e561f 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -34,11 +34,11 @@
 
 FdBuffer::~FdBuffer() {}
 
-status_t FdBuffer::read(unique_fd* fd, int64_t timeout) {
-    struct pollfd pfds = {.fd = fd->get(), .events = POLLIN};
+status_t FdBuffer::read(int fd, int64_t timeout) {
+    struct pollfd pfds = {.fd = fd, .events = POLLIN};
     mStartTime = uptimeMillis();
 
-    fcntl(fd->get(), F_SETFL, fcntl(fd->get(), F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
 
     while (true) {
         if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
@@ -67,16 +67,16 @@
                 VLOG("return event has error %s", strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             } else {
-                ssize_t amt = ::read(fd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite());
+                ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
                 if (amt < 0) {
                     if (errno == EAGAIN || errno == EWOULDBLOCK) {
                         continue;
                     } else {
-                        VLOG("Fail to read %d: %s", fd->get(), strerror(errno));
+                        VLOG("Fail to read %d: %s", fd, strerror(errno));
                         return -errno;
                     }
                 } else if (amt == 0) {
-                    VLOG("Reached EOF of fd=%d", fd->get());
+                    VLOG("Reached EOF of fd=%d", fd);
                     break;
                 }
                 mBuffer.wp()->move(amt);
@@ -87,7 +87,7 @@
     return NO_ERROR;
 }
 
-status_t FdBuffer::readFully(unique_fd* fd) {
+status_t FdBuffer::readFully(int fd) {
     mStartTime = uptimeMillis();
 
     while (true) {
@@ -99,10 +99,10 @@
         }
         if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
 
-        ssize_t amt = TEMP_FAILURE_RETRY(
-                ::read(fd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite()));
+        ssize_t amt =
+                TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()));
         if (amt < 0) {
-            VLOG("Fail to read %d: %s", fd->get(), strerror(errno));
+            VLOG("Fail to read %d: %s", fd, strerror(errno));
             return -errno;
         } else if (amt == 0) {
             VLOG("Done reading %zu bytes", mBuffer.size());
@@ -116,20 +116,20 @@
     return NO_ERROR;
 }
 
-status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, unique_fd* fromFd,
+status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd,
                                              int64_t timeoutMs, const bool isSysfs) {
     struct pollfd pfds[] = {
-            {.fd = fd->get(), .events = POLLIN},
-            {.fd = toFd->get(), .events = POLLOUT},
-            {.fd = fromFd->get(), .events = POLLIN},
+            {.fd = fd, .events = POLLIN},
+            {.fd = toFd.get(), .events = POLLOUT},
+            {.fd = fromFd.get(), .events = POLLIN},
     };
 
     mStartTime = uptimeMillis();
 
     // mark all fds non blocking
-    fcntl(fd->get(), F_SETFL, fcntl(fd->get(), F_GETFL, 0) | O_NONBLOCK);
-    fcntl(toFd->get(), F_SETFL, fcntl(toFd->get(), F_GETFL, 0) | O_NONBLOCK);
-    fcntl(fromFd->get(), F_SETFL, fcntl(fromFd->get(), F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+    fcntl(toFd.get(), F_SETFL, fcntl(toFd.get(), F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fromFd.get(), F_SETFL, fcntl(fromFd.get(), F_GETFL, 0) | O_NONBLOCK);
 
     // A circular buffer holds data read from fd and writes to parsing process
     uint8_t cirBuf[BUFFER_SIZE];
@@ -166,10 +166,10 @@
         for (int i = 0; i < 3; ++i) {
             if ((pfds[i].revents & POLLERR) != 0) {
                 if (i == 0 && isSysfs) {
-                    VLOG("fd %d is sysfs, ignore its POLLERR return value", fd->get());
+                    VLOG("fd %d is sysfs, ignore its POLLERR return value", fd);
                     continue;
                 }
-                VLOG("fd[%d]=%d returns error events: %s", i, fd->get(), strerror(errno));
+                VLOG("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             }
         }
@@ -178,17 +178,17 @@
         if (cirSize != BUFFER_SIZE && pfds[0].fd != -1) {
             ssize_t amt;
             if (rpos >= wpos) {
-                amt = ::read(fd->get(), cirBuf + rpos, BUFFER_SIZE - rpos);
+                amt = ::read(fd, cirBuf + rpos, BUFFER_SIZE - rpos);
             } else {
-                amt = ::read(fd->get(), cirBuf + rpos, wpos - rpos);
+                amt = ::read(fd, cirBuf + rpos, wpos - rpos);
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                    VLOG("Fail to read fd %d: %s", fd->get(), strerror(errno));
+                    VLOG("Fail to read fd %d: %s", fd, strerror(errno));
                     return -errno;
                 }  // otherwise just continue
             } else if (amt == 0) {
-                VLOG("Reached EOF of input file %d", fd->get());
+                VLOG("Reached EOF of input file %d", fd);
                 pfds[0].fd = -1;  // reach EOF so don't have to poll pfds[0].
             } else {
                 rpos += amt;
@@ -200,13 +200,13 @@
         if (cirSize > 0 && pfds[1].fd != -1) {
             ssize_t amt;
             if (rpos > wpos) {
-                amt = ::write(toFd->get(), cirBuf + wpos, rpos - wpos);
+                amt = ::write(toFd.get(), cirBuf + wpos, rpos - wpos);
             } else {
-                amt = ::write(toFd->get(), cirBuf + wpos, BUFFER_SIZE - wpos);
+                amt = ::write(toFd.get(), cirBuf + wpos, BUFFER_SIZE - wpos);
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                    VLOG("Fail to write toFd %d: %s", toFd->get(), strerror(errno));
+                    VLOG("Fail to write toFd.get() %d: %s", toFd.get(), strerror(errno));
                     return -errno;
                 }  // otherwise just continue
             } else {
@@ -217,8 +217,8 @@
 
         // if buffer is empty and fd is closed, close write fd.
         if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) {
-            VLOG("Close write pipe %d", toFd->get());
-            toFd->reset();
+            VLOG("Close write pipe %d", toFd.get());
+            toFd.reset();
             pfds[1].fd = -1;
         }
 
@@ -231,14 +231,14 @@
         }
 
         // read from parsing process
-        ssize_t amt = ::read(fromFd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite());
+        ssize_t amt = ::read(fromFd.get(), mBuffer.writeBuffer(), mBuffer.currentToWrite());
         if (amt < 0) {
             if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                VLOG("Fail to read fromFd %d: %s", fromFd->get(), strerror(errno));
+                VLOG("Fail to read fromFd.get() %d: %s", fromFd.get(), strerror(errno));
                 return -errno;
             }  // otherwise just continue
         } else if (amt == 0) {
-            VLOG("Reached EOF of fromFd %d", fromFd->get());
+            VLOG("Reached EOF of fromFd.get() %d", fromFd.get());
             break;
         } else {
             mBuffer.wp()->move(amt);
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index db3a74b..f467da8 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -40,13 +40,13 @@
      * Returns NO_ERROR if there were no errors or if we timed out.
      * Will mark the file O_NONBLOCK.
      */
-    status_t read(unique_fd* fd, int64_t timeoutMs);
+    status_t read(int fd, int64_t timeoutMs);
 
     /**
      * Read the data until we hit eof.
      * Returns NO_ERROR if there were no errors.
      */
-    status_t readFully(unique_fd* fd);
+    status_t readFully(int fd);
 
     /**
      * Read processed results by streaming data to a parsing process, e.g. incident helper.
@@ -58,8 +58,8 @@
      *
      * Poll will return POLLERR if fd is from sysfs, handle this edge case.
      */
-    status_t readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, unique_fd* fromFd,
-                                       int64_t timeoutMs, const bool isSysfs = false);
+    status_t readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd, int64_t timeoutMs,
+                                       const bool isSysfs = false);
 
     /**
      * Whether we timed out.
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index aeccefd..d02b4dd 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -352,8 +352,7 @@
             printPrivacy(p, out, String8(""));
         } else if (opt == "parse") {
             FdBuffer buf;
-            unique_fd infd(fileno(in));
-            status_t error = buf.read(&infd, 60000);
+            status_t error = buf.read(fileno(in), 60000);
             if (error != NO_ERROR) {
                 fprintf(err, "Error reading from stdin\n");
                 return error;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index d964651..3f693fa 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -264,8 +264,9 @@
     }
 
     // parent process
-    status_t readStatus = buffer.readProcessedDataInStream(
-            &fd, &p2cPipe.writeFd(), &c2pPipe.readFd(), this->timeoutMs, mIsSysfs);
+    status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()),
+                                                           std::move(c2pPipe.readFd()),
+                                                           this->timeoutMs, mIsSysfs);
 
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
@@ -356,9 +357,9 @@
     VLOG("GZipSection '%s' editPos=%zd, dataBeginAt=%zd", this->name.string(), editPos,
          dataBeginAt);
 
-    status_t readStatus =
-            buffer.readProcessedDataInStream(&fd, &p2cPipe.writeFd(), &c2pPipe.readFd(),
-                                             this->timeoutMs, isSysfs(mFilenames[index]));
+    status_t readStatus = buffer.readProcessedDataInStream(
+            fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs,
+            isSysfs(mFilenames[index]));
 
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("GZipSection '%s' failed to read data from gzip: %s, timedout: %s",
@@ -468,7 +469,7 @@
     pthread_attr_destroy(&attr);
 
     // Loop reading until either the timeout or the worker side is done (i.e. eof).
-    err = buffer.read(&data->pipe.readFd(), this->timeoutMs);
+    err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
     if (err != NO_ERROR) {
         // TODO: Log this error into the incident report.
         ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
@@ -575,7 +576,7 @@
     }
 
     cmdPipe.writeFd().reset();
-    status_t readStatus = buffer.read(&ihPipe.readFd(), this->timeoutMs);
+    status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s",
               this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -894,7 +895,7 @@
         // Parent process.
         // Read from the pipe concurrently to avoid blocking the child.
         FdBuffer buffer;
-        err = buffer.readFully(&dumpPipe.readFd());
+        err = buffer.readFully(dumpPipe.readFd().get());
         if (err != NO_ERROR) {
             ALOGW("TombstoneSection '%s' failed to read stack dump: %d", this->name.string(), err);
             dumpPipe.readFd().reset();
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index bf77017..7a05d7e 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -37,7 +37,6 @@
 public:
     virtual void SetUp() override {
         ASSERT_NE(tf.fd, -1);
-        tffd.reset(tf.fd);
         ASSERT_NE(p2cPipe.init(), -1);
         ASSERT_NE(c2pPipe.init(), -1);
     }
@@ -57,13 +56,13 @@
         EXPECT_EQ(expected[i], '\0');
     }
 
-    bool DoDataStream(unique_fd* rFd, unique_fd* wFd) {
+    bool DoDataStream(const unique_fd& rFd, const unique_fd& wFd) {
         char buf[BUFFER_SIZE];
         ssize_t nRead;
-        while ((nRead = read(rFd->get(), buf, BUFFER_SIZE)) > 0) {
+        while ((nRead = read(rFd.get(), buf, BUFFER_SIZE)) > 0) {
             ssize_t nWritten = 0;
             while (nWritten < nRead) {
-                ssize_t amt = write(wFd->get(), buf + nWritten, nRead - nWritten);
+                ssize_t amt = write(wFd.get(), buf + nWritten, nRead - nWritten);
                 if (amt < 0) {
                     return false;
                 }
@@ -76,7 +75,6 @@
 protected:
     FdBuffer buffer;
     TemporaryFile tf;
-    unique_fd tffd;
     Fpipe p2cPipe;
     Fpipe c2pPipe;
 
@@ -87,7 +85,7 @@
 TEST_F(FdBufferTest, ReadAndWrite) {
     std::string testdata = "FdBuffer test string";
     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
-    ASSERT_EQ(NO_ERROR, buffer.read(&tffd, READ_TIMEOUT));
+    ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
     AssertBufferReadSuccessful(testdata.size());
     AssertBufferContent(testdata.c_str());
 }
@@ -100,7 +98,7 @@
 TEST_F(FdBufferTest, ReadAndIterate) {
     std::string testdata = "FdBuffer test string";
     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
-    ASSERT_EQ(NO_ERROR, buffer.read(&tffd, READ_TIMEOUT));
+    ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
 
     int i = 0;
     EncodedBuffer::iterator it = buffer.data();
@@ -128,7 +126,7 @@
     } else {
         c2pPipe.writeFd().reset();
 
-        status_t status = buffer.read(&c2pPipe.readFd(), QUICK_TIMEOUT_MS);
+        status_t status = buffer.read(c2pPipe.readFd().get(), QUICK_TIMEOUT_MS);
         ASSERT_EQ(NO_ERROR, status);
         EXPECT_TRUE(buffer.timedOut());
 
@@ -148,7 +146,7 @@
         p2cPipe.writeFd().reset();
         c2pPipe.readFd().reset();
         ASSERT_TRUE(WriteStringToFd(HEAD, c2pPipe.writeFd()));
-        ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd()));
+        ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
         // Must exit here otherwise the child process will continue executing the test binary.
@@ -157,8 +155,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
         AssertBufferContent(expected.c_str());
         wait(&pid);
@@ -189,8 +188,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
         AssertBufferContent(expected.c_str());
         wait(&pid);
@@ -206,7 +206,7 @@
     if (pid == 0) {
         p2cPipe.writeFd().reset();
         c2pPipe.readFd().reset();
-        ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd()));
+        ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
         _exit(EXIT_SUCCESS);
@@ -214,8 +214,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         AssertBufferReadSuccessful(0);
         AssertBufferContent("");
         wait(&pid);
@@ -233,7 +234,7 @@
     if (pid == 0) {
         p2cPipe.writeFd().reset();
         c2pPipe.readFd().reset();
-        ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd()));
+        ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
         _exit(EXIT_SUCCESS);
@@ -241,8 +242,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&fd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         EXPECT_EQ(buffer.size(), fourMB);
         EXPECT_FALSE(buffer.timedOut());
         EXPECT_TRUE(buffer.truncated());
@@ -278,8 +280,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), QUICK_TIMEOUT_MS));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), QUICK_TIMEOUT_MS));
         EXPECT_TRUE(buffer.timedOut());
         kill(pid, SIGKILL);  // reap the child process
     }
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index d129269..10c2981 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -58,8 +58,7 @@
 
     void writeToFdBuffer(string str) {
         ASSERT_TRUE(WriteStringToFile(str, tf.path));
-        unique_fd tffd(tf.fd);
-        ASSERT_EQ(NO_ERROR, buffer.read(&tffd, 10000));
+        ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
         ASSERT_EQ(str.size(), buffer.size());
     }
 
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index b67764b..50b05cd 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -127,25 +127,25 @@
 }
 
 AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
-    const string& name, ActivityForegroundStateChanged::Activity activity) {
+    const string& name, ActivityForegroundStateChanged::State state) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
     field_value_matcher->set_field(4);  // Activity field.
-    field_value_matcher->set_eq_int(activity);
+    field_value_matcher->set_eq_int(state);
     return atom_matcher;
 }
 
 AtomMatcher CreateMoveToBackgroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
+        "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND);
 }
 
 AtomMatcher CreateMoveToForegroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
+        "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND);
 }
 
 Predicate CreateScheduledJobPredicate() {
@@ -315,25 +315,25 @@
 }
 
 std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-    const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
+    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(
         android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
     event->write(uid);
     event->write("pkg_name");
     event->write("class_name");
-    event->write(activity);
+    event->write(state);
     event->init();
     return event;
 }
 
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cfb9d87..7fe8e62 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -16,7 +16,6 @@
 
 syntax = "proto2";
 
-// TODO: Not the right package and class name
 package android.os.statsd;
 option java_package = "com.android.os";
 option java_outer_classname = "AtomsProto";
@@ -49,7 +48,7 @@
     oneof pushed {
         // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
         BleScanStateChanged ble_scan_state_changed = 2;
-        // TODO: 3 is blank, but need not be
+        // 3 is available for use
         BleScanResultReceived ble_scan_result_received = 4;
         SensorStateChanged sensor_state_changed = 5;
         GpsScanStateChanged gps_scan_state_changed = 6;
@@ -60,12 +59,12 @@
         LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
         MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
         WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
-        // TODO: 14-19 are blank, but need not be
+        // 14 - 19 are available
         BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
         DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
         DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
         AudioStateChanged audio_state_changed = 23;
-        MediaCodecActivityChanged media_codec_activity_changed = 24;
+        MediaCodecStateChanged media_codec_state_changed = 24;
         CameraStateChanged camera_state_changed = 25;
         FlashlightStateChanged flashlight_state_changed = 26;
         UidProcessStateChanged uid_process_state_changed = 27;
@@ -74,8 +73,7 @@
         BatteryLevelChanged battery_level_changed = 30;
         ChargingStateChanged charging_state_changed = 31;
         PluggedStateChanged plugged_state_changed = 32;
-        // TODO: 33 is blank, but is available for use.
-        DeviceOnStatusChanged device_on_status_changed = 34;
+        // 33 - 34 are available
         WakeupAlarmOccurred wakeup_alarm_occurred = 35;
         KernelWakeupReported kernel_wakeup_reported = 36;
         WifiLockStateChanged wifi_lock_state_changed = 37;
@@ -86,12 +84,12 @@
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
         IsolatedUidChanged isolated_uid_changed = 43;
         PacketWakeupOccurred packet_wakeup_occurred = 44;
-        DropboxErrorChanged dropbox_error_changed = 45;
+        // 45 is available
         AnomalyDetected anomaly_detected = 46;
         AppBreadcrumbReported app_breadcrumb_reported = 47;
-        AppStartChanged app_start_changed = 48;
-        AppStartCancelChanged app_start_cancel_changed = 49;
-        AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
+        AppStartOccurred app_start_occurred = 48;
+        AppStartCanceled app_start_canceled = 49;
+        AppStartFullyDrawn app_start_fully_drawn = 50;
         LmkKillOccurred lmk_kill_occurred = 51;
         PictureInPictureStateChanged picture_in_picture_state_changed = 52;
         WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
@@ -106,7 +104,7 @@
         KeyguardStateChanged keyguard_state_changed = 62;
         KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63;
         KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
-        AppDied app_died=65;
+        AppDied app_died = 65;
         ResourceConfigurationChanged resource_configuration_changed = 66;
         BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
         BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
@@ -119,6 +117,12 @@
         MobileConnectionStateChanged mobile_connection_state_changed = 75;
         MobileRadioTechnologyChanged mobile_radio_technology_changed = 76;
         UsbDeviceAttached usb_device_attached = 77;
+        AppCrashOccurred app_crash_occurred = 78;
+        ANROccurred anr_occurred = 79;
+        WTFOccurred wtf_occurred = 80;
+        LowMemReported low_mem_reported = 81;
+
+
     }
 
     // Pulled events will start at field 10000.
@@ -134,7 +138,7 @@
         CpuTimePerFreq cpu_time_per_freq = 10008;
         CpuTimePerUid cpu_time_per_uid = 10009;
         CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
-        WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
+        WifiActivityInfo wifi_activity_info = 10011;
         ModemActivityInfo modem_activity_info = 10012;
         BluetoothActivityInfo bluetooth_activity_info = 10007;
         ProcessMemoryState process_memory_state = 10013;
@@ -224,21 +228,19 @@
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message ProcessLifeCycleStateChanged {
-    // TODO: should be a string tagged w/ uid annotation
     optional int32 uid = 1 [(is_uid) = true];
 
     // The process name (usually same as the app name).
-    optional string name = 2;
+    optional string process_name = 2;
 
     // What lifecycle state the process changed to.
     // This enum is specific to atoms.proto.
-    enum Event {
-        PROCESS_FINISHED = 0;
-        PROCESS_STARTED = 1;
-        PROCESS_CRASHED = 2;
-        PROCESS_ANRED = 3;
+    enum State {
+        FINISHED = 0;
+        STARTED = 1;
+        CRASHED = 2;
     }
-    optional Event event = 3;
+    optional State state = 3;
 }
 
 /**
@@ -247,7 +249,6 @@
  * Logged from:
  *   packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
  */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
 message BleScanStateChanged {
     repeated AttributionNode attribution_node = 1;
 
@@ -278,7 +279,7 @@
     repeated AttributionNode attribution_node = 1;
 
     // Number of ble scan results returned.
-    optional int32 num_of_results = 2;
+    optional int32 num_results = 2;
 }
 
 /**
@@ -290,7 +291,6 @@
 message SensorStateChanged {
     repeated AttributionNode attribution_node = 1;
 
-    // TODO: Is there a way to get the actual name of the sensor?
     // The id (int) of the sensor.
     optional int32 sensor_id = 2;
 
@@ -329,7 +329,7 @@
     repeated AttributionNode attribution_node = 1;
 
     // Name of the sync (as named in the app). Can be chosen at run-time.
-    optional string name = 2;
+    optional string sync_name = 2;
 
     enum State {
         OFF = 0;
@@ -348,7 +348,7 @@
     repeated AttributionNode attribution_node = 1;
 
     // Name of the job (as named in the app)
-    optional string name = 2;
+    optional string job_name = 2;
 
     enum State {
         FINISHED = 0;
@@ -387,7 +387,7 @@
  * Logged from:
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
-message MediaCodecActivityChanged {
+message MediaCodecStateChanged {
     repeated AttributionNode attribution_node = 1;
 
     enum State {
@@ -561,22 +561,6 @@
     optional android.os.BatteryPluggedStateEnum state = 1;
 }
 
-// TODO: Define this more precisely.
-// TODO: Log the ON state somewhere. It isn't currently logged anywhere.
-/**
- * Logs when the device turns off or on.
- *
- * Logged from:
- *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message DeviceOnStatusChanged {
-    enum State {
-        OFF = 0;
-        ON = 1;
-    }
-    optional State state = 1;
-}
-
 /**
  * Logs when an app's wakeup alarm fires.
  *
@@ -598,8 +582,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message MobileRadioPowerStateChanged {
-    // TODO: Add attribution instead of uid?
-    optional int32 uid = 1 [(is_uid) = true];
+    repeated AttributionNode attribution_node = 1;
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -613,8 +596,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message WifiRadioPowerStateChanged {
-    // TODO: Add attribution instead of uid?
-    optional int32 uid = 1 [(is_uid) = true];
+    repeated AttributionNode attribution_node = 1;
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -1154,7 +1136,6 @@
 message DaveyOccurred {
     // The UID that logged this atom.
     optional int32 uid = 1 [(is_uid) = true];
-    ;
 
     // Amount of time it took to render the frame. Should be >=700ms.
     optional int64 jank_duration_millis = 2;
@@ -1221,42 +1202,70 @@
     optional string pkg_name = 2;
     optional string class_name = 3;
 
-    enum Activity {
-        MOVE_TO_BACKGROUND = 0;
-        MOVE_TO_FOREGROUND = 1;
+    enum State {
+        BACKGROUND = 0;
+        FOREGROUND = 1;
     }
-    optional Activity activity = 4;
+    optional State state = 4;
 }
 
 /**
- * Logs when an error is written to dropbox.
+ * Logs when an app crashes.
  * Logged from:
  *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  */
-message DropboxErrorChanged {
-    // The uid if available. -1 means not available.
+message AppCrashOccurred {
     optional int32 uid = 1 [(is_uid) = true];
 
-    // Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
-    optional string tag = 2;
+    optional string event_type = 2;
 
     // The name of the process.
+    // system_server if it is not by an app
     optional string process_name = 3;
 
     // The pid if available. -1 means not available.
     optional sint32 pid = 4;
+}
 
-    // 1 indicates is instant app. -1 indicates Not applicable.
-    optional sint32 is_instant_app = 5;
+/**
+ * Logs when a WTF (What a Terrible Failure) happened.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message WTFOccurred {
+    optional int32 uid = 1 [(is_uid) = true];
 
-    // The activity name if available.
-    optional string activity_name = 6;
+    optional string tag = 2;
 
-    // The package name if available.
-    optional string package_name = 7;
+    // The name of the process.
+    // system_server if it is not by an app
+    optional string process_name = 3;
 
-    // 1 indicates in foreground. -1 indicates not available.
-    optional sint32 is_foreground = 8;
+    // The pid if available. -1 means not available.
+    optional sint32 pid = 4;
+}
+
+/**
+ * Logs when system server reports low memory.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message LowMemReported {
+}
+
+/**
+ * Logs when an app ANR (App Not Responding) occurs.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/AppErrors.java
+ */
+message ANROccurred {
+    optional int32 uid = 1 [(is_uid) = true];
+
+    optional string process_name = 2;
+
+    optional string short_component_name = 3;
+
+    optional string reason = 4;
 }
 
 /*
@@ -1299,7 +1308,7 @@
     optional int64 alert_id = 3;
 }
 
-message AppStartChanged {
+message AppStartOccurred {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
@@ -1307,7 +1316,7 @@
     optional string pkg_name = 2;
 
     enum TransitionType {
-        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        UNKNOWN = 0;
         WARM = 1;
         HOT = 2;
         COLD = 3;
@@ -1346,7 +1355,7 @@
     optional int32 package_optimization_compilation_reason = 15;
 }
 
-message AppStartCancelChanged {
+message AppStartCanceled {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
@@ -1354,7 +1363,7 @@
     optional string pkg_name = 2;
 
     enum TransitionType {
-        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        UNKNOWN = 0;
         WARM = 1;
         HOT = 2;
         COLD = 3;
@@ -1366,7 +1375,7 @@
     optional string activity_name = 4;
 }
 
-message AppStartFullyDrawnChanged {
+message AppStartFullyDrawn {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
@@ -1374,7 +1383,7 @@
     optional string pkg_name = 2;
 
     enum TransitionType {
-        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        UNKNOWN = 0;
         WITH_BUNDLE = 1;
         WITHOUT_BUNDLE = 2;
     }
@@ -1459,8 +1468,8 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message IsolatedUidChanged {
-    // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
     // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
+    // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
     optional int32 parent_uid = 1;
 
     optional int32 isolated_uid = 2;
@@ -1621,9 +1630,8 @@
 message WifiBytesTransferByFgBg {
     optional int32 uid = 1 [(is_uid) = true];
 
-    // 1 denotes foreground and 0 denotes background. This is called Set in
-    // NetworkStats.
-    optional int32 is_foreground = 2;
+    // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+    optional bool is_foreground = 2;
 
     optional int64 rx_bytes = 3;
 
@@ -1663,7 +1671,7 @@
 
     // 1 denotes foreground and 0 denotes background. This is called Set in
     // NetworkStats.
-    optional int32 is_foreground = 2;
+    optional bool is_foreground = 2;
 
     optional int64 rx_bytes = 3;
 
@@ -1760,7 +1768,7 @@
 /**
  * Pulls Wifi Controller Activity Energy Info
  */
-message WifiActivityEnergyInfo {
+message WifiActivityInfo {
     // timestamp(wall clock) of record creation
     optional uint64 timestamp_millis = 1;
     // stack reported state
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 0e23bf0..2f0e885 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -120,11 +120,11 @@
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
         // wifi_activity_energy_info
-        {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+        {android::util::WIFI_ACTIVITY_INFO,
          {{},
           {},
           1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+          new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
         // modem_activity_info
         {android::util::MODEM_ACTIVITY_INFO,
          {{},
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index ef637df..22ff942 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -76,6 +76,7 @@
 const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14;
 const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15;
 const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16;
+const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17;
 
 const int FIELD_ID_MATCHER_STATS_ID = 1;
 const int FIELD_ID_MATCHER_STATS_COUNT = 2;
@@ -255,6 +256,20 @@
     }
 }
 
+void StatsdStats::noteMetricDimensionInConditionSize(
+        const ConfigKey& key, const int64_t& id, int size) {
+    lock_guard<std::mutex> lock(mLock);
+    // if name doesn't exist before, it will create the key with count 0.
+    auto statsIt = mConfigStats.find(key);
+    if (statsIt == mConfigStats.end()) {
+        return;
+    }
+    auto& metricsDimensionMap = statsIt->second->metric_dimension_in_condition_stats;
+    if (size > metricsDimensionMap[id]) {
+        metricsDimensionMap[id] = size;
+    }
+}
+
 void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
     lock_guard<std::mutex> lock(mLock);
 
@@ -339,6 +354,7 @@
         config.second->matcher_stats.clear();
         config.second->condition_stats.clear();
         config.second->metric_stats.clear();
+        config.second->metric_dimension_in_condition_stats.clear();
         config.second->alert_stats.clear();
     }
 }
@@ -504,6 +520,13 @@
         proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
         proto->end(tmpToken);
     }
+    for (const auto& pair : configStats.metric_dimension_in_condition_stats) {
+        uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                         FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS);
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
+        proto->end(tmpToken);
+    }
 
     for (const auto& pair : configStats.alert_stats) {
         uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 7f8755b..bd395c4 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -57,6 +57,12 @@
     // it means some data has been dropped. The map size is capped by kMaxConfigCount.
     std::map<const int64_t, int> metric_stats;
 
+    // Stores the max number of output tuple of dimensions in condition across dimensions in what
+    // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is
+    // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by
+    // kMaxConfigCount.
+    std::map<const int64_t, int> metric_dimension_in_condition_stats;
+
     // Stores the number of times an anomaly detection alert has been declared.
     // The map size is capped by kMaxConfigCount.
     std::map<const int64_t, int> alert_stats;
@@ -183,6 +189,19 @@
      */
     void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
 
+
+    /**
+     * Report the max size of output tuple of dimension in condition across dimensions in what.
+     *
+     * Note: only report when the metric has an output dimension in condition, and the max tuple
+     * count > kDimensionKeySizeSoftLimit.
+     *
+     * [key]: The config key that this metric belongs to.
+     * [id]: The id of the metric.
+     * [size]: The output tuple size.
+     */
+    void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
+
     /**
      * Report a matcher has been matched.
      *
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c28bb88..f02f307 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -308,11 +308,14 @@
                     if (mMetric2ConditionLinks.size() == 0 ||
                         trueDim.contains(linkedConditionDimensionKey)) {
                         if (!whatIt.second.empty()) {
+                            auto newEventKey = MetricDimensionKey(whatIt.first, trueDim);
+                            if (hitGuardRailLocked(newEventKey)) {
+                                continue;
+                            }
                             unique_ptr<DurationTracker> newTracker =
                                 whatIt.second.begin()->second->clone(eventTime);
                             if (newTracker != nullptr) {
-                                newTracker->setEventKey(
-                                    MetricDimensionKey(whatIt.first, trueDim));
+                                newTracker->setEventKey(newEventKey);
                                 newTracker->onConditionChanged(true, eventTime);
                                 whatIt.second[trueDim] = std::move(newTracker);
                             }
@@ -370,11 +373,14 @@
         for (const auto& conditionDimension : conditionDimensionsKeySet) {
             for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
                 if (!whatIt.second.empty()) {
+                    auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
+                    if (hitGuardRailLocked(newEventKey)) {
+                        continue;
+                    }
                     unique_ptr<DurationTracker> newTracker =
                         whatIt.second.begin()->second->clone(eventTime);
                     if (newTracker != nullptr) {
-                        newTracker->setEventKey(MetricDimensionKey(
-                                whatIt.first, conditionDimension));
+                        newTracker->setEventKey(MetricDimensionKey(newEventKey));
                         newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
                         whatIt.second[conditionDimension] = std::move(newTracker);
                     }
@@ -397,10 +403,13 @@
             for (const auto& conditionDimension : conditionDimensionsKeys) {
                 if (!whatIt.second.empty() &&
                     whatIt.second.find(conditionDimension) == whatIt.second.end()) {
+                    auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
+                    if (hitGuardRailLocked(newEventKey)) {
+                        continue;
+                    }
                     auto newTracker = whatIt.second.begin()->second->clone(eventTime);
                     if (newTracker != nullptr) {
-                        newTracker->setEventKey(
-                            MetricDimensionKey(whatIt.first, conditionDimension));
+                        newTracker->setEventKey(newEventKey);
                         newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
                         whatIt.second[conditionDimension] = std::move(newTracker);
                     }
@@ -552,15 +561,35 @@
 }
 
 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
-    // 1. Report the tuple count if the tuple count > soft limit
-    if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
-        size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
-        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
-        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("DurationMetric %lld dropping data for dimension key %s",
-                (long long)mMetricId, newKey.toString().c_str());
-            return true;
+    auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
+    if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+        auto condIt = whatIt->second.find(newKey.getDimensionKeyInCondition());
+        if (condIt != whatIt->second.end()) {
+            return false;
+        }
+        if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+            size_t newTupleCount = whatIt->second.size() + 1;
+            StatsdStats::getInstance().noteMetricDimensionInConditionSize(
+                    mConfigKey, mMetricId, newTupleCount);
+            // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+            if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+                ALOGE("DurationMetric %lld dropping data for condition dimension key %s",
+                    (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str());
+                return true;
+            }
+        }
+    } else {
+        // 1. Report the tuple count if the tuple count > soft limit
+        if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+            size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
+            StatsdStats::getInstance().noteMetricDimensionSize(
+                    mConfigKey, mMetricId, newTupleCount);
+            // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+            if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+                ALOGE("DurationMetric %lld dropping data for what dimension key %s",
+                    (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
+                return true;
+            }
         }
     }
     return false;
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index dd3b37c..a25df3f 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -241,6 +241,7 @@
         repeated ConditionStats condition_stats = 14;
         repeated MetricStats metric_stats = 15;
         repeated AlertStats alert_stats = 16;
+        repeated MetricStats metric_dimension_in_condition_stats = 17;
     }
 
     repeated ConfigStats config_stats = 3;
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index 920273b..3a0c224 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -20,5 +20,5 @@
 
 on post-fs-data
     # Create directory for statsd
-    mkdir /data/misc/stats-data/ 0770 statsd statsd
-    mkdir /data/misc/stats-service/ 0770 statsd statsd
+    mkdir /data/misc/stats-data/ 0770 statsd system
+    mkdir /data/misc/stats-service/ 0770 statsd system
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 73e7c44..5a6aba6 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -356,7 +356,7 @@
     EXPECT_EQ("location1", atom.attribution_node(0).tag());
     EXPECT_EQ(2222, atom.attribution_node(1).uid());
     EXPECT_EQ("location2", atom.attribution_node(1).tag());
-    EXPECT_EQ(999, atom.num_of_results());
+    EXPECT_EQ(999, atom.num_results());
 }
 
 
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 2e6a0f0..2b91324 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -34,7 +34,7 @@
     *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
     *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
 
-    auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_CHANGED);
+    auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_OCCURRED);
     *config.add_atom_matcher() = atomMatcher;
 
     auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
@@ -49,18 +49,18 @@
     gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
     gaugeMetric->set_sampling_type(sampling_type);
     auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
-    fieldMatcher->set_field(android::util::APP_START_CHANGED);
+    fieldMatcher->set_field(android::util::APP_START_OCCURRED);
     fieldMatcher->add_child()->set_field(3);  // type (enum)
     fieldMatcher->add_child()->set_field(4);  // activity_name(str)
     fieldMatcher->add_child()->set_field(7);  // activity_start_msec(int64)
     *gaugeMetric->mutable_dimensions_in_what() =
-        CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ });
+        CreateDimensions(android::util::APP_START_OCCURRED, {1 /* uid field */ });
     gaugeMetric->set_bucket(FIVE_MINUTES);
 
     auto links = gaugeMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
     auto dimensionWhat = links->mutable_fields_in_what();
-    dimensionWhat->set_field(android::util::APP_START_CHANGED);
+    dimensionWhat->set_field(android::util::APP_START_OCCURRED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
     auto dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
@@ -68,12 +68,12 @@
     return config;
 }
 
-std::unique_ptr<LogEvent> CreateAppStartChangedEvent(
-    const int uid, const string& pkg_name, AppStartChanged::TransitionType type,
+std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
+    const int uid, const string& pkg_name, AppStartOccurred::TransitionType type,
     const string& activity_name, const string& calling_pkg_name, const bool is_instant_app,
     int64_t activity_start_msec, uint64_t timestampNs) {
     auto logEvent = std::make_unique<LogEvent>(
-        android::util::APP_START_CHANGED, timestampNs);
+        android::util::APP_START_OCCURRED, timestampNs);
     logEvent->write(uid);
     logEvent->write(pkg_name);
     logEvent->write(type);
@@ -112,32 +112,32 @@
             appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100));
 
 
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::WARM, "activity_name1", "calling_pkg_name1",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1",
             true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::HOT, "activity_name2", "calling_pkg_name2",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2",
             true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::COLD, "activity_name3", "calling_pkg_name3",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3",
             true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::WARM, "activity_name4", "calling_pkg_name4",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4",
             true /*is_instant_app*/, 104 /*activity_start_msec*/,
             bucketStartTimeNs + bucketSizeNs + 30));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::COLD, "activity_name5", "calling_pkg_name5",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5",
             true /*is_instant_app*/, 105 /*activity_start_msec*/,
             bucketStartTimeNs + 2 * bucketSizeNs));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::HOT, "activity_name6", "calling_pkg_name6",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6",
             false /*is_instant_app*/, 106 /*activity_start_msec*/,
             bucketStartTimeNs + 2 * bucketSizeNs + 10));
 
         events.push_back(CreateMoveToBackgroundEvent(
                 appUid2, bucketStartTimeNs + bucketSizeNs + 10));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid2, "app2", AppStartChanged::COLD, "activity_name7", "calling_pkg_name7",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7",
             true /*is_instant_app*/, 201 /*activity_start_msec*/,
             bucketStartTimeNs + 2 * bucketSizeNs + 10));
 
@@ -159,7 +159,7 @@
         EXPECT_EQ(2, gaugeMetrics.data_size());
 
         auto data = gaugeMetrics.data(0);
-        EXPECT_EQ(android::util::APP_START_CHANGED, data.dimensions_in_what().field());
+        EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field());
         EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
         EXPECT_EQ(1 /* uid field */,
                   data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -171,29 +171,29 @@
             EXPECT_EQ(2, data.bucket_info(0).wall_clock_timestamp_nanos_size());
             EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::HOT, data.bucket_info(0).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name2",
-                      data.bucket_info(0).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(102L,
-                      data.bucket_info(0).atom(0).app_start_changed().activity_start_millis());
-            EXPECT_EQ(AppStartChanged::COLD,
-                      data.bucket_info(0).atom(1).app_start_changed().type());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(0).atom(1).app_start_occurred().type());
             EXPECT_EQ("activity_name3",
-                      data.bucket_info(0).atom(1).app_start_changed().activity_name());
+                      data.bucket_info(0).atom(1).app_start_occurred().activity_name());
             EXPECT_EQ(103L,
-                      data.bucket_info(0).atom(1).app_start_changed().activity_start_millis());
+                      data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
 
             EXPECT_EQ(1, data.bucket_info(1).atom_size());
             EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
             EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::WARM,
-                      data.bucket_info(1).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::WARM,
+                      data.bucket_info(1).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name4",
-                      data.bucket_info(1).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(104L,
-                      data.bucket_info(1).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
 
             EXPECT_EQ(2, data.bucket_info(2).atom_size());
             EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -202,41 +202,41 @@
                       data.bucket_info(2).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
                       data.bucket_info(2).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::COLD,
-                      data.bucket_info(2).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(2).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name5",
-                      data.bucket_info(2).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(105L,
-                      data.bucket_info(2).atom(0).app_start_changed().activity_start_millis());
-            EXPECT_EQ(AppStartChanged::HOT,
-                      data.bucket_info(2).atom(1).app_start_changed().type());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(2).atom(1).app_start_occurred().type());
             EXPECT_EQ("activity_name6",
-                      data.bucket_info(2).atom(1).app_start_changed().activity_name());
+                      data.bucket_info(2).atom(1).app_start_occurred().activity_name());
             EXPECT_EQ(106L,
-                      data.bucket_info(2).atom(1).app_start_changed().activity_start_millis());
+                      data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
         } else {
             EXPECT_EQ(1, data.bucket_info(0).atom_size());
             EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
             EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::HOT, data.bucket_info(0).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name2",
-                      data.bucket_info(0).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(102L,
-                      data.bucket_info(0).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
 
             EXPECT_EQ(1, data.bucket_info(1).atom_size());
             EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
             EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::WARM,
-                      data.bucket_info(1).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::WARM,
+                      data.bucket_info(1).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name4",
-                      data.bucket_info(1).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(104L,
-                      data.bucket_info(1).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
 
             EXPECT_EQ(1, data.bucket_info(2).atom_size());
             EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -245,17 +245,17 @@
                       data.bucket_info(2).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
                       data.bucket_info(2).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::COLD,
-                      data.bucket_info(2).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(2).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name5",
-                      data.bucket_info(2).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(105L,
-                      data.bucket_info(2).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
         }
 
         data = gaugeMetrics.data(1);
 
-        EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED);
+        EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED);
         EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
         EXPECT_EQ(1 /* uid field */,
                   data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -266,10 +266,10 @@
         EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
         EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_nanos());
         EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
-        EXPECT_EQ(AppStartChanged::COLD, data.bucket_info(0).atom(0).app_start_changed().type());
+        EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
         EXPECT_EQ("activity_name7",
-                  data.bucket_info(0).atom(0).app_start_changed().activity_name());
-        EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_changed().activity_start_millis());
+                  data.bucket_info(0).atom(0).app_start_occurred().activity_name());
+        EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
     }
 }
 
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 5c4eda8..04ce73a7 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -210,7 +210,7 @@
 
     stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
     stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
-    stats.noteAtomLogged(android::util::DROPBOX_ERROR_CHANGED, now + 3);
+    stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3);
     // pulled event, should ignore
     stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFER, now + 4);
 
@@ -228,7 +228,7 @@
         if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
             sensorAtomGood = true;
         }
-        if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) {
+        if (atomStats.tag() == android::util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
             dropboxAtomGood = true;
         }
     }
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index ce44a35..6621500 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -152,42 +152,42 @@
 }
 
 AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
-    const string& name, ActivityForegroundStateChanged::Activity activity) {
+    const string& name, ActivityForegroundStateChanged::State state) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
     field_value_matcher->set_field(4);  // Activity field.
-    field_value_matcher->set_eq_int(activity);
+    field_value_matcher->set_eq_int(state);
     return atom_matcher;
 }
 
 AtomMatcher CreateMoveToBackgroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
+        "Background", ActivityForegroundStateChanged::BACKGROUND);
 }
 
 AtomMatcher CreateMoveToForegroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
+        "Foreground", ActivityForegroundStateChanged::FOREGROUND);
 }
 
 AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
-    const string& name, ProcessLifeCycleStateChanged::Event event) {
+    const string& name, ProcessLifeCycleStateChanged::State state) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
     field_value_matcher->set_field(3);  // Process state field.
-    field_value_matcher->set_eq_int(event);
+    field_value_matcher->set_eq_int(state);
     return atom_matcher;
 }
 
 AtomMatcher CreateProcessCrashAtomMatcher() {
     return CreateProcessLifeCycleStateChangedAtomMatcher(
-        "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
+        "Crashed", ProcessLifeCycleStateChanged::CRASHED);
 }
 
 Predicate CreateScheduledJobPredicate() {
@@ -241,8 +241,8 @@
 Predicate CreateIsInBackgroundPredicate() {
     Predicate predicate;
     predicate.set_id(StringToId("IsInBackground"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("Background"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground"));
     return predicate;
 }
 
@@ -373,25 +373,25 @@
 }
 
 std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-    const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
+    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(
         android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
     event->write(uid);
     event->write("pkg_name");
     event->write("class_name");
-    event->write(activity);
+    event->write(state);
     event->init();
     return event;
 }
 
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
@@ -418,19 +418,19 @@
 }
 
 std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
-    const int uid, const ProcessLifeCycleStateChanged::Event event, uint64_t timestampNs) {
+    const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) {
     auto logEvent = std::make_unique<LogEvent>(
         android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
     logEvent->write(uid);
     logEvent->write("");
-    logEvent->write(event);
+    logEvent->write(state);
     logEvent->init();
     return logEvent;
 }
 
 std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
     return CreateProcessLifeCycleStateChangedEvent(
-        uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs);
+        uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 74e3c3e..a2aaf12 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -25,7 +25,8 @@
     void unpinSlice(String pkg, in Uri uri, in IBinder token);
     boolean hasSliceAccess(String pkg);
     SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
-    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
+    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+            in String[] autoGrantPermissions);
     void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
     Uri[] getPinnedSlices(String pkg);
 
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 88b198b..d5a98a5 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -404,7 +404,8 @@
      * Does the permission check to see if a caller has access to a specific slice.
      * @hide
      */
-    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) {
         try {
             if (UserHandle.isSameApp(uid, Process.myUid())) {
                 return;
@@ -412,7 +413,7 @@
             if (pkg == null) {
                 throw new SecurityException("No pkg specified");
             }
-            int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+            int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
             if (result == PERMISSION_DENIED) {
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
@@ -424,6 +425,8 @@
                         Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+                // Notify a change has happened because we just granted a permission.
+                mContext.getContentResolver().notifyChange(uri, null);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index bbeb3849..fe5742d 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -149,10 +149,31 @@
     private static final boolean DEBUG = false;
 
     private static final long SLICE_BIND_ANR = 2000;
+    private final String[] mAutoGrantPermissions;
 
     private String mCallback;
     private SliceManager mSliceManager;
 
+    /**
+     * A version of constructing a SliceProvider that allows autogranting slice permissions
+     * to apps that hold specific platform permissions.
+     * <p>
+     * When an app tries to bind a slice from this provider that it does not have access to,
+     * This provider will check if the caller holds permissions to any of the autoGrantPermissions
+     * specified, if they do they will be granted persisted uri access to all slices of this
+     * provider.
+     *
+     * @param autoGrantPermissions List of permissions that holders are auto-granted access
+     *                             to slices.
+     */
+    public SliceProvider(@NonNull String... autoGrantPermissions) {
+        mAutoGrantPermissions = autoGrantPermissions;
+    }
+
+    public SliceProvider() {
+        mAutoGrantPermissions = new String[0];
+    }
+
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
         super.attachInfo(context, info);
@@ -402,7 +423,7 @@
                 : getContext().getPackageManager().getNameForUid(callingUid);
         try {
             mSliceManager.enforceSlicePermission(sliceUri, pkg,
-                    callingPid, callingUid);
+                    callingPid, callingUid, mAutoGrantPermissions);
         } catch (SecurityException e) {
             return createPermissionSlice(getContext(), sliceUri, pkg);
         }
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 57f0588..8034bb6 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -56,7 +56,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 128 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
      */
     public static final String AUTH_HMAC_MD5 = "hmac(md5)";
 
@@ -65,7 +66,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 160 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
      */
     public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
 
@@ -73,7 +75,8 @@
      * SHA256 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 256 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
      */
     public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
 
@@ -81,7 +84,8 @@
      * SHA384 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 384 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
      */
     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
 
@@ -89,7 +93,8 @@
      * SHA512 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 512 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
      */
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
@@ -112,6 +117,7 @@
         AUTH_HMAC_MD5,
         AUTH_HMAC_SHA1,
         AUTH_HMAC_SHA256,
+        AUTH_HMAC_SHA384,
         AUTH_HMAC_SHA512,
         AUTH_CRYPT_AES_GCM
     })
@@ -126,11 +132,14 @@
      * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
      * defined as constants in this class.
      *
+     * <p>For algorithms that produce an integrity check value, the truncation length is a required
+     * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
+     *
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      */
     public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
-        this(algorithm, key, key.length * 8);
+        this(algorithm, key, 0);
     }
 
     /**
@@ -228,6 +237,7 @@
             case AUTH_CRYPT_AES_GCM:
                 // The keying material for GCM is a key plus a 32-bit salt
                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
                 break;
             default:
                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index a88fe04..1525508 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -274,7 +274,8 @@
      *
      * @param destinationAddress the destination address for traffic bearing the requested SPI.
      *     For inbound traffic, the destination should be an address currently assigned on-device.
-     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI
+     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is
+     *     reserved and may not be used. See RFC 4303 Section 2.1.
      * @return the reserved SecurityParameterIndex
      * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
      *     currently allocated for this user
@@ -502,7 +503,7 @@
      * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link
      * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the
      * caller. The caller should not close the {@code FileDescriptor} returned by {@link
-     * #getSocket}, but should use {@link #close} instead.
+     * #getFileDescriptor}, but should use {@link #close} instead.
      *
      * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic
      * of the next user who binds to that port. To prevent this scenario, these sockets are held
@@ -541,8 +542,8 @@
             mCloseGuard.open("constructor");
         }
 
-        /** Get the wrapped socket. */
-        public FileDescriptor getSocket() {
+        /** Get the encapsulation socket's file descriptor. */
+        public FileDescriptor getFileDescriptor() {
             if (mPfd == null) {
                 return null;
             }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 4431bcef1..758cd2b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -62,6 +62,7 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -457,6 +458,7 @@
 
             // get the version code, but don't do anything with it: creator knew about all our flags
             porBuf.getInt();
+            HashSet<X509Certificate> certHistorySet = new HashSet<>();
             while (porBuf.hasRemaining()) {
                 levelCount++;
                 ByteBuffer level = getLengthPrefixedSlice(porBuf);
@@ -495,6 +497,12 @@
                 lastCert = new VerbatimX509Certificate(lastCert, encodedCert);
 
                 lastSigAlgorithm = sigAlgorithm;
+                if (certHistorySet.contains(lastCert)) {
+                    throw new SecurityException("Encountered duplicate entries in "
+                            + "Proof-of-rotation record at certificate #" + levelCount + ".  All "
+                            + "signing certificates should be unique");
+                }
+                certHistorySet.add(lastCert);
                 certs.add(lastCert);
                 flagsList.add(flags);
             }
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 3b8fc5c..f15e1a1 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -72,22 +72,31 @@
                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
         long dataSize = apk.length() - signingBlockSize;
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
+        int merkleTreeSize = levelOffset[levelOffset.length - 1];
 
         ByteBuffer output = bufferFactory.create(
-                CHUNK_SIZE_BYTES +  // fsverity header + extensions + padding
-                levelOffset[levelOffset.length - 1]);  // Merkle tree size
+                merkleTreeSize
+                + CHUNK_SIZE_BYTES);  // maximum size of fsverity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
 
-        ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES);
-        ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit());
+        ByteBuffer tree = slice(output, 0, merkleTreeSize);
+        ByteBuffer header = slice(output, merkleTreeSize,
+                merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
+        ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
+                merkleTreeSize + CHUNK_SIZE_BYTES);
         byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
         ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
         apkDigest.order(ByteOrder.LITTLE_ENDIAN);
 
+        // NB: Buffer limit is set inside once finished.
         calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
 
-        output.rewind();
+        // Put the reverse offset to fs-verity header at the end.
+        output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
+        output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
+                + 4);  // size of this integer right before EOF
+        output.flip();
+
         return new ApkVerityResult(output, apkDigestBytes);
     }
 
@@ -101,7 +110,8 @@
         ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
                 .order(ByteOrder.LITTLE_ENDIAN);
         ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES);
+        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
+                CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
 
         calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
 
@@ -328,10 +338,10 @@
         buffer.put((byte) 12);              // log2(block-size): log2(4096)
         buffer.put((byte) 7);               // log2(leaves-per-node): log2(4096 / 32)
 
-        buffer.putShort((short) 1);         // meta algorithm, SHA256_MODE == 1
-        buffer.putShort((short) 1);         // data algorithm, SHA256_MODE == 1
+        buffer.putShort((short) 1);         // meta algorithm, SHA256 == 1
+        buffer.putShort((short) 1);         // data algorithm, SHA256 == 1
 
-        buffer.putInt(0x0);                 // flags
+        buffer.putInt(0);                   // flags
         buffer.putInt(0);                   // reserved
 
         buffer.putLong(fileSize);           // original file size
@@ -362,12 +372,11 @@
         //
         // struct fsverity_extension_patch {
         //   __le64 offset;
-        //   u8 length;
-        //   u8 reserved[7];
         //   u8 databytes[];
         // };
 
         final int kSizeOfFsverityExtensionHeader = 8;
+        final int kExtensionSizeAlignment = 8;
 
         {
             // struct fsverity_extension #1
@@ -385,24 +394,25 @@
 
         {
             // struct fsverity_extension #2
-            final int kSizeOfFsverityPatchExtension =
-                    8 +  // offset size
-                    1 +  // size of length from offset (up to 255)
-                    7 +  // reserved
-                    ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
-            final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8);
+            final int kTotalSize = kSizeOfFsverityExtensionHeader
+                    + 8 // offset size
+                    + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
 
-            buffer.putShort((short)  // total size of extension, padded to 64-bit alignment
-                    (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding));
+            buffer.putShort((short) kTotalSize);
             buffer.put((byte) 1);    // ID of patch extension
             skip(buffer, 5);         // reserved
 
             // struct fsverity_extension_patch
-            buffer.putLong(eocdOffset);                                 // offset
-            buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE);  // length
-            skip(buffer, 7);                                            // reserved
-            buffer.putInt(Math.toIntExact(signingBlockOffset));         // databytes
-            skip(buffer, kPadding);                                     // padding
+            buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);  // offset
+            buffer.putInt(Math.toIntExact(signingBlockOffset));  // databytes
+
+            // The extension needs to be 0-padded at the end, since the length may not be multiple
+            // of 8.
+            int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment;
+            if (kPadding == kExtensionSizeAlignment) {
+                kPadding = 0;
+            }
+            skip(buffer, kPadding);                              // padding
         }
 
         buffer.flip();
diff --git a/core/java/android/util/apk/VerbatimX509Certificate.java b/core/java/android/util/apk/VerbatimX509Certificate.java
index 9984c6d..391c5fc 100644
--- a/core/java/android/util/apk/VerbatimX509Certificate.java
+++ b/core/java/android/util/apk/VerbatimX509Certificate.java
@@ -18,6 +18,7 @@
 
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
 
 /**
  * For legacy reasons we need to return exactly the original encoded certificate bytes, instead
@@ -25,6 +26,7 @@
  */
 class VerbatimX509Certificate extends WrappedX509Certificate {
     private final byte[] mEncodedVerbatim;
+    private int mHash = -1;
 
     VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
         super(wrapped);
@@ -35,4 +37,30 @@
     public byte[] getEncoded() throws CertificateEncodingException {
         return mEncodedVerbatim;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof VerbatimX509Certificate)) return false;
+
+        try {
+            byte[] a = this.getEncoded();
+            byte[] b = ((VerbatimX509Certificate) o).getEncoded();
+            return Arrays.equals(a, b);
+        } catch (CertificateEncodingException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHash == -1) {
+            try {
+                mHash = Arrays.hashCode(this.getEncoded());
+            } catch (CertificateEncodingException e) {
+                mHash = 0;
+            }
+        }
+        return mHash;
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f6c669b..ea3710c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13917,11 +13917,15 @@
         mAttachInfo.mUnbufferedDispatchRequested = true;
     }
 
+    private boolean hasSize() {
+        return (mBottom > mTop) && (mRight > mLeft);
+    }
+
     private boolean canTakeFocus() {
         return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
                 && ((mViewFlags & FOCUSABLE) == FOCUSABLE)
                 && ((mViewFlags & ENABLED_MASK) == ENABLED)
-                && (sCanFocusZeroSized || !isLayoutValid() || (mBottom > mTop) && (mRight > mLeft));
+                && (sCanFocusZeroSized || !isLayoutValid() || hasSize());
     }
 
     /**
@@ -13982,7 +13986,7 @@
                             || focusableChangedByAuto == 0
                             || viewRootImpl == null
                             || viewRootImpl.mThread == Thread.currentThread()) {
-                        shouldNotifyFocusableAvailable = true;
+                        shouldNotifyFocusableAvailable = canTakeFocus();
                     }
                 }
             }
@@ -14001,11 +14005,10 @@
 
                 needGlobalAttributesUpdate(true);
 
-                // a view becoming visible is worth notifying the parent
-                // about in case nothing has focus.  even if this specific view
-                // isn't focusable, it may contain something that is, so let
-                // the root view try to give this focus if nothing else does.
-                shouldNotifyFocusableAvailable = true;
+                // a view becoming visible is worth notifying the parent about in case nothing has
+                // focus. Even if this specific view isn't focusable, it may contain something that
+                // is, so let the root view try to give this focus if nothing else does.
+                shouldNotifyFocusableAvailable = hasSize();
             }
         }
 
@@ -14014,16 +14017,14 @@
                 // a view becoming enabled should notify the parent as long as the view is also
                 // visible and the parent wasn't already notified by becoming visible during this
                 // setFlags invocation.
-                shouldNotifyFocusableAvailable = true;
+                shouldNotifyFocusableAvailable = canTakeFocus();
             } else {
                 if (isFocused()) clearFocus();
             }
         }
 
-        if (shouldNotifyFocusableAvailable) {
-            if (mParent != null && canTakeFocus()) {
-                mParent.focusableViewAvailable(this);
-            }
+        if (shouldNotifyFocusableAvailable && mParent != null) {
+            mParent.focusableViewAvailable(this);
         }
 
         /* Check if the GONE bit has changed */
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 51dd929..e8fc598 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1000,6 +1000,11 @@
             setPatternInProgress(false);
             cancelLineAnimations();
             notifyPatternDetected();
+            // Also clear pattern if fading is enabled
+            if (mFadePattern) {
+                clearPatternDrawLookup();
+                mPatternDisplayMode = DisplayMode.Correct;
+            }
             invalidate();
         }
         if (PROFILE_DRAWING) {
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 449d3e7..c63f319 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -33,11 +33,16 @@
          a light UI more visible. -->
     <drawable name="screen_background_light_transparent">#80ffffff</drawable>
     <color name="safe_mode_text">#80ffffff</color>
+    <!-- The color white, equivalent to 0xffffffff -->
     <color name="white">#ffffffff</color>
+    <!-- The color black, equivalent to 0xff000000 -->
     <color name="black">#ff000000</color>
     <color name="red">#ffff0000</color>
+    <!-- Fully transparent, equivalent to 0x00000000 -->
     <color name="transparent">#00000000</color>
+    <!-- Equivalent to 0xff000000 -->
     <color name="background_dark">#ff000000</color>
+    <!-- Equivalent to 0xffffffff -->
     <color name="background_light">#ffffffff</color>
     <color name="bright_foreground_dark">@android:color/background_light</color>
     <color name="bright_foreground_light">@android:color/background_dark</color>
@@ -80,6 +85,7 @@
 
     <!-- For settings framework -->
     <color name="lighter_gray">#ddd</color>
+    <!-- A dark gray, equivalent to 0xffaaaaaa -->
     <color name="darker_gray">#aaa</color>
 
     <!-- For security permissions -->
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index 917c781..4297eea 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -50,29 +50,29 @@
     <!-- General purpose colors for Holo-themed elements -->
     <eat-comment />
 
-    <!-- A light Holo shade of blue -->
+    <!-- A light Holo shade of blue. Equivalent to #ff33b5e5. -->
     <color name="holo_blue_light">#ff33b5e5</color>
-    <!-- A light Holo shade of gray -->
+    <!-- A light Holo shade of gray. Equivalent to #33999999. -->
     <color name="holo_gray_light">#33999999</color>
-    <!-- A light Holo shade of green -->
+    <!-- A light Holo shade of green. Equivalent to #ff99cc00. -->
     <color name="holo_green_light">#ff99cc00</color>
-    <!-- A light Holo shade of red -->
+    <!-- A light Holo shade of red. Equivalent to #ffff4444. <-->
     <color name="holo_red_light">#ffff4444</color>
-    <!-- A dark Holo shade of blue -->
+    <!-- A dark Holo shade of blue. Equivalent to #ff0099cc -->
     <color name="holo_blue_dark">#ff0099cc</color>
-    <!-- A dark Holo shade of green -->
+    <!-- A dark Holo shade of green. Equivalent to #ff669900 -->
     <color name="holo_green_dark">#ff669900</color>
-    <!-- A dark Holo shade of red -->
+    <!-- A dark Holo shade of red. Equivalent to #ffcc0000 -->
     <color name="holo_red_dark">#ffcc0000</color>
-    <!-- A Holo shade of purple -->
+    <!-- A Holo shade of purple. Equivalent to #ffaa66cc -->
     <color name="holo_purple">#ffaa66cc</color>
-    <!-- A light Holo shade of orange -->
+    <!-- A light Holo shade of orange. Equivalent to #ffffbb33. -->
     <color name="holo_orange_light">#ffffbb33</color>
-    <!-- A dark Holo shade of orange -->
+    <!-- A dark Holo shade of orange. Equivalent to ffff8800. -->
     <color name="holo_orange_dark">#ffff8800</color>
-    <!-- A really bright Holo shade of blue -->
+    <!-- A really bright Holo shade of blue. Equivalent to #ff00ddff. -->
     <color name="holo_blue_bright">#ff00ddff</color>
-    <!-- A really bright Holo shade of gray -->
+    <!-- A really bright Holo shade of gray. Equivalent to #33cccccc. -->
     <color name="holo_gray_bright">#33CCCCCC</color>
 
     <!-- Forward compatibility for Material-style theme colors -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 66d25df..2a6b331 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -30,33 +30,32 @@
         <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
     </string-array>
 
     <string translatable="false" name="status_bar_rotate">rotate</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0246c80..42cc54f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -708,23 +708,38 @@
   <public type="dimen" name="thumbnail_height" id="0x01050001" />
   <public type="dimen" name="thumbnail_width" id="0x01050002" />
 
+  <!-- Equivalent to 0xffaaaaaa -->
   <public type="color" name="darker_gray" id="0x01060000" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="primary_text_dark" id="0x01060001" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="primary_text_dark_nodisable" id="0x01060002" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="primary_text_light" id="0x01060003" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="primary_text_light_nodisable" id="0x01060004" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="secondary_text_dark" id="0x01060005" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="secondary_text_dark_nodisable" id="0x01060006" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="secondary_text_light" id="0x01060007" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="secondary_text_light_nodisable" id="0x01060008" />
+  <!-- Equivalent to 0xff808080 -->
   <public type="color" name="tab_indicator_text" id="0x01060009" />
+  <!-- Equivalent to 0xff000000 -->
   <public type="color" name="widget_edittext_dark" id="0x0106000a" />
   <public type="color" name="white" id="0x0106000b" />
   <public type="color" name="black" id="0x0106000c" />
   <public type="color" name="transparent" id="0x0106000d" />
+  <!-- Equivalent to 0xff000000 -->
   <public type="color" name="background_dark" id="0x0106000e" />
+  <!-- Equivalent to 0xffffffff -->
   <public type="color" name="background_light" id="0x0106000f" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="tertiary_text_dark" id="0x01060010" />
+  <!-- {@deprecated Use a text color from your theme instead.} -->
   <public type="color" name="tertiary_text_light" id="0x01060011" />
 
   <public type="array" name="emailAddressTypes" id="0x01070000" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cb8d629..cbd7b4d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2732,6 +2732,10 @@
   <java-symbol type="string" name="status_bar_alarm_clock" />
   <java-symbol type="string" name="status_bar_secure" />
   <java-symbol type="string" name="status_bar_clock" />
+  <java-symbol type="string" name="status_bar_airplane" />
+  <java-symbol type="string" name="status_bar_mobile" />
+  <java-symbol type="string" name="status_bar_ethernet" />
+  <java-symbol type="string" name="status_bar_vpn" />
 
   <!-- Locale picker -->
   <java-symbol type="id" name="locale_search_menu" />
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 5261c04..00dc22e 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -855,7 +855,8 @@
      */
     @java.lang.Deprecated
     public ImageDecoder setResize(int width, int height) {
-        return this.setTargetSize(width, height);
+        this.setTargetSize(width, height);
+        return this;
     }
 
     /**
@@ -873,9 +874,8 @@
      *
      *  @param width must be greater than 0.
      *  @param height must be greater than 0.
-     *  @return this object for chaining.
      */
-    public ImageDecoder setTargetSize(int width, int height) {
+    public void setTargetSize(int width, int height) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Dimensions must be positive! "
                     + "provided (" + width + ", " + height + ")");
@@ -883,7 +883,6 @@
 
         mDesiredWidth = width;
         mDesiredHeight = height;
-        return this;
     }
 
     /** @removed
@@ -891,7 +890,8 @@
      */
     @java.lang.Deprecated
     public ImageDecoder setResize(int sampleSize) {
-        return this.setTargetSampleSize(sampleSize);
+        this.setTargetSampleSize(sampleSize);
+        return this;
     }
 
     private int getTargetDimension(int original, int sampleSize, int computed) {
@@ -941,13 +941,12 @@
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
-     *  @return this object for chaining.
      */
-    public ImageDecoder setTargetSampleSize(int sampleSize) {
+    public void setTargetSampleSize(int sampleSize) {
         Size size = this.getSampledSize(sampleSize);
         int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
         int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
-        return this.setTargetSize(targetWidth, targetHeight);
+        this.setTargetSize(targetWidth, targetHeight);
     }
 
     private boolean requestedResize() {
@@ -1006,14 +1005,12 @@
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
      *  @param allocator Type of allocator to use.
-     *  @return this object for chaining.
      */
-    public ImageDecoder setAllocator(@Allocator int allocator) {
+    public void setAllocator(@Allocator int allocator) {
         if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
             throw new IllegalArgumentException("invalid allocator " + allocator);
         }
         mAllocator = allocator;
-        return this;
     }
 
     /**
@@ -1039,12 +1036,9 @@
      *
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
-     *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setUnpremultipliedRequired(boolean unpremultipliedRequired) {
+    public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
         mUnpremultipliedRequired = unpremultipliedRequired;
-        return this;
     }
 
     /** @removed
@@ -1052,7 +1046,8 @@
      */
     @java.lang.Deprecated
     public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
-        return this.setUnpremultipliedRequired(unpremultipliedRequired);
+        this.setUnpremultipliedRequired(unpremultipliedRequired);
+        return this;
     }
 
     /**
@@ -1086,11 +1081,9 @@
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
+    public void setPostProcessor(@Nullable PostProcessor p) {
         mPostProcessor = p;
-        return this;
     }
 
     /**
@@ -1110,11 +1103,9 @@
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
+    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
         mOnPartialImageListener = l;
-        return this;
     }
 
     /**
@@ -1140,11 +1131,9 @@
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setCrop(@Nullable Rect subset) {
+    public void setCrop(@Nullable Rect subset) {
         mCropRect = subset;
-        return this;
     }
 
     /**
@@ -1164,13 +1153,10 @@
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
-     *  @return this object for chaining.
-     *
      *  @hide
      */
-    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+    public void setOutPaddingRect(@NonNull Rect outPadding) {
         mOutPaddingRect = outPadding;
-        return this;
     }
 
     /**
@@ -1191,12 +1177,9 @@
      *
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
-     *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setMutableRequired(boolean mutable) {
+    public void setMutableRequired(boolean mutable) {
         mMutable = mutable;
-        return this;
     }
 
     /** @removed
@@ -1204,7 +1187,8 @@
      */
     @java.lang.Deprecated
     public ImageDecoder setMutable(boolean mutable) {
-        return this.setMutableRequired(mutable);
+        this.setMutableRequired(mutable);
+        return this;
     }
 
     /**
@@ -1236,12 +1220,9 @@
      *
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
-     *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setConserveMemory(boolean conserveMemory) {
+    public void setConserveMemory(boolean conserveMemory) {
         mConserveMemory = conserveMemory;
-        return this;
     }
 
     /**
@@ -1269,12 +1250,9 @@
      *
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
-     *
-     *  @return this object for chaining.
      */
-    public ImageDecoder setDecodeAsAlphaMaskEnabled(boolean enabled) {
+    public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
         mDecodeAsAlphaMask = enabled;
-        return this;
     }
 
     /** @removed
@@ -1282,7 +1260,8 @@
      */
     @java.lang.Deprecated
     public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
-        return this.setDecodeAsAlphaMaskEnabled(enabled);
+        this.setDecodeAsAlphaMaskEnabled(enabled);
+        return this;
     }
 
     /** @removed
@@ -1290,7 +1269,8 @@
      */
     @java.lang.Deprecated
     public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
-        return this.setDecodeAsAlphaMask(asAlphaMask);
+        this.setDecodeAsAlphaMask(asAlphaMask);
+        return this;
     }
 
     /**
@@ -1350,9 +1330,8 @@
      * <p>Like all setters on ImageDecoder, this must be called inside
      * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      */
-    public ImageDecoder setTargetColorSpace(ColorSpace colorSpace) {
+    public void setTargetColorSpace(ColorSpace colorSpace) {
         mDesiredColorSpace = colorSpace;
-        return this;
     }
 
     @Override
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 81644eb..fe05c13 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -547,9 +547,6 @@
         try {
             args = args != null ? args : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
-            if (!args.containsTag(KeymasterDefs.KM_TAG_USER_ID)) {
-                args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser());
-            }
             return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 5d596cb..c342acd 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -673,7 +673,7 @@
     }
 
     /**
-     * @hide Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
      *
      * @see Builder#setUnlockedDeviceRequired(boolean)
      */
@@ -1288,7 +1288,7 @@
         }
 
         /**
-         * @hide Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
          * using this key. If this is set to {@code true}, any attempt to decrypt using this key
          * while the screen is locked will fail. A locked device requires a PIN, password,
          * fingerprint, or other trusted factor to access.
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index cc7870c..22568ce 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -508,7 +508,7 @@
     }
 
     /**
-     * @hide Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
      *
      * @see Builder#setUnlockedDeviceRequired(boolean)
      */
@@ -928,7 +928,7 @@
         }
 
         /**
-         * @hide Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
          * using this key. If this is set to {@code true}, any attempt to decrypt using this key
          * while the screen is locked will fail. A locked device requires a PIN, password,
          * fingerprint, or other trusted factor to access.
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 6e50121..14a9970 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -101,7 +101,6 @@
      *         require user authentication.
      */
     public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) {
-        args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser());
 
         if (spec.isUserConfirmationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 8d62cac..5173743 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -705,7 +705,9 @@
     public @interface KeyType {}
 
     /**
-     * Contains the opaque data an app uses to request keys from a license server
+     * Contains the opaque data an app uses to request keys from a license server.
+     * These request types may or may not be generated by a given plugin. Refer
+     * to plugin vendor documentation for more information.
      */
     public static final class KeyRequest {
         private byte[] mData;
@@ -730,8 +732,8 @@
         public static final int REQUEST_TYPE_RELEASE = 2;
 
         /**
-         * Keys are already loaded. No license request is necessary, and no
-         * key request data is returned.
+         * Keys are already loaded and are available for use. No license request is necessary, and
+         * no key request data is returned.
          */
         public static final int REQUEST_TYPE_NONE = 3;
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 53f7e44..ad300f4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,4 +50,6 @@
     public abstract void onStateChanged(State state);
 
     public abstract int getDetailY();
+
+    public void setExpansion(float expansion) {}
 }
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml
index 12d8016..e15a734 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml
@@ -14,6 +14,6 @@
     limitations under the License.
 -->
 <transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/qs_detail_transition" />
-    <item android:drawable="?android:attr/colorPrimary" />
+    <item android:drawable="@drawable/qs_customizer_background_transition" />
+    <item android:drawable="@drawable/qs_customizer_background_primary" />
 </transition>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
new file mode 100644
index 0000000..abe1429
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="?android:attr/colorPrimary"/>
+        <corners android:radius="?android:attr/dialogCornerRadius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
new file mode 100644
index 0000000..ed8f61a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="@color/qs_detail_transition"/>
+        <corners android:radius="?android:attr/dialogCornerRadius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
new file mode 100644
index 0000000..557cae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="?android:attr/colorSecondary"/>
+        <corners
+            android:topLeftRadius="?android:attr/dialogCornerRadius"
+            android:topRightRadius="?android:attr/dialogCornerRadius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml
index 71ad85b..51febc7 100644
--- a/packages/SystemUI/res/layout/qs_customize_divider.xml
+++ b/packages/SystemUI/res/layout/qs_customize_divider.xml
@@ -20,9 +20,8 @@
     android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:gravity="center"
     android:paddingTop="20dp"
-    android:paddingStart="16dp"
-    android:paddingEnd="8dp"
     android:paddingBottom="13dp"
     android:textAppearance="@android:style/TextAppearance.Material.Body2"
     android:textColor="?android:attr/colorAccent"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index b3b6a0c..506e6c8 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -22,7 +22,6 @@
     android:layout_height="0dp"
     android:elevation="4dp"
     android:orientation="vertical"
-    android:background="@drawable/qs_customizer_background"
     android:gravity="center_horizontal">
 
 </com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 04d0e65..d70a37a 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -15,24 +15,44 @@
      limitations under the License.
 -->
 
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <Toolbar
-        android:id="@*android:id/action_bar"
+<merge xmlns:android="http://schemas.android.com/apk/res/android">->
+    <View
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="28dp"
-        android:navigationContentDescription="@*android:string/action_bar_up_description"
-        style="?android:attr/toolbarStyle" />
+        android:layout_height="@*android:dimen/quick_qs_offset_height"
+        android:background="@android:color/transparent" />
 
-    <android.support.v7.widget.RecyclerView
-        android:id="@android:id/list"
+    <com.android.keyguard.AlphaOptimizedLinearLayout
+        android:id="@+id/customize_container"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
-        android:scrollIndicators="top"
-        android:scrollbars="vertical"
-        android:importantForAccessibility="no" />
+        android:layout_marginLeft="@dimen/notification_side_paddings"
+        android:layout_marginRight="@dimen/notification_side_paddings"
+        android:orientation="vertical"
+        android:background="@drawable/qs_customizer_background">
+        <Toolbar
+            android:id="@*android:id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@drawable/qs_customizer_toolbar"
+            android:navigationContentDescription="@*android:string/action_bar_up_description"
+            style="?android:attr/toolbarStyle" />
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingTop="28dp"
+            android:paddingLeft="@dimen/qs_tile_layout_margin_side"
+            android:paddingRight="@dimen/qs_tile_layout_margin_side"
+            android:paddingBottom="28dp"
+            android:clipToPadding="false"
+            android:scrollIndicators="top"
+            android:scrollbars="vertical"
+            android:scrollbarStyle="outsideOverlay"
+            android:importantForAccessibility="no" />
+    </com.android.keyguard.AlphaOptimizedLinearLayout>
 
     <View
         android:id="@+id/nav_bar_background"
diff --git a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
index ff55f99..a2250b1 100644
--- a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
+++ b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
@@ -17,9 +17,8 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/qs_tile_height"
     android:layout_width="match_parent"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp"
-    android:paddingTop="8dp"
+    android:layout_marginTop="@dimen/qs_tile_margin_top_bottom"
+    android:layout_marginBottom="@dimen/qs_tile_margin_top_bottom"
     android:gravity="center" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index cd3271f..c03f25c 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -21,7 +21,7 @@
     android:layout_marginTop="8dp"
     android:layout_marginBottom="14dp"
     android:layout_below="@id/quick_status_bar_system_icons"
-    >
+    android:paddingEnd="@dimen/signal_cluster_battery_padding" >
 
     <com.android.systemui.statusbar.phone.StatusIconContainer
         android:id="@+id/statusIcons"
@@ -29,9 +29,4 @@
         android:layout_height="match_parent"
         android:layout_weight="1" />
 
-    <include layout="@layout/signal_cluster_view"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginStart="@dimen/signal_cluster_margin_start" />
-
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
new file mode 100644
index 0000000..d607c8c
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<com.android.systemui.statusbar.StatusBarMobileView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/mobile_combo"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_vertical"
+    android:orientation="horizontal">
+    <FrameLayout
+        android:id="@+id/inout_container"
+        android:layout_height="17dp"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical">
+        <ImageView
+            android:id="@+id/mobile_in"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_down"
+            android:visibility="gone"
+            android:paddingEnd="2dp"
+        />
+        <ImageView
+            android:id="@+id/mobile_out"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_up"
+            android:paddingEnd="2dp"
+            android:visibility="gone"
+        />
+    </FrameLayout>
+    <ImageView
+        android:id="@+id/mobile_type"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingEnd="1dp"
+        android:visibility="gone" />
+    <Space
+        android:id="@+id/mobile_roaming_space"
+        android:layout_height="match_parent"
+        android:layout_width="@dimen/roaming_icon_start_padding"
+        android:visibility="gone"
+    />
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical">
+        <com.android.systemui.statusbar.AnimatedImageView
+            android:id="@+id/mobile_signal"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            systemui:hasOverlappingRendering="false"
+        />
+        <ImageView
+            android:id="@+id/mobile_roaming"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/stat_sys_roaming"
+            android:contentDescription="@string/data_connection_roaming"
+            android:visibility="gone" />
+    </FrameLayout>
+</com.android.systemui.statusbar.StatusBarMobileView>
+
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml
new file mode 100644
index 0000000..08cef55
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<com.android.systemui.statusbar.StatusBarWifiView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/wifi_combo"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:paddingStart="4dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal" >
+
+    <FrameLayout
+            android:id="@+id/inout_container"
+            android:layout_height="17dp"
+            android:layout_width="wrap_content"
+            android:gravity="center_vertical" >
+        <ImageView
+            android:id="@+id/wifi_in"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_down"
+            android:visibility="gone"
+            android:paddingEnd="2dp"
+        />
+        <ImageView
+            android:id="@+id/wifi_out"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_up"
+            android:paddingEnd="2dp"
+            android:visibility="gone"
+        />
+    </FrameLayout>
+    <FrameLayout
+        android:id="@+id/wifi_combo"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:gravity="center_vertical" >
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:theme="?attr/lightIconTheme"
+            android:id="@+id/wifi_signal"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+    </FrameLayout>
+
+    <View
+        android:id="@+id/wifi_signal_spacer"
+        android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
+        android:layout_height="4dp"
+        android:visibility="gone" />
+
+    <!-- Looks like CarStatusBar uses this... -->
+    <ViewStub
+        android:id="@+id/connected_device_signals_stub"
+        android:layout="@layout/connected_device_signal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <View
+        android:id="@+id/wifi_airplane_spacer"
+        android:layout_width="@dimen/status_bar_airplane_spacer_width"
+        android:layout_height="4dp"
+        android:visibility="gone"
+    />
+</com.android.systemui.statusbar.StatusBarWifiView>
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 1fafb2f..258b82a 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -24,14 +24,10 @@
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="match_parent"
+        android:paddingEnd="4dp"
         android:gravity="center_vertical"
         android:orientation="horizontal"/>
 
-    <include layout="@layout/signal_cluster_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
-
     <com.android.systemui.BatteryMeterView android:id="@+id/battery"
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0f07ca4..84ca657 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -317,8 +317,10 @@
     <dimen name="pull_span_min">25dp</dimen>
 
     <dimen name="qs_tile_height">106dp</dimen>
+    <dimen name="qs_tile_layout_margin_side">9dp</dimen>
     <dimen name="qs_tile_margin_horizontal">18dp</dimen>
     <dimen name="qs_tile_margin_vertical">24dp</dimen>
+    <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top">18dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <!-- Width for the spacer, used between QS tiles. -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 8923952..c548cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -167,6 +167,13 @@
         }
     }
 
+    @Override
+    public void setExpansion(float expansion) {
+        for (TileRecord tr : mTiles) {
+            tr.tileView.setExpansion(expansion);
+        }
+    }
+
     public void setPageListener(PageListener listener) {
         mPageListener = listener;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 29f3c43..018a635 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -291,6 +291,7 @@
         mHeader.setExpansion(mKeyguardShowing, expansion, panelTranslationY);
         mFooter.setExpansion(mKeyguardShowing ? 1 : expansion);
         mQSPanel.getQsTileRevealController().setExpansion(expansion);
+        mQSPanel.getTileLayout().setExpansion(expansion);
         mQSPanel.setTranslationY(translationScaleY * heightDiff);
         mQSDetail.setFullyExpanded(fullyExpanded);
 
@@ -359,7 +360,6 @@
         // The customize state changed, so our height changed.
         mContainer.updateExpansion();
         mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
-        mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
         mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
         // Let the panel know the position changed and it needs to update where notifications
         // and whatnot are.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 61e3065..6368a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -616,5 +616,7 @@
         boolean updateResources();
 
         void setListening(boolean listening);
+
+        default void setExpansion(float expansion) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 0ac8b9c..df65d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -47,7 +47,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.R.id;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSDetail.Callback;
@@ -161,7 +160,6 @@
 
         // Set light text on the header icons because they will always be on a black background
         applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
-        applyDarkness(id.signal_cluster, tintArea, intensity, colorForeground);
 
         // Set the correct tint for the status icons so they contrast
         mIconManager.setTint(fillColor);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 1cb89c4..64e7a63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -23,6 +23,7 @@
     protected int mCellHeight;
     protected int mCellMarginHorizontal;
     protected int mCellMarginVertical;
+    protected int mSidePadding;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     private int mCellMarginTop;
@@ -80,6 +81,7 @@
         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
+        mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
         if (mColumns != columns) {
             mColumns = columns;
             requestLayout();
@@ -93,7 +95,7 @@
         final int numTiles = mRecords.size();
         final int width = MeasureSpec.getSize(widthMeasureSpec);
         final int numRows = (numTiles + mColumns - 1) / mColumns;
-        mCellWidth = (width - (mCellMarginHorizontal * (mColumns + 1))) / mColumns;
+        mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
 
         // Measure each QS tile.
         View previousView = this;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index ec18376..31c455d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -44,7 +44,6 @@
         float intensity = colorForeground == Color.WHITE ? 0f : 1f;
         Rect tintArea = new Rect(0, 0, 0, 0);
 
-        applyDarkness(R.id.signal_cluster, tintArea, intensity, colorForeground);
         applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
         applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index a3d6c6c..4aa83d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.os.Bundle;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.GridLayoutManager;
@@ -37,6 +38,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
@@ -81,10 +83,9 @@
 
     public QSCustomizer(Context context, AttributeSet attrs) {
         super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
-        mClipper = new QSDetailClipper(this);
 
         LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
-
+        mClipper = new QSDetailClipper(findViewById(R.id.customize_container));
         mToolbar = findViewById(com.android.internal.R.id.action_bar);
         TypedValue value = new TypedValue();
         mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
@@ -100,7 +101,10 @@
         mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
                 mContext.getString(com.android.internal.R.string.reset));
         mToolbar.setTitle(R.string.qs_edit);
-
+        int accentColor = Utils.getColorAttr(context, android.R.attr.colorAccent);
+        mToolbar.setTitleTextColor(accentColor);
+        mToolbar.getNavigationIcon().setTint(accentColor);
+        mToolbar.getOverflowIcon().setTint(accentColor);
         mRecyclerView = findViewById(android.R.id.list);
         mTileAdapter = new TileAdapter(getContext());
         mTileQueryHelper = new TileQueryHelper(context, mTileAdapter);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 943a176..6f664d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -102,7 +102,9 @@
             mTokenMap.remove(service.getToken());
             mTiles.remove(tile.getComponent());
             final String slot = tile.getComponent().getClassName();
-            mMainHandler.post(() -> mHost.getIconController().removeIcon(slot));
+            // TileServices doesn't know how to add more than 1 icon per slot, so remove all
+            mMainHandler.post(() -> mHost.getIconController()
+                    .removeAllIconsForSlot(slot));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 3cb4c71..d21b06f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -32,12 +32,10 @@
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 
-
 import java.util.Objects;
 
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
-    private static final int DEFAULT_MAX_LINES = 2;
     private static final boolean DUAL_TARGET_ALLOWED = false;
     private View mDivider;
     protected TextView mLabel;
@@ -87,22 +85,17 @@
         mLabelContainer.setClipChildren(false);
         mLabelContainer.setClipToPadding(false);
         mLabel = mLabelContainer.findViewById(R.id.tile_label);
-        mLabel.setSelected(true); // Allow marquee to work.
         mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock);
         mDivider = mLabelContainer.findViewById(R.id.underline);
         mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
         mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
         mSecondLine = mLabelContainer.findViewById(R.id.app_label);
         mSecondLine.setAlpha(.6f);
-        mSecondLine.setSelected(true); // Allow marquee to work.
         addView(mLabelContainer);
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mLabel.getMaxLines() != DEFAULT_MAX_LINES) {
-            mLabel.setMaxLines(DEFAULT_MAX_LINES);
-        }
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
         // Remeasure view if the secondary label text will be cut off.
@@ -114,6 +107,15 @@
     }
 
     @Override
+    public void setExpansion(float expansion) {
+        // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
+        // other expansion ratios since there is no way way to pause the marquee.
+        boolean selected = expansion == 1f ? true : expansion == 0f ? false : mLabel.isSelected();
+        mLabel.setSelected(selected);
+        mSecondLine.setSelected(selected);
+    }
+
+    @Override
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
         if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java
new file mode 100644
index 0000000..cdb0514
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java
@@ -0,0 +1,78 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.content.Context;
+import android.content.res.Resources.Theme;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+
+/**
+ * NeutralGoodDrawable implements a drawable that will load 2 underlying drawable resources, one
+ * with each the DualToneDarkTheme and DualToneLightTheme, choosing which one based on what
+ * DarkIconDispatcher tells us about darkness
+ */
+public class NeutralGoodDrawable extends LayerDrawable {
+
+    public static NeutralGoodDrawable create(Context context, int resId) {
+        int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
+        int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
+        ContextThemeWrapper light = new ContextThemeWrapper(context, dualToneLightTheme);
+        ContextThemeWrapper dark = new ContextThemeWrapper(context, dualToneDarkTheme);
+
+        return create(light, dark, resId);
+    }
+
+    /**
+     * For the on-the-go young entrepreneurial who wants to cache contexts
+     * @param light - a context using the R.attr.lightIconTheme
+     * @param dark - a context using the R.attr.darkIconTheme
+     * @param resId - the resId for our drawable
+     */
+    public static NeutralGoodDrawable create(Context light, Context dark, int resId) {
+        return new NeutralGoodDrawable(
+                new Drawable[] {
+                        light.getDrawable(resId).mutate(),
+                        dark.getDrawable(resId).mutate() });
+    }
+
+    protected NeutralGoodDrawable(Drawable []drawables) {
+        super(drawables);
+
+        for (int i = 0; i < drawables.length; i++) {
+            setLayerGravity(i, Gravity.CENTER);
+        }
+
+        mutate();
+        setDarkIntensity(0);
+    }
+
+    public void setDarkIntensity(float intensity) {
+
+        getDrawable(0).setAlpha((int) ((1 - intensity) * 255f));
+        getDrawable(1).setAlpha((int) (intensity * 255f));
+
+        invalidateSelf();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java
new file mode 100644
index 0000000..56f78f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java
@@ -0,0 +1,33 @@
+/*
+ * 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.systemui.statusbar;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds an array of {@link com.android.internal.statusbar.StatusBarIcon}s and draws them
+ * in a linear layout
+ */
+public class StatusBarIconContainer {
+    private final List<StatusBarIcon> mIcons = new ArrayList<>();
+
+    public StatusBarIconContainer(List<StatusBarIcon> icons) {
+        mIcons.addAll(icons);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 603902a..bd6bd12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -23,6 +25,7 @@
 import android.app.Notification;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -56,7 +59,7 @@
 import java.text.NumberFormat;
 import java.util.Arrays;
 
-public class StatusBarIconView extends AnimatedImageView {
+public class StatusBarIconView extends AnimatedImageView implements StatusIconDisplayable {
     public static final int NO_COLOR = 0;
 
     /**
@@ -867,6 +870,21 @@
         mOnDismissListener = onDismissListener;
     }
 
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        setImageTintList(ColorStateList.valueOf(getTint(area, this, tint)));
+    }
+
+    @Override
+    public boolean isIconVisible() {
+        return mIcon != null && mIcon.visible;
+    }
+
+    @Override
+    public boolean isIconBlocked() {
+        return mBlocked;
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
new file mode 100644
index 0000000..b7620f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+
+public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements DarkReceiver,
+        StatusIconDisplayable {
+    private static final String TAG = "StatusBarMobileView";
+
+    private String mSlot;
+    private MobileIconState mState;
+    private SignalDrawable mMobileDrawable;
+    private View mInoutContainer;
+    private ImageView mIn;
+    private ImageView mOut;
+    private ImageView mMobile, mMobileType, mMobileRoaming;
+    private View mMobileRoamingSpace;
+
+    public static StatusBarMobileView fromContext(Context context) {
+        LayoutInflater inflater = LayoutInflater.from(context);
+
+        return (StatusBarMobileView)
+                inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
+    }
+
+    public StatusBarMobileView(Context context) {
+        super(context);
+    }
+
+    public StatusBarMobileView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        init();
+    }
+
+    private void init() {
+        mMobile = findViewById(R.id.mobile_signal);
+        mMobileType = findViewById(R.id.mobile_type);
+        mMobileRoaming = findViewById(R.id.mobile_roaming);
+        mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
+        mIn = findViewById(R.id.mobile_in);
+        mOut = findViewById(R.id.mobile_out);
+        mInoutContainer = findViewById(R.id.inout_container);
+
+        mMobileDrawable = new SignalDrawable(getContext());
+        mMobile.setImageDrawable(mMobileDrawable);
+    }
+
+    public void applyMobileState(MobileIconState state) {
+        if (state == null) {
+            setVisibility(View.GONE);
+            mState = null;
+            return;
+        }
+
+        if (mState == null) {
+            mState = state;
+            initViewState();
+            return;
+        }
+
+        if (!mState.equals(state)) {
+            updateState(state);
+        }
+    }
+
+    private void initViewState() {
+        setContentDescription(mState.contentDescription);
+        if (!mState.visible) {
+            setVisibility(View.GONE);
+        } else {
+            setVisibility(View.VISIBLE);
+        }
+        mMobileDrawable.setLevel(mState.strengthId);
+        if (mState.typeId > 0) {
+            mMobileType.setContentDescription(mState.typeContentDescription);
+            mMobileType.setImageResource(mState.typeId);
+            mMobileType.setVisibility(View.VISIBLE);
+        } else {
+            mMobileType.setVisibility(View.GONE);
+        }
+
+        mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
+        mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
+        mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
+                ? View.VISIBLE : View.GONE);
+    }
+
+    private void updateState(MobileIconState state) {
+        setContentDescription(state.contentDescription);
+        if (mState.visible != state.visible) {
+            setVisibility(state.visible ? View.VISIBLE : View.GONE);
+        }
+        if (mState.strengthId != state.strengthId) {
+            mMobileDrawable.setLevel(state.strengthId);
+        }
+        if (mState.typeId != state.typeId && state.typeId != 0) {
+            mMobileType.setContentDescription(state.typeContentDescription);
+            mMobileType.setImageResource(state.typeId);
+            mMobileType.setVisibility(View.VISIBLE);
+        } else {
+            mMobileType.setVisibility(View.GONE);
+        }
+
+        mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
+        mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
+        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility((state.activityIn || state.activityOut)
+                ? View.VISIBLE : View.GONE);
+
+        mState = state;
+    }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mMobileDrawable.setDarkIntensity(darkIntensity);
+        ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint));
+        mIn.setImageTintList(color);
+        mOut.setImageTintList(color);
+        mMobileType.setImageTintList(color);
+        mMobileRoaming.setImageTintList(color);
+    }
+
+    @Override
+    public String getSlot() {
+        return mSlot;
+    }
+
+    public void setSlot(String slot) {
+        mSlot = slot;
+    }
+
+    @Override
+    public void setStaticDrawableColor(int color) {
+        ColorStateList list = ColorStateList.valueOf(color);
+        float intensity = color == Color.WHITE ? 0 : 1;
+        mMobileDrawable.setDarkIntensity(intensity);
+
+        mIn.setImageTintList(list);
+        mOut.setImageTintList(list);
+        mMobileType.setImageTintList(list);
+        mMobileRoaming.setImageTintList(list);
+    }
+
+    @Override
+    public boolean isIconVisible() {
+        return mState.visible;
+    }
+
+    @VisibleForTesting
+    public MobileIconState getState() {
+        return mState;
+    }
+
+    @Override
+    public String toString() {
+        return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
new file mode 100644
index 0000000..afd373e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+
+/**
+ * Start small: StatusBarWifiView will be able to layout from a WifiIconState
+ */
+public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements DarkReceiver,
+        StatusIconDisplayable {
+    private static final String TAG = "StatusBarWifiView";
+
+    private ImageView mWifiIcon;
+    private ImageView mIn;
+    private ImageView mOut;
+    private View mInoutContainer;
+    private View mSignalSpacer;
+    private View mAirplaneSpacer;
+    private WifiIconState mState;
+    private String mSlot;
+    private float mDarkIntensity = 0;
+
+    private ContextThemeWrapper mDarkContext;
+    private ContextThemeWrapper mLightContext;
+
+    public static StatusBarWifiView fromContext(Context context) {
+        LayoutInflater inflater = LayoutInflater.from(context);
+        return (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null);
+    }
+
+    public StatusBarWifiView(Context context) {
+        super(context);
+    }
+
+    public StatusBarWifiView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        init();
+    }
+
+    public void setSlot(String slot) {
+        mSlot = slot;
+    }
+
+    @Override
+    public void setStaticDrawableColor(int color) {
+        ColorStateList list = ColorStateList.valueOf(color);
+        mWifiIcon.setImageTintList(list);
+        mIn.setImageTintList(list);
+        mOut.setImageTintList(list);
+    }
+
+    @Override
+    public String getSlot() {
+        return mSlot;
+    }
+
+    @Override
+    public boolean isIconVisible() {
+        return mState != null && mState.visible;
+    }
+
+    private void init() {
+        int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
+        int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
+        mLightContext = new ContextThemeWrapper(mContext, dualToneLightTheme);
+        mDarkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme);
+
+        mWifiIcon = findViewById(R.id.wifi_signal);
+        mIn = findViewById(R.id.wifi_in);
+        mOut = findViewById(R.id.wifi_out);
+        mSignalSpacer = findViewById(R.id.wifi_signal_spacer);
+        mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer);
+        mInoutContainer = findViewById(R.id.inout_container);
+    }
+
+    public void applyWifiState(WifiIconState state) {
+        if (state == null) {
+            setVisibility(View.GONE);
+            mState = null;
+            return;
+        }
+
+        if (mState == null) {
+            mState = state;
+            initViewState();
+        }
+
+        if (!mState.equals(state)) {
+            updateState(state);
+        }
+    }
+
+    private void updateState(WifiIconState state) {
+        if (mState.resId != state.resId && state.resId >= 0) {
+            NeutralGoodDrawable drawable = NeutralGoodDrawable
+                    .create(mLightContext, mDarkContext, state.resId);
+            drawable.setDarkIntensity(mDarkIntensity);
+            mWifiIcon.setImageDrawable(drawable);
+        }
+
+        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility(
+                (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE);
+        mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
+        mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE);
+        if (mState.visible != state.visible) {
+            setVisibility(state.visible ? View.VISIBLE : View.GONE);
+        }
+
+        mState = state;
+    }
+
+    private void initViewState() {
+        if (mState.resId >= 0) {
+            NeutralGoodDrawable drawable = NeutralGoodDrawable.create(
+                    mLightContext, mDarkContext, mState.resId);
+            drawable.setDarkIntensity(mDarkIntensity);
+            mWifiIcon.setImageDrawable(drawable);
+        }
+
+        mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility(
+                (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE);
+        mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
+        mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE);
+        setVisibility(mState.visible ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mDarkIntensity = darkIntensity;
+        Drawable d = mWifiIcon.getDrawable();
+        if (d instanceof NeutralGoodDrawable) {
+            ((NeutralGoodDrawable)d).setDarkIntensity(darkIntensity);
+        }
+        mIn.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint)));
+        mOut.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint)));
+    }
+
+
+    @Override
+    public String toString() {
+        return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
new file mode 100644
index 0000000..ccab0d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+
+public interface StatusIconDisplayable extends DarkReceiver {
+    String getSlot();
+    void setStaticDrawableColor(int color);
+    boolean isIconVisible();
+    default boolean isIconBlocked() {
+        return false;
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f42473d..75b31c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -17,8 +17,6 @@
 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
 import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
 
-import static com.android.systemui.statusbar.phone.StatusBar.reinflateSignalCluster;
-
 import android.annotation.Nullable;
 import android.app.Fragment;
 import android.app.StatusBarManager;
@@ -34,7 +32,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
@@ -63,7 +60,6 @@
     private int mDisabled1;
     private StatusBar mStatusBarComponent;
     private DarkIconManager mDarkIconManager;
-    private SignalClusterView mSignalClusterView;
     private View mOperatorNameFrame;
 
     private SignalCallback mSignalCallback = new SignalCallback() {
@@ -99,9 +95,6 @@
         Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
         mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
         mClockView = mStatusBar.findViewById(R.id.clock);
-        mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster);
-        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
-        // Default to showing until we know otherwise.
         showSystemIconArea(false);
         initEmergencyCryptkeeperText();
         initOperatorName();
@@ -128,7 +121,6 @@
     @Override
     public void onDestroyView() {
         super.onDestroyView();
-        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSignalClusterView);
         Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager);
         if (mNetworkController.hasEmergencyCryptKeeperText()) {
             mNetworkController.removeCallback(mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 3f9ae80..80c4eb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -69,7 +69,7 @@
         mReceivers.remove(object);
     }
 
-    public void applyDark(ImageView object) {
+    public void applyDark(DarkReceiver object) {
         mReceivers.get(object).onDarkChanged(mTintArea, mDarkIntensity, mIconTint);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index edfd02b..48540b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -20,25 +20,33 @@
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.settingslib.Utils;
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarMobileView;
+import com.android.systemui.statusbar.StatusBarWifiView;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.util.leak.LeakDetector;
+import java.util.ArrayList;
 
 public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver {
+    private static final String TAG = "DemoStatusIcons";
+
     private final LinearLayout mStatusIcons;
+    private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
     private final int mIconSize;
 
+    private StatusBarWifiView mWifiView;
     private boolean mDemoMode;
     private int mColor;
 
@@ -56,6 +64,7 @@
     }
 
     public void remove() {
+        mMobileViews.clear();
         ((ViewGroup) getParent()).removeView(this);
     }
 
@@ -66,7 +75,7 @@
 
     private void updateColors() {
         for (int i = 0; i < getChildCount(); i++) {
-            StatusBarIconView child = (StatusBarIconView) getChildAt(i);
+            StatusIconDisplayable child = (StatusIconDisplayable) getChildAt(i);
             child.setStaticDrawableColor(mColor);
         }
     }
@@ -145,6 +154,7 @@
         }
     }
 
+    /// Can only be used to update non-signal related slots
     private void updateSlot(String slot, String iconPkg, int iconId) {
         if (!mDemoMode) return;
         if (iconPkg == null) {
@@ -152,7 +162,11 @@
         }
         int removeIndex = -1;
         for (int i = 0; i < getChildCount(); i++) {
-            StatusBarIconView v = (StatusBarIconView) getChildAt(i);
+            View child = getChildAt(i);
+            if (!(child instanceof StatusBarIconView)) {
+                continue;
+            }
+            StatusBarIconView v = (StatusBarIconView) child;
             if (slot.equals(v.getTag())) {
                 if (iconId == 0) {
                     removeIndex = i;
@@ -182,8 +196,101 @@
         addView(v, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize));
     }
 
+    public void addDemoWifiView(WifiIconState state) {
+        Log.d(TAG, "addDemoWifiView: ");
+        StatusBarWifiView view = StatusBarWifiView.fromContext(mContext);
+        view.setSlot(state.slot);
+
+        int viewIndex = getChildCount();
+        // If we have mobile views, put wifi before them
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (child instanceof StatusBarMobileView) {
+                viewIndex = i;
+                break;
+            }
+        }
+
+        mWifiView = view;
+        mWifiView.applyWifiState(state);
+        mWifiView.setStaticDrawableColor(mColor);
+        addView(view, viewIndex);
+    }
+
+    public void updateWifiState(WifiIconState state) {
+        Log.d(TAG, "updateWifiState: ");
+        if (mWifiView == null) {
+            addDemoWifiView(state);
+        } else {
+            mWifiView.applyWifiState(state);
+        }
+    }
+
+    public void addMobileView(MobileIconState state) {
+        Log.d(TAG, "addMobileView: ");
+        StatusBarMobileView view = StatusBarMobileView.fromContext(mContext);
+
+        view.setSlot(state.slot);
+        view.applyMobileState(state);
+        view.setStaticDrawableColor(mColor);
+
+        // mobile always goes at the end
+        mMobileViews.add(view);
+        addView(view, getChildCount());
+    }
+
+    public void updateMobileState(MobileIconState state) {
+        Log.d(TAG, "updateMobileState: ");
+        // If the view for this subId exists already, use it
+        for (int i = 0; i < mMobileViews.size(); i++) {
+            StatusBarMobileView view = mMobileViews.get(i);
+            if (view.getState().subId == state.subId) {
+                view.applyMobileState(state);
+                return;
+            }
+        }
+
+        // Else we have to add it
+        addMobileView(state);
+    }
+
+    public void onRemoveIcon(StatusIconDisplayable view) {
+        if (view.getSlot().equals("wifi")) {
+            removeView(mWifiView);
+            mWifiView = null;
+        } else {
+            StatusBarMobileView mobileView = matchingMobileView(view);
+            if (mobileView != null) {
+                removeView(mobileView);
+                mMobileViews.remove(mobileView);
+            }
+        }
+    }
+
+    private StatusBarMobileView matchingMobileView(StatusIconDisplayable otherView) {
+        if (!(otherView instanceof StatusBarMobileView)) {
+            return null;
+        }
+
+        StatusBarMobileView v = (StatusBarMobileView) otherView;
+        for (StatusBarMobileView view : mMobileViews) {
+            if (view.getState().subId == v.getState().subId) {
+                return view;
+            }
+        }
+
+        return null;
+    }
+
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint));
+
+        if (mWifiView != null) {
+            mWifiView.onDarkChanged(area, darkIntensity, tint);
+        }
+        for (StatusBarMobileView view : mMobileViews) {
+            view.onDarkChanged(area, darkIntensity, tint);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 994c0ab..b817809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -207,7 +207,6 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         updateLayoutConsideringCutout();
-        setSignalClusterLayoutWidth();
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
@@ -296,17 +295,6 @@
         return true;
     }
 
-    //TODO: Something is setting signal_cluster to MATCH_PARENT. Why?
-    private void setSignalClusterLayoutWidth() {
-        View signalCluster = findViewById(R.id.signal_cluster);
-        if (signalCluster == null) {
-            return;
-        }
-
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) signalCluster.getLayoutParams();
-        lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
-    }
-
     public void setListening(boolean listening) {
         if (listening == mBatteryListening) {
             return;
@@ -459,7 +447,6 @@
         mIconManager.setTint(iconColor);
         Rect tintArea = new Rect(0, 0, 0, 0);
 
-        applyDarkness(R.id.signal_cluster, tintArea, intensity, iconColor);
         applyDarkness(R.id.battery, tintArea, intensity, iconColor);
         applyDarkness(R.id.clock, tintArea, intensity, iconColor);
         // Reload user avatar
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 33c3ee9..3e7b0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -297,7 +297,7 @@
             mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                     mContext.getString(R.string.accessibility_location_active));
         } else {
-            mIconController.removeIcon(mSlotLocation);
+            mIconController.removeAllIconsForSlot(mSlotLocation);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index fb1addf..12bdfc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -32,7 +32,7 @@
     private final PhoneStatusBarView mView;
     private final float mIconAlphaWhenOpaque;
 
-    private View mLeftSide, mStatusIcons, mSignalCluster, mBattery, mClock;
+    private View mLeftSide, mStatusIcons, mBattery, mClock;
     private Animator mCurrentAnimation;
 
     public PhoneStatusBarTransitions(PhoneStatusBarView view) {
@@ -45,7 +45,6 @@
     public void init() {
         mLeftSide = mView.findViewById(R.id.notification_icon_area);
         mStatusIcons = mView.findViewById(R.id.statusIcons);
-        mSignalCluster = mView.findViewById(R.id.signal_cluster);
         mBattery = mView.findViewById(R.id.battery);
         mClock = mView.findViewById(R.id.clock);
         applyModeBackground(-1, getMode(), false /*animate*/);
@@ -90,7 +89,6 @@
             anims.playTogether(
                     animateTransitionTo(mLeftSide, newAlpha),
                     animateTransitionTo(mStatusIcons, newAlpha),
-                    animateTransitionTo(mSignalCluster, newAlpha),
                     animateTransitionTo(mBattery, newAlphaBC),
                     animateTransitionTo(mClock, newAlphaBC)
                     );
@@ -102,7 +100,6 @@
         } else {
             mLeftSide.setAlpha(newAlpha);
             mStatusIcons.setAlpha(newAlpha);
-            mSignalCluster.setAlpha(newAlpha);
             mBattery.setAlpha(newAlphaBC);
             mClock.setAlpha(newAlphaBC);
         }
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 beeba83..750d2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -348,6 +348,7 @@
     protected boolean mBouncerShowing;
 
     private PhoneStatusBarPolicy mIconPolicy;
+    private StatusBarSignalPolicy mSignalPolicy;
 
     private VolumeComponent mVolumeComponent;
     private BrightnessMirrorController mBrightnessMirrorController;
@@ -747,6 +748,7 @@
 
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
+        mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
 
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         mUnlockMethodCache.addListener(this);
@@ -1131,7 +1133,6 @@
     }
 
     protected void reevaluateStyles() {
-        inflateSignalClusters();
         inflateFooterView();
         updateFooter();
         inflateEmptyShadeView();
@@ -1145,36 +1146,6 @@
         }
     }
 
-    private void inflateSignalClusters() {
-        if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar);
-    }
-
-    public static SignalClusterView reinflateSignalCluster(View view) {
-        Context context = view.getContext();
-        SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster);
-        if (signalCluster != null) {
-            ViewParent parent = signalCluster.getParent();
-            if (parent instanceof ViewGroup) {
-                ViewGroup viewParent = (ViewGroup) parent;
-                int index = viewParent.indexOfChild(signalCluster);
-                viewParent.removeView(signalCluster);
-                SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(context)
-                        .inflate(R.layout.signal_cluster_view, viewParent, false);
-                ViewGroup.MarginLayoutParams layoutParams =
-                        (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams();
-                layoutParams.setMarginsRelative(
-                        context.getResources().getDimensionPixelSize(
-                                R.dimen.signal_cluster_margin_start),
-                        0, 0, 0);
-                newCluster.setLayoutParams(layoutParams);
-                viewParent.addView(newCluster, index);
-                return newCluster;
-            }
-            return signalCluster;
-        }
-        return null;
-    }
-
     private void inflateEmptyShadeView() {
         if (mStackScroller == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 956bebb..94e004b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -16,12 +16,16 @@
 
 import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
 import static android.app.StatusBarManager.DISABLE_NONE;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
 
 import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -33,19 +37,39 @@
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarMobileView;
+import com.android.systemui.statusbar.StatusBarWifiView;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.util.Utils.DisableStateTracker;
+import java.util.List;
 
 public interface StatusBarIconController {
 
+    /**
+     * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon
+     * in that slot and not added as a sub slot.
+     */
+    public static final int TAG_PRIMARY = 0;
+
     public void addIconGroup(IconManager iconManager);
     public void removeIconGroup(IconManager iconManager);
     public void setExternalIcon(String slot);
     public void setIcon(String slot, int resourceId, CharSequence contentDescription);
     public void setIcon(String slot, StatusBarIcon icon);
-    public void setIconVisibility(String slotTty, boolean b);
-    public void removeIcon(String slot);
+    public void setSignalIcon(String slot, WifiIconState state);
+    public void setMobileIcons(String slot, List<MobileIconState> states);
+    public void setIconVisibility(String slot, boolean b);
+    /**
+     * If you don't know what to pass for `tag`, either remove all icons for slot, or use
+     * TAG_PRIMARY to refer to the first icon at a given slot.
+     */
+    public void removeIcon(String slot, int tag);
+    public void removeAllIconsForSlot(String slot);
 
     public static final String ICON_BLACKLIST = "icon_blacklist";
 
@@ -79,9 +103,9 @@
 
         @Override
         protected void onIconAdded(int index, String slot, boolean blocked,
-                StatusBarIcon icon) {
-            StatusBarIconView v = addIcon(index, slot, blocked, icon);
-            mDarkIconDispatcher.addDarkReceiver(v);
+                StatusBarIconHolder holder) {
+            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
+            mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
         }
 
         @Override
@@ -95,21 +119,21 @@
         @Override
         protected void destroy() {
             for (int i = 0; i < mGroup.getChildCount(); i++) {
-                mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(i));
+                mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i));
             }
             mGroup.removeAllViews();
         }
 
         @Override
         protected void onRemoveIcon(int viewIndex) {
-            mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(viewIndex));
+            mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex));
             super.onRemoveIcon(viewIndex);
         }
 
         @Override
         public void onSetIcon(int viewIndex, StatusBarIcon icon) {
             super.onSetIcon(viewIndex, icon);
-            mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex));
+            mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
         }
 
         @Override
@@ -135,17 +159,18 @@
         }
 
         @Override
-        protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIcon icon) {
-            StatusBarIconView v = addIcon(index, slot, blocked, icon);
-            v.setStaticDrawableColor(mColor);
+        protected void onIconAdded(int index, String slot, boolean blocked,
+                StatusBarIconHolder holder) {
+            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
+            view.setStaticDrawableColor(mColor);
         }
 
         public void setTint(int color) {
             mColor = color;
             for (int i = 0; i < mGroup.getChildCount(); i++) {
                 View child = mGroup.getChildAt(i);
-                if (child instanceof StatusBarIconView) {
-                    StatusBarIconView icon = (StatusBarIconView) child;
+                if (child instanceof StatusIconDisplayable) {
+                    StatusIconDisplayable icon = (StatusIconDisplayable) child;
                     icon.setStaticDrawableColor(mColor);
                 }
             }
@@ -171,6 +196,7 @@
 
         // Enables SystemUI demo mode to take effect in this group
         protected boolean mDemoable = true;
+        private boolean mIsInDemoMode;
         protected DemoStatusIcons mDemoStatusIcons;
 
         public IconManager(ViewGroup group) {
@@ -205,10 +231,27 @@
         }
 
         protected void onIconAdded(int index, String slot, boolean blocked,
-                StatusBarIcon icon) {
-            addIcon(index, slot, blocked, icon);
+                StatusBarIconHolder holder) {
+            addHolder(index, slot, blocked, holder);
         }
 
+        protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
+                StatusBarIconHolder holder) {
+            switch (holder.getType()) {
+                case TYPE_ICON:
+                    return addIcon(index, slot, blocked, holder.getIcon());
+
+                case TYPE_WIFI:
+                    return addSignalIcon(index, slot, holder.getWifiState());
+
+                case TYPE_MOBILE:
+                    return addMobileIcon(index, slot, holder.getMobileState());
+            }
+
+            return null;
+        }
+
+        @VisibleForTesting
         protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
                 StatusBarIcon icon) {
             StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
@@ -218,10 +261,45 @@
         }
 
         @VisibleForTesting
-        protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
+        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+            StatusBarWifiView view = onCreateStatusBarWifiView(slot);
+            view.applyWifiState(state);
+            mGroup.addView(view, index, onCreateLayoutParams());
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.addDemoWifiView(state);
+            }
+            return view;
+        }
+
+        @VisibleForTesting
+        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
+            StatusBarMobileView view = onCreateStatusBarMobileView(slot);
+            view.applyMobileState(state);
+            mGroup.addView(view, index, onCreateLayoutParams());
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.addMobileView(state);
+            }
+            return view;
+        }
+
+        private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
             return new StatusBarIconView(mContext, slot, null, blocked);
         }
 
+        private StatusBarWifiView onCreateStatusBarWifiView(String slot) {
+            StatusBarWifiView view = StatusBarWifiView.fromContext(mContext);
+            view.setSlot(slot);
+            return view;
+        }
+
+        private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
+            StatusBarMobileView view = StatusBarMobileView.fromContext(mContext);
+            view.setSlot(slot);
+            return view;
+        }
+
         protected LinearLayout.LayoutParams onCreateLayoutParams() {
             return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
         }
@@ -256,6 +334,9 @@
         }
 
         protected void onRemoveIcon(int viewIndex) {
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
+            }
             mGroup.removeViewAt(viewIndex);
         }
 
@@ -264,17 +345,59 @@
             view.set(icon);
         }
 
+        public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
+            switch (holder.getType()) {
+                case TYPE_ICON:
+                    onSetIcon(viewIndex, holder.getIcon());
+                    return;
+                case TYPE_WIFI:
+                    onSetSignalIcon(viewIndex, holder.getWifiState());
+                    return;
+
+                case TYPE_MOBILE:
+                    onSetMobileIcon(viewIndex, holder.getMobileState());
+                default:
+                    break;
+            }
+        }
+
+        public void onSetSignalIcon(int viewIndex, WifiIconState state) {
+            StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex);
+            if (wifiView != null) {
+                wifiView.applyWifiState(state);
+            }
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.updateWifiState(state);
+            }
+        }
+
+        public void onSetMobileIcon(int viewIndex, MobileIconState state) {
+            StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex);
+            if (view != null) {
+                view.applyMobileState(state);
+            }
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.updateMobileState(state);
+            }
+        }
+
         @Override
         public void dispatchDemoCommand(String command, Bundle args) {
             if (!mDemoable) {
                 return;
             }
 
-            if (mDemoStatusIcons != null && command.equals(COMMAND_EXIT)) {
-                mDemoStatusIcons.dispatchDemoCommand(command, args);
-                exitDemoMode();
+            if (command.equals(COMMAND_EXIT)) {
+                if (mDemoStatusIcons != null) {
+                    mDemoStatusIcons.dispatchDemoCommand(command, args);
+                    exitDemoMode();
+                }
+                mIsInDemoMode = false;
             } else {
                 if (mDemoStatusIcons == null) {
+                    mIsInDemoMode = true;
                     mDemoStatusIcons = createDemoStatusIcons();
                 }
                 mDemoStatusIcons.dispatchDemoCommand(command, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 8f5e705..510af03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -16,13 +16,14 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Dependency;
@@ -30,7 +31,9 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.IconLogger;
@@ -40,8 +43,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
-import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.STATUS_BAR_ICON_MANAGER_TAG;
+import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
 
 /**
  * Receives the callbacks from CommandQueue related to icons and tracks the state of
@@ -50,20 +54,25 @@
  */
 public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
         ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
+
     private static final String TAG = "StatusBarIconController";
 
     private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
     private final ArraySet<String> mIconBlacklist = new ArraySet<>();
     private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
 
+    // Points to light or dark context depending on the... context?
     private Context mContext;
-    private DemoStatusIcons mDemoStatusIcons;
-    private IconManager mStatusBarIconManager;
+    private Context mLightContext;
+    private Context mDarkContext;
+
+    private boolean mIsDark = false;
 
     public StatusBarIconControllerImpl(Context context) {
         super(context.getResources().getStringArray(
                 com.android.internal.R.array.config_statusBarIcons));
         Dependency.get(ConfigurationController.class).addCallback(this);
+
         mContext = context;
 
         loadDimens();
@@ -76,12 +85,16 @@
     @Override
     public void addIconGroup(IconManager group) {
         mIconGroups.add(group);
-        for (int i = 0; i < mIcons.size(); i++) {
-            StatusBarIcon icon = mIcons.get(i);
-            if (icon != null) {
-                String slot = mSlots.get(i);
-                boolean blocked = mIconBlacklist.contains(slot);
-                group.onIconAdded(getViewIndex(getSlotIndex(slot)), slot, blocked, icon);
+        List<Slot> allSlots = getSlots();
+        for (int i = 0; i < allSlots.size(); i++) {
+            Slot slot = allSlots.get(i);
+            List<StatusBarIconHolder> holders = slot.getHolderList();
+            boolean blocked = mIconBlacklist.contains(slot.getName());
+
+            for (StatusBarIconHolder holder : holders) {
+                int tag = holder.getTag();
+                int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
+                group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
             }
         }
     }
@@ -99,104 +112,209 @@
         }
         mIconBlacklist.clear();
         mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue));
-        ArrayList<StatusBarIcon> current = new ArrayList<>(mIcons);
-        ArrayList<String> currentSlots = new ArrayList<>(mSlots);
+        ArrayList<Slot> currentSlots = getSlots();
+        ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
+
+        // This is a little hacky... Peel off all of the holders on all of the slots
+        // but keep them around so they can be re-added
+
         // Remove all the icons.
-        for (int i = current.size() - 1; i >= 0; i--) {
-            removeIcon(currentSlots.get(i));
+        for (int i = currentSlots.size() - 1; i >= 0; i--) {
+            Slot s = currentSlots.get(i);
+            slotsToReAdd.put(s, s.getHolderList());
+            removeAllIconsForSlot(s.getName());
         }
+
         // Add them all back
-        for (int i = 0; i < current.size(); i++) {
-            setIcon(currentSlots.get(i), current.get(i));
+        for (int i = 0; i < currentSlots.size(); i++) {
+            Slot item = currentSlots.get(i);
+            List<StatusBarIconHolder> iconsForSlot = slotsToReAdd.get(item);
+            if (iconsForSlot == null) continue;
+            for (StatusBarIconHolder holder : iconsForSlot) {
+                setIcon(getSlotIndex(item.getName()), holder);
+            }
         }
     }
 
     private void loadDimens() {
     }
 
-    private void addSystemIcon(int index, StatusBarIcon icon) {
-        String slot = getSlot(index);
-        int viewIndex = getViewIndex(index);
+    private void addSystemIcon(int index, StatusBarIconHolder holder) {
+        String slot = getSlotName(index);
+        int viewIndex = getViewIndex(index, holder.getTag());
         boolean blocked = mIconBlacklist.contains(slot);
 
-        mIconLogger.onIconVisibility(getSlot(index), icon.visible);
-        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon));
+        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
+        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
     }
 
     @Override
     public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
         int index = getSlotIndex(slot);
-        StatusBarIcon icon = getIcon(index);
-        if (icon == null) {
-            icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
-                    Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);
-            setIcon(slot, icon);
+        StatusBarIconHolder holder = getIcon(index, 0);
+        if (holder == null) {
+            StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
+                    Icon.createWithResource(
+                            mContext, resourceId), 0, 0, contentDescription);
+            holder = StatusBarIconHolder.fromIcon(icon);
+            setIcon(index, holder);
         } else {
-            icon.icon = Icon.createWithResource(mContext, resourceId);
-            icon.contentDescription = contentDescription;
-            handleSet(index, icon);
+            holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
+            holder.getIcon().contentDescription = contentDescription;
+            handleSet(index, holder);
+        }
+    }
+
+    /**
+     * Signal icons need to be handled differently, because they can be
+     * composite views
+     */
+    @Override
+    public void setSignalIcon(String slot, WifiIconState state) {
+
+        int index = getSlotIndex(slot);
+
+        if (state == null) {
+            removeIcon(index, 0);
+            return;
+        }
+
+        StatusBarIconHolder holder = getIcon(index, 0);
+        if (holder == null) {
+            holder = StatusBarIconHolder.fromWifiIconState(state);
+            setIcon(index, holder);
+        } else {
+            holder.setWifiState(state);
+            handleSet(index, holder);
+        }
+    }
+
+    /**
+     * Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted
+     * by subId. Don't worry this definitely makes sense and works.
+     * @param slot da slot
+     * @param iconStates All of the mobile icon states
+     */
+    @Override
+    public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
+        Slot mobileSlot = getSlot(slot);
+        int slotIndex = getSlotIndex(slot);
+
+        for (MobileIconState state : iconStates) {
+            StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
+            if (holder == null) {
+                holder = StatusBarIconHolder.fromMobileIconState(state);
+                setIcon(slotIndex, holder);
+            } else {
+                holder.setMobileState(state);
+                handleSet(slotIndex, holder);
+            }
         }
     }
 
     @Override
     public void setExternalIcon(String slot) {
-        int viewIndex = getViewIndex(getSlotIndex(slot));
+        int viewIndex = getViewIndex(getSlotIndex(slot), 0);
         int height = mContext.getResources().getDimensionPixelSize(
                 R.dimen.status_bar_icon_drawing_size);
         mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
     }
 
+    //TODO: remove this (used in command queue and for 3rd party tiles?)
     @Override
     public void setIcon(String slot, StatusBarIcon icon) {
         setIcon(getSlotIndex(slot), icon);
     }
 
+    /**
+     * For backwards compatibility, in the event that someone gives us a slot and a status bar icon
+     */
+    private void setIcon(int index, StatusBarIcon icon) {
+        if (icon == null) {
+            removeAllIconsForSlot(getSlotName(index));
+            return;
+        }
+
+        StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
+        setIcon(index, holder);
+    }
+
     @Override
-    public void removeIcon(String slot) {
-        int index = getSlotIndex(slot);
-        removeIcon(index);
+    public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
+        boolean isNew = getIcon(index, holder.getTag()) == null;
+        super.setIcon(index, holder);
+
+        if (isNew) {
+            addSystemIcon(index, holder);
+        } else {
+            handleSet(index, holder);
+        }
     }
 
     public void setIconVisibility(String slot, boolean visibility) {
         int index = getSlotIndex(slot);
-        StatusBarIcon icon = getIcon(index);
-        if (icon == null || icon.visible == visibility) {
+        StatusBarIconHolder holder = getIcon(index, 0);
+        if (holder == null || holder.isVisible() == visibility) {
             return;
         }
-        icon.visible = visibility;
-        handleSet(index, icon);
+
+        holder.setVisible(visibility);
+        handleSet(index, holder);
+    }
+
+    public void removeIcon(String slot) {
+        removeAllIconsForSlot(slot);
     }
 
     @Override
-    public void removeIcon(int index) {
-        if (getIcon(index) == null) {
+    public void removeIcon(String slot, int tag) {
+        removeIcon(getSlotIndex(slot), tag);
+    }
+
+    @Override
+    public void removeAllIconsForSlot(String slotName) {
+        Slot slot = getSlot(slotName);
+        if (!slot.hasIconsInSlot()) {
             return;
         }
-        mIconLogger.onIconHidden(getSlot(index));
-        super.removeIcon(index);
-        int viewIndex = getViewIndex(index);
+
+        mIconLogger.onIconHidden(slotName);
+
+        int slotIndex = getSlotIndex(slotName);
+        List<StatusBarIconHolder> iconsToRemove = slot.getHolderList();
+        for (StatusBarIconHolder holder : iconsToRemove) {
+            int viewIndex = getViewIndex(slotIndex, holder.getTag());
+            slot.removeForTag(holder.getTag());
+            mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
+        }
+    }
+
+    @Override
+    public void removeIcon(int index, int tag) {
+        if (getIcon(index, tag) == null) {
+            return;
+        }
+        mIconLogger.onIconHidden(getSlotName(index));
+        super.removeIcon(index, tag);
+        int viewIndex = getViewIndex(index, 0);
         mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
     }
 
-    @Override
-    public void setIcon(int index, StatusBarIcon icon) {
-        if (icon == null) {
-            removeIcon(index);
-            return;
-        }
-        boolean isNew = getIcon(index) == null;
-        super.setIcon(index, icon);
-        if (isNew) {
-            addSystemIcon(index, icon);
-        } else {
-            handleSet(index, icon);
-        }
+    private void handleSet(int index, StatusBarIconHolder holder) {
+        int viewIndex = getViewIndex(index, holder.getTag());
+        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
+        mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
     }
 
-    private void handleSet(int index, StatusBarIcon icon) {
-        int viewIndex = getViewIndex(index);
-        mIconLogger.onIconVisibility(getSlot(index), icon.visible);
-        mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));
+    /**
+     * For mobile essentially (an array of holders in one slot)
+     */
+    private void handleSet(int slotIndex, List<StatusBarIconHolder> holders) {
+        for (StatusBarIconHolder holder : holders) {
+            int viewIndex = getViewIndex(slotIndex, holder.getTag());
+            mIconLogger.onIconVisibility(getSlotName(slotIndex), holder.isVisible());
+            mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
+        }
     }
 
     @Override
@@ -208,7 +326,7 @@
                 int N = group.getChildCount();
                 pw.println("  icon views: " + N);
                 for (int i = 0; i < N; i++) {
-                    StatusBarIconView ic = (StatusBarIconView) group.getChildAt(i);
+                    StatusIconDisplayable ic = (StatusIconDisplayable) group.getChildAt(i);
                     pw.println("    [" + i + "] icon=" + ic);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
new file mode 100644
index 0000000..e854dd0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -0,0 +1,131 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+
+/**
+ * Wraps {@link com.android.internal.statusbar.StatusBarIcon} so we can still have a uniform list
+ */
+public class StatusBarIconHolder {
+    public static final int TYPE_ICON = 0;
+    public static final int TYPE_WIFI = 1;
+    public static final int TYPE_MOBILE = 2;
+
+    private StatusBarIcon mIcon;
+    private WifiIconState mWifiState;
+    private MobileIconState mMobileState;
+    private int mType = TYPE_ICON;
+    private int mTag = 0;
+    private boolean mVisible = true;
+
+    public static StatusBarIconHolder fromIcon(StatusBarIcon icon) {
+        StatusBarIconHolder wrapper = new StatusBarIconHolder();
+        wrapper.mIcon = icon;
+
+        return wrapper;
+    }
+
+    public static StatusBarIconHolder fromResId(Context context, int resId,
+            CharSequence contentDescription) {
+        StatusBarIconHolder holder = new StatusBarIconHolder();
+        holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
+                Icon.createWithResource( context, resId), 0, 0, contentDescription);
+        return holder;
+    }
+
+    public static StatusBarIconHolder fromWifiIconState(WifiIconState state) {
+        StatusBarIconHolder holder = new StatusBarIconHolder();
+        holder.mWifiState = state;
+        holder.mType = TYPE_WIFI;
+        return holder;
+    }
+
+    public static StatusBarIconHolder fromMobileIconState(MobileIconState state) {
+        StatusBarIconHolder holder = new StatusBarIconHolder();
+        holder.mMobileState = state;
+        holder.mType = TYPE_MOBILE;
+        holder.mTag = state.subId;
+        return holder;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    @Nullable
+    public StatusBarIcon getIcon() {
+        return mIcon;
+    }
+
+    @Nullable
+    public WifiIconState getWifiState() {
+        return mWifiState;
+    }
+
+    public void setWifiState(WifiIconState state) {
+        mWifiState = state;
+    }
+
+    @Nullable
+    public MobileIconState getMobileState() {
+        return mMobileState;
+    }
+
+    public void setMobileState(MobileIconState state) {
+        mMobileState = state;
+    }
+
+    public boolean isVisible() {
+        switch (mType) {
+            case TYPE_ICON:
+                return mIcon.visible;
+            case TYPE_WIFI:
+                return mWifiState.visible;
+            case TYPE_MOBILE:
+                return mMobileState.visible;
+
+            default: return true;
+        }
+    }
+
+    public void setVisible(boolean visible) {
+        switch (mType) {
+            case TYPE_ICON:
+                mIcon.visible = visible;
+                break;
+
+            case TYPE_WIFI:
+                mWifiState.visible = visible;
+                break;
+
+            case TYPE_MOBILE:
+                mMobileState.visible = visible;
+                break;
+        }
+    }
+
+    public int getTag() {
+        return mTag;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index 1aa3a43..c773170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -16,64 +16,78 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
 
 public class StatusBarIconList {
-    protected ArrayList<String> mSlots = new ArrayList<>();
-    protected ArrayList<StatusBarIcon> mIcons = new ArrayList<>();
+    private ArrayList<Slot> mSlots = new ArrayList<>();
 
     public StatusBarIconList(String[] slots) {
         final int N = slots.length;
         for (int i=0; i < N; i++) {
-            mSlots.add(slots[i]);
-            mIcons.add(null);
+            mSlots.add(new Slot(slots[i], null));
         }
     }
 
     public int getSlotIndex(String slot) {
         final int N = mSlots.size();
         for (int i=0; i<N; i++) {
-            if (slot.equals(mSlots.get(i))) {
+            Slot item = mSlots.get(i);
+            if (item.getName().equals(slot)) {
                 return i;
             }
         }
         // Auto insert new items at the beginning.
-        mSlots.add(0, slot);
-        mIcons.add(0, null);
+        mSlots.add(0, new Slot(slot, null));
         return 0;
     }
 
+    protected ArrayList<Slot> getSlots() {
+        return new ArrayList<>(mSlots);
+    }
+
+    protected Slot getSlot(String name) {
+        return mSlots.get(getSlotIndex(name));
+    }
+
     public int size() {
         return mSlots.size();
     }
 
-    public void setIcon(int index, StatusBarIcon icon) {
-        mIcons.set(index, icon);
+    public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
+        mSlots.get(index).addHolder(holder);
     }
 
-    public void removeIcon(int index) {
-        mIcons.set(index, null);
+    public void removeIcon(int index, int tag) {
+        mSlots.get(index).removeForTag(tag);
     }
 
-    public String getSlot(int index) {
-        return mSlots.get(index);
+    public String getSlotName(int index) {
+        return mSlots.get(index).getName();
     }
 
-    public StatusBarIcon getIcon(int index) {
-        return mIcons.get(index);
+    public StatusBarIconHolder getIcon(int index, int tag) {
+        return mSlots.get(index).getHolderForTag(tag);
     }
 
-    public int getViewIndex(int index) {
+    public int getViewIndex(int slotIndex, int tag) {
         int count = 0;
-        for (int i = 0; i < index; i++) {
-            if (mIcons.get(i) != null) {
-                count++;
+        for (int i = 0; i < slotIndex; i++) {
+            Slot item = mSlots.get(i);
+            if (item.hasIconsInSlot()) {
+                count += item.numberOfIcons();
             }
         }
-        return count;
+
+        Slot viewItem = mSlots.get(slotIndex);
+        return count + viewItem.viewIndexOffsetForTag(tag);
     }
 
     public void dump(PrintWriter pw) {
@@ -81,7 +95,163 @@
         final int N = mSlots.size();
         pw.println("  icon slots: " + N);
         for (int i=0; i<N; i++) {
-            pw.printf("    %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i));
+            pw.printf("    %2d:%s\n", i, mSlots.get(i).toString());
+        }
+    }
+
+    public static class Slot {
+        private final String mName;
+        private StatusBarIconHolder mHolder;
+        /**
+         * Only used if multiple icons are added to the same slot.
+         *
+         * If there are mSubSlots, then these are structured like:
+         *      [ First item | (the rest) ]
+         *
+         * The tricky thing to keep in mind here is that the list [mHolder, mSubSlots] is ordered
+         * ascending, but for view logic we should go backwards through the list. I.e., the first
+         * element (mHolder) should be the highest index, because higher priority items go to the
+         * right of lower priority items
+         */
+        private ArrayList<StatusBarIconHolder> mSubSlots;
+
+        public Slot(String name, StatusBarIconHolder iconHolder) {
+            mName = name;
+            mHolder = iconHolder;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        @Nullable
+        public StatusBarIconHolder getHolderForTag(int tag) {
+            if (tag == TAG_PRIMARY) {
+                return mHolder;
+            }
+
+            if (mSubSlots != null) {
+                for (StatusBarIconHolder holder : mSubSlots) {
+                    if (holder.getTag() == tag) {
+                        return holder;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        public void addHolder(StatusBarIconHolder holder) {
+            int tag = holder.getTag();
+            if (tag == TAG_PRIMARY) {
+                mHolder = holder;
+            } else {
+                setSubSlot(holder, tag);
+            }
+        }
+
+        public void removeForTag(int tag) {
+            if (tag == TAG_PRIMARY) {
+                mHolder = null;
+            } else {
+                int index = getIndexForTag(tag);
+                if (index != -1) {
+                    mSubSlots.remove(index);
+                }
+            }
+        }
+
+        @VisibleForTesting
+        public void clear() {
+            mHolder = null;
+            if (mSubSlots != null) {
+                mSubSlots = null;
+            }
+        }
+
+        private void setSubSlot(StatusBarIconHolder holder, int tag) {
+            if (mSubSlots == null) {
+                mSubSlots = new ArrayList<>();
+                mSubSlots.add(holder);
+                return;
+            }
+
+            if (getIndexForTag(tag) != -1) {
+                // Holder exists for tag; no-op
+                return;
+            }
+
+            // These holders get added to the end. Confused yet?
+            mSubSlots.add(holder);
+        }
+
+        private int getIndexForTag(int tag) {
+            for (int i = 0; i < mSubSlots.size(); i++) {
+                StatusBarIconHolder h = mSubSlots.get(i);
+                if (h.getTag() == tag) {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        public boolean hasIconsInSlot() {
+            if (mHolder != null) return true;
+            if (mSubSlots == null) return false;
+
+            return mSubSlots.size() > 0;
+        }
+
+        public int numberOfIcons() {
+            int num = mHolder == null ? 0 : 1;
+            if (mSubSlots == null) return num;
+
+            return num + mSubSlots.size();
+        }
+
+        /**
+         * View index is backwards from regular index
+         * @param tag the tag of the holder being viewed
+         * @return (1 + mSubSlots.size() - indexOfTag)
+         */
+        public int viewIndexOffsetForTag(int tag) {
+            if (mSubSlots == null) {
+                return 0;
+            }
+
+            int subSlots = mSubSlots.size();
+            if (tag == TAG_PRIMARY) {
+                return subSlots;
+            }
+
+            return subSlots - getIndexForTag(tag) - 1;
+        }
+
+        public List<StatusBarIconHolder> getHolderList() {
+            ArrayList<StatusBarIconHolder> holders = new ArrayList<>();
+            if (mHolder != null) {
+                holders.add(mHolder);
+            }
+
+            if (mSubSlots != null) {
+                holders.addAll(mSubSlots);
+            }
+
+            return holders;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%s) %s", mName, subSlotsString());
+        }
+
+        private String subSlotsString() {
+            if (mSubSlots == null) {
+                return "";
+            }
+
+            return "" + mSubSlots.size() + " subSlots";
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
new file mode 100644
index 0000000..c5a3a0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.SubscriptionInfo;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.tuner.TunerService.Tunable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
+        SecurityController.SecurityControllerCallback, Tunable {
+    private static final String TAG = "StatusBarSignalPolicy";
+
+    private final String mSlotAirplane;
+    private final String mSlotMobile;
+    private final String mSlotWifi;
+    private final String mSlotEthernet;
+    private final String mSlotVpn;
+
+    private final Context mContext;
+    private final StatusBarIconController mIconController;
+    private final NetworkController mNetworkController;
+    private final SecurityController mSecurityController;
+    private final Handler mHandler = Handler.getMain();
+
+    private boolean mBlockAirplane;
+    private boolean mBlockMobile;
+    private boolean mBlockWifi;
+    private boolean mBlockEthernet;
+    private boolean mActivityEnabled;
+    private boolean mForceBlockWifi;
+
+    // Track as little state as possible, and only for padding purposes
+    private boolean mIsAirplaneMode = false;
+    private boolean mWifiVisible = false;
+
+    private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>();
+    private WifiIconState mWifiIconState = new WifiIconState();
+
+    public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
+        mContext = context;
+
+        mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
+        mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
+        mSlotWifi     = mContext.getString(com.android.internal.R.string.status_bar_wifi);
+        mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
+        mSlotVpn      = mContext.getString(com.android.internal.R.string.status_bar_vpn);
+
+        mIconController = iconController;
+        mNetworkController = Dependency.get(NetworkController.class);
+        mSecurityController = Dependency.get(SecurityController.class);
+
+        mNetworkController.addCallback(this);
+        mSecurityController.addCallback(this);
+    }
+
+    public void destroy() {
+        mNetworkController.removeCallback(this);
+        mSecurityController.removeCallback(this);
+    }
+
+    private void updateVpn() {
+        boolean vpnVisible = mSecurityController.isVpnEnabled();
+        int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
+
+        mIconController.setIcon(mSlotVpn, vpnIconId, null);
+        mIconController.setIconVisibility(mSlotVpn, vpnVisible);
+    }
+
+    private int currentVpnIconId(boolean isBranded) {
+        return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
+    }
+
+    private void updateActivityEnabled() {
+        mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
+    }
+
+    /**
+     * From SecurityController
+     */
+    @Override
+    public void onStateChanged() {
+        mHandler.post(this::updateVpn);
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+            return;
+        }
+        ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
+        boolean blockAirplane = blockList.contains(mSlotAirplane);
+        boolean blockMobile = blockList.contains(mSlotMobile);
+        boolean blockWifi = blockList.contains(mSlotWifi);
+        boolean blockEthernet = blockList.contains(mSlotEthernet);
+
+        if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
+                || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
+            mBlockAirplane = blockAirplane;
+            mBlockMobile = blockMobile;
+            mBlockEthernet = blockEthernet;
+            mBlockWifi = blockWifi || mForceBlockWifi;
+            // Re-register to get new callbacks.
+            mNetworkController.removeCallback(this);
+        }
+    }
+
+    @Override
+    public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
+            boolean activityIn, boolean activityOut, String description, boolean isTransient,
+            String statusLabel) {
+
+        boolean visible = statusIcon.visible && !mBlockWifi;
+        boolean in = activityIn && mActivityEnabled && visible;
+        boolean out = activityOut && mActivityEnabled && visible;
+
+        mWifiIconState.visible = visible;
+        mWifiIconState.resId = statusIcon.icon;
+        mWifiIconState.activityIn = in;
+        mWifiIconState.activityOut = out;
+        mWifiIconState.slot = mSlotWifi;
+        mWifiIconState.airplaneSpacerVisible = mIsAirplaneMode;
+        mWifiIconState.contentDescription = statusIcon.contentDescription;
+
+        if (mWifiIconState.visible && mWifiIconState.resId > 0) {
+            mIconController.setSignalIcon(mSlotWifi, mWifiIconState.copy());
+            mIconController.setIconVisibility(mSlotWifi, true);
+        } else {
+            mIconController.setIconVisibility(mSlotWifi, false);
+        }
+    }
+
+    @Override
+    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
+            String description, boolean isWide, int subId, boolean roaming) {
+        MobileIconState state = getState(subId);
+        if (state == null) {
+            return;
+        }
+
+        state.visible = statusIcon.visible && !mBlockMobile;
+        state.strengthId = statusIcon.icon;
+        state.typeId = statusType;
+        state.contentDescription = statusIcon.contentDescription;
+        state.typeContentDescription = typeContentDescription;
+        state.roaming = roaming;
+        state.activityIn = activityIn && mActivityEnabled;
+        state.activityOut = activityOut && mActivityEnabled;
+
+        // Always send a copy to maintain value type semantics
+        mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
+    }
+
+    private MobileIconState getState(int subId) {
+        for (MobileIconState state : mMobileStates) {
+            if (state.subId == subId) {
+                return state;
+            }
+        }
+        Log.e(TAG, "Unexpected subscription " + subId);
+        return null;
+    }
+
+
+    /**
+     * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators
+     * so we don't have to update the icon manager at this point, just remove the old ones
+     * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8)
+     */
+    @Override
+    public void setSubs(List<SubscriptionInfo> subs) {
+        if (hasCorrectSubs(subs)) {
+            return;
+        }
+
+        mIconController.removeAllIconsForSlot(mSlotMobile);
+        mMobileStates.clear();
+        final int n = subs.size();
+        for (int i = 0; i < n; i++) {
+            mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
+        }
+    }
+
+    private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
+        final int N = subs.size();
+        if (N != mMobileStates.size()) {
+            return false;
+        }
+        for (int i = 0; i < N; i++) {
+            if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void setNoSims(boolean show, boolean simDetected) {
+        // Noop yay!
+    }
+
+
+    @Override
+    public void setEthernetIndicators(IconState state) {
+        boolean visible = state.visible && !mBlockEthernet;
+        int resId = state.icon;
+        String description = state.contentDescription;
+
+        if (resId > 0) {
+            mIconController.setIcon(mSlotEthernet, resId, description);
+            mIconController.setIconVisibility(mSlotEthernet, true);
+        } else {
+            mIconController.setIconVisibility(mSlotEthernet, false);
+        }
+    }
+
+    @Override
+    public void setIsAirplaneMode(IconState icon) {
+        mIsAirplaneMode = icon.visible && !mBlockAirplane;
+        int resId = icon.icon;
+        String description = icon.contentDescription;
+
+        if (mIsAirplaneMode && resId > 0) {
+            mIconController.setIcon(mSlotAirplane, resId, description);
+            mIconController.setIconVisibility(mSlotAirplane, true);
+        } else {
+            mIconController.setIconVisibility(mSlotAirplane, false);
+        }
+    }
+
+    @Override
+    public void setMobileDataEnabled(boolean enabled) {
+        // Don't care.
+    }
+
+    private static abstract class SignalIconState {
+        public boolean visible;
+        public boolean activityOut;
+        public boolean activityIn;
+        public String slot;
+        public String contentDescription;
+
+        @Override
+        public boolean equals(Object o) {
+            // Skipping reference equality bc this should be more of a value type
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            SignalIconState that = (SignalIconState) o;
+            return visible == that.visible &&
+                    activityOut == that.activityOut &&
+                    activityIn == that.activityIn &&
+                    Objects.equals(contentDescription, that.contentDescription) &&
+                    Objects.equals(slot, that.slot);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(visible, activityOut, slot);
+        }
+
+        protected void copyTo(SignalIconState other) {
+            other.visible = visible;
+            other.activityIn = activityIn;
+            other.activityOut = activityOut;
+            other.slot = slot;
+            other.contentDescription = contentDescription;
+        }
+    }
+
+    public static class WifiIconState extends SignalIconState{
+        public int resId;
+        public boolean airplaneSpacerVisible;
+        public boolean signalSpacerVisible;
+
+        @Override
+        public boolean equals(Object o) {
+            // Skipping reference equality bc this should be more of a value type
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            if (!super.equals(o)) {
+                return false;
+            }
+            WifiIconState that = (WifiIconState) o;
+            return resId == that.resId &&
+                    airplaneSpacerVisible == that.airplaneSpacerVisible &&
+                    signalSpacerVisible == that.signalSpacerVisible;
+        }
+
+        public void copyTo(WifiIconState other) {
+            super.copyTo(other);
+            other.resId = resId;
+            other.airplaneSpacerVisible = airplaneSpacerVisible;
+            other.signalSpacerVisible = signalSpacerVisible;
+        }
+
+        public WifiIconState copy() {
+            WifiIconState newState = new WifiIconState();
+            copyTo(newState);
+            return newState;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(),
+                    resId, airplaneSpacerVisible, signalSpacerVisible);
+        }
+
+        @Override public String toString() {
+            return "WifiIconState(resId=" + resId + ", visible=" + visible + ")";
+        }
+    }
+
+    /**
+     * A little different. This one delegates to SignalDrawable instead of a specific resId
+     */
+    public static class MobileIconState extends SignalIconState {
+        public int subId;
+        public int strengthId;
+        public int typeId;
+        public boolean roaming;
+        public boolean needsLeadingPadding;
+        public String typeContentDescription;
+
+        private MobileIconState(int subId) {
+            super();
+            this.subId = subId;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            if (!super.equals(o)) {
+                return false;
+            }
+            MobileIconState that = (MobileIconState) o;
+            return subId == that.subId &&
+                    strengthId == that.strengthId &&
+                    typeId == that.typeId &&
+                    roaming == that.roaming &&
+                    needsLeadingPadding == that.needsLeadingPadding &&
+                    Objects.equals(typeContentDescription, that.typeContentDescription);
+        }
+
+        @Override
+        public int hashCode() {
+
+            return Objects
+                    .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
+                            typeContentDescription);
+        }
+
+        public void copyTo(MobileIconState other) {
+            super.copyTo(other);
+            other.subId = subId;
+            other.strengthId = strengthId;
+            other.typeId = typeId;
+            other.roaming = roaming;
+            other.needsLeadingPadding = needsLeadingPadding;
+            other.typeContentDescription = typeContentDescription;
+        }
+
+        private static List<MobileIconState> copyStates(List<MobileIconState> inStates) {
+            ArrayList<MobileIconState> outStates = new ArrayList<>();
+            for (MobileIconState state : inStates) {
+                MobileIconState copy = new MobileIconState(state.subId);
+                state.copyTo(copy);
+                outStates.add(copy);
+            }
+
+            return outStates;
+        }
+
+        @Override public String toString() {
+            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
+                    + roaming + ", visible=" + visible + ")";
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index dab28d6..255e10e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -18,13 +18,13 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.Log;
 
 import android.view.View;
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.stack.ViewState;
 
 /**
@@ -97,7 +97,7 @@
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
-        float width = getWidth();
+        float width = getWidth() - getPaddingEnd();
         float translationX = width;
         float contentStart = getPaddingStart();
         int childCount = getChildCount();
@@ -109,18 +109,22 @@
         //TODO: Dots
         for (int i = childCount - 1; i >= 0; i--) {
             View child = getChildAt(i);
-            if (!(child instanceof StatusBarIconView)) {
+            if (!(child instanceof StatusIconDisplayable)) {
+                if (DEBUG) Log.d(TAG, "skipping child (wrong type)");
                 continue;
             }
 
+            StatusIconDisplayable iconView = (StatusIconDisplayable) child;
+
             ViewState childState = getViewStateFromChild(child);
             if (childState == null ) {
+                if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") no ViewState");
                 continue;
             }
 
-            // Rely on StatusBarIcon for truth about visibility
-            if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+            if (!iconView.isIconVisible() || iconView.isIconBlocked()) {
                 childState.hidden = true;
+                if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
                 continue;
             }
 
@@ -175,8 +179,8 @@
 
             vs.initFrom(child);
             vs.alpha = 1.0f;
-            if (child instanceof StatusBarIconView) {
-                vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+            if (child instanceof StatusIconDisplayable) {
+                vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
             } else {
                 vs.hidden = false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
index 58944c6..945ed76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
@@ -35,7 +35,7 @@
 
     // Used to reapply darkness on an object, must have previously been added through
     // addDarkReceiver.
-    void applyDark(ImageView object);
+    void applyDark(DarkReceiver object);
 
     int DEFAULT_ICON_TINT = Color.WHITE;
     Rect sTmpRect = new Rect();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 4533aa5..4c100cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -54,6 +54,7 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 
+import com.android.systemui.statusbar.policy.MobileSignalController.MobileState;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -848,6 +849,11 @@
                         subs.add(addSignalController(i, i));
                     }
                     mCallbackHandler.setSubs(subs);
+                    for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                        int key = mMobileSignalControllers.keyAt(i);
+                        MobileSignalController controller = mMobileSignalControllers.get(key);
+                        controller.notifyListeners();
+                    }
                 }
             }
             String nosim = args.getString("nosim");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
index 2792d8c..07ac11b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
@@ -1,17 +1,23 @@
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.StatusBarIconHolder;
 import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarIconList.Slot;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,45 +47,125 @@
     @Test
     public void testAddSlotSlidesIcons() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIcon = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(0, sbIcon);
+        StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(0, sbHolder);
         statusBarIconList.getSlotIndex("zzz"); // new content added in front
-        assertNull(statusBarIconList.getIcon(0));
-        assertEquals(sbIcon, statusBarIconList.getIcon(1));
+        assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY));
+        assertEquals(sbHolder, statusBarIconList.getIcon(1, TAG_PRIMARY));
     }
 
     @Test
     public void testGetAndSetIcon() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIconA = mock(StatusBarIcon.class);
-        StatusBarIcon sbIconB = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(0, sbIconA);
-        statusBarIconList.setIcon(1, sbIconB);
-        assertEquals(sbIconA, statusBarIconList.getIcon(0));
-        assertEquals(sbIconB, statusBarIconList.getIcon(1));
-        assertNull(statusBarIconList.getIcon(2)); // icon not set
+        StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(0, sbHolderA);
+        statusBarIconList.setIcon(1, sbHolderB);
+        assertEquals(sbHolderA, statusBarIconList.getIcon(0, TAG_PRIMARY));
+        assertEquals(sbHolderB, statusBarIconList.getIcon(1, TAG_PRIMARY));
+        assertNull(statusBarIconList.getIcon(2, TAG_PRIMARY)); // icon not set
     }
 
     @Test
     public void testRemoveIcon() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIconA = mock(StatusBarIcon.class);
-        StatusBarIcon sbIconB = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(0, sbIconA);
-        statusBarIconList.setIcon(1, sbIconB);
-        statusBarIconList.removeIcon(0);
-        assertNull(statusBarIconList.getIcon(0)); // icon not set
+        StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(0, sbHolderA);
+        statusBarIconList.setIcon(1, sbHolderB);
+        statusBarIconList.removeIcon(0, TAG_PRIMARY);
+        assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); // icon not set
     }
 
     @Test
-    public void testGetViewIndex() {
+    public void testGetViewIndex_NoMultiples() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIcon = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(2, sbIcon);
-        assertEquals(0, statusBarIconList.getViewIndex(2)); // Icon for item 2 is 0th child view.
-        statusBarIconList.setIcon(0, sbIcon);
-        assertEquals(0, statusBarIconList.getViewIndex(0)); // Icon for item 0 is 0th child view,
-        assertEquals(1, statusBarIconList.getViewIndex(2)); // and item 2 is now 1st child view.
+        StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(2, sbHolder);
+        // Icon for item 2 is 0th child view.
+        assertEquals(0, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+        statusBarIconList.setIcon(0, sbHolder);
+        // Icon for item 0 is 0th child view,
+        assertEquals(0, statusBarIconList.getViewIndex(0, TAG_PRIMARY));
+        // and item 2 is now 1st child view.
+        assertEquals(1, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
     }
 
+    @Test
+    public void testGetViewIndex_MultipleIconsPerSlot() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
+
+        statusBarIconList.setIcon(2, sbHolder); // item 2, one icon 0th child
+
+        // All of these can be added to the same slot
+        // no tag bc it defaults to 0
+        StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class);
+        int sb3Tag = 1;
+        when(sbHolder3.getTag()).thenReturn(sb3Tag);
+        StatusBarIconHolder sbHolder4 = mock(StatusBarIconHolder.class);
+        int sb4Tag = 2;
+        when(sbHolder4.getTag()).thenReturn(sb4Tag);
+
+        // Put a holder at slot 1, verify that it is first
+        statusBarIconList.setIcon(1, sbHolder2);
+        assertEquals(0, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
+
+        // Put another holder at slot 1, verify it's index 0 and the rest come after
+        statusBarIconList.setIcon(1, sbHolder3);
+        assertEquals(0, statusBarIconList.getViewIndex(1, sb3Tag));
+        assertEquals(1, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
+        // First icon should be at the end
+        assertEquals(2, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+
+        // Put another one in there just for good measure
+        statusBarIconList.setIcon(1, sbHolder4);
+        assertEquals(0, statusBarIconList.getViewIndex(1, sb4Tag));
+        assertEquals(1, statusBarIconList.getViewIndex(1, sb3Tag));
+        assertEquals(2, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
+        assertEquals(3, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+    }
+
+    /**
+     * StatusBarIconList.Slot tests
+     */
+
+    @Test
+    public void testSlot_OrderIsPreserved() {
+        Slot testSlot = new Slot("test_name", null);
+
+        // no tag bc it defaults to 0
+        StatusBarIconHolder sbHolder1 = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class);
+        int sb2Tag = 1;
+        when(sbHolder2.getTag()).thenReturn(sb2Tag);
+        StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class);
+        int sb3Tag = 2;
+        when(sbHolder3.getTag()).thenReturn(sb3Tag);
+
+        ArrayList<StatusBarIconHolder> expected = new ArrayList<>();
+        expected.add(sbHolder1);
+        expected.add(sbHolder2);
+        expected.add(sbHolder3);
+
+
+        // Add 3 icons in the same slot, and verify that the list we get is equal to what we gave
+        for (StatusBarIconHolder holder : expected) {
+            testSlot.addHolder(holder);
+        }
+        assertTrue(listsEqual(expected, testSlot.getHolderList()));
+    }
+
+    private boolean listsEqual(List<StatusBarIconHolder> list1, List<StatusBarIconHolder> list2) {
+        if (list1.size() != list2.size())  return false;
+
+        for (int i = 0; i < list1.size(); i++) {
+            if (!list1.get(i).equals(list2.get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 5db7792..72b0156 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -14,10 +14,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.graphics.Rect;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -25,9 +32,14 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarMobileView;
+import com.android.systemui.statusbar.StatusBarWifiView;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -50,50 +62,119 @@
     public void testSetCalledOnAdd_IconManager() {
         LinearLayout layout = new LinearLayout(mContext);
         TestIconManager manager = new TestIconManager(layout);
-        StatusBarIcon icon = mock(StatusBarIcon.class);
-
-        manager.onIconAdded(0, "test_slot", false, icon);
-        verify(manager.mMock).set(eq(icon));
+        testCallOnAdd_forManager(manager);
     }
 
     @Test
     public void testSetCalledOnAdd_DarkIconManager() {
         LinearLayout layout = new LinearLayout(mContext);
         TestDarkIconManager manager = new TestDarkIconManager(layout);
-        StatusBarIcon icon = mock(StatusBarIcon.class);
-
-        manager.onIconAdded(0, "test_slot", false, icon);
-        verify(manager.mMock).set(eq(icon));
+        testCallOnAdd_forManager(manager);
     }
 
-    private static class TestDarkIconManager extends DarkIconManager {
 
-        private final StatusBarIconView mMock;
+    private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) {
+        StatusBarIconHolder holder = holderForType(TYPE_ICON);
+        manager.onIconAdded(0, "test_slot", false, holder);
+        assertTrue("Expected StatusBarIconView",
+                (manager.getViewAt(0) instanceof StatusBarIconView));
+
+        holder = holderForType(TYPE_WIFI);
+        manager.onIconAdded(1, "test_wifi", false, holder);
+        assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView);
+
+        holder = holderForType(TYPE_MOBILE);
+        manager.onIconAdded(2, "test_mobile", false, holder);
+        assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView);
+    }
+
+    private StatusBarIconHolder holderForType(int type) {
+        switch (type) {
+            case TYPE_MOBILE:
+                return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class));
+
+            case TYPE_WIFI:
+                return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class));
+
+            case TYPE_ICON:
+            default:
+                return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class));
+        }
+    }
+
+    private static class TestDarkIconManager extends DarkIconManager
+            implements TestableIconManager {
 
         public TestDarkIconManager(LinearLayout group) {
             super(group);
-            mMock = mock(StatusBarIconView.class);
         }
 
         @Override
-        protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
-            return mMock;
+        public StatusIconDisplayable getViewAt(int index) {
+            return (StatusIconDisplayable) mGroup.getChildAt(index);
+        }
+
+        @Override
+        protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
+                StatusBarIcon icon) {
+            StatusBarIconView mock = mock(StatusBarIconView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
+        }
+
+        @Override
+        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+            StatusBarWifiView mock = mock(StatusBarWifiView.class);
+            mGroup.addView(mock, index);
+            return mock;
+        }
+
+        @Override
+        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
+            StatusBarMobileView mock = mock(StatusBarMobileView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
         }
     }
 
-    private static class TestIconManager extends IconManager {
-
-        private final StatusBarIconView mMock;
-
+    private static class TestIconManager extends IconManager implements TestableIconManager {
         public TestIconManager(ViewGroup group) {
             super(group);
-            mMock = mock(StatusBarIconView.class);
         }
 
         @Override
-        protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
-            return mMock;
+        public StatusIconDisplayable getViewAt(int index) {
+            return (StatusIconDisplayable) mGroup.getChildAt(index);
+        }
+
+        @Override
+        protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
+                StatusBarIcon icon) {
+            StatusBarIconView mock = mock(StatusBarIconView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
+        }
+
+        @Override
+        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+            StatusBarWifiView mock = mock(StatusBarWifiView.class);
+            mGroup.addView(mock, index);
+            return mock;
+        }
+
+        @Override
+        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
+            StatusBarMobileView mock = mock(StatusBarMobileView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
         }
     }
 
+    private interface TestableIconManager {
+        StatusIconDisplayable getViewAt(int index);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 6b501af..8e34685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -19,6 +19,9 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import java.util.List;
 
 public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
         implements StatusBarIconController {
@@ -53,12 +56,23 @@
     }
 
     @Override
-    public void setIconVisibility(String slotTty, boolean b) {
-
+    public void setSignalIcon(String slot, WifiIconState state) {
     }
 
     @Override
-    public void removeIcon(String slot) {
-
+    public void setMobileIcons(String slot, List<MobileIconState> states) {
     }
+
+    @Override
+    public void setIconVisibility(String slotTty, boolean b) {
+    }
+
+    @Override
+    public void removeIcon(String slot, int tag) {
+    }
+
+    @Override
+    public void removeAllIconsForSlot(String slot) {
+    }
+
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f6ff359..40f9476 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -266,9 +266,9 @@
         @GuardedBy("LOCK")
         private boolean mValue;
 
-        public DebugFlag(String key) {
+        public DebugFlag(String key, boolean defaultValue) {
             mKey = key;
-            refresh();
+            mValue = SystemProperties.getBoolean(key, defaultValue);
         }
 
         void refresh() {
@@ -290,7 +290,7 @@
      */
     private static final class DebugFlags {
         static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
-                new DebugFlag("debug.optimize_startinput");
+                new DebugFlag("debug.optimize_startinput", false);
     }
 
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d09a161..bde6bd8 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -931,7 +931,7 @@
             return mPort;
         }
 
-        public FileDescriptor getSocket() {
+        public FileDescriptor getFileDescriptor() {
             return mSocket;
         }
 
@@ -1065,7 +1065,10 @@
     public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
             String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
         checkInetAddress(destinationAddress);
-        /* requestedSpi can be anything in the int range, so no check is needed. */
+        // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
+        if (requestedSpi > 0 && requestedSpi < 256) {
+            throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
+        }
         checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e735387..f756afb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4493,8 +4493,8 @@
         StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
             component.app.uid, component.realActivity.getPackageName(),
             component.realActivity.getShortClassName(), resumed ?
-                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
-                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
+                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
+                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
         if (resumed) {
             if (mUsageStatsService != null) {
                 mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -13179,10 +13179,6 @@
                     + android.Manifest.permission.SHUTDOWN);
         }
 
-        // TODO: Where should the corresponding '1' (start) write go?
-        StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
-                StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
-
         boolean timedout = false;
 
         synchronized(this) {
@@ -15197,6 +15193,12 @@
                 crashInfo.throwFileName,
                 crashInfo.throwLineNumber);
 
+        StatsLog.write(StatsLog.APP_CRASH_OCCURRED,
+                Binder.getCallingUid(),
+                eventType,
+                processName,
+                Binder.getCallingPid());
+
         addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
 
         mAppErrors.crashApplication(r, crashInfo);
@@ -15367,6 +15369,9 @@
         EventLog.writeEvent(EventLogTags.AM_WTF, UserHandle.getUserId(callingUid), callingPid,
                 processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage);
 
+        StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName,
+                callingPid);
+
         addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);
 
         return r;
@@ -15488,19 +15493,6 @@
         final String dropboxTag = processClass(process) + "_" + eventType;
         if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
 
-        // Log to StatsLog before the rate-limiting.
-        // The logging below is adapated from appendDropboxProcessHeaders.
-        StatsLog.write(StatsLog.DROPBOX_ERROR_CHANGED,
-                process != null ? process.uid : -1,
-                dropboxTag,
-                processName,
-                process != null ? process.pid : -1,
-                (process != null && process.info != null) ?
-                        (process.info.isInstantApp() ? 1 : 0) : -1,
-                activity != null ? activity.shortComponentName : null,
-                activity != null ? activity.packageName : null,
-                process != null ? (process.isInterestingToUserLocked() ? 1 : 0) : -1);
-
         // Rate-limit how often we're willing to do the heavy lifting below to
         // collect and record logs; currently 5 logs per 10 second period.
         final long now = SystemClock.elapsedRealtime();
@@ -19780,6 +19772,7 @@
             catPw.flush();
         }
         dropBuilder.append(catSw.toString());
+        StatsLog.write(StatsLog.LOW_MEM_REPORTED);
         addErrorToDropBox("lowmem", null, "system_server", null,
                 null, tag.toString(), dropBuilder.toString(), null, null);
         //Slog.i(TAG, "Sent to dropbox:");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 724dd3f..47d0423 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -489,7 +489,7 @@
         builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
         mMetricsLogger.write(builder);
         StatsLog.write(
-                StatsLog.APP_START_CANCEL_CHANGED,
+                StatsLog.APP_START_CANCELED,
                 info.launchedActivity.appInfo.uid,
                 info.launchedActivity.packageName,
                 convertAppStartTransitionType(type),
@@ -561,7 +561,7 @@
                 packageOptimizationInfo.getCompilationFilter());
         mMetricsLogger.write(builder);
         StatsLog.write(
-                StatsLog.APP_START_CHANGED,
+                StatsLog.APP_START_OCCURRED,
                 info.applicationInfo.uid,
                 info.packageName,
                 convertAppStartTransitionType(info.type),
@@ -582,15 +582,15 @@
 
     private int convertAppStartTransitionType(int tronType) {
         if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
-            return StatsLog.APP_START_CHANGED__TYPE__COLD;
+            return StatsLog.APP_START_OCCURRED__TYPE__COLD;
         }
         if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
-            return StatsLog.APP_START_CHANGED__TYPE__WARM;
+            return StatsLog.APP_START_OCCURRED__TYPE__WARM;
         }
         if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
-            return StatsLog.APP_START_CHANGED__TYPE__HOT;
+            return StatsLog.APP_START_OCCURRED__TYPE__HOT;
         }
-        return StatsLog.APP_START_CHANGED__TYPE__APP_START_TRANSITION_TYPE_UNKNOWN;
+        return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
      }
 
     void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) {
@@ -611,12 +611,12 @@
                 info.currentTransitionProcessRunning ? 1 : 0);
         mMetricsLogger.write(builder);
         StatsLog.write(
-                StatsLog.APP_START_FULLY_DRAWN_CHANGED,
+                StatsLog.APP_START_FULLY_DRAWN,
                 info.launchedActivity.appInfo.uid,
                 info.launchedActivity.packageName,
                 restoredFromBundle
-                        ? StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITH_BUNDLE
-                        : StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITHOUT_BUNDLE,
+                        ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
+                        : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
                 info.launchedActivity.info.name,
                 info.currentTransitionProcessRunning,
                 startupTimeMs);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index b2872e4..7ee20fa2 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -47,6 +47,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -1039,6 +1040,8 @@
             Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
         }
 
+        StatsLog.write(StatsLog.ANR_OCCURRED, app.uid, app.processName,
+                activity == null ? "unknown": activity.shortComponentName, annotation);
         mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                 cpuInfo, tracesFile, null);
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index b338029..8ecd93e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -337,7 +337,7 @@
         synchronized (mStats) {
             mStats.noteProcessStartLocked(name, uid);
             StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__STARTED);
         }
     }
 
@@ -345,15 +345,13 @@
         synchronized (mStats) {
             mStats.noteProcessCrashLocked(name, uid);
             StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__CRASHED);
         }
     }
 
     void noteProcessAnr(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessAnrLocked(name, uid);
-            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
         }
     }
 
@@ -361,7 +359,7 @@
         synchronized (mStats) {
             mStats.noteProcessFinishLocked(name, uid);
             StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__FINISHED);
         }
     }
 
@@ -768,8 +766,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOnLocked(uid);
-            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uid, null,
-                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid, null,
+                    StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -777,8 +775,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOffLocked(uid);
-            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uid,
-                    null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid,
+                    null, StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -795,8 +793,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetVideoLocked();
-            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, -1, null,
-                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__RESET);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, -1, null,
+                    StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__RESET);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 24abf86..9d3f48b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -484,11 +484,11 @@
         }
     }
 
-    public void installApkVerity(String filePath, FileDescriptor verityInput)
+    public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.installApkVerity(filePath, verityInput);
+            mInstalld.installApkVerity(filePath, verityInput, contentSize);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 9807342..892fa12 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -450,11 +450,9 @@
 
             for (String isa : dexCodeInstructionSets) {
                 try {
-                    String[] status = DexFile.getDexFileOptimizationStatus(path, isa);
-                    String compilationStatus = status[0];
-                    String compilationReason = status[1];
-                    pw.println(isa + ": [status=" + compilationStatus
-                            +"] reason=[" + compilationReason + "]");
+                    DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa);
+                    pw.println(isa + ": [status=" + info.getStatus()
+                            +"] [reason=" + info.getReason() + "]");
                 } catch (IOException ioe) {
                     pw.println(isa + ": [Exception]: " + ioe.getMessage());
                 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7b77c96..cb1972f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17322,8 +17322,11 @@
                     if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
                     FileDescriptor fd = result.getUnownedFileDescriptor();
                     try {
-                        mInstaller.installApkVerity(apkPath, fd);
-                    } catch (InstallerException e) {
+                        final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
+                        mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
+                        mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
+                    } catch (InstallerException | IOException | DigestException |
+                             NoSuchAlgorithmException e) {
                         res.setError(INSTALL_FAILED_INTERNAL_ERROR,
                                 "Failed to set up verity: " + e);
                         return;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index cdafe57..9c2ad463 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -527,9 +527,10 @@
             String compilationFilter;
             try {
                 String isa = VMRuntime.getInstructionSet(abi);
-                String[] stats = DexFile.getDexFileOptimizationStatus(info.getBaseCodePath(), isa);
-                compilationFilter = stats[0];
-                compilationReason = stats[1];
+                DexFile.OptimizationInfo optInfo =
+                        DexFile.getDexFileOptimizationInfo(info.getBaseCodePath(), isa);
+                compilationFilter = optInfo.getStatus();
+                compilationReason = optInfo.getReason();
             } catch (FileNotFoundException e) {
                 Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e);
                 compilationFilter = "error";
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index d2d0e60..180f343 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -26,6 +26,7 @@
 import android.util.apk.ApkSignatureVerifier;
 import android.util.apk.ByteBufferFactory;
 import android.util.apk.SignatureNotFoundException;
+import android.util.Pair;
 import android.util.Slog;
 
 import java.io.FileDescriptor;
@@ -59,12 +60,15 @@
                 return SetupResult.skipped();
             }
 
-            shm = generateApkVerityIntoSharedMemory(apkPath, signedRootHash);
+            Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath,
+                    signedRootHash);
+            shm = result.first;
+            int contentSize = result.second;
             FileDescriptor rfd = shm.getFileDescriptor();
             if (rfd == null || !rfd.valid()) {
                 return SetupResult.failed();
             }
-            return SetupResult.ok(Os.dup(rfd));
+            return SetupResult.ok(Os.dup(rfd), contentSize);
         } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException |
                 SignatureNotFoundException | ErrnoException e) {
             Slog.e(TAG, "Failed to set up apk verity: ", e);
@@ -85,10 +89,20 @@
     }
 
     /**
-     * Returns a {@code SharedMemory} that contains Merkle tree and fsverity headers for the given
-     * apk, in the form that can immediately be used for fsverity setup.
+     * {@see ApkSignatureVerifier#getVerityRootHash(String)}.
      */
-    private static SharedMemory generateApkVerityIntoSharedMemory(
+    public static byte[] getVerityRootHash(@NonNull String apkPath)
+            throws IOException, SignatureNotFoundException, SecurityException {
+        return ApkSignatureVerifier.getVerityRootHash(apkPath);
+    }
+
+    /**
+     * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains
+     * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used
+     * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has
+     * length equals to the returned {@code Integer}.
+     */
+    private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory(
             String apkPath, byte[] expectedRootHash)
             throws IOException, SecurityException, DigestException, NoSuchAlgorithmException,
                    SignatureNotFoundException {
@@ -101,6 +115,7 @@
             throw new SecurityException("Locally generated verity root hash does not match");
         }
 
+        int contentSize = shmBufferFactory.getBufferLimit();
         SharedMemory shm = shmBufferFactory.releaseSharedMemory();
         if (shm == null) {
             throw new IllegalStateException("Failed to generate verity tree into shared memory");
@@ -108,7 +123,7 @@
         if (!shm.setProtect(PROT_READ)) {
             throw new SecurityException("Failed to set up shared memory correctly");
         }
-        return shm;
+        return Pair.create(shm, contentSize);
     }
 
     public static class SetupResult {
@@ -123,22 +138,24 @@
 
         private final int mCode;
         private final FileDescriptor mFileDescriptor;
+        private final int mContentSize;
 
-        public static SetupResult ok(@NonNull FileDescriptor fileDescriptor) {
-            return new SetupResult(RESULT_OK, fileDescriptor);
+        public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) {
+            return new SetupResult(RESULT_OK, fileDescriptor, contentSize);
         }
 
         public static SetupResult skipped() {
-            return new SetupResult(RESULT_SKIPPED, null);
+            return new SetupResult(RESULT_SKIPPED, null, -1);
         }
 
         public static SetupResult failed() {
-            return new SetupResult(RESULT_FAILED, null);
+            return new SetupResult(RESULT_FAILED, null, -1);
         }
 
-        private SetupResult(int code, FileDescriptor fileDescriptor) {
+        private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) {
             this.mCode = code;
             this.mFileDescriptor = fileDescriptor;
+            this.mContentSize = contentSize;
         }
 
         public boolean isFailed() {
@@ -152,6 +169,10 @@
         public @NonNull FileDescriptor getUnownedFileDescriptor() {
             return mFileDescriptor;
         }
+
+        public int getContentSize() {
+            return mContentSize;
+        }
     }
 
     /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */
@@ -188,5 +209,9 @@
             mShm = null;
             return tmp;
         }
+
+        public int getBufferLimit() {
+            return mBuffer == null ? -1 : mBuffer.limit();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 0b7d9d0..fd0b6f1 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -168,7 +168,8 @@
     }
 
     @Override
-    public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
+    public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token)
+            throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
         int user = Binder.getCallingUserHandle().getIdentifier();
@@ -210,7 +211,8 @@
     }
 
     @Override
-    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) throws RemoteException {
         if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                 == PERMISSION_GRANTED) {
             return SliceManager.PERMISSION_GRANTED;
@@ -218,6 +220,11 @@
         if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
             return SliceManager.PERMISSION_GRANTED;
         }
+        for (String perm : autoGrantPermissions) {
+            if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
+                return SliceManager.PERMISSION_USER_GRANTED;
+            }
+        }
         synchronized (mLock) {
             if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
                 return SliceManager.PERMISSION_USER_GRANTED;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d252a56..753394d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -39,6 +39,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
@@ -68,14 +69,21 @@
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
+import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -89,14 +97,17 @@
      * How long to wait on an individual subsystem to return its stats.
      */
     private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+    private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
 
     public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+    public static final String CONFIG_DIR = "/data/misc/stats-service";
 
     static final String TAG = "StatsCompanionService";
     static final boolean DEBUG = false;
 
     public static final int CODE_DATA_BROADCAST = 1;
     public static final int CODE_SUBSCRIBER_BROADCAST = 1;
+    public static final int DEATH_THRESHOLD = 10;
 
     private final Context mContext;
     private final AlarmManager mAlarmManager;
@@ -119,6 +130,10 @@
             new StatFs(Environment.getRootDirectory().getAbsolutePath());
     private final StatFs mStatFsTemp =
             new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+    @GuardedBy("sStatsdLock")
+    private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
+    @GuardedBy("sStatsdLock")
+    private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
 
     private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
     private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -156,7 +171,7 @@
                         informAllUidsLocked(context);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
-                        forgetEverything();
+                        forgetEverythingLocked();
                     }
                 }
             }
@@ -751,7 +766,7 @@
         });
     }
 
-    private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         if (mWifiManager == null) {
             mWifiManager =
@@ -919,8 +934,8 @@
                 pullKernelUidCpuActiveTime(tagId, ret);
                 break;
             }
-            case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
-                pullWifiActivityEnergyInfo(tagId, ret);
+            case StatsLog.WIFI_ACTIVITY_INFO: {
+                pullWifiActivityInfo(tagId, ret);
                 break;
             }
             case StatsLog.MODEM_ACTIVITY_INFO: {
@@ -1055,7 +1070,7 @@
                     sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
-                    forgetEverything();
+                    forgetEverythingLocked();
                 }
                 // Setup broadcast receiver for updates.
                 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
@@ -1087,7 +1102,7 @@
                 Slog.i(TAG, "Told statsd that StatsCompanionService is alive.");
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
-                forgetEverything();
+                forgetEverythingLocked();
             }
         }
     }
@@ -1096,18 +1111,60 @@
         @Override
         public void binderDied() {
             Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
-            forgetEverything();
+            synchronized (sStatsdLock) {
+                long now = SystemClock.elapsedRealtime();
+                for (Long timeMillis : mDeathTimeMillis) {
+                    long ageMillis = now - timeMillis;
+                    if (ageMillis > MILLIS_IN_A_DAY) {
+                        mDeathTimeMillis.remove(timeMillis);
+                    }
+                }
+                for (Long timeMillis : mDeletedFiles.keySet()) {
+                    long ageMillis = now - timeMillis;
+                    if (ageMillis > MILLIS_IN_A_DAY * 7) {
+                        mDeletedFiles.remove(timeMillis);
+                    }
+                }
+                mDeathTimeMillis.add(now);
+                if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
+                    mDeathTimeMillis.clear();
+                    File[] configs = FileUtils.listFilesOrEmpty(new File(CONFIG_DIR));
+                    if (configs.length > 0) {
+                        String fileName = configs[0].getName();
+                        if (configs[0].delete()) {
+                            mDeletedFiles.put(now, fileName);
+                        }
+                    }
+                }
+                forgetEverythingLocked();
+            }
         }
     }
 
-    private void forgetEverything() {
+    private void forgetEverythingLocked() {
+        sStatsd = null;
+        mContext.unregisterReceiver(mAppUpdateReceiver);
+        mContext.unregisterReceiver(mUserUpdateReceiver);
+        mContext.unregisterReceiver(mShutdownEventReceiver);
+        cancelAnomalyAlarm();
+        cancelPullingAlarm();
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+
         synchronized (sStatsdLock) {
-            sStatsd = null;
-            mContext.unregisterReceiver(mAppUpdateReceiver);
-            mContext.unregisterReceiver(mUserUpdateReceiver);
-            mContext.unregisterReceiver(mShutdownEventReceiver);
-            cancelAnomalyAlarm();
-            cancelPullingAlarm();
+            writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
+            if (mDeletedFiles.size() > 0) {
+                writer.println("  timestamp, deleted file name");
+            }
+            long lastBootMillis =
+                    SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
+            for (Long elapsedMillis : mDeletedFiles.keySet()) {
+                long deletionMillis = lastBootMillis + elapsedMillis;
+                writer.println("  " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+            }
         }
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 1073a80..5e2a364 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -15,8 +15,10 @@
 package com.android.server.slice;
 
 import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -35,9 +37,11 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -63,6 +67,7 @@
     private SliceManagerService mService;
     private PinnedSliceState mCreatedSliceState;
     private IBinder mToken = new Binder();
+    private TestableContext mContextSpy;
 
     @Before
     public void setup() {
@@ -72,7 +77,8 @@
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
         mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
 
-        mService = spy(new SliceManagerService(mContext, TestableLooper.get(this).getLooper()));
+        mContextSpy = spy(mContext);
+        mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper()));
         mCreatedSliceState = mock(PinnedSliceState.class);
         doReturn(mCreatedSliceState).when(mService).createPinnedSlice(eq(TEST_URI), anyString());
     }
@@ -103,4 +109,23 @@
         verify(mCreatedSliceState, never()).destroy();
     }
 
+    @Test
+    public void testCheckAutoGrantPermissions() throws RemoteException {
+        String[] testPerms = new String[] {
+                "perm1",
+                "perm2",
+        };
+        when(mContextSpy.checkUriPermission(any(), anyInt(), anyInt(), anyInt()))
+                .thenReturn(PERMISSION_DENIED);
+        when(mContextSpy.checkPermission("perm1", Process.myPid(), Process.myUid()))
+                .thenReturn(PERMISSION_DENIED);
+        when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
+                .thenReturn(PERMISSION_GRANTED);
+        mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+                Process.myUid(), testPerms);
+
+        verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
+        verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
+    }
+
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 92ebf36..dbad02f 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -83,6 +83,8 @@
             } catch (RemoteException e) {
                 Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
             }
+        } else {
+            Rlog.e(mTag, "Weak reference of callback is null.");
         }
     }
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index 6d72181..2831127 100644
--- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
@@ -29,27 +29,47 @@
 public final class ImsCallForwardInfo implements Parcelable {
     // Refer to ImsUtInterface#CDIV_CF_XXX
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
     public int mCondition;
     // 0: disabled, 1: enabled
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
     public int mStatus;
     // 0x91: International, 0x81: Unknown
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
     public int mToA;
     // Service class
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
     public int mServiceClass;
     // Number (it will not include the "sip" or "tel" URI scheme)
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
     public String mNumber;
     // No reply timer for CF
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
     public int mTimeSeconds;
 
     /** @hide */
+    // TODO: Will be removed in the future, use public constructor instead.
     public ImsCallForwardInfo() {
     }
 
+    /**
+     * IMS Call Forward Information.
+     */
+    public ImsCallForwardInfo(int condition, int status, int toA, int serviceClass, String number,
+            int replyTimerSec) {
+        mCondition = condition;
+        mStatus = status;
+        mToA = toA;
+        mServiceClass = serviceClass;
+        mNumber = number;
+        mTimeSeconds = replyTimerSec;
+    }
+
     /** @hide */
     public ImsCallForwardInfo(Parcel in) {
         readFromParcel(in);
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 1ddf199..49ead77 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -15,19 +15,24 @@
  */
 package android.telephony.ims;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
- * Provided STK Call Control Suplementary Service information
+ * Provides STK Call Control Supplementary Service information.
  *
  * {@hide}
  */
 @SystemApi
 public final class ImsSsData implements Parcelable {
 
-    //ServiceType
+    // Supplementary Service Type
+    // Call Forwarding
     public static final int SS_CFU = 0;
     public static final int SS_CF_BUSY = 1;
     public static final int SS_CF_NO_REPLY = 2;
@@ -35,12 +40,16 @@
     public static final int SS_CF_ALL = 4;
     public static final int SS_CF_ALL_CONDITIONAL = 5;
     public static final int SS_CFUT = 6;
+    // Called Line Presentation
     public static final int SS_CLIP = 7;
     public static final int SS_CLIR = 8;
     public static final int SS_COLP = 9;
     public static final int SS_COLR = 10;
+    // Calling Name Presentation
     public static final int SS_CNAP = 11;
+    // Call Waiting
     public static final int SS_WAIT = 12;
+    // Call Barring
     public static final int SS_BAOC = 13;
     public static final int SS_BAOIC = 14;
     public static final int SS_BAOIC_EXC_HOME = 15;
@@ -52,14 +61,14 @@
     public static final int SS_INCOMING_BARRING_DN = 21;
     public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
 
-    //SSRequestType
+    //Supplementary Service Request Types
     public static final int SS_ACTIVATION = 0;
     public static final int SS_DEACTIVATION = 1;
     public static final int SS_INTERROGATION = 2;
     public static final int SS_REGISTRATION = 3;
     public static final int SS_ERASURE = 4;
 
-    //TeleserviceType
+    // Supplementary Service Teleservice Type
     public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
     public static final int SS_ALL_TELESEVICES = 1;
     public static final int SS_TELEPHONY = 2;
@@ -67,40 +76,226 @@
     public static final int SS_SMS_SERVICES = 4;
     public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
 
-    // Refer to ServiceType
+    // Service Class of Supplementary Service
+    // See 27.007 +CCFC or +CLCK
     /** @hide */
+    public static final int SERVICE_CLASS_NONE = 0; // no user input
+    /** @hide */
+    public static final int SERVICE_CLASS_VOICE = 1;
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA = (1 << 1);
+    /** @hide */
+    public static final int SERVICE_CLASS_FAX = (1 << 2);
+    /** @hide */
+    public static final int SERVICE_CLASS_SMS = (1 << 3);
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA_SYNC = (1 << 4);
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5);
+    /** @hide */
+    public static final int SERVICE_CLASS_PACKET = (1 << 6);
+    /** @hide */
+    public static final int SERVICE_CLASS_PAD = (1 << 7);
+
+    /**
+     * Result code used if the operation was successful. See {@link #result}.
+     * @hide
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SS_" }, value = {
+            SS_CFU,
+            SS_CF_BUSY,
+            SS_CF_NO_REPLY,
+            SS_CF_NOT_REACHABLE,
+            SS_CF_ALL,
+            SS_CF_ALL_CONDITIONAL,
+            SS_CFUT,
+            SS_CLIP,
+            SS_CLIR,
+            SS_COLP,
+            SS_COLR,
+            SS_CNAP,
+            SS_WAIT,
+            SS_BAOC,
+            SS_BAOIC,
+            SS_BAOIC_EXC_HOME,
+            SS_BAIC,
+            SS_BAIC_ROAMING,
+            SS_ALL_BARRING,
+            SS_OUTGOING_BARRING,
+            SS_INCOMING_BARRING,
+            SS_INCOMING_BARRING_DN,
+            SS_INCOMING_BARRING_ANONYMOUS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceType{}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SERVICE_CLASS" }, value = {
+            SERVICE_CLASS_NONE,
+            SERVICE_CLASS_VOICE,
+            SERVICE_CLASS_DATA,
+            SERVICE_CLASS_FAX,
+            SERVICE_CLASS_SMS,
+            SERVICE_CLASS_DATA_SYNC,
+            SERVICE_CLASS_DATA_ASYNC,
+            SERVICE_CLASS_PACKET,
+            SERVICE_CLASS_PAD
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceClass{}
+
+    /**
+     * The Service type of this Supplementary service. Valid values include:
+     *     SS_CFU,
+     *     SS_CF_BUSY,
+     *     SS_CF_NO_REPLY,
+     *     SS_CF_NOT_REACHABLE,
+     *     SS_CF_ALL,
+     *     SS_CF_ALL_CONDITIONAL,
+     *     SS_CFUT,
+     *     SS_CLIP,
+     *     SS_CLIR,
+     *     SS_COLP,
+     *     SS_COLR,
+     *     SS_CNAP,
+     *     SS_WAIT,
+     *     SS_BAOC,
+     *     SS_BAOIC,
+     *     SS_BAOIC_EXC_HOME,
+     *     SS_BAIC,
+     *     SS_BAIC_ROAMING,
+     *     SS_ALL_BARRING,
+     *     SS_OUTGOING_BARRING,
+     *     SS_INCOMING_BARRING,
+     *     SS_INCOMING_BARRING_DN,
+     *     SS_INCOMING_BARRING_ANONYMOUS
+     *
+     * @hide
+     */
+    // TODO: Make final, do not modify this field directly!
     public int serviceType;
-    // Refere to SSRequestType
-    /** @hide */
+
+    /**
+     * Supplementary Service request Type. Valid values are:
+     *     SS_ACTIVATION,
+     *     SS_DEACTIVATION,
+     *     SS_INTERROGATION,
+     *     SS_REGISTRATION,
+     *     SS_ERASURE
+     *
+     * @hide
+     */
+    // TODO: Make final, do not modify this field directly!
     public int requestType;
-    // Refer to TeleserviceType
-    /** @hide */
+
+    /**
+     * Supplementary Service teleservice type:
+     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
+     *     SS_TELESERVICE_ALL_TELESEVICES,
+     *     SS_TELESERVICE_TELEPHONY,
+     *     SS_TELESERVICE_ALL_DATA,
+     *     SS_TELESERVICE_SMS,
+     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
     public int teleserviceType;
-    // Service Class
-    /** @hide */
+
+    /**
+     * Supplementary Service service class. Valid values are:
+     *     SERVICE_CLASS_NONE,
+     *     SERVICE_CLASS_VOICE,
+     *     SERVICE_CLASS_DATA,
+     *     SERVICE_CLASS_FAX,
+     *     SERVICE_CLASS_SMS,
+     *     SERVICE_CLASS_DATA_SYNC,
+     *     SERVICE_CLASS_DATA_ASYNC,
+     *     SERVICE_CLASS_PACKET,
+     *     SERVICE_CLASS_PAD
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
     public int serviceClass;
-    // Error information
-    /** @hide */
-    public int result;
 
-    /** @hide */
-    public int[] ssInfo; /* Valid for all supplementary services.
-                             This field will be empty for RequestType SS_INTERROGATION
-                             and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
-                             SS_INCOMING_BARRING_ANONYMOUS.*/
+    /**
+     * Result of Supplementary Service operation. Valid values are:
+     *     RESULT_SUCCESS if the result is success, or
+     *     ImsReasonInfo code if the result is a failure.
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public final int result;
 
-    /** @hide */
-    public ImsCallForwardInfo[] cfInfo; /* Valid only for supplementary services
-                                            ServiceType SS_CF_* and RequestType SS_INTERROGATION */
+    private int[] mSsInfo;
+    private ImsCallForwardInfo[] mCfInfo;
+    private ImsSsInfo[] mImsSsInfo;
 
-    /** @hide */
-    public ImsSsInfo[] imsSsInfo;   /* Valid only for ServiceType SS_INCOMING_BARRING_DN and
-                                        ServiceType SS_INCOMING_BARRING_ANONYMOUS */
-
-    public ImsSsData() {}
+    /**
+     * Generate IMS Supplementary Service information.
+     * @param serviceType The Supplementary Service type. Valid entries:
+     *     SS_CFU,
+     *     SS_CF_BUSY,
+     *     SS_CF_NO_REPLY,
+     *     SS_CF_NOT_REACHABLE,
+     *     SS_CF_ALL,
+     *     SS_CF_ALL_CONDITIONAL,
+     *     SS_CFUT,
+     *     SS_CLIP,
+     *     SS_CLIR,
+     *     SS_COLP,
+     *     SS_COLR,
+     *     SS_CNAP,
+     *     SS_WAIT,
+     *     SS_BAOC,
+     *     SS_BAOIC,
+     *     SS_BAOIC_EXC_HOME,
+     *     SS_BAIC,
+     *     SS_BAIC_ROAMING,
+     *     SS_ALL_BARRING,
+     *     SS_OUTGOING_BARRING,
+     *     SS_INCOMING_BARRING,
+     *     SS_INCOMING_BARRING_DN,
+     *     SS_INCOMING_BARRING_ANONYMOUS
+     * @param requestType Supplementary Service request Type. Valid values are:
+     *     SS_ACTIVATION,
+     *     SS_DEACTIVATION,
+     *     SS_INTERROGATION,
+     *     SS_REGISTRATION,
+     *     SS_ERASURE
+     * @param teleserviceType Supplementary Service teleservice type:
+     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
+     *     SS_TELESERVICE_ALL_TELESEVICES,
+     *     SS_TELESERVICE_TELEPHONY,
+     *     SS_TELESERVICE_ALL_DATA,
+     *     SS_TELESERVICE_SMS,
+     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+     * @param result Result of Supplementary Service operation. Valid values are 0 if the result is
+     *               success, or ImsReasonInfo code if the result is a failure.
+     */
+    public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
+            @ServiceClass int serviceClass, int result) {
+        this.serviceType = serviceType;
+        this.requestType = requestType;
+        this.teleserviceType = teleserviceType;
+        this.serviceClass = serviceClass;
+        this.result = result;
+    }
 
     private ImsSsData(Parcel in) {
-        readFromParcel(in);
+        serviceType = in.readInt();
+        requestType = in.readInt();
+        teleserviceType = in.readInt();
+        serviceClass = in.readInt();
+        result = in.readInt();
+        mSsInfo = in.createIntArray();
+        mCfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
     }
 
     public static final Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
@@ -122,18 +317,8 @@
         out.writeInt(teleserviceType);
         out.writeInt(serviceClass);
         out.writeInt(result);
-        out.writeIntArray(ssInfo);
-        out.writeParcelableArray(cfInfo, 0);
-    }
-
-    private void readFromParcel(Parcel in) {
-        serviceType = in.readInt();
-        requestType = in.readInt();
-        teleserviceType = in.readInt();
-        serviceClass = in.readInt();
-        result = in.readInt();
-        ssInfo = in.createIntArray();
-        cfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
+        out.writeIntArray(mSsInfo);
+        out.writeParcelableArray(mCfInfo, 0);
     }
 
     @Override
@@ -200,7 +385,55 @@
     }
 
     public boolean isTypeInterrogation() {
-        return (requestType == SS_INTERROGATION);
+        return (serviceType == SS_INTERROGATION);
+    }
+
+    /** @hide */
+    public void setSuppServiceInfo(int[] ssInfo) {
+        mSsInfo = ssInfo;
+    }
+
+    /** @hide */
+    public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
+        mImsSsInfo = imsSsInfo;
+    }
+
+    /** @hide */
+    public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
+        mCfInfo = cfInfo;
+    }
+
+    /**
+     * This field will be null for RequestType SS_INTERROGATION
+     * and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
+     * SS_INCOMING_BARRING_ANONYMOUS.
+     *
+     * @hide
+     */
+    public int[] getSuppServiceInfo() {
+        return mSsInfo;
+    }
+
+    /**
+     * Valid only for ServiceTypes
+     *  - SS_INCOMING_BARRING_DN and
+     *  - ServiceType SS_INCOMING_BARRING_ANONYMOUS.
+     *  Will be null otherwise.
+     * @hide
+     */
+    public ImsSsInfo[] getImsSpecificSuppServiceInfo() {
+        return mImsSsInfo;
+    }
+
+    /**
+     * Valid only for supplementary services
+     * - ServiceType SS_CF_* and
+     * - RequestType SS_INTERROGATION.
+     * Will be null otherwise.
+     * @hide
+     **/
+    public ImsCallForwardInfo[] getCallForwardInfo() {
+        return mCfInfo;
     }
 
     public String toString() {
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 1d1292f..c6f8622 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -36,13 +36,31 @@
 
     // 0: disabled, 1: enabled
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter!
     public int mStatus;
     /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter!
     public String mIcbNum;
 
+    /**@hide*/
+    // TODO: Remove! Do not use this constructor, instead use public version.
     public ImsSsInfo() {
     }
 
+    /**
+     *
+     * @param status The status of the service registration of activation/deactiviation. Valid
+     *    entries include:
+     *    {@link #NOT_REGISTERED},
+     *    {@link #DISABLED},
+     *    {@link #ENABLED}
+     * @param icbNum The Incoming barring number.
+     */
+    public ImsSsInfo(int status, String icbNum) {
+        mStatus = status;
+        mIcbNum = icbNum;
+    }
+
     private ImsSsInfo(Parcel in) {
         readFromParcel(in);
     }
@@ -81,6 +99,12 @@
         }
     };
 
+    /**
+     * @return Supplementary Service Configuration status. Valid Values are:
+     *     {@link #NOT_REGISTERED},
+     *     {@link #DISABLED},
+     *     {@link #ENABLED}
+     */
     public int getStatus() {
         return mStatus;
     }
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
index 3734412..e247951 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
+++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
@@ -173,7 +173,7 @@
     private static String getCompilerFilter(String pkg) throws IOException {
         String cmd = String.format("dumpsys package %s", pkg);
         String[] lines = runShellCommandSplitLines(cmd);
-        final String substr = "compilation_filter=";
+        final String substr = "[status=";
         for (String line : lines) {
             int startIndex = line.indexOf(substr);
             if (startIndex < 0) {
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 6bdfdc6..85e8361 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -22,8 +22,12 @@
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+
+import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
+import java.util.Map.Entry;
 import java.util.Random;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,19 +44,29 @@
     };
 
     @Test
-    public void testDefaultTruncLen() throws Exception {
-        IpSecAlgorithm explicit =
+    public void testNoTruncLen() throws Exception {
+        Entry<String, Integer>[] authAndAeadList =
+                new Entry[] {
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+                };
+
+        // Expect auth and aead algorithms to throw errors if trunclen is omitted.
+        for (Entry<String, Integer> algData : authAndAeadList) {
+            try {
                 new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256);
-        IpSecAlgorithm implicit =
-                new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
-        assertTrue(
-                "Default Truncation Length Incorrect, Explicit: "
-                        + explicit
-                        + "implicit: "
-                        + implicit,
-                IpSecAlgorithm.equals(explicit, implicit));
+                        algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8));
+                fail("Expected exception on unprovided auth trunclen");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+
+        // Ensure crypt works with no truncation length supplied.
+        new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
     }
 
     @Test
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f186ee5..771faaf 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -62,7 +62,8 @@
         c.setAuthentication(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_HMAC_MD5,
-                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0},
+                        128));
         c.setAuthenticatedEncryption(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0ca20de..970596d 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -179,7 +179,7 @@
 
         IpSecManager.UdpEncapsulationSocket encapSocket =
                 mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT);
-        assertNotNull(encapSocket.getSocket());
+        assertNotNull(encapSocket.getFileDescriptor());
         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
 
         encapSocket.close();
@@ -202,7 +202,7 @@
         IpSecManager.UdpEncapsulationSocket encapSocket =
                 mIpSecManager.openUdpEncapsulationSocket();
 
-        assertNotNull(encapSocket.getSocket());
+        assertNotNull(encapSocket.getFileDescriptor());
         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
 
         encapSocket.close();