Merge "Import launcher style reveal animations to QS."
diff --git a/Android.mk b/Android.mk
index 59278b0..470714b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -194,6 +194,7 @@
     -since $(SRC_API_DIR)/25.txt 25 \
     -since $(SRC_API_DIR)/26.txt 26 \
     -since $(SRC_API_DIR)/27.txt 27 \
+    -since ./frameworks/base/api/current.txt P \
     -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
     -overview $(LOCAL_PATH)/core/java/overview.html \
 
diff --git a/api/current.txt b/api/current.txt
index fff502a..d072986 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1378,6 +1378,7 @@
     field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e
     field public static final int textEditSuggestionItemLayout = 16843636; // 0x1010374
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
+    field public static final int textFontWeight = 16844166; // 0x1010586
     field public static final int textIsSelectable = 16843542; // 0x1010316
     field public static final int textOff = 16843045; // 0x1010125
     field public static final int textOn = 16843044; // 0x1010124
@@ -6702,11 +6703,6 @@
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
-    field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
-    field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
-    field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
-    field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
-    field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
     field public static final int WIPE_EUICC = 4; // 0x4
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -8972,6 +8968,7 @@
 
   public class ClipboardManager extends android.text.ClipboardManager {
     method public void addPrimaryClipChangedListener(android.content.ClipboardManager.OnPrimaryClipChangedListener);
+    method public void clearPrimaryClip();
     method public android.content.ClipData getPrimaryClip();
     method public android.content.ClipDescription getPrimaryClipDescription();
     method public deprecated java.lang.CharSequence getText();
@@ -33629,6 +33626,17 @@
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
     field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
     field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
+    field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
+    field public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5; // 0x5
+    field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2
+    field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3
+    field public static final int USER_OPERATION_ERROR_MAX_USERS = 6; // 0x6
+    field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
+  }
+
+  public static class UserManager.UserOperationException extends java.lang.RuntimeException {
+    method public int getUserOperationResult();
   }
 
   public abstract class VibrationEffect implements android.os.Parcelable {
@@ -35103,7 +35111,7 @@
     field public static final java.lang.String FEATURES = "features";
     field public static final int FEATURES_HD_CALL = 4; // 0x4
     field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
-    field public static final int FEATURES_RTT = 16; // 0x10
+    field public static final int FEATURES_RTT = 32; // 0x20
     field public static final int FEATURES_VIDEO = 1; // 0x1
     field public static final int FEATURES_WIFI = 8; // 0x8
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
diff --git a/api/system-current.txt b/api/system-current.txt
index 3aca59a..bd1eef6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -368,7 +368,6 @@
   }
 
   public final class StatsManager {
-    method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
     method public boolean addConfiguration(long, byte[]);
     method public byte[] getData(long);
     method public byte[] getMetadata();
@@ -617,6 +616,7 @@
     method public java.lang.String transportDirName();
     field public static final int AGENT_ERROR = -1003; // 0xfffffc15
     field public static final int AGENT_UNKNOWN = -1004; // 0xfffffc14
+    field public static final java.lang.String EXTRA_TRANSPORT_REGISTRATION = "android.app.backup.extra.TRANSPORT_REGISTRATION";
     field public static final int FLAG_INCREMENTAL = 2; // 0x2
     field public static final int FLAG_NON_INCREMENTAL = 4; // 0x4
     field public static final int FLAG_USER_INITIATED = 1; // 0x1
@@ -4180,6 +4180,7 @@
     field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names";
     field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
     field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
+    field public static final java.lang.String EUICC_PROVISIONED = "euicc_provisioned";
     field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
     field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 6bdd9be..3a47fe1 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -15,7 +15,8 @@
 LOCAL_PATH:= $(call my-dir)
 
 # proto files used in incidentd to generate cppstream proto headers.
-PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto
+PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto \
+        frameworks/base/core/proto/android/os/data.proto
 
 # ========= #
 # incidentd #
@@ -131,7 +132,7 @@
 LOCAL_MODULE_CLASS := NATIVE_TESTS
 gen_src_dir := $(local-generated-sources-dir)
 # generate cppstream proto for testing
-GEN_PROTO := $(gen_src_dir)/log.proto.timestamp
+GEN_PROTO := $(gen_src_dir)/test.proto.timestamp
 $(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
 $(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
 $(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index db60794..64da677 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -76,6 +76,7 @@
                         return -errno;
                     }
                 } else if (amt == 0) {
+                    VLOG("Reached EOF of fd=%d", fd);
                     break;
                 }
                 mBuffer.wp()->move(amt);
@@ -156,10 +157,10 @@
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
                     VLOG("Fail to read fd %d: %s", fd, strerror(errno));
                     return -errno;
-                }                   // otherwise just continue
-            } else if (amt == 0) {  // reach EOF so don't have to poll pfds[0].
-                ::close(pfds[0].fd);
-                pfds[0].fd = -1;
+                }  // otherwise just continue
+            } else if (amt == 0) {
+                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;
                 cirSize += amt;
@@ -187,6 +188,7 @@
 
         // 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);
             ::close(pfds[1].fd);
             pfds[1].fd = -1;
         }
@@ -207,6 +209,7 @@
                 return -errno;
             }  // otherwise just continue
         } else if (amt == 0) {
+            VLOG("Reached EOF of fromFd %d", fromFd);
             break;
         } else {
             mBuffer.wp()->move(amt);
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 5bfa093..66a3de1 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -26,7 +26,7 @@
 using namespace std;
 
 /**
- * Reads a file into a buffer, and then writes that data to an FdSet.
+ * Reads data from fd into a buffer, fd must be closed explicitly.
  */
 class FdBuffer {
 public:
@@ -83,6 +83,11 @@
      */
     EncodedBuffer::iterator data() const;
 
+    /**
+     * Return the internal buffer, don't call unless you are familiar with EncodedBuffer.
+     */
+    EncodedBuffer* getInternalBuffer() { return &mBuffer; }
+
 private:
     EncodedBuffer mBuffer;
     int64_t mStartTime;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 64eae3a..334d77c 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -18,12 +18,8 @@
 
 #include "Section.h"
 
-#include <errno.h>
-#include <sys/prctl.h>
-#include <unistd.h>
 #include <wait.h>
 
-#include <memory>
 #include <mutex>
 
 #include <android-base/file.h>
@@ -37,6 +33,7 @@
 #include "FdBuffer.h"
 #include "Privacy.h"
 #include "PrivacyBuffer.h"
+#include "frameworks/base/core/proto/android/os/data.proto.h"
 #include "frameworks/base/core/proto/android/util/log.proto.h"
 #include "incidentd_util.h"
 
@@ -52,31 +49,11 @@
 const int WAIT_MAX = 5;
 const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
 const char INCIDENT_HELPER[] = "/system/bin/incident_helper";
+const char GZIP[] = "/system/bin/gzip";
 
-static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe,
-                                          Fpipe& c2pPipe) {
+static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c2pPipe) {
     const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL};
-    // fork used in multithreaded environment, avoid adding unnecessary code in child process
-    pid_t pid = fork();
-    if (pid == 0) {
-        if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 || !p2cPipe.close() ||
-            TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 || !c2pPipe.close()) {
-            ALOGW("%s can't setup stdin and stdout for incident helper", name);
-            _exit(EXIT_FAILURE);
-        }
-
-        /* make sure the child dies when incidentd dies */
-        prctl(PR_SET_PDEATHSIG, SIGKILL);
-
-        execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
-
-        ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
-        _exit(EXIT_FAILURE);  // always exits with failure if any
-    }
-    // close the fds used in incident helper
-    close(p2cPipe.readFd());
-    close(c2pPipe.writeFd());
-    return pid;
+    return fork_execute_cmd(INCIDENT_HELPER, const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
 }
 
 // ================================================================================
@@ -254,10 +231,12 @@
     return NO_ERROR;
 }
 // ================================================================================
+static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sys/", 5) == 0; }
+
 FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
     : Section(id, timeoutMs), mFilename(filename) {
     name = filename;
-    mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
+    mIsSysfs = isSysfs(filename);
 }
 
 FileSection::~FileSection() {}
@@ -280,7 +259,7 @@
         return -errno;
     }
 
-    pid_t pid = fork_execute_incident_helper(this->id, this->name.string(), p2cPipe, c2pPipe);
+    pid_t pid = fork_execute_incident_helper(this->id, &p2cPipe, &c2pPipe);
     if (pid == -1) {
         ALOGW("FileSection '%s' failed to fork", this->name.string());
         return -errno;
@@ -289,6 +268,8 @@
     // parent process
     status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
                                                            this->timeoutMs, mIsSysfs);
+    close(fd);  // close the fd anyway.
+
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
               this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -313,7 +294,99 @@
 
     return NO_ERROR;
 }
+// ================================================================================
+GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) {
+    name = "gzip ";
+    name += filename;
+    va_list args;
+    va_start(args, filename);
+    mFilenames = varargs(filename, args);
+    va_end(args);
+}
 
+GZipSection::~GZipSection() {}
+
+status_t GZipSection::Execute(ReportRequestSet* requests) const {
+    // Reads the files in order, use the first available one.
+    int index = 0;
+    int fd = -1;
+    while (mFilenames[index] != NULL) {
+        fd = open(mFilenames[index], O_RDONLY | O_CLOEXEC);
+        if (fd != -1) {
+            break;
+        }
+        ALOGW("GZipSection failed to open file %s", mFilenames[index]);
+        index++;  // look at the next file.
+    }
+    VLOG("GZipSection is using file %s, fd=%d", mFilenames[index], fd);
+    if (fd == -1) return -1;
+
+    FdBuffer buffer;
+    Fpipe p2cPipe;
+    Fpipe c2pPipe;
+    // initiate pipes to pass data to/from gzip
+    if (!p2cPipe.init() || !c2pPipe.init()) {
+        ALOGW("GZipSection '%s' failed to setup pipes", this->name.string());
+        return -errno;
+    }
+
+    const char* gzipArgs[]{GZIP, NULL};
+    pid_t pid = fork_execute_cmd(GZIP, const_cast<char**>(gzipArgs), &p2cPipe, &c2pPipe);
+    if (pid == -1) {
+        ALOGW("GZipSection '%s' failed to fork", this->name.string());
+        return -errno;
+    }
+    // parent process
+
+    // construct Fdbuffer to output GZippedfileProto, the reason to do this instead of using
+    // ProtoOutputStream is to avoid allocation of another buffer inside ProtoOutputStream.
+    EncodedBuffer* internalBuffer = buffer.getInternalBuffer();
+    internalBuffer->writeHeader((uint32_t)GZippedFileProto::FILENAME, WIRE_TYPE_LENGTH_DELIMITED);
+    String8 usedFile(mFilenames[index]);
+    internalBuffer->writeRawVarint32(usedFile.size());
+    for (size_t i = 0; i < usedFile.size(); i++) {
+        internalBuffer->writeRawByte(mFilenames[index][i]);
+    }
+    internalBuffer->writeHeader((uint32_t)GZippedFileProto::GZIPPED_DATA,
+                                WIRE_TYPE_LENGTH_DELIMITED);
+    size_t editPos = internalBuffer->wp()->pos();
+    internalBuffer->wp()->move(8);  // reserve 8 bytes for the varint of the data size.
+    size_t dataBeginAt = internalBuffer->wp()->pos();
+    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]));
+    close(fd);  // close the fd anyway.
+
+    if (readStatus != NO_ERROR || buffer.timedOut()) {
+        ALOGW("GZipSection '%s' failed to read data from gzip: %s, timedout: %s",
+              this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+        kill_child(pid);
+        return readStatus;
+    }
+
+    status_t gzipStatus = wait_child(pid);
+    if (gzipStatus != NO_ERROR) {
+        ALOGW("GZipSection '%s' abnormal child process: %s", this->name.string(),
+              strerror(-gzipStatus));
+        return gzipStatus;
+    }
+    // Revisit the actual size from gzip result and edit the internal buffer accordingly.
+    size_t dataSize = buffer.size() - dataBeginAt;
+    internalBuffer->wp()->rewind()->move(editPos);
+    internalBuffer->writeRawVarint32(dataSize);
+    internalBuffer->copy(dataBeginAt, dataSize);
+    VLOG("GZipSection '%s' wrote %zd bytes in %d ms, dataSize=%zd", this->name.string(),
+         buffer.size(), (int)buffer.durationMs(), dataSize);
+    status_t err = write_report_requests(this->id, buffer, requests);
+    if (err != NO_ERROR) {
+        ALOGW("GZipSection '%s' failed writing: %s", this->name.string(), strerror(-err));
+        return err;
+    }
+
+    return NO_ERROR;
+}
 // ================================================================================
 struct WorkerThreadData : public virtual RefBase {
     const WorkerThreadSection* section;
@@ -457,42 +530,20 @@
 }
 
 // ================================================================================
-void CommandSection::init(const char* command, va_list args) {
-    va_list copied_args;
-    int numOfArgs = 0;
-
-    va_copy(copied_args, args);
-    while (va_arg(copied_args, const char*) != NULL) {
-        numOfArgs++;
-    }
-    va_end(copied_args);
-
-    // allocate extra 1 for command and 1 for NULL terminator
-    mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2));
-
-    mCommand[0] = command;
-    name = command;
-    for (int i = 0; i < numOfArgs; i++) {
-        const char* arg = va_arg(args, const char*);
-        mCommand[i + 1] = arg;
-        name += " ";
-        name += arg;
-    }
-    mCommand[numOfArgs + 1] = NULL;
-}
-
 CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
     : Section(id, timeoutMs) {
+    name = command;
     va_list args;
     va_start(args, command);
-    init(command, args);
+    mCommand = varargs(command, args);
     va_end(args);
 }
 
 CommandSection::CommandSection(int id, const char* command, ...) : Section(id) {
+    name = command;
     va_list args;
     va_start(args, command);
-    init(command, args);
+    mCommand = varargs(command, args);
     va_end(args);
 }
 
@@ -527,7 +578,7 @@
               strerror(errno));
         _exit(err);  // exit with command error code
     }
-    pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe);
+    pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe);
     if (ihPid == -1) {
         ALOGW("CommandSection '%s' failed to fork", this->name.string());
         return -errno;
@@ -544,8 +595,7 @@
     }
 
     // TODO: wait for command here has one trade-off: the failed status of command won't be detected
-    // until
-    //       buffer timeout, but it has advatage on starting the data stream earlier.
+    // until buffer timeout, but it has advatage on starting the data stream earlier.
     status_t cmdStatus = wait_child(cmdPid);
     status_t ihStatus = wait_child(ihPid);
     if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index d644681..8294be1 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -84,6 +84,21 @@
 };
 
 /**
+ * Section that reads in a file and gzips the content.
+ */
+class GZipSection : public Section {
+public:
+    GZipSection(int id, const char* filename, ...);
+    virtual ~GZipSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+    // It looks up the content from multiple files and stops when the first one is available.
+    const char** mFilenames;
+};
+
+/**
  * Base class for sections that call a command that might need a timeout.
  */
 class WorkerThreadSection : public Section {
@@ -111,8 +126,6 @@
 
 private:
     const char** mCommand;
-
-    void init(const char* command, va_list args);
 };
 
 /**
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 2415860..fc7cec9 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -13,8 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
+#include "Log.h"
+
 #include "incidentd_util.h"
 
+#include <sys/prctl.h>
+
 #include "section_list.h"
 
 const Privacy* get_privacy_of_section(int id) {
@@ -50,4 +55,49 @@
 
 int Fpipe::readFd() const { return mRead.get(); }
 
-int Fpipe::writeFd() const { return mWrite.get(); }
\ No newline at end of file
+int Fpipe::writeFd() const { return mWrite.get(); }
+
+pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output) {
+    // fork used in multithreaded environment, avoid adding unnecessary code in child process
+    pid_t pid = fork();
+    if (pid == 0) {
+        if (TEMP_FAILURE_RETRY(dup2(input->readFd(), STDIN_FILENO)) < 0 || !input->close() ||
+            TEMP_FAILURE_RETRY(dup2(output->writeFd(), STDOUT_FILENO)) < 0 || !output->close()) {
+            ALOGW("Can't setup stdin and stdout for command %s", cmd);
+            _exit(EXIT_FAILURE);
+        }
+
+        /* make sure the child dies when incidentd dies */
+        prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+        execv(cmd, argv);
+
+        ALOGW("%s failed in the child process: %s", cmd, strerror(errno));
+        _exit(EXIT_FAILURE);  // always exits with failure if any
+    }
+    // close the fds used in child process.
+    close(input->readFd());
+    close(output->writeFd());
+    return pid;
+}
+// ================================================================================
+const char** varargs(const char* first, va_list rest) {
+    va_list copied_rest;
+    int numOfArgs = 1;  // first is already count.
+
+    va_copy(copied_rest, rest);
+    while (va_arg(copied_rest, const char*) != NULL) {
+        numOfArgs++;
+    }
+    va_end(copied_rest);
+
+    // allocate extra 1 for NULL terminator
+    const char** ret = (const char**)malloc(sizeof(const char*) * (numOfArgs + 1));
+    ret[0] = first;
+    for (int i = 0; i < numOfArgs; i++) {
+        const char* arg = va_arg(rest, const char*);
+        ret[i + 1] = arg;
+    }
+    ret[numOfArgs + 1] = NULL;
+    return ret;
+}
diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h
index 09aa040..db7ec82 100644
--- a/cmds/incidentd/src/incidentd_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -20,12 +20,20 @@
 
 #include <android-base/unique_fd.h>
 
+#include <stdarg.h>
+
 #include "Privacy.h"
 
 using namespace android::base;
 
+/**
+ * Looks up Privacy of a section in the auto-gen PRIVACY_POLICY_LIST;
+ */
 const Privacy* get_privacy_of_section(int id);
 
+/**
+ * This class wraps android::base::Pipe.
+ */
 class Fpipe {
 public:
     Fpipe();
@@ -41,4 +49,15 @@
     unique_fd mWrite;
 };
 
+/**
+ * Forks and exec a command with two pipes, one connects stdin for input,
+ * one connects stdout for output. It returns the pid of the child.
+ */
+pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output);
+
+/**
+ * Grabs varargs from stack and stores them in heap with NULL-terminated array.
+ */
+const char** varargs(const char* first, va_list rest);
+
 #endif  // INCIDENTD_UTIL_H
\ No newline at end of file
diff --git a/cmds/incidentd/testdata/kmsg.txt b/cmds/incidentd/testdata/kmsg.txt
new file mode 100644
index 0000000..a8e3c02
--- /dev/null
+++ b/cmds/incidentd/testdata/kmsg.txt
@@ -0,0 +1,47 @@
+[0] bldr_log_init: bldr_log_base=0x83600000, bldr_log_size=458752
+B -    626409 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B -    729255 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B -    729285 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 9:0
+D -    104829 - APPSBL Image Loaded, Delta - (2498816 Bytes)
+B -    729468 - SBL1, End
+D -    643611 - SBL1, Delta
+S - Flash Throughput, 129000 KB/s  (4729638 Bytes,  36613 us)
+S - DDR Frequency, 1017 MHz
+0x400, 0x400
+B -    482296 - Basic DDR tests done
+B -    544638 - clock_init, Start
+D -       244 - clock_init, Delta
+B -    544913 - HTC RPM DATARAM UPDATE info: Done
+B -    545004 - Image Load, Start
+B -    548359 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 5:0
+D -      3386 - QSEE Dev Config Image Loaded, Delta - (46232 Bytes)
+B -    548725 - Image Load, Start
+B -    550860 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 512:0
+D -      2166 - APDP Image Loaded, Delta - (7696 Bytes)
+B -    550891 - Image Load, Start
+B -    601612 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 7:0
+D -     50782 - QSEE Image Loaded, Delta - (1648640 Bytes)
+B -    601704 - Image Load, Start
+D -       244 - SEC Image Loaded, Delta - (4096 Bytes)
+B -    602344 - 0x1310 = 0x24
+B -    602375 - is_above_vbat_weak = 1, pon_reasons (with usb_in checked) = 0x31
+B -    602466 - sbl1_efs_handle_cookies, Start
+D -        91 - sbl1_efs_handle_cookies, Delta
+B -    602558 - Image Load, Start
+B -    613446 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 21:0
+D -     11010 - QHEE Image Loaded, Delta - (258280 Bytes)
+B -    613568 - Image Load, Start
+B -    624274 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 10:0
+D -     10736 - RPM Image Loaded, Delta - (224104 Bytes)
+B -    624335 - Image Load, Start
+D -         0 - STI Image Loaded, Delta - (0 Bytes)
+B -    624548 - Image Load, Start
+m_driver_init, Delta
+B -    471804 - pm_sbl_chg
+ ******************** [ START SECOND] ******************** 
+^@B -    736605 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B -    839451 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device
+B -    839482 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 9:0
+D -    104828 - APPSBL Image Loaded, Delta - (2498816 Bytes)
+B -    839665 - SBL1, End
+D -    753838 - SBL1, Delta
diff --git a/cmds/incidentd/testdata/kmsg.txt.gz b/cmds/incidentd/testdata/kmsg.txt.gz
new file mode 100644
index 0000000..fba449f
--- /dev/null
+++ b/cmds/incidentd/testdata/kmsg.txt.gz
Binary files differ
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 026bf74..1528224 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -39,10 +39,22 @@
 using namespace android::os;
 using namespace std;
 using ::testing::StrEq;
+using ::testing::Test;
 using ::testing::internal::CaptureStdout;
 using ::testing::internal::GetCapturedStdout;
 
 // NOTICE: this test requires /system/bin/incident_helper is installed.
+class SectionTest : public Test {
+public:
+    virtual void SetUp() override { ASSERT_NE(tf.fd, -1); }
+
+protected:
+    TemporaryFile tf;
+    ReportRequestSet requests;
+
+    const std::string kTestPath = GetExecutableDirectory();
+    const std::string kTestDataPath = kTestPath + "/testdata/";
+};
 
 class SimpleListener : public IIncidentReportStatusListener {
 public:
@@ -58,10 +70,8 @@
     virtual IBinder* onAsBinder() override { return nullptr; };
 };
 
-TEST(SectionTest, HeaderSection) {
-    TemporaryFile output2;
+TEST_F(SectionTest, HeaderSection) {
     HeaderSection hs;
-    ReportRequestSet requests;
 
     IncidentReportArgs args1, args2;
     args1.addSection(1);
@@ -77,7 +87,7 @@
     args2.addHeader(head2);
 
     requests.add(new ReportRequest(args1, new SimpleListener(), -1));
-    requests.add(new ReportRequest(args2, new SimpleListener(), output2.fd));
+    requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd));
     requests.setMainFd(STDOUT_FILENO);
 
     string content;
@@ -87,28 +97,25 @@
                                            "\x12\x3"
                                            "axe\n\x05\x12\x03pup"));
 
-    EXPECT_TRUE(ReadFileToString(output2.path, &content));
+    EXPECT_TRUE(ReadFileToString(tf.path, &content));
     EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
 }
 
-TEST(SectionTest, MetadataSection) {
+TEST_F(SectionTest, MetadataSection) {
     MetadataSection ms;
-    ReportRequestSet requests;
 
     requests.setMainFd(STDOUT_FILENO);
     requests.sectionStats(1)->set_success(true);
 
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, ms.Execute(&requests));
-    EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1"));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b(\x1"
+                                           "2\x4\b\x1\x10\x1"));
 }
 
-TEST(SectionTest, FileSection) {
-    TemporaryFile tf;
+TEST_F(SectionTest, FileSection) {
     FileSection fs(REVERSE_PARSER, tf.path);
-    ReportRequestSet requests;
 
-    ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path));
 
     requests.setMainFd(STDOUT_FILENO);
@@ -120,66 +127,79 @@
     EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai"));
 }
 
-TEST(SectionTest, FileSectionTimeout) {
-    TemporaryFile tf;
-    // id -1 is timeout parser
+TEST_F(SectionTest, FileSectionTimeout) {
     FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS);
-    ReportRequestSet requests;
     ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
 }
 
-TEST(SectionTest, CommandSectionConstructor) {
+TEST_F(SectionTest, GZipSection) {
+    const std::string testFile = kTestDataPath + "kmsg.txt";
+    const std::string testGzFile = testFile + ".gz";
+    GZipSection gs(NOOP_PARSER, "/tmp/nonexist", testFile.c_str(), NULL);
+
+    requests.setMainFd(tf.fd);
+    requests.setMainDest(android::os::DEST_LOCAL);
+
+    ASSERT_EQ(NO_ERROR, gs.Execute(&requests));
+    std::string expect, gzFile, actual;
+    ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile));
+    ASSERT_TRUE(ReadFileToString(tf.path, &actual));
+    expect = "\x2\xC6\x6\n\"" + testFile + "\x12\x9F\x6" + gzFile;
+    EXPECT_THAT(actual, StrEq(expect));
+}
+
+TEST_F(SectionTest, GZipSectionNoFileFound) {
+    GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL);
+    requests.setMainFd(STDOUT_FILENO);
+    ASSERT_EQ(-1, gs.Execute(&requests));
+}
+
+TEST_F(SectionTest, CommandSectionConstructor) {
     CommandSection cs1(1, "echo", "\"this is a test\"", "ooo", NULL);
     CommandSection cs2(2, "single_command", NULL);
     CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL);
     CommandSection cs4(2, 43214, "single_command", NULL);
 
-    EXPECT_THAT(cs1.name.string(), StrEq("echo \"this is a test\" ooo"));
+    EXPECT_THAT(cs1.name.string(), StrEq("echo"));
     EXPECT_THAT(cs2.name.string(), StrEq("single_command"));
     EXPECT_EQ(3123, cs3.timeoutMs);
     EXPECT_EQ(43214, cs4.timeoutMs);
-    EXPECT_THAT(cs3.name.string(), StrEq("echo \"this is a test\" ooo"));
+    EXPECT_THAT(cs3.name.string(), StrEq("echo"));
     EXPECT_THAT(cs4.name.string(), StrEq("single_command"));
 }
 
-TEST(SectionTest, CommandSectionEcho) {
+TEST_F(SectionTest, CommandSectionEcho) {
     CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL);
-    ReportRequestSet requests;
     requests.setMainFd(STDOUT_FILENO);
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
     EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba"));
 }
 
-TEST(SectionTest, CommandSectionCommandTimeout) {
+TEST_F(SectionTest, CommandSectionCommandTimeout) {
     CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
-    ReportRequestSet requests;
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
 }
 
-TEST(SectionTest, CommandSectionIncidentHelperTimeout) {
+TEST_F(SectionTest, CommandSectionIncidentHelperTimeout) {
     CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
-    ReportRequestSet requests;
     requests.setMainFd(STDOUT_FILENO);
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
 }
 
-TEST(SectionTest, CommandSectionBadCommand) {
+TEST_F(SectionTest, CommandSectionBadCommand) {
     CommandSection cs(NOOP_PARSER, "echoo", "about", NULL);
-    ReportRequestSet requests;
     ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
 }
 
-TEST(SectionTest, CommandSectionBadCommandAndTimeout) {
+TEST_F(SectionTest, CommandSectionBadCommandAndTimeout) {
     CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
-    ReportRequestSet requests;
     // timeout will return first
     ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
 }
 
-TEST(SectionTest, LogSectionBinary) {
+TEST_F(SectionTest, LogSectionBinary) {
     LogSection ls(1, LOG_ID_EVENTS);
-    ReportRequestSet requests;
     requests.setMainFd(STDOUT_FILENO);
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
@@ -187,9 +207,8 @@
     EXPECT_FALSE(results.empty());
 }
 
-TEST(SectionTest, LogSectionSystem) {
+TEST_F(SectionTest, LogSectionSystem) {
     LogSection ls(1, LOG_ID_SYSTEM);
-    ReportRequestSet requests;
     requests.setMainFd(STDOUT_FILENO);
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
@@ -197,12 +216,9 @@
     EXPECT_FALSE(results.empty());
 }
 
-TEST(SectionTest, TestFilterPiiTaggedFields) {
-    TemporaryFile tf;
+TEST_F(SectionTest, TestFilterPiiTaggedFields) {
     FileSection fs(NOOP_PARSER, tf.path);
-    ReportRequestSet requests;
 
-    ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
 
     requests.setMainFd(STDOUT_FILENO);
@@ -212,11 +228,9 @@
     EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
 }
 
-TEST(SectionTest, TestBadFdRequest) {
-    TemporaryFile input;
-    FileSection fs(NOOP_PARSER, input.path);
-    ReportRequestSet requests;
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+TEST_F(SectionTest, TestBadFdRequest) {
+    FileSection fs(NOOP_PARSER, tf.path);
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
 
     IncidentReportArgs args;
     args.setAll(true);
@@ -231,11 +245,9 @@
     EXPECT_EQ(badFdRequest->err, -EBADF);
 }
 
-TEST(SectionTest, TestBadRequests) {
-    TemporaryFile input;
-    FileSection fs(NOOP_PARSER, input.path);
-    ReportRequestSet requests;
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+TEST_F(SectionTest, TestBadRequests) {
+    FileSection fs(NOOP_PARSER, tf.path);
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
 
     IncidentReportArgs args;
     args.setAll(true);
@@ -244,16 +256,14 @@
     EXPECT_EQ(fs.Execute(&requests), -EBADF);
 }
 
-TEST(SectionTest, TestMultipleRequests) {
-    TemporaryFile input, output1, output2, output3;
-    FileSection fs(NOOP_PARSER, input.path);
-    ReportRequestSet requests;
+TEST_F(SectionTest, TestMultipleRequests) {
+    TemporaryFile output1, output2, output3;
+    FileSection fs(NOOP_PARSER, tf.path);
 
-    ASSERT_TRUE(input.fd != -1);
     ASSERT_TRUE(output1.fd != -1);
     ASSERT_TRUE(output2.fd != -1);
     ASSERT_TRUE(output3.fd != -1);
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
 
     IncidentReportArgs args1, args2, args3;
     args1.setAll(true);
@@ -286,17 +296,15 @@
     EXPECT_THAT(content, StrEq(""));
 }
 
-TEST(SectionTest, TestMultipleRequestsBySpec) {
-    TemporaryFile input, output1, output2, output3;
-    FileSection fs(NOOP_PARSER, input.path);
-    ReportRequestSet requests;
+TEST_F(SectionTest, TestMultipleRequestsBySpec) {
+    TemporaryFile output1, output2, output3;
+    FileSection fs(NOOP_PARSER, tf.path);
 
-    ASSERT_TRUE(input.fd != -1);
     ASSERT_TRUE(output1.fd != -1);
     ASSERT_TRUE(output2.fd != -1);
     ASSERT_TRUE(output3.fd != -1);
 
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
 
     IncidentReportArgs args1, args2, args3;
     args1.setAll(true);
@@ -328,4 +336,4 @@
     c = (char)STRING_FIELD_2.size();
     EXPECT_TRUE(ReadFileToString(output3.path, &content));
     EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2));
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d7ce352..7f76ab1 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -21,9 +21,11 @@
     src/statsd_config.proto \
     src/FieldValue.cpp \
     src/stats_log_util.cpp \
-    src/anomaly/AnomalyMonitor.cpp \
+    src/anomaly/AlarmMonitor.cpp \
+    src/anomaly/AlarmTracker.cpp \
     src/anomaly/AnomalyTracker.cpp \
     src/anomaly/DurationAnomalyTracker.cpp \
+    src/anomaly/subscriber_util.cpp \
     src/condition/CombinationConditionTracker.cpp \
     src/condition/condition_util.cpp \
     src/condition/SimpleConditionTracker.cpp \
@@ -172,7 +174,8 @@
     src/atom_field_options.proto \
     src/atoms.proto \
     src/stats_log.proto \
-    tests/AnomalyMonitor_test.cpp \
+    tests/AlarmMonitor_test.cpp \
+    tests/anomaly/AlarmTracker_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
     tests/external/puller_util_test.cpp \
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 21f30e2..b0e2c43 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -217,6 +217,14 @@
     const Field mMatcher;
     const int32_t mMask;
 
+    inline const Field& getMatcher() const {
+        return mMatcher;
+    }
+
+    inline int32_t getMask() const {
+        return mMask;
+    }
+
     bool hasAnyPositionMatcher(int* prefix) const {
         if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) {
             (*prefix) = mMatcher.getPrefix(2);
@@ -224,6 +232,10 @@
         }
         return false;
     }
+
+    inline bool operator!=(const Matcher& that) const {
+        return mMatcher != that.getMatcher() || mMask != that.getMask();
+    };
 };
 
 /**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 1502a00..cc706313 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -166,10 +166,11 @@
     }
 }
 
-void getDimensionForCondition(const LogEvent& event, Metric2Condition links,
+void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
+                              const Metric2Condition& links,
                               vector<HashableDimensionKey>* conditionDimension) {
     // Get the dimension first by using dimension from what.
-    filterValues(links.metricFields, event.getValues(), conditionDimension);
+    filterValues(links.metricFields, eventValues, conditionDimension);
 
     // Then replace the field with the dimension from condition.
     for (auto& dim : *conditionDimension) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 89fe317..57bdf68 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -40,7 +40,7 @@
         mValues = values;
     }
 
-    HashableDimensionKey(){};
+    HashableDimensionKey() {};
 
     HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){};
 
@@ -144,7 +144,8 @@
 void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values,
                        std::vector<FieldValue>* output);
 
-void getDimensionForCondition(const LogEvent& event, Metric2Condition links,
+void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
+                              const Metric2Condition& links,
                               std::vector<HashableDimensionKey>* conditionDimension);
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 3c9dd68..087e596 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -66,11 +66,13 @@
 #define STATS_DATA_DIR "/data/misc/stats-data"
 
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
-                                     const sp<AnomalyMonitor>& anomalyMonitor,
+                                     const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                                     const sp<AlarmMonitor>& periodicAlarmMonitor,
                                      const long timeBaseSec,
                                      const std::function<void(const ConfigKey&)>& sendBroadcast)
     : mUidMap(uidMap),
-      mAnomalyMonitor(anomalyMonitor),
+      mAnomalyAlarmMonitor(anomalyAlarmMonitor),
+      mPeriodicAlarmMonitor(periodicAlarmMonitor),
       mSendBroadcast(sendBroadcast),
       mTimeBaseSec(timeBaseSec) {
     StatsPullerManager statsPullerManager;
@@ -82,10 +84,19 @@
 
 void StatsLogProcessor::onAnomalyAlarmFired(
         const uint64_t timestampNs,
-        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     for (const auto& itr : mMetricsManagers) {
-        itr.second->onAnomalyAlarmFired(timestampNs, anomalySet);
+        itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
+    }
+}
+void StatsLogProcessor::onPeriodicAlarmFired(
+        const uint64_t timestampNs,
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
+
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    for (const auto& itr : mMetricsManagers) {
+        itr.second->onPeriodicAlarmFired(timestampNs, alarmSet);
     }
 }
 
@@ -170,7 +181,9 @@
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     VLOG("Updated configuration for key %s", key.ToString().c_str());
-    sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
+    sp<MetricsManager> newMetricsManager =
+        new MetricsManager(key, config, mTimeBaseSec, mUidMap,
+                           mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
         ALOGE("Can't accept more configs!");
@@ -179,7 +192,6 @@
 
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
-        newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
         if (newMetricsManager->shouldAddUidMapListener()) {
             // We have to add listener after the MetricsManager is constructed because it's
             // not safe to create wp or sp from this pointer inside its constructor.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1444306..4d9f185 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -34,7 +34,8 @@
 
 class StatsLogProcessor : public ConfigListener {
 public:
-    StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
+    StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
                       const long timeBaseSec,
                       const std::function<void(const ConfigKey&)>& sendBroadcast);
     virtual ~StatsLogProcessor();
@@ -48,10 +49,15 @@
 
     void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector<uint8_t>* outData);
 
-    /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */
+    /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
     void onAnomalyAlarmFired(
             const uint64_t timestampNs,
-            unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet);
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
+
+    /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
+    void onPeriodicAlarmFired(
+            const uint64_t timestampNs,
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
 
     /* Flushes data to disk. Data on memory will be gone after written to disk. */
     void WriteDataToDisk();
@@ -76,7 +82,9 @@
 
     StatsPullerManager mStatsPullerManager;
 
-    sp<AnomalyMonitor> mAnomalyMonitor;
+    sp<AlarmMonitor> mAnomalyAlarmMonitor;
+
+    sp<AlarmMonitor> mPeriodicAlarmMonitor;
 
     void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs,
                             vector<uint8_t>* outData);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c27b130..ee4f434 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -50,49 +50,73 @@
 constexpr const char* kPermissionDump = "android.permission.DUMP";
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
-// ======================================================================
 /**
  * Watches for the death of the stats companion (system process).
  */
 class CompanionDeathRecipient : public IBinder::DeathRecipient {
 public:
-    CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor);
+    CompanionDeathRecipient(const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                            const sp<AlarmMonitor>& periodicAlarmMonitor) :
+                                mAnomalyAlarmMonitor(anomalyAlarmMonitor),
+                                mPeriodicAlarmMonitor(periodicAlarmMonitor)  {}
     virtual void binderDied(const wp<IBinder>& who);
 
 private:
-    const sp<AnomalyMonitor> mAnomalyMonitor;
+   sp<AlarmMonitor> mAnomalyAlarmMonitor;
+   sp<AlarmMonitor> mPeriodicAlarmMonitor;
 };
 
-CompanionDeathRecipient::CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor)
-    : mAnomalyMonitor(anomalyMonitor) {
-}
-
 void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
     ALOGW("statscompanion service died");
-    mAnomalyMonitor->setStatsCompanionService(nullptr);
+    mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
+    mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
     SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
 }
 
-// ======================================================================
 StatsService::StatsService(const sp<Looper>& handlerLooper)
-    : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
-{
+    : mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
+       [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+           if (sc != nullptr) {
+               sc->setAnomalyAlarm(timeMillis);
+               StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
+           }
+       },
+       [](const sp<IStatsCompanionService>& sc) {
+           if (sc != nullptr) {
+               sc->cancelAnomalyAlarm();
+               StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
+           }
+       })),
+   mPeriodicAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
+      [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+           if (sc != nullptr) {
+               sc->setAlarmForSubscriberTriggering(timeMillis);
+               StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
+           }
+      },
+      [](const sp<IStatsCompanionService>& sc) {
+           if (sc != nullptr) {
+               sc->cancelAlarmForSubscriberTriggering();
+               StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
+           }
+
+      }))  {
     mUidMap = new UidMap();
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, getElapsedRealtimeSec(),
-        [this](const ConfigKey& key) {
-            sp<IStatsCompanionService> sc = getStatsCompanionService();
-            auto receiver = mConfigManager->GetConfigReceiver(key);
-            if (sc == nullptr) {
-                VLOG("Could not find StatsCompanionService");
-            } else if (receiver == nullptr) {
-                VLOG("Statscompanion could not find a broadcast receiver for %s",
-                     key.ToString().c_str());
-            } else {
-                sc->sendDataBroadcast(receiver);
-            }
+    mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
+                                       getElapsedRealtimeSec(), [this](const ConfigKey& key) {
+        sp<IStatsCompanionService> sc = getStatsCompanionService();
+        auto receiver = mConfigManager->GetConfigReceiver(key);
+        if (sc == nullptr) {
+            VLOG("Could not find StatsCompanionService");
+        } else if (receiver == nullptr) {
+            VLOG("Statscompanion could not find a broadcast receiver for %s",
+                 key.ToString().c_str());
+        } else {
+            sc->sendDataBroadcast(receiver);
         }
+    }
     );
 
     mConfigManager->AddListener(mProcessor);
@@ -423,6 +447,13 @@
             }
 
             if (args[1] == "update") {
+                char* endp;
+                int64_t configID = strtoll(name.c_str(), &endp, 10);
+                if (endp == name.c_str() || *endp != '\0') {
+                    fprintf(err, "Error parsing config ID.\n");
+                    return UNKNOWN_ERROR;
+                }
+
                 // Read stream into buffer.
                 string buffer;
                 if (!android::base::ReadFdToString(fileno(in), &buffer)) {
@@ -438,7 +469,7 @@
                 }
 
                 // Add / update the config.
-                mConfigManager->UpdateConfig(ConfigKey(uid, StrToInt64(name)), config);
+                mConfigManager->UpdateConfig(ConfigKey(uid, configID), config);
             } else {
                 if (argCount == 2) {
                     cmd_remove_all_configs(out);
@@ -615,7 +646,8 @@
 
 status_t StatsService::cmd_clear_puller_cache(FILE* out) {
     IPCThreadState* ipc = IPCThreadState::self();
-    VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
+    VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
+            ipc->getCallingPid(), ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
         int cleared = mStatsPullerManager.ForceClearPullerCache();
         fprintf(out, "Puller removed %d cached data!\n", cleared);
@@ -670,18 +702,40 @@
         return Status::fromExceptionCode(Status::EX_SECURITY,
                                          "Only system uid can call informAnomalyAlarmFired");
     }
+
     uint64_t currentTimeSec = getElapsedRealtimeSec();
-    std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet =
-            mAnomalyMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
-    if (anomalySet.size() > 0) {
+    std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
+            mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+    if (alarmSet.size() > 0) {
         VLOG("Found an anomaly alarm that fired.");
-        mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet);
+        mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet);
     } else {
         VLOG("Cannot find an anomaly alarm that fired. Perhaps it was recently cancelled.");
     }
     return Status::ok();
 }
 
+Status StatsService::informAlarmForSubscriberTriggeringFired() {
+    VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
+
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(
+                Status::EX_SECURITY,
+                "Only system uid can call informAlarmForSubscriberTriggeringFired");
+    }
+
+    uint64_t currentTimeSec = time(nullptr);
+    std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
+            mPeriodicAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+    if (alarmSet.size() > 0) {
+        VLOG("Found periodic alarm fired.");
+        mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet);
+    } else {
+        ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled.");
+    }
+    return Status::ok();
+}
+
 Status StatsService::informPollAlarmFired() {
     VLOG("StatsService::informPollAlarmFired was called");
 
@@ -766,10 +820,11 @@
                 "statscompanion unavailable despite it contacting statsd!");
     }
     VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
-    IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor));
-    mAnomalyMonitor->setStatsCompanionService(statsCompanion);
+    IInterface::asBinder(statsCompanion)->linkToDeath(
+            new CompanionDeathRecipient(mAnomalyAlarmMonitor, mPeriodicAlarmMonitor));
+    mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
+    mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
     SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
-
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 9690de7..e0a1299 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -18,7 +18,7 @@
 #define STATS_SERVICE_H
 
 #include "StatsLogProcessor.h"
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
 #include "config/ConfigManager.h"
 #include "external/StatsPullerManager.h"
 #include "packages/UidMap.h"
@@ -58,6 +58,8 @@
     virtual Status statsCompanionReady();
     virtual Status informAnomalyAlarmFired();
     virtual Status informPollAlarmFired();
+    virtual Status informAlarmForSubscriberTriggeringFired();
+
     virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                     const vector<String16>& app);
     virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
@@ -244,9 +246,14 @@
     sp<StatsLogProcessor> mProcessor;
 
     /**
-     * The anomaly detector.
+     * The alarm monitor for anomaly detection.
      */
-    const sp<AnomalyMonitor> mAnomalyMonitor;
+    const sp<AlarmMonitor> mAnomalyAlarmMonitor;
+
+    /**
+     * The alarm monitor for alarms to directly trigger subscriber.
+     */
+    const sp<AlarmMonitor> mPeriodicAlarmMonitor;
 
     /**
      * Whether this is an eng build.
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
similarity index 74%
rename from cmds/statsd/src/anomaly/AnomalyMonitor.cpp
rename to cmds/statsd/src/anomaly/AlarmMonitor.cpp
index ca34dc6..78f0c2b 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -17,21 +17,24 @@
 #define DEBUG false
 #include "Log.h"
 
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
 #include "guardrail/StatsdStats.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
-AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec)
-    : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) {
-}
+AlarmMonitor::AlarmMonitor(
+        uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
+        const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
+        const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
+    : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+      mUpdateAlarm(updateAlarm),
+      mCancelAlarm(cancelAlarm) {}
 
-AnomalyMonitor::~AnomalyMonitor() {
-}
+AlarmMonitor::~AlarmMonitor() {}
 
-void AnomalyMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
     std::lock_guard<std::mutex> lock(mLock);
     sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
@@ -40,13 +43,13 @@
         return;
     }
     VLOG("Creating link to statsCompanionService");
-    const sp<const AnomalyAlarm> top = mPq.top();
+    const sp<const InternalAlarm> top = mPq.top();
     if (top != nullptr) {
         updateRegisteredAlarmTime_l(top->timestampSec);
     }
 }
 
-void AnomalyMonitor::add(sp<const AnomalyAlarm> alarm) {
+void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
     std::lock_guard<std::mutex> lock(mLock);
     if (alarm == nullptr) {
         ALOGW("Asked to add a null alarm.");
@@ -66,7 +69,7 @@
     }
 }
 
-void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) {
+void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
     std::lock_guard<std::mutex> lock(mLock);
     if (alarm == nullptr) {
         ALOGW("Asked to remove a null alarm.");
@@ -89,13 +92,13 @@
 
 // More efficient than repeatedly calling remove(mPq.top()) since it batches the
 // updates to the registered alarm.
-unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popSoonerThan(
+unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> AlarmMonitor::popSoonerThan(
         uint32_t timestampSec) {
     VLOG("Removing alarms with time <= %u", timestampSec);
-    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms;
+    unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> oldAlarms;
     std::lock_guard<std::mutex> lock(mLock);
 
-    for (sp<const AnomalyAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
+    for (sp<const InternalAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
         t = mPq.top()) {
         oldAlarms.insert(t);
         mPq.pop();  // remove t
@@ -113,25 +116,19 @@
     return oldAlarms;
 }
 
-void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
+void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
     VLOG("Updating reg alarm time to %u", timestampSec);
     mRegisteredAlarmTimeSec = timestampSec;
-    if (mStatsCompanionService != nullptr) {
-        mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
-        StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
-    }
+    mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec));
 }
 
-void AnomalyMonitor::cancelRegisteredAlarmTime_l() {
+void AlarmMonitor::cancelRegisteredAlarmTime_l() {
     VLOG("Cancelling reg alarm.");
     mRegisteredAlarmTimeSec = 0;
-    if (mStatsCompanionService != nullptr) {
-        mStatsCompanionService->cancelAnomalyAlarm();
-        StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
-    }
+    mCancelAlarm(mStatsCompanionService);
 }
 
-int64_t AnomalyMonitor::secToMs(uint32_t timeSec) {
+int64_t AlarmMonitor::secToMs(uint32_t timeSec) {
     return ((int64_t)timeSec) * 1000;
 }
 
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
similarity index 77%
rename from cmds/statsd/src/anomaly/AnomalyMonitor.h
rename to cmds/statsd/src/anomaly/AlarmMonitor.h
index 7acc7904..3badb1f 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -41,33 +41,34 @@
  * threshold.
  * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
  */
-struct AnomalyAlarm : public RefBase {
-    AnomalyAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
+struct InternalAlarm : public RefBase {
+    InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
     }
 
     const uint32_t timestampSec;
 
-    /** AnomalyAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
+    /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
     struct SmallerTimestamp {
-        bool operator()(sp<const AnomalyAlarm> a, sp<const AnomalyAlarm> b) const {
+        bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const {
             return (a->timestampSec < b->timestampSec);
         }
     };
 };
 
-// TODO: Rename this file to AnomalyAlarmMonitor.
 /**
- * Manages alarms for Anomaly Detection.
+ * Manages internal alarms that may get registered with the AlarmManager.
  */
-class AnomalyMonitor : public RefBase {
+class AlarmMonitor : public RefBase {
 public:
     /**
      * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
      * from the registered alarm by more than this amount, update the registered
      * alarm.
      */
-    AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec);
-    ~AnomalyMonitor();
+    AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
+                 const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
+                 const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+    ~AlarmMonitor();
 
     /**
      * Tells AnomalyMonitor what IStatsCompanionService to use and, if
@@ -80,20 +81,20 @@
     /**
      * Adds the given alarm (reference) to the queue.
      */
-    void add(sp<const AnomalyAlarm> alarm);
+    void add(sp<const InternalAlarm> alarm);
 
     /**
      * Removes the given alarm (reference) from the queue.
      * Note that alarm comparison is reference-based; if another alarm exists
      * with the same timestampSec, that alarm will still remain in the queue.
      */
-    void remove(sp<const AnomalyAlarm> alarm);
+    void remove(sp<const InternalAlarm> alarm);
 
     /**
      * Returns and removes all alarms whose timestamp <= the given timestampSec.
      * Always updates the registered alarm if return is non-empty.
      */
-    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan(
+    unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> popSoonerThan(
             uint32_t timestampSec);
 
     /**
@@ -119,7 +120,7 @@
     /**
      * Priority queue of alarms, prioritized by soonest alarm.timestampSec.
      */
-    indexed_priority_queue<AnomalyAlarm, AnomalyAlarm::SmallerTimestamp> mPq;
+    indexed_priority_queue<InternalAlarm, InternalAlarm::SmallerTimestamp> mPq;
 
     /**
      * Binder interface for communicating with StatsCompanionService.
@@ -146,6 +147,13 @@
 
     /** Converts uint32 timestamp in seconds to a Java long in msec. */
     int64_t secToMs(uint32_t timeSec);
+
+    // Callback function to update the alarm via StatsCompanionService.
+    std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+
+    // Callback function to cancel the alarm via StatsCompanionService.
+    std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
new file mode 100644
index 0000000..eb28383
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include "anomaly/AlarmTracker.h"
+#include "anomaly/subscriber_util.h"
+#include "HashableDimensionKey.h"
+#include "stats_util.h"
+#include "storage/StorageManager.h"
+
+#include <statslog.h>
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AlarmTracker::AlarmTracker(uint64_t startMillis,
+                           const Alarm& alarm, const ConfigKey& configKey,
+                           const sp<AlarmMonitor>& alarmMonitor)
+    : mAlarmConfig(alarm),
+      mConfigKey(configKey),
+      mAlarmMonitor(alarmMonitor) {
+    VLOG("AlarmTracker() called");
+    mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC;
+    mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
+    mAlarmMonitor->add(mInternalAlarm);
+}
+
+AlarmTracker::~AlarmTracker() {
+    VLOG("~AlarmTracker() called");
+    if (mInternalAlarm != nullptr) {
+        mAlarmMonitor->remove(mInternalAlarm);
+    }
+}
+
+void AlarmTracker::addSubscription(const Subscription& subscription) {
+    mSubscriptions.push_back(subscription);
+}
+
+uint64_t AlarmTracker::findNextAlarmSec(uint64_t currentTimeSec) {
+    int periodsForward = (currentTimeSec - mAlarmSec) * MS_PER_SEC / mAlarmConfig.period_millis();
+    return mAlarmSec + (periodsForward + 1) * mAlarmConfig.period_millis() / MS_PER_SEC;
+}
+
+void AlarmTracker::informAlarmsFired(
+        const uint64_t& timestampNs,
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
+    if (firedAlarms.empty() || firedAlarms.find(mInternalAlarm) == firedAlarms.end()) {
+        return;
+    }
+    if (!mSubscriptions.empty()) {
+        triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
+                           mSubscriptions);
+    }
+    firedAlarms.erase(mInternalAlarm);
+    mAlarmSec = findNextAlarmSec(timestampNs / NS_PER_SEC);
+    mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
+    mAlarmMonitor->add(mInternalAlarm);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
new file mode 100644
index 0000000..d59dacaa
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+
+#include "AlarmMonitor.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alarm
+
+#include <android/os/IStatsCompanionService.h>
+#include <stdlib.h>
+#include <utils/RefBase.h>
+
+using android::os::IStatsCompanionService;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class AlarmTracker : public virtual RefBase {
+public:
+    AlarmTracker(uint64_t startMillis,
+                 const Alarm& alarm, const ConfigKey& configKey,
+                 const sp<AlarmMonitor>& subscriberAlarmMonitor);
+
+    virtual ~AlarmTracker();
+
+    void onAlarmFired();
+
+    void addSubscription(const Subscription& subscription);
+
+    void informAlarmsFired(const uint64_t& timestampNs,
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms);
+
+protected:
+    uint64_t findNextAlarmSec(uint64_t currentTimeMillis);
+
+    // statsd_config.proto Alarm message that defines this tracker.
+    const Alarm mAlarmConfig;
+
+    // A reference to the Alarm's config key.
+    const ConfigKey& mConfigKey;
+
+    // The subscriptions that depend on this alarm.
+    std::vector<Subscription> mSubscriptions;
+
+    // Alarm monitor.
+    sp<AlarmMonitor> mAlarmMonitor;
+
+    // The current expected alarm time in seconds.
+    uint64_t mAlarmSec;
+
+    // The current alarm.
+    sp<const InternalAlarm> mInternalAlarm;
+
+    FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index c40eb81..642604e 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "AnomalyTracker.h"
+#include "subscriber_util.h"
 #include "external/Perfetto.h"
 #include "guardrail/StatsdStats.h"
 #include "subscriber/IncidentdReporter.h"
@@ -231,40 +232,7 @@
 }
 
 void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
-    VLOG("informSubscribers called.");
-    if (mSubscriptions.empty()) {
-        // The config just wanted to log the anomaly. That's fine.
-        VLOG("No Subscriptions were associated with the alert.");
-        return;
-    }
-
-    for (const Subscription& subscription : mSubscriptions) {
-        if (subscription.probability_of_informing() < 1
-                && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
-            // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
-            // The config writer was advised to use -0.1 and 1.1 for never/always.
-            ALOGI("Fate decided that a subscriber would not be informed.");
-            continue;
-        }
-        switch (subscription.subscriber_information_case()) {
-            case Subscription::SubscriberInformationCase::kIncidentdDetails:
-                if (!GenerateIncidentReport(subscription.incidentd_details(), mAlert, mConfigKey)) {
-                    ALOGW("Failed to generate incident report.");
-                }
-                break;
-            case Subscription::SubscriberInformationCase::kPerfettoDetails:
-                if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) {
-                    ALOGW("Failed to generate prefetto traces.");
-                }
-                break;
-            case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
-                SubscriberReporter::getInstance().alertBroadcastSubscriber(mConfigKey, subscription,
-                                                                           key);
-                break;
-            default:
-                break;
-        }
-    }
+    triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 3be959d..e3f493c 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -23,7 +23,7 @@
 #include <gtest/gtest_prod.h>
 #include <utils/RefBase.h>
 
-#include "AnomalyMonitor.h"
+#include "AlarmMonitor.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert
 #include "stats_util.h"  // HashableDimensionKey and DimToValMap
@@ -64,9 +64,9 @@
     void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
                                  const MetricDimensionKey& key, const int64_t& currentBucketValue);
 
-    // Init the AnomalyMonitor which is shared across anomaly trackers.
-    virtual void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
-        return;  // Base AnomalyTracker class has no need for the AnomalyMonitor.
+    // Init the AlarmMonitor which is shared across anomaly trackers.
+    virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
+        return; // Base AnomalyTracker class has no need for the AlarmMonitor.
     }
 
     // Helper function to return the sum value of past buckets at given dimension.
@@ -92,11 +92,10 @@
     }
 
     // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
-    // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor.
-    virtual void informAlarmsFired(
-            const uint64_t& timestampNs,
-            unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) {
-        return;  // The base AnomalyTracker class doesn't have alarms.
+    // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
+    virtual void informAlarmsFired(const uint64_t& timestampNs,
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
+        return; // The base AnomalyTracker class doesn't have alarms.
     }
 
 protected:
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 3ba943c..31d50be 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -24,8 +24,9 @@
 namespace os {
 namespace statsd {
 
-DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey)
-    : AnomalyTracker(alert, configKey) {
+DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
+                                               const sp<AlarmMonitor>& alarmMonitor)
+    : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) {
 }
 
 DurationAnomalyTracker::~DurationAnomalyTracker() {
@@ -59,10 +60,10 @@
         VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period");
         timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1;
     }
-    sp<const AnomalyAlarm> alarm = new AnomalyAlarm{timestampSec};
+    sp<const InternalAlarm> alarm = new InternalAlarm{timestampSec};
     mAlarms.insert({dimensionKey, alarm});
-    if (mAnomalyMonitor != nullptr) {
-        mAnomalyMonitor->add(alarm);
+    if (mAlarmMonitor != nullptr) {
+        mAlarmMonitor->add(alarm);
     }
 }
 
@@ -70,8 +71,8 @@
     auto itr = mAlarms.find(dimensionKey);
     if (itr != mAlarms.end()) {
         mAlarms.erase(dimensionKey);
-        if (mAnomalyMonitor != nullptr) {
-            mAnomalyMonitor->remove(itr->second);
+        if (mAlarmMonitor != nullptr) {
+            mAlarmMonitor->remove(itr->second);
         }
     }
 }
@@ -86,16 +87,16 @@
     }
 }
 
-void DurationAnomalyTracker::informAlarmsFired(
-        const uint64_t& timestampNs,
-        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) {
+void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs,
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
+
     if (firedAlarms.empty() || mAlarms.empty()) return;
     // Find the intersection of firedAlarms and mAlarms.
     // The for loop is inefficient, since it loops over all keys, but that's okay since it is very
-    // seldomly called. The alternative would be having AnomalyAlarms store information about the
+    // seldomly called. The alternative would be having InternalAlarms store information about the
     // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that
     // is rarely ever called.
-    unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+    unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms;
     for (const auto& kv : mAlarms) {
         if (firedAlarms.count(kv.second) > 0) {
             matchedAlarms.insert({kv.first, kv.second});
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 15aef29..51186df 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include "AnomalyMonitor.h"
+#include "AlarmMonitor.h"
 #include "AnomalyTracker.h"
 
 namespace android {
@@ -27,7 +27,8 @@
 
 class DurationAnomalyTracker : public virtual AnomalyTracker {
 public:
-    DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey);
+    DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
+                           const sp<AlarmMonitor>& alarmMonitor);
 
     virtual ~DurationAnomalyTracker();
 
@@ -40,11 +41,6 @@
     // Stop all the alarms owned by this tracker.
     void stopAllAlarms();
 
-    // Init the AnomalyMonitor which is shared across anomaly trackers.
-    void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) override {
-        mAnomalyMonitor = anomalyMonitor;
-    }
-
     // Declares the anomaly when the alarm expired given the current timestamp.
     void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
                                       const uint64_t& timestampNs);
@@ -53,17 +49,16 @@
     // and removes it from firedAlarms.
     // Note that this will generally be called from a different thread from the other functions;
     // the caller is responsible for thread safety.
-    void informAlarmsFired(
-            const uint64_t& timestampNs,
-            unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) override;
+    void informAlarmsFired(const uint64_t& timestampNs,
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
 
 protected:
     // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
     // are still active.
-    std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+    std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms;
 
     // Anomaly alarm monitor.
-    sp<AnomalyMonitor> mAnomalyMonitor;
+    sp<AlarmMonitor> mAlarmMonitor;
 
     // Resets all bucket data. For use when all the data gets stale.
     void resetStorage() override;
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
new file mode 100644
index 0000000..e796d19
--- /dev/null
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IServiceManager.h>
+
+#include "external/Perfetto.h"
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+#include "subscriber/IncidentdReporter.h"
+#include "subscriber/SubscriberReporter.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void triggerSubscribers(const int64_t rule_id,
+                        const MetricDimensionKey& dimensionKey,
+                        const ConfigKey& configKey,
+                        const std::vector<Subscription>& subscriptions) {
+    VLOG("informSubscribers called.");
+    if (subscriptions.empty()) {
+        VLOG("No Subscriptions were associated.");
+        return;
+    }
+
+    for (const Subscription& subscription : subscriptions) {
+        if (subscription.probability_of_informing() < 1
+                && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
+            // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
+            // The config writer was advised to use -0.1 and 1.1 for never/always.
+            ALOGI("Fate decided that a subscriber would not be informed.");
+            continue;
+        }
+        switch (subscription.subscriber_information_case()) {
+            case Subscription::SubscriberInformationCase::kIncidentdDetails:
+                if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) {
+                    ALOGW("Failed to generate incident report.");
+                }
+                break;
+            case Subscription::SubscriberInformationCase::kPerfettoDetails:
+                if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) {
+                    ALOGW("Failed to generate prefetto traces.");
+                }
+                break;
+            case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
+                SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription,
+                                                                           dimensionKey);
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
new file mode 100644
index 0000000..dba8981
--- /dev/null
+++ b/cmds/statsd/src/anomaly/subscriber_util.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "config/ConfigKey.h"
+#include "HashableDimensionKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void triggerSubscribers(const int64_t rule_id,
+                        const MetricDimensionKey& dimensionKey,
+                        const ConfigKey& configKey,
+                        const std::vector<Subscription>& subscriptions);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7159b9b..30ce20a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -130,6 +130,9 @@
         FullBatteryCapacity full_battery_capacity = 10020;
         Temperature temperature = 10021;
     }
+
+    // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
+    // 100,000 are reserved for non-AOSP (e.g. OEMs) to use.
 }
 
 /**
@@ -753,8 +756,11 @@
  *   frameworks/base/libs/hwui/JankTracker.cpp
  */
 message DaveyOccurred {
+    // The UID that logged this atom.
+    optional int32 uid = 1;
+
     // Amount of time it took to render the frame. Should be >=700ms.
-    optional int64 jank_duration_millis = 1;
+    optional int64 jank_duration_millis = 2;
 }
 
 /**
@@ -1529,4 +1535,4 @@
 
     // Temperature in degrees C.
     optional float temperature_C = 3;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index e2e0253..2a5679c 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <android/os/StatsLogEventWrapper.h>
+
 using android::os::StatsLogEventWrapper;
 
 namespace android {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 66cb1d0..0881d44 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -47,11 +47,13 @@
 const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
 // const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp
 const int FIELD_ID_LOGGER_ERROR_STATS = 11;
+const int FIELD_ID_SUBSCRIBER_ALARM_STATS = 12;
 
 const int FIELD_ID_ATOM_STATS_TAG = 1;
 const int FIELD_ID_ATOM_STATS_COUNT = 2;
 
 const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
+const int FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED = 1;
 
 const int FIELD_ID_LOGGER_STATS_TIME = 1;
 const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
@@ -248,6 +250,11 @@
     mAnomalyAlarmRegisteredStats++;
 }
 
+void StatsdStats::noteRegisteredPeriodicAlarmChanged() {
+    lock_guard<std::mutex> lock(mLock);
+    mPeriodicAlarmRegisteredStats++;
+}
+
 void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) {
     lock_guard<std::mutex> lock(mLock);
     mPulledAtomStats[pullAtomId].minPullIntervalSec = intervalSec;
@@ -297,6 +304,7 @@
     std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
     mAlertStats.clear();
     mAnomalyAlarmRegisteredStats = 0;
+    mPeriodicAlarmRegisteredStats = 0;
     mMatcherStats.clear();
     mLoggerErrors.clear();
     for (auto& config : mConfigStats) {
@@ -367,7 +375,7 @@
     fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
     for (const auto& configStats : mIceBox) {
         fprintf(out,
-                "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+                "Config {%d_%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
                 "#matcher=%d, #alert=%d,  valid=%d\n",
                 configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
                 configStats.deletion_time_sec(), configStats.metric_count(),
@@ -462,6 +470,11 @@
         fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
     }
 
+    if (mPeriodicAlarmRegisteredStats > 0) {
+        fprintf(out, "********SubscriberAlarmStats stats***********\n");
+        fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
+    }
+
     fprintf(out,
             "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
             "lost=%d\n",
@@ -531,6 +544,13 @@
         proto.end(token);
     }
 
+    if (mPeriodicAlarmRegisteredStats > 0) {
+        long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SUBSCRIBER_ALARM_STATS);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED,
+                    mPeriodicAlarmRegisteredStats);
+        proto.end(token);
+    }
+
     const int numBytes = mUidMapStats.ByteSize();
     vector<char> buffer(numBytes);
     mUidMapStats.SerializeToArray(&buffer[0], numBytes);
@@ -566,4 +586,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 8c16e4e..24ac688 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -170,6 +170,11 @@
     void noteRegisteredAnomalyAlarmChanged();
 
     /**
+     * Report that statsd modified the periodic alarm registered with StatsCompanionService.
+     */
+    void noteRegisteredPeriodicAlarmChanged();
+
+    /**
      * Records the number of snapshot and delta entries that are being dropped from the uid map.
      */
     void noteUidMapDropped(int snapshots, int deltas);
@@ -264,6 +269,9 @@
     // StatsCompanionService.
     int mAnomalyAlarmRegisteredStats = 0;
 
+    // Stores the number of times statsd registers the periodic alarm changes
+    int mPeriodicAlarmRegisteredStats = 0;
+
     // Stores the number of times an anomaly detection alert has been declared
     // (per config, per alert name). The map size is capped by kMaxConfigCount.
     std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 9c65371..80329c3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -101,6 +101,18 @@
     }
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
+    if (mDimensionsInWhat.size() == mInternalDimensions.size()) {
+        bool mUseWhatDimensionAsInternalDimension = true;
+        for (size_t i = 0; mUseWhatDimensionAsInternalDimension &&
+            i < mDimensionsInWhat.size(); ++i) {
+            if (mDimensionsInWhat[i] != mInternalDimensions[i]) {
+                mUseWhatDimensionAsInternalDimension = false;
+            }
+        }
+    } else {
+        mUseWhatDimensionAsInternalDimension = false;
+    }
+
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
@@ -109,9 +121,11 @@
     VLOG("~DurationMetric() called");
 }
 
-sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert) {
+sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
+        const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
     std::lock_guard<std::mutex> lock(mMutex);
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, mConfigKey);
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
     if (anomalyTracker != nullptr) {
         mAnomalyTrackers.push_back(anomalyTracker);
     }
@@ -139,29 +153,56 @@
     flushIfNeededLocked(eventTime);
 
     // Now for each of the on-going event, check if the condition has changed for them.
-    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
-        pair.second->onSlicedConditionMayChange(eventTime);
+    for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+        for (auto& pair : whatIt.second) {
+            pair.second->onSlicedConditionMayChange(eventTime);
+        }
     }
 
-
-    std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
-    mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
-                                      &conditionDimensionsKeySet);
-
-    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
-        conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+    if (mDimensionsInCondition.empty()) {
+        return;
     }
-    std::unordered_set<MetricDimensionKey> newKeys;
-    for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) {
-        for (auto& pair : mCurrentSlicedDurationTrackerMap) {
-            auto newKey =
-                MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey);
-            if (newKeys.find(newKey) == newKeys.end()) {
-                mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime);
-                mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey);
-                mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime);
+
+    if (mMetric2ConditionLinks.empty()) {
+        std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+        mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
+                                          &conditionDimensionsKeySet);
+        for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+            for (const auto& pair : whatIt.second) {
+                conditionDimensionsKeySet.erase(pair.first);
             }
-            newKeys.insert(newKey);
+        }
+        for (const auto& conditionDimension : conditionDimensionsKeySet) {
+            for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+                if (!whatIt.second.empty()) {
+                    unique_ptr<DurationTracker> newTracker =
+                        whatIt.second.begin()->second->clone(eventTime);
+                    newTracker->setEventKey(MetricDimensionKey(whatIt.first, conditionDimension));
+                    newTracker->onSlicedConditionMayChange(eventTime);
+                    whatIt.second[conditionDimension] = std::move(newTracker);
+                }
+            }
+        }
+    } else {
+        for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+            ConditionKey conditionKey;
+            for (const auto& link : mMetric2ConditionLinks) {
+                getDimensionForCondition(whatIt.first.getValues(), link,
+                                         &conditionKey[link.conditionId]);
+            }
+            std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
+            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+                           &conditionDimensionsKeys);
+
+            for (const auto& conditionDimension : conditionDimensionsKeys) {
+                if (!whatIt.second.empty() &&
+                    whatIt.second.find(conditionDimension) == whatIt.second.end()) {
+                    auto newTracker = whatIt.second.begin()->second->clone(eventTime);
+                    newTracker->setEventKey(MetricDimensionKey(whatIt.first, conditionDimension));
+                    newTracker->onSlicedConditionMayChange(eventTime);
+                    whatIt.second[conditionDimension] = std::move(newTracker);
+                }
+            }
         }
     }
 }
@@ -173,8 +214,10 @@
     flushIfNeededLocked(eventTime);
     // TODO: need to populate the condition change time from the event which triggers the condition
     // change, instead of using current time.
-    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
-        pair.second->onConditionChanged(conditionMet, eventTime);
+    for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+        for (auto& pair : whatIt.second) {
+            pair.second->onConditionChanged(conditionMet, eventTime);
+        }
     }
 }
 
@@ -239,13 +282,20 @@
         return;
     }
     VLOG("flushing...........");
-    for (auto it = mCurrentSlicedDurationTrackerMap.begin();
-            it != mCurrentSlicedDurationTrackerMap.end();) {
-        if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
-            VLOG("erase bucket for key %s", it->first.c_str());
-            it = mCurrentSlicedDurationTrackerMap.erase(it);
+    for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
+            whatIt != mCurrentSlicedDurationTrackerMap.end();) {
+        for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
+            if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
+                VLOG("erase bucket for key %s %s", whatIt->first.c_str(), it->first.c_str());
+                it = whatIt->second.erase(it);
+            } else {
+                ++it;
+            }
+        }
+        if (whatIt->second.empty()) {
+            whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
         } else {
-            ++it;
+            whatIt++;
         }
     }
 
@@ -255,13 +305,20 @@
 }
 
 void DurationMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) {
-    for (auto it = mCurrentSlicedDurationTrackerMap.begin();
-         it != mCurrentSlicedDurationTrackerMap.end();) {
-        if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
-            VLOG("erase bucket for key %s", it->first.c_str());
-            it = mCurrentSlicedDurationTrackerMap.erase(it);
+    for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
+            whatIt != mCurrentSlicedDurationTrackerMap.end();) {
+        for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
+            if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+                VLOG("erase bucket for key %s %s", whatIt->first.c_str(), it->first.c_str());
+                it = whatIt->second.erase(it);
+            } else {
+                ++it;
+            }
+        }
+        if (whatIt->second.empty()) {
+            whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
         } else {
-            ++it;
+            whatIt++;
         }
     }
 }
@@ -274,18 +331,16 @@
     fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
             (unsigned long)mCurrentSlicedDurationTrackerMap.size());
     if (verbose) {
-        for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
-            fprintf(out, "\t%s\n", slice.first.c_str());
-            slice.second->dumpStates(out, verbose);
+        for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+            for (const auto& slice : whatIt.second) {
+                fprintf(out, "\t%s\t%s\n", whatIt.first.c_str(), slice.first.c_str());
+                slice.second->dumpStates(out, verbose);
+            }
         }
     }
 }
 
 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
-    // the key is not new, we are good.
-    if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
-        return false;
-    }
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
@@ -300,49 +355,162 @@
     return false;
 }
 
-void DurationMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const MetricDimensionKey& eventKey,
-        const ConditionKey& conditionKeys, bool condition,
-        const LogEvent& event) {
-    flushIfNeededLocked(event.GetElapsedTimestampNs());
+void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
+                                              const ConditionKey& conditionKeys,
+                                              bool condition, const LogEvent& event) {
+    const auto& whatKey = eventKey.getDimensionKeyInWhat();
+    const auto& condKey = eventKey.getDimensionKeyInCondition();
 
-    if (matcherIndex == mStopAllIndex) {
-        for (auto& pair : mCurrentSlicedDurationTrackerMap) {
-            pair.second->noteStopAll(event.GetElapsedTimestampNs());
-        }
-        return;
-    }
-
-    if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
+    auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
+    if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
         if (hitGuardRailLocked(eventKey)) {
             return;
         }
-        mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
+        mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
+    } else {
+        if (whatIt->second.find(condKey) == whatIt->second.end()) {
+            if (hitGuardRailLocked(eventKey)) {
+                return;
+            }
+            mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
+        }
     }
 
-    auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
+    auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(condKey);
+    if (mUseWhatDimensionAsInternalDimension) {
+        it->second->noteStart(whatKey, condition,
+                              event.GetElapsedTimestampNs(), conditionKeys);
+        return;
+    }
 
     std::vector<HashableDimensionKey> values;
     filterValues(mInternalDimensions, event.getValues(), &values);
     if (values.empty()) {
-        if (matcherIndex == mStartIndex) {
-            it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
-                                  event.GetElapsedTimestampNs(), conditionKeys);
-        } else if (matcherIndex == mStopIndex) {
-            it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetElapsedTimestampNs(), false);
-        }
+        it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
+                              event.GetElapsedTimestampNs(), conditionKeys);
     } else {
         for (const auto& value : values) {
-            if (matcherIndex == mStartIndex) {
-                it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys);
-            } else if (matcherIndex == mStopIndex) {
-                it->second->noteStop(value, event.GetElapsedTimestampNs(), false);
-            }
+            it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys);
         }
     }
 
 }
 
+void DurationMetricProducer::onMatchedLogEventInternalLocked(
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
+        const ConditionKey& conditionKeys, bool condition,
+        const LogEvent& event) {
+    ALOGW("Not used in duration tracker.");
+}
+
+void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
+                                                     const LogEvent& event) {
+    uint64_t eventTimeNs = event.GetElapsedTimestampNs();
+    if (eventTimeNs < mStartTimeNs) {
+        return;
+    }
+
+    flushIfNeededLocked(event.GetElapsedTimestampNs());
+
+    // Handles Stopall events.
+    if (matcherIndex == mStopAllIndex) {
+        for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+            for (auto& pair : whatIt.second) {
+                pair.second->noteStopAll(event.GetElapsedTimestampNs());
+            }
+        }
+        return;
+    }
+
+    vector<HashableDimensionKey> dimensionInWhatValues;
+    if (!mDimensionsInWhat.empty()) {
+        filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
+    } else {
+        dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
+    }
+
+    // Handles Stop events.
+    if (matcherIndex == mStopIndex) {
+        if (mUseWhatDimensionAsInternalDimension) {
+            for (const HashableDimensionKey& whatKey : dimensionInWhatValues) {
+                auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
+                if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+                    for (const auto& condIt : whatIt->second) {
+                        condIt.second->noteStop(whatKey, event.GetElapsedTimestampNs(), false);
+                    }
+                }
+            }
+            return;
+        }
+
+        std::vector<HashableDimensionKey> internalDimensionKeys;
+        filterValues(mInternalDimensions, event.getValues(), &internalDimensionKeys);
+        if (internalDimensionKeys.empty()) {
+            internalDimensionKeys.push_back(DEFAULT_DIMENSION_KEY);
+        }
+        for (const HashableDimensionKey& whatDimension : dimensionInWhatValues) {
+            auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
+            if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+                for (const auto& condIt : whatIt->second) {
+                    for (const auto& internalDimensionKey : internalDimensionKeys) {
+                        condIt.second->noteStop(
+                            internalDimensionKey, event.GetElapsedTimestampNs(), false);
+                    }
+                }
+            }
+        }
+        return;
+    }
+
+    bool condition;
+    ConditionKey conditionKey;
+    std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
+    if (mConditionSliced) {
+        for (const auto& link : mMetric2ConditionLinks) {
+            getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
+        }
+
+        auto conditionState =
+            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+                           &dimensionKeysInCondition);
+        condition = (conditionState == ConditionState::kTrue);
+        if (mDimensionsInCondition.empty() && condition) {
+            dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
+        }
+    } else {
+        condition = mCondition;
+        if (condition) {
+            dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
+        }
+    }
+
+    for (const auto& whatDimension : dimensionInWhatValues) {
+        auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
+        // If the what dimension is already there, we should update all the trackers even
+        // the condition is false.
+        if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+            for (const auto& condIt : whatIt->second) {
+                const bool cond = dimensionKeysInCondition.find(condIt.first) !=
+                        dimensionKeysInCondition.end();
+                handleStartEvent(MetricDimensionKey(whatDimension, condIt.first),
+                                 conditionKey, cond, event);
+            }
+        } else {
+            // If it is a new what dimension key, we need to handle the start events for all current
+            // condition dimensions.
+            for (const auto& conditionDimension : dimensionKeysInCondition) {
+                handleStartEvent(MetricDimensionKey(whatDimension, conditionDimension),
+                                 conditionKey, condition, event);
+            }
+        }
+        if (dimensionKeysInCondition.empty()) {
+            handleStartEvent(MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
+                             conditionKey, condition, event);
+        }
+    }
+}
+
+
 size_t DurationMetricProducer::byteSizeLocked() const {
     size_t totalSize = 0;
     for (const auto& pair : mPastBuckets) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 5f29281..73d074f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -46,15 +46,20 @@
 
     virtual ~DurationMetricProducer();
 
-    sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) override;
+    sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+                                         const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
 
 protected:
+    void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKeys, bool condition,
             const LogEvent& event) override;
 
 private:
+    void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
+                          bool condition, const LogEvent& event);
+
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
 
@@ -91,12 +96,16 @@
     // The dimension from the atom predicate. e.g., uid, wakelock name.
     vector<Matcher> mInternalDimensions;
 
+    // This boolean is true iff When mInternalDimensions == mDimensionsInWhat
+    bool mUseWhatDimensionAsInternalDimension;
+
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
 
-    // The current bucket.
-    std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+    // The duration trackers in the current bucket.
+    std::unordered_map<HashableDimensionKey,
+        std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>>
             mCurrentSlicedDurationTrackerMap;
 
     // Helper function to create a duration tracker given the metric aggregation type.
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index f3307dc..18694a1 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -37,7 +37,7 @@
     std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
         for (const auto& link : mMetric2ConditionLinks) {
-            getDimensionForCondition(event, link, &conditionKey[link.conditionId]);
+            getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
         }
 
         auto conditionState =
@@ -48,37 +48,30 @@
         condition = mCondition;
     }
 
-    vector<HashableDimensionKey> dimensionInWhatValues;
-    if (mDimensionsInWhat.size() > 0) {
-        filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
+    if (mDimensionsInCondition.empty() && condition) {
+        dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
     }
 
-    if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
-        onMatchedLogEventInternalLocked(
-            matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
-    } else if (dimensionKeysInCondition.empty()) {
-        for (const HashableDimensionKey& whatValue : dimensionInWhatValues) {
-            onMatchedLogEventInternalLocked(matcherIndex,
-                                            MetricDimensionKey(whatValue, DEFAULT_DIMENSION_KEY),
-                                            conditionKey, condition, event);
-        }
-    } else if (dimensionInWhatValues.empty()) {
+    vector<HashableDimensionKey> dimensionInWhatValues;
+    if (!mDimensionsInWhat.empty()) {
+        filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
+    } else {
+        dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
+    }
+
+    for (const auto& whatDimension : dimensionInWhatValues) {
         for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
             onMatchedLogEventInternalLocked(
-                matcherIndex,
-                MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey),
-                conditionKey, condition, event);
+                    matcherIndex, MetricDimensionKey(whatDimension, conditionDimensionKey),
+                    conditionKey, condition, event);
         }
-    } else {
-        for (const auto& whatValue : dimensionInWhatValues) {
-            for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
-                onMatchedLogEventInternalLocked(
-                        matcherIndex, MetricDimensionKey(whatValue, conditionDimensionKey),
-                        conditionKey, condition, event);
-            }
+        if (dimensionKeysInCondition.empty()) {
+            onMatchedLogEventInternalLocked(
+                    matcherIndex, MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
+                     conditionKey, condition, event);
         }
     }
-}
+ }
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 8663e5e..2bf6241 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -124,7 +124,8 @@
     }
 
     /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
-    virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) {
+    virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+                                                 const sp<AlarmMonitor>& anomalyAlarmMonitor) {
         std::lock_guard<std::mutex> lock(mMutex);
         sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
         if (anomalyTracker != nullptr) {
@@ -232,7 +233,7 @@
             const LogEvent& event) = 0;
 
     // Consume the parsed stats log entry that already matched the "what" of the metric.
-    void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
+    virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
 
     mutable std::mutex mMutex;
 };
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e75b710..4c8a7d8 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -49,13 +49,17 @@
 const int FIELD_ID_METRICS = 1;
 
 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
-                               const long timeBaseSec, sp<UidMap> uidMap)
+                               const long timeBaseSec,
+                               const sp<UidMap> &uidMap,
+                               const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                               const sp<AlarmMonitor>& periodicAlarmMonitor)
     : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(timeBaseSec * NS_PER_SEC) {
     mConfigValid =
-            initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers,
-                             mAllConditionTrackers,
-                             mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
-                             mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
+            initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
+                             timeBaseSec, mTagIds, mAllAtomMatchers,
+                             mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
+                             mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
+                             mTrackerToConditionMap, mNoReportMetricIds);
 
     if (config.allowed_log_source_size() == 0) {
         // TODO(b/70794411): uncomment the following line and remove the hard coded log source
@@ -198,31 +202,59 @@
         // Uid is 3rd from last field and must match the caller's uid,
         // unless that caller is statsd itself (statsd is allowed to spoof uids).
         long appHookUid = event.GetLong(event.size()-2, &err);
+        if (err != NO_ERROR ) {
+            VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
+            return;
+        }
         int32_t loggerUid = event.GetUid();
-        if (err != NO_ERROR || (loggerUid != appHookUid && loggerUid != AID_STATSD)) {
-            VLOG("AppHook has invalid uid: claimed %ld but caller is %d", appHookUid, loggerUid);
+        if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
+            VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
+                 appHookUid, loggerUid);
             return;
         }
 
         // Label is 2nd from last field and must be from [0, 15].
         long appHookLabel = event.GetLong(event.size()-1, &err);
-        if (err != NO_ERROR || appHookLabel < 0 || appHookLabel > 15) {
-            VLOG("AppHook does not have valid label %ld", appHookLabel);
+        if (err != NO_ERROR ) {
+            VLOG("APP_BREADCRUMB_REPORTED had error when parsing the label field");
+            return;
+        } else if (appHookLabel < 0 || appHookLabel > 15) {
+            VLOG("APP_BREADCRUMB_REPORTED does not have valid label %ld", appHookLabel);
             return;
         }
 
         // The state must be from 0,3. This part of code must be manually updated.
         long appHookState = event.GetLong(event.size(), &err);
-        if (err != NO_ERROR || appHookState < 0 || appHookState > 3) {
-            VLOG("AppHook does not have valid state %ld", appHookState);
+        if (err != NO_ERROR ) {
+            VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
+            return;
+        } else if (appHookState < 0 || appHookState > 3) {
+            VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
             return;
         }
     } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
         // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
         // Check that the davey duration is reasonable. Max length check is for privacy.
         status_t err = NO_ERROR;
+
+        // Uid is the first field provided.
+        long jankUid = event.GetLong(1, &err);
+        if (err != NO_ERROR ) {
+            VLOG("Davey occurred had error when parsing the uid");
+            return;
+        }
+        int32_t loggerUid = event.GetUid();
+        if (loggerUid != jankUid && loggerUid != AID_STATSD) {
+            VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
+                 loggerUid);
+            return;
+        }
+
         long duration = event.GetLong(event.size(), &err);
-        if (err != NO_ERROR || duration > 100000) {
+        if (err != NO_ERROR ) {
+            VLOG("Davey occurred had error when parsing the duration");
+            return;
+        } else if (duration > 100000) {
             VLOG("Davey duration is unreasonably long: %ld", duration);
             return;
         }
@@ -312,16 +344,19 @@
     }
 }
 
-void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs,
-                         unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet) {
+void MetricsManager::onAnomalyAlarmFired(
+        const uint64_t timestampNs,
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
     for (const auto& itr : mAllAnomalyTrackers) {
-        itr->informAlarmsFired(timestampNs, anomalySet);
+        itr->informAlarmsFired(timestampNs, alarmSet);
     }
 }
 
-void MetricsManager::setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
-    for (auto& itr : mAllAnomalyTrackers) {
-        itr->setAnomalyMonitor(anomalyMonitor);
+void MetricsManager::onPeriodicAlarmFired(
+        const uint64_t timestampNs,
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
+    for (const auto& itr : mAllPeriodicAlarmTrackers) {
+        itr->informAlarmsFired(timestampNs, alarmSet);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index d4f844f..b50ef4a 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,7 +16,8 @@
 
 #pragma once
 
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
+#include "anomaly/AlarmTracker.h"
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionTracker.h"
 #include "config/ConfigKey.h"
@@ -36,7 +37,8 @@
 class MetricsManager : public PackageInfoListener {
 public:
     MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const long timeBaseSec,
-                   sp<UidMap> uidMap);
+                   const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                   const sp<AlarmMonitor>& periodicAlarmMonitor);
 
     virtual ~MetricsManager();
 
@@ -47,9 +49,11 @@
 
     void onAnomalyAlarmFired(
         const uint64_t timestampNs,
-        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
-    void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
+    void onPeriodicAlarmFired(
+        const uint64_t timestampNs,
+        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
     void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
                           const int64_t version) override;
@@ -120,6 +124,9 @@
     // Hold all alert trackers.
     std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
 
+    // Hold all periodic alarm trackers.
+    std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
+
     // To make the log processing more efficient, we want to do as much filtering as possible
     // before we go into individual trackers and conditions to match.
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 71e5c33..9912afa 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -17,16 +17,19 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
+#include "metrics_manager_util.h"
+
 #include "../condition/CombinationConditionTracker.h"
 #include "../condition/SimpleConditionTracker.h"
 #include "../external/StatsPullerManager.h"
 #include "../matchers/CombinationLogMatchingTracker.h"
 #include "../matchers/SimpleLogMatchingTracker.h"
-#include "CountMetricProducer.h"
-#include "DurationMetricProducer.h"
-#include "EventMetricProducer.h"
-#include "GaugeMetricProducer.h"
-#include "ValueMetricProducer.h"
+#include "../metrics/CountMetricProducer.h"
+#include "../metrics/DurationMetricProducer.h"
+#include "../metrics/EventMetricProducer.h"
+#include "../metrics/GaugeMetricProducer.h"
+#include "../metrics/ValueMetricProducer.h"
+
 #include "stats_util.h"
 
 using std::set;
@@ -494,6 +497,7 @@
 
 bool initAlerts(const StatsdConfig& config,
                 const unordered_map<int64_t, int>& metricProducerMap,
+                const sp<AlarmMonitor>& anomalyAlarmMonitor,
                 vector<sp<MetricProducer>>& allMetricProducers,
                 vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
     unordered_map<int64_t, int> anomalyTrackerMap;
@@ -512,7 +516,7 @@
         }
         const int metricIndex = itr->second;
         sp<MetricProducer> metric = allMetricProducers[metricIndex];
-        sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert);
+        sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
         if (anomalyTracker == nullptr) {
             // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
             return false;
@@ -522,6 +526,9 @@
     }
     for (int i = 0; i < config.subscription_size(); ++i) {
         const Subscription& subscription = config.subscription(i);
+        if (subscription.rule_type() != Subscription::ALERT) {
+            continue;
+        }
         if (subscription.subscriber_information_case() ==
             Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
             ALOGW("subscription \"%lld\" has no subscriber info.\"",
@@ -540,13 +547,60 @@
     return true;
 }
 
+bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
+                const sp<AlarmMonitor>& periodicAlarmMonitor,
+                const long timeBaseSec,
+                vector<sp<AlarmTracker>>& allAlarmTrackers) {
+    unordered_map<int64_t, int> alarmTrackerMap;
+    uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC;
+    for (int i = 0; i < config.alarm_size(); i++) {
+        const Alarm& alarm = config.alarm(i);
+        if (alarm.offset_millis() <= 0) {
+            ALOGW("Alarm offset_millis should be larger than 0.");
+            return false;
+        }
+        if (alarm.period_millis() <= 0) {
+            ALOGW("Alarm period_millis should be larger than 0.");
+            return false;
+        }
+        alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
+        allAlarmTrackers.push_back(
+            new AlarmTracker(startMillis, alarm, key, periodicAlarmMonitor));
+    }
+    for (int i = 0; i < config.subscription_size(); ++i) {
+        const Subscription& subscription = config.subscription(i);
+        if (subscription.rule_type() != Subscription::ALARM) {
+            continue;
+        }
+        if (subscription.subscriber_information_case() ==
+            Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
+            ALOGW("subscription \"%lld\" has no subscriber info.\"",
+                (long long)subscription.id());
+            return false;
+        }
+        const auto& itr = alarmTrackerMap.find(subscription.rule_id());
+        if (itr == alarmTrackerMap.end()) {
+            ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
+                (long long)subscription.id(), (long long)subscription.rule_id());
+            return false;
+        }
+        const int trackerIndex = itr->second;
+        allAlarmTrackers[trackerIndex]->addSubscription(subscription);
+    }
+    return true;
+}
+
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
                       const UidMap& uidMap,
-                      const long timeBaseSec, set<int>& allTagIds,
+                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor,
+                      const long timeBaseSec,
+                      set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+                      vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
                       unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToConditionMap,
@@ -573,10 +627,16 @@
         ALOGE("initMetricProducers failed");
         return false;
     }
-    if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) {
+    if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers,
+                    allAnomalyTrackers)) {
         ALOGE("initAlerts failed");
         return false;
     }
+    if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseSec, allPeriodicAlarmTrackers)) {
+        ALOGE("initAlarms failed");
+        return false;
+    }
+
     return true;
 }
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 4f19ada..edda53d 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -13,16 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef METRIC_UTIL_H
-#define METRIC_UTIL_H
+
+#pragma once
+
 #include <memory>
 #include <set>
 #include <unordered_map>
 #include <vector>
 
+#include "../anomaly/AlarmTracker.h"
 #include "../condition/ConditionTracker.h"
 #include "../external/StatsPullerManagerImpl.h"
 #include "../matchers/LogMatchingTracker.h"
+#include "../metrics/MetricProducer.h"
 
 namespace android {
 namespace os {
@@ -93,11 +96,15 @@
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
                       const UidMap& uidMap,
-                      const long timeBaseSec, std::set<int>& allTagIds,
+                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor,
+                      const long timeBaseSec,
+                      std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+                      vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
                       std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
@@ -106,4 +113,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-#endif  // METRIC_UTIL_H
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 272e90b..269f25b 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -164,4 +164,4 @@
   optional ConfigKey config_key = 1;
 
   repeated ConfigMetricsReport reports = 2;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 6a1db72..781eced 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -195,6 +195,20 @@
     }
 }
 
+bool StorageManager::readFileToString(const char* file, string* content) {
+    int fd = open(file, O_RDONLY | O_CLOEXEC);
+    bool res = false;
+    if (fd != -1) {
+        if (android::base::ReadFdToString(fd, content)) {
+            res = true;
+        } else {
+            VLOG("Failed to read file %s\n", file);
+        }
+        close(fd);
+    }
+    return res;
+}
+
 void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
     if (dir == NULL) {
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index d319674..6c8ed0a 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -37,6 +37,11 @@
     static void writeFile(const char* file, const void* buffer, int numBytes);
 
     /**
+     * Reads the file content to the buffer.
+     */
+    static bool readFileToString(const char* file, string* content);
+
+    /**
      * Deletes a single file given a file name.
      */
     static void deleteFile(const char* file);
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index d9a8fc8..1c18f67 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -28,10 +28,10 @@
 namespace os {
 namespace statsd {
 
-bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
+bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
                             const ConfigKey& configKey) {
     if (config.section_size() == 0) {
-        VLOG("The alert %lld contains zero section in config(%d,%lld)", alert.id(),
+        VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
             configKey.GetUid(), (long long) configKey.GetId());
         return false;
     }
@@ -39,7 +39,7 @@
     IncidentReportArgs incidentReport;
 
     android::os::IncidentHeaderProto header;
-    header.set_alert_id(alert.id());
+    header.set_alert_id(rule_id);
     header.mutable_config_key()->set_uid(configKey.GetUid());
     header.mutable_config_key()->set_id(configKey.GetId());
     incidentReport.addHeader(header);
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
index 229ed77..1b83fe2 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -26,7 +26,7 @@
 /**
  * Calls incidentd to trigger an incident report and put in dropbox for uploading.
  */
-bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
+bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
                             const ConfigKey& configKey);
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
similarity index 70%
rename from cmds/statsd/tests/AnomalyMonitor_test.cpp
rename to cmds/statsd/tests/AlarmMonitor_test.cpp
index 920ca08..1fccb35 100644
--- a/cmds/statsd/tests/AnomalyMonitor_test.cpp
+++ b/cmds/statsd/tests/AlarmMonitor_test.cpp
@@ -12,28 +12,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
 
 #include <gtest/gtest.h>
 
 using namespace android::os::statsd;
 
 #ifdef __ANDROID__
-TEST(AnomalyMonitor, popSoonerThan) {
+TEST(AlarmMonitor, popSoonerThan) {
     std::string emptyMetricId;
     std::string emptyDimensionId;
-    unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
-    AnomalyMonitor am(2);
+    unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
+    AlarmMonitor am(2, [](const sp<IStatsCompanionService>&, int64_t){},
+                    [](const sp<IStatsCompanionService>&){});
 
     set = am.popSoonerThan(5);
     EXPECT_TRUE(set.empty());
 
-    sp<const AnomalyAlarm> a = new AnomalyAlarm{10};
-    sp<const AnomalyAlarm> b = new AnomalyAlarm{20};
-    sp<const AnomalyAlarm> c = new AnomalyAlarm{20};
-    sp<const AnomalyAlarm> d = new AnomalyAlarm{30};
-    sp<const AnomalyAlarm> e = new AnomalyAlarm{40};
-    sp<const AnomalyAlarm> f = new AnomalyAlarm{50};
+    sp<const InternalAlarm> a = new InternalAlarm{10};
+    sp<const InternalAlarm> b = new InternalAlarm{20};
+    sp<const InternalAlarm> c = new InternalAlarm{20};
+    sp<const InternalAlarm> d = new InternalAlarm{30};
+    sp<const InternalAlarm> e = new InternalAlarm{40};
+    sp<const InternalAlarm> f = new InternalAlarm{50};
 
     am.add(a);
     am.add(b);
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 62bdba4..90c3a2f 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -65,8 +65,12 @@
 const int64_t testConfigId = 12345;
 
 TEST(ConfigManagerTest, TestFakeConfig) {
-    auto metricsManager = std::make_unique<MetricsManager>(ConfigKey(0, testConfigId),
-                                                           build_fake_config(), 1000, new UidMap());
+    auto metricsManager = std::make_unique<MetricsManager>(
+        ConfigKey(0, testConfigId), build_fake_config(), 1000, new UidMap(),
+        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+                         [](const sp<IStatsCompanionService>&){}),
+        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+                         [](const sp<IStatsCompanionService>&){}));
     EXPECT_TRUE(metricsManager->isConfigValid());
 }
 
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index f90ca40..5d8c3f7 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -271,19 +271,25 @@
 
 TEST(MetricsManagerTest, TestGoodConfig) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildGoodConfig();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap,
+                                 anomalyAlarmMonitor, periodicAlarmMonitor,
+                                 timeBaseSec, allTagIds, allAtomMatchers,
                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                 allAlarmTrackers,
                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                  noReportMetricIds));
     EXPECT_EQ(1u, allMetricProducers.size());
@@ -293,112 +299,148 @@
 
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildDimensionMetricsWithMultiTags();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+                                  anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                   noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildCircleMatchers();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+                                  anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                   noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildMissingMatchers();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+                                  anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                   noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildMissingPredicate();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+                                  anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                   noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildCirclePredicates();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+                                  anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                   noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
     UidMap uidMap;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildAlertWithUnknownMetric();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+                                  anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  allAlarmTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
                                   noReportMetricIds));
 }
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index cb72697..3238b74 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -41,7 +41,13 @@
  */
 class MockMetricsManager : public MetricsManager {
 public:
-    MockMetricsManager() : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, new UidMap()) {
+    MockMetricsManager() : MetricsManager(
+        ConfigKey(1, 12345), StatsdConfig(), 1000,
+        new UidMap(),
+        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+                         [](const sp<IStatsCompanionService>&){}),
+        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+                         [](const sp<IStatsCompanionService>&){})) {
     }
 
     MOCK_METHOD0(byteSize, size_t());
@@ -50,9 +56,11 @@
 
 TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
     sp<UidMap> m = new UidMap();
-    sp<AnomalyMonitor> anomalyMonitor;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
-    StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
+    StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
+        [](const ConfigKey& key) {});
 
     MockMetricsManager mockMetricsManager;
 
@@ -67,11 +75,11 @@
 
 TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
     sp<UidMap> m = new UidMap();
-    sp<AnomalyMonitor> anomalyMonitor;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) {
-        broadcastCount++;
-    });
+    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
 
     MockMetricsManager mockMetricsManager;
 
@@ -93,9 +101,10 @@
 
 TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
     sp<UidMap> m = new UidMap();
-    sp<AnomalyMonitor> anomalyMonitor;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyMonitor, 0,
+    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
                         [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
 
     MockMetricsManager mockMetricsManager;
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index f26c10d..ca656ed 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -36,9 +36,11 @@
 
 TEST(UidMapTest, TestIsolatedUID) {
     sp<UidMap> m = new UidMap();
-    sp<AnomalyMonitor> anomalyMonitor;
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
-    StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
+    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+        [](const ConfigKey& key) {});
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
     addEvent.write(100);  // parent UID
     addEvent.write(101);  // isolated UID
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
new file mode 100644
index 0000000..3330ee9
--- /dev/null
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/anomaly/AlarmTracker.h"
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const ConfigKey kConfigKey(0, 12345);
+
+TEST(AlarmTrackerTest, TestTriggerTimestamp) {
+    sp<AlarmMonitor> subscriberAlarmMonitor =
+        new AlarmMonitor(100, [](const sp<IStatsCompanionService>&, int64_t){},
+                         [](const sp<IStatsCompanionService>&){});
+    Alarm alarm;
+    alarm.set_offset_millis(15 * MS_PER_SEC);
+    alarm.set_period_millis(60 * 60 * MS_PER_SEC);  // 1hr
+    uint64_t startMillis = 100000000 * MS_PER_SEC;
+    AlarmTracker tracker(startMillis, alarm, kConfigKey,
+                         subscriberAlarmMonitor);
+
+    EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15);
+
+    uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
+    std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
+        subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+    EXPECT_TRUE(firedAlarmSet.empty());
+    tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+    EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15);
+
+    currentTimeSec = startMillis / MS_PER_SEC + 7000;
+    firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+    EXPECT_EQ(firedAlarmSet.size(), 1u);
+    tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+    EXPECT_TRUE(firedAlarmSet.empty());
+    EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15 + 2 * 60 * 60);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 20ddbe9..9a0de0d 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -201,6 +201,7 @@
 }
 
 TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
+    sp<AlarmMonitor> alarmMonitor;
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
     uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
@@ -222,7 +223,7 @@
                                       bucketStartTimeNs);
     countProducer.setBucketSize(60 * NS_PER_SEC);
 
-    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
 
     // Bucket is flushed yet.
@@ -315,6 +316,7 @@
 }
 
 TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
+    sp<AlarmMonitor> alarmMonitor;
     Alert alert;
     alert.set_id(11);
     alert.set_metric_id(1);
@@ -337,7 +339,7 @@
                                       bucketStartTimeNs);
     countProducer.setBucketSize(60 * NS_PER_SEC);
 
-    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
 
     int tagId = 1;
     LogEvent event1(tagId, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 7969596..1b22d75 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -239,6 +239,7 @@
 }
 
 TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
+    sp<AlarmMonitor> alarmMonitor;
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
     uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
@@ -263,7 +264,7 @@
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
     durationProducer.setBucketSize(60 * NS_PER_SEC);
 
-    sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
 
     LogEvent start_event(tagId, startTimeNs);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 0eb8ce2..77b3ace 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -129,6 +129,7 @@
 }
 
 TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
+    sp<AlarmMonitor> alarmMonitor;
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
@@ -145,8 +146,9 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       -1 /* -1 means no pulling */, bucketStartTimeNs,
                                       pullerManager);
+
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
-    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -339,6 +341,7 @@
 }
 
 TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+    sp<AlarmMonitor> alarmMonitor;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     shared_ptr<MockStatsPullerManager> pullerManager =
@@ -363,7 +366,7 @@
     alert.set_num_buckets(2);
     const int32_t refPeriodSec = 60;
     alert.set_refractory_period_secs(refPeriodSec);
-    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
 
     int tagId = 1;
     std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index a164c12..83b1cbf 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -276,13 +276,15 @@
     alert.set_num_buckets(2);
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                true, {anomalyTracker});
 
     tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
-    sp<const AnomalyAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
+    sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
     EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
 
     // Remove the anomaly alarm when the duration is no longer fully met.
@@ -336,7 +338,9 @@
     alert.set_num_buckets(2);
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                true, {anomalyTracker});
@@ -390,7 +394,9 @@
     alert.set_num_buckets(2);
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                true, {anomalyTracker});
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index cb731c5..aa41038 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -334,7 +334,9 @@
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
 
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, true, {anomalyTracker});
@@ -403,7 +405,9 @@
     uint64_t bucketNum = 0;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
 
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
                                  true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, false, {anomalyTracker});
@@ -453,14 +457,16 @@
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
-    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    sp<AlarmMonitor> alarmMonitor;
+    sp<DurationAnomalyTracker> anomalyTracker =
+        new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
                                  true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
                                  bucketSizeNs, false, {anomalyTracker});
 
     tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
-    sp<const AnomalyAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
+    sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
     EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
 
@@ -487,7 +493,7 @@
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
 
     // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
-    std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> firedAlarms({alarm});
+    std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
     anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
     EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index ce4fa32..a0addcc 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -346,6 +346,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+    sp<AlarmMonitor> alarmMonitor;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(metricId);
@@ -365,7 +366,7 @@
                                       -1 /*not pulled*/, bucketStartTimeNs);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
-    sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
 
 
     shared_ptr<LogEvent> event1
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7568348..242b6eb 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -391,9 +391,10 @@
 sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
                                               const ConfigKey& key) {
     sp<UidMap> uidMap = new UidMap();
-    sp<AnomalyMonitor> anomalyMonitor = new AnomalyMonitor(10); // 10 seconds
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
     sp<StatsLogProcessor> processor = new StatsLogProcessor(
-        uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){});
+        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
     processor->OnConfigUpdated(key, config);
     return processor;
 }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 8a8f044..cab6744 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -365,6 +365,11 @@
     public abstract boolean isCallerRecents(int callingUid);
 
     /**
+     * Returns whether the recents component is the home activity for the given user.
+     */
+    public abstract boolean isRecentsComponentHomeActivity(int userId);
+
+    /**
      * Whether an UID is active or idle.
      */
     public abstract boolean isUidActive(int uid);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21d146a..872370e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3900,8 +3900,7 @@
 
     @Override
     public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport, PendingTransactionActions pendingActions,
-            String reason) {
+            int configChanges, PendingTransactionActions pendingActions, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
             if (userLeaving) {
@@ -3915,15 +3914,6 @@
             if (r.isPreHoneycomb()) {
                 QueuedWork.waitToFinish();
             }
-
-            // Tell the activity manager we have paused.
-            if (!dontReport) {
-                try {
-                    ActivityManager.getService().activityPaused(token);
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-            }
             mSomeActivitiesChanged = true;
         }
     }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f8f50a2..21fb18a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1354,11 +1354,10 @@
         if (badgeColor == null) {
             return null;
         }
-        badgeColor.setTint(getUserBadgeColor(user));
         Drawable badgeForeground = getDrawableForDensity(
                 com.android.internal.R.drawable.ic_corp_badge_case, density);
-        Drawable badge = new LayerDrawable(
-                new Drawable[] {badgeColor, badgeForeground });
+        badgeForeground.setTint(getUserBadgeColor(user));
+        Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground });
         return badge;
     }
 
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index cb52a85..6bc66ec 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -65,8 +65,7 @@
 
     /** Pause the activity. */
     public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport, PendingTransactionActions pendingActions,
-            String reason);
+            int configChanges, PendingTransactionActions pendingActions, String reason);
 
     /** Resume the activity. */
     public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index c2c91c2..ee6a5ca 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -16,6 +16,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.IBinder;
@@ -75,14 +76,6 @@
     }
 
     /**
-     * Temporary. Will be deleted.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
-        return addConfiguration(configKey, config);
-    }
-
-    /**
      * Clients can send a configuration and simultaneously registers the name of a broadcast
      * receiver that listens for when it should request data.
      *
@@ -226,10 +219,11 @@
      * the retrieved metrics from statsd memory.
      *
      * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
+     * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
+     * crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getData(long configKey) {
+    public @Nullable byte[] getData(long configKey) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
@@ -239,7 +233,7 @@
                 }
                 return service.getData(configKey);
             } catch (RemoteException e) {
-                if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data");
+                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when getting data");
                 return null;
             }
         }
@@ -250,10 +244,10 @@
      * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
      * This getter is not destructive and will not reset any metrics/counters.
      *
-     * @return Serialized StatsdStatsReport proto. Returns null on failure.
+     * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getMetadata() {
+    public @Nullable byte[] getMetadata() {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 16e36bc..7b6a288 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -56,8 +56,11 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManager.UserOperationException;
+import android.os.UserManager.UserOperationResult;
 import android.provider.ContactsContract.Directory;
 import android.provider.Settings;
 import android.security.AttestedKeyPair;
@@ -5627,6 +5630,29 @@
     }
 
     /**
+     * Called by a device owner to set the default SMS application.
+     * <p>
+     * The calling device admin must be a device owner. If it is not, a security exception will be
+     * thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The name of the package to set as the default SMS application.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     *
+     * @hide
+     */
+    public void setDefaultSmsApplication(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("setDefaultSmsApplication");
+        if (mService != null) {
+            try {
+                mService.setDefaultSmsApplication(admin, packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Called by a profile owner or device owner to grant permission to a package to manage
      * application restrictions for the calling user via {@link #setApplicationRestrictions} and
      * {@link #getApplicationRestrictions}.
@@ -6549,6 +6575,9 @@
      * <p>
      * If the adminExtras are not null, they will be stored on the device until the user is started
      * for the first time. Then the extras will be passed to the admin when onEnable is called.
+     * <p>From {@link android.os.Build.VERSION_CODES#P} onwards, if targeting
+     * {@link android.os.Build.VERSION_CODES#P}, throws {@link UserOperationException} instead of
+     * returning {@code null} on failure.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param name The user's name.
@@ -6563,6 +6592,9 @@
      * @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the
      *         user could not be created.
      * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws UserOperationException if the user could not be created and the calling app is
+     * targeting {@link android.os.Build.VERSION_CODES#P} and running on
+     * {@link android.os.Build.VERSION_CODES#P}.
      */
     public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin,
             @NonNull String name,
@@ -6571,6 +6603,8 @@
         throwIfParentInstance("createAndManageUser");
         try {
             return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
+        } catch (ServiceSpecificException e) {
+            throw new UserOperationException(e.getMessage(), e.errorCode);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -6614,77 +6648,15 @@
     }
 
     /**
-     * Indicates user operation is successful.
-     *
-     * @see #startUserInBackground(ComponentName, UserHandle)
-     * @see #stopUser(ComponentName, UserHandle)
-     * @see #logoutUser(ComponentName)
-     */
-    public static final int USER_OPERATION_SUCCESS = 0;
-
-    /**
-     * Indicates user operation failed for unknown reason.
-     *
-     * @see #startUserInBackground(ComponentName, UserHandle)
-     * @see #stopUser(ComponentName, UserHandle)
-     * @see #logoutUser(ComponentName)
-     */
-    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
-
-    /**
-     * Indicates user operation failed because target user is a managed profile.
-     *
-     * @see #startUserInBackground(ComponentName, UserHandle)
-     * @see #stopUser(ComponentName, UserHandle)
-     * @see #logoutUser(ComponentName)
-     */
-    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
-
-    /**
-     * Indicates user operation failed because maximum running user limit has reached.
-     *
-     * @see #startUserInBackground(ComponentName, UserHandle)
-     */
-    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
-
-    /**
-     * Indicates user operation failed because the target user is in foreground.
-     *
-     * @see #stopUser(ComponentName, UserHandle)
-     * @see #logoutUser(ComponentName)
-     */
-    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
-
-    /**
-     * Result returned from
-     * <ul>
-     * <li>{@link #startUserInBackground(ComponentName, UserHandle)}</li>
-     * <li>{@link #stopUser(ComponentName, UserHandle)}</li>
-     * <li>{@link #logoutUser(ComponentName)}</li>
-     * </ul>
-     *
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "USER_OPERATION_" }, value = {
-            USER_OPERATION_SUCCESS,
-            USER_OPERATION_ERROR_UNKNOWN,
-            USER_OPERATION_ERROR_MANAGED_PROFILE,
-            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
-            USER_OPERATION_ERROR_CURRENT_USER
-    })
-    public @interface UserOperationResult {}
-
-    /**
      * Called by a device owner to start the specified secondary user in background.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle the user to be started in background.
      * @return one of the following result codes:
-     * {@link #USER_OPERATION_ERROR_UNKNOWN},
-     * {@link #USER_OPERATION_SUCCESS},
-     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
-     * {@link #USER_OPERATION_ERROR_MAX_RUNNING_USERS},
+     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+     * {@link UserManager#USER_OPERATION_SUCCESS},
+     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link UserManager#USER_OPERATION_ERROR_MAX_RUNNING_USERS},
      * @throws SecurityException if {@code admin} is not a device owner.
      * @see #getSecondaryUsers(ComponentName)
      */
@@ -6704,10 +6676,10 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle the user to be stopped.
      * @return one of the following result codes:
-     * {@link #USER_OPERATION_ERROR_UNKNOWN},
-     * {@link #USER_OPERATION_SUCCESS},
-     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
-     * {@link #USER_OPERATION_ERROR_CURRENT_USER}
+     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+     * {@link UserManager#USER_OPERATION_SUCCESS},
+     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
      * @throws SecurityException if {@code admin} is not a device owner.
      * @see #getSecondaryUsers(ComponentName)
      */
@@ -6727,10 +6699,10 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return one of the following result codes:
-     * {@link #USER_OPERATION_ERROR_UNKNOWN},
-     * {@link #USER_OPERATION_SUCCESS},
-     * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE},
-     * {@link #USER_OPERATION_ERROR_CURRENT_USER}
+     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+     * {@link UserManager#USER_OPERATION_SUCCESS},
+     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
      * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
      * @see #getSecondaryUsers(ComponentName)
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5218a73..c29369f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -192,6 +192,8 @@
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
     void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
 
+    void setDefaultSmsApplication(in ComponentName admin, String packageName);
+
     void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings);
     Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName);
     boolean setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index f456395..0963594 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -83,6 +83,13 @@
      */
     public static final int FLAG_NON_INCREMENTAL = 1 << 2;
 
+    /**
+     * Used as a boolean extra in the binding intent of transports. We pass {@code true} to
+     * notify transports that the current connection is used for registering the transport.
+     */
+    public static final String EXTRA_TRANSPORT_REGISTRATION =
+            "android.app.backup.extra.TRANSPORT_REGISTRATION";
+
     IBackupTransport mBinderImpl = new TransportImpl();
 
     public IBinder getBinder() {
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 578f0e3..65e4291 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -42,8 +42,8 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
-        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
-                pendingActions, "PAUSE_ACTIVITY_ITEM");
+        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
+                "PAUSE_ACTIVITY_ITEM");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index b66d61b..059e0af 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -185,8 +185,8 @@
                     break;
                 case ON_PAUSE:
                     mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
-                            false /* userLeaving */, 0 /* configChanges */,
-                            true /* dontReport */, mPendingActions, "LIFECYCLER_PAUSE_ACTIVITY");
+                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,
+                            "LIFECYCLER_PAUSE_ACTIVITY");
                     break;
                 case ON_STOP:
                     mTransactionHandler.handleStopActivity(r.token, false /* show */,
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 718e465..73b6eb2 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -16,13 +16,16 @@
 
 package android.content;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.os.Handler;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 
 /**
@@ -45,6 +48,7 @@
 @SystemService(Context.CLIPBOARD_SERVICE)
 public class ClipboardManager extends android.text.ClipboardManager {
     private final Context mContext;
+    private final Handler mHandler;
     private final IClipboard mService;
 
     private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners
@@ -52,20 +56,11 @@
 
     private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener
             = new IOnPrimaryClipChangedListener.Stub() {
-        public void dispatchPrimaryClipChanged() {
-            mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED);
-        }
-    };
-
-    static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1;
-
-    private final Handler mHandler = new Handler() {
         @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_REPORT_PRIMARY_CLIP_CHANGED:
-                    reportPrimaryClipChanged();
-            }
+        public void dispatchPrimaryClipChanged() {
+            mHandler.post(() -> {
+                reportPrimaryClipChanged();
+            });
         }
     };
 
@@ -89,6 +84,7 @@
     /** {@hide} */
     public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
         mContext = context;
+        mHandler = handler;
         mService = IClipboard.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
     }
@@ -98,12 +94,13 @@
      * is involved in normal cut and paste operations.
      *
      * @param clip The clipped data item to set.
+     * @see #getPrimaryClip()
+     * @see #clearPrimaryClip()
      */
-    public void setPrimaryClip(ClipData clip) {
+    public void setPrimaryClip(@NonNull ClipData clip) {
         try {
-            if (clip != null) {
-                clip.prepareToLeaveProcess(true);
-            }
+            Preconditions.checkNotNull(clip);
+            clip.prepareToLeaveProcess(true);
             mService.setPrimaryClip(clip, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -111,9 +108,24 @@
     }
 
     /**
-     * Returns the current primary clip on the clipboard.
+     * Clears any current primary clip on the clipboard.
+     *
+     * @see #setPrimaryClip(ClipData)
      */
-    public ClipData getPrimaryClip() {
+    public void clearPrimaryClip() {
+        try {
+            mService.clearPrimaryClip(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current primary clip on the clipboard.
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public @Nullable ClipData getPrimaryClip() {
         try {
             return mService.getPrimaryClip(mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -124,8 +136,10 @@
     /**
      * Returns a description of the current primary clip on the clipboard
      * but not a copy of its data.
+     *
+     * @see #setPrimaryClip(ClipData)
      */
-    public ClipDescription getPrimaryClipDescription() {
+    public @Nullable ClipDescription getPrimaryClipDescription() {
         try {
             return mService.getPrimaryClipDescription(mContext.getOpPackageName());
         } catch (RemoteException e) {
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index af0b8f0..135a436 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -27,6 +27,7 @@
  */
 interface IClipboard {
     void setPrimaryClip(in ClipData clip, String callingPackage);
+    void clearPrimaryClip(String callingPackage);
     ClipData getPrimaryClip(String pkg);
     ClipDescription getPrimaryClipDescription(String callingPackage);
     boolean hasPrimaryClip(String callingPackage);
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 41aa9c3..5b3d3e5 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -236,6 +236,11 @@
             int userId);
 
     /**
+     * @return The default home activity component name.
+     */
+    public abstract ComponentName getDefaultHomeActivity(int userId);
+
+    /**
      * Called by DeviceOwnerManagerService to set the package names of device owner and profile
      * owners.
      */
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 93690bf..2baf539 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1615,12 +1615,7 @@
         dest.writeInt(mnc);
 
         fixUpLocaleList();
-        final int localeListSize = mLocaleList.size();
-        dest.writeInt(localeListSize);
-        for (int i = 0; i < localeListSize; ++i) {
-            final Locale l = mLocaleList.get(i);
-            dest.writeString(l.toLanguageTag());
-        }
+        dest.writeParcelable(mLocaleList, flags);
 
         if(userSetLocale) {
             dest.writeInt(1);
@@ -1654,12 +1649,7 @@
         mcc = source.readInt();
         mnc = source.readInt();
 
-        final int localeListSize = source.readInt();
-        final Locale[] localeArray = new Locale[localeListSize];
-        for (int i = 0; i < localeListSize; ++i) {
-            localeArray[i] = Locale.forLanguageTag(source.readString());
-        }
-        mLocaleList = new LocaleList(localeArray);
+        mLocaleList = source.readParcelable(LocaleList.class.getClassLoader());
         locale = mLocaleList.get(0);
 
         userSetLocale = (source.readInt()==1);
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index eae5217..29c298e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -41,7 +41,7 @@
     oneway void cancelAnomalyAlarm();
 
     /**
-      * Register a repeating alarm for polling to fire at the given timestamp and every
+      * Register a repeating alarm for pulling to fire at the given timestamp and every
       * intervalMs thereafter (in ms since epoch).
       * If polling alarm had already been registered, it will be replaced by new one.
       * Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
@@ -49,9 +49,19 @@
       */
     oneway void setPullingAlarms(long timestampMs, long intervalMs);
 
-    /** Cancel any repeating polling alarm. */
+    /** Cancel any repeating pulling alarm. */
     oneway void cancelPullingAlarms();
 
+    /**
+      * Register an alarm when we want to trigger subscribers at the given
+      * timestamp (in ms since epoch).
+      * If an alarm had already been registered, it will be replaced by new one.
+      */
+    oneway void setAlarmForSubscriberTriggering(long timestampMs);
+
+    /** Cancel any alarm for the purpose of subscriber triggering. */
+    oneway void cancelAlarmForSubscriberTriggering();
+
     /** Pull the specified data. Results will be sent to statsd when complete. */
     StatsLogEventWrapper[] pullData(int pullCode);
 
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 682a24f1..2a68714 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -47,6 +47,13 @@
     void informPollAlarmFired();
 
     /**
+     * Tells statsd that it is time to handle periodic alarms. Statsd will be responsible for
+     * determing what alarm subscriber to trigger.
+     * Two-way binder call so that caller's method (and corresponding wakelocks) will linger.
+     */
+    void informAlarmForSubscriberTriggeringFired();
+
+    /**
      * Tells statsd to store data to disk.
      */
     void writeDataToDisk();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 811cc5e..97d415e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -538,6 +538,7 @@
             ServiceType.DATA_SAVER,
             ServiceType.FORCE_ALL_APPS_STANDBY,
             ServiceType.OPTIONAL_SENSORS,
+            ServiceType.AOD,
     })
     public @interface ServiceType {
         int NULL = 0;
@@ -551,6 +552,7 @@
         int SOUND = 8;
         int BATTERY_STATS = 9;
         int DATA_SAVER = 10;
+        int AOD = 14;
 
         /**
          * Whether to enable force-app-standby on all apps or not.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1856200..2693bab 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -913,6 +913,9 @@
      * Specifies if user switching is blocked on the current user.
      *
      * <p> This restriction can only be set by the device owner, it will be applied to all users.
+     * Device owner can still switch user via
+     * {@link DevicePolicyManager#switchUser(ComponentName, UserHandle)} when this restriction is
+     * set.
      *
      * <p>The default value is <code>false</code>.
      *
@@ -1039,6 +1042,85 @@
      */
     public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1;
 
+    /**
+     * Indicates user operation is successful.
+     */
+    public static final int USER_OPERATION_SUCCESS = 0;
+
+    /**
+     * Indicates user operation failed for unknown reason.
+     */
+    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
+
+    /**
+     * Indicates user operation failed because target user is a managed profile.
+     */
+    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
+
+    /**
+     * Indicates user operation failed because maximum running user limit has been reached.
+     */
+    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
+
+    /**
+     * Indicates user operation failed because the target user is in the foreground.
+     */
+    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
+
+    /**
+     * Indicates user operation failed because device has low data storage.
+     */
+    public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5;
+
+    /**
+     * Indicates user operation failed because maximum user limit has been reached.
+     */
+    public static final int USER_OPERATION_ERROR_MAX_USERS = 6;
+
+    /**
+     * Result returned from various user operations.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "USER_OPERATION_" }, value = {
+            USER_OPERATION_SUCCESS,
+            USER_OPERATION_ERROR_UNKNOWN,
+            USER_OPERATION_ERROR_MANAGED_PROFILE,
+            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
+            USER_OPERATION_ERROR_CURRENT_USER,
+            USER_OPERATION_ERROR_LOW_STORAGE,
+            USER_OPERATION_ERROR_MAX_USERS
+    })
+    public @interface UserOperationResult {}
+
+    /**
+     * Thrown to indicate user operation failed.
+     */
+    public static class UserOperationException extends RuntimeException {
+        private final @UserOperationResult int mUserOperationResult;
+
+        /**
+         * Constructs a UserOperationException with specific result code.
+         *
+         * @param message the detail message
+         * @param userOperationResult the result code
+         * @hide
+         */
+        public UserOperationException(String message,
+                @UserOperationResult int userOperationResult) {
+            super(message);
+            mUserOperationResult = userOperationResult;
+        }
+
+        /**
+         * Returns the operation result code.
+         */
+        public @UserOperationResult int getUserOperationResult() {
+            return mUserOperationResult;
+        }
+    }
+
     /** @hide */
     public static UserManager get(Context context) {
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 57418c8..ca4c796 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -493,11 +493,12 @@
      * Instructs the zygote to pre-load the classes and native libraries at the given paths
      * for the specified abi. Not all zygotes support this function.
      */
-    public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
-                                        String abi) throws ZygoteStartFailedEx, IOException {
+    public boolean preloadPackageForAbi(String packagePath, String libsPath, String libFileName,
+                                        String cacheKey, String abi) throws ZygoteStartFailedEx,
+                                                                            IOException {
         synchronized(mLock) {
             ZygoteState state = openZygoteSocketIfNeeded(abi);
-            state.writer.write("4");
+            state.writer.write("5");
             state.writer.newLine();
 
             state.writer.write("--preload-package");
@@ -509,6 +510,9 @@
             state.writer.write(libsPath);
             state.writer.newLine();
 
+            state.writer.write(libFileName);
+            state.writer.newLine();
+
             state.writer.write(cacheKey);
             state.writer.newLine();
 
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 60df467..70de09e 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -223,14 +223,14 @@
         /** Call was WIFI call. */
         public static final int FEATURES_WIFI = 1 << 3;
 
-        /** Call was on RTT at some point */
-        public static final int FEATURES_RTT = 1 << 4;
-
         /**
          * Indicates the call underwent Assisted Dialing.
          * @hide
          */
-        public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+        public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4;
+
+        /** Call was on RTT at some point */
+        public static final int FEATURES_RTT = 1 << 5;
 
         /**
          * The phone number as the user entered it.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cb38c0f..0299407 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8581,6 +8581,7 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @SystemApi
         public static final String EUICC_PROVISIONED = "euicc_provisioned";
 
         /**
diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationDialog.java
index e9df370..f6127e1 100644
--- a/core/java/android/security/ConfirmationDialog.java
+++ b/core/java/android/security/ConfirmationDialog.java
@@ -17,7 +17,10 @@
 package android.security;
 
 import android.annotation.NonNull;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -86,6 +89,7 @@
     private byte[] mExtraData;
     private ConfirmationCallback mCallback;
     private Executor mExecutor;
+    private Context mContext;
 
     private final KeyStore mKeyStore = KeyStore.getInstance();
 
@@ -190,15 +194,39 @@
             if (mExtraData == null) {
                 throw new IllegalArgumentException("extraData must be set");
             }
-            return new ConfirmationDialog(mPromptText, mExtraData);
+            return new ConfirmationDialog(context, mPromptText, mExtraData);
         }
     }
 
-    private ConfirmationDialog(CharSequence promptText, byte[] extraData) {
+    private ConfirmationDialog(Context context, CharSequence promptText, byte[] extraData) {
+        mContext = context;
         mPromptText = promptText;
         mExtraData = extraData;
     }
 
+    private static final int UI_OPTION_ACCESSIBILITY_INVERTED_FLAG = 1 << 0;
+    private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1;
+
+    private int getUiOptionsAsFlags() {
+        int uiOptionsAsFlags = 0;
+        try {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            int inversionEnabled = Settings.Secure.getInt(contentResolver,
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+            if (inversionEnabled == 1) {
+                uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
+            }
+            float fontScale = Settings.System.getFloat(contentResolver,
+                    Settings.System.FONT_SCALE);
+            if (fontScale > 1.0) {
+                uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
+            }
+        } catch (SettingNotFoundException e) {
+            Log.w(TAG, "Unexpected SettingNotFoundException");
+        }
+        return uiOptionsAsFlags;
+    }
+
     /**
      * Requests a confirmation prompt to be presented to the user.
      *
@@ -220,8 +248,7 @@
         mCallback = callback;
         mExecutor = executor;
 
-        int uiOptionsAsFlags = 0;
-        // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed.
+        int uiOptionsAsFlags = getUiOptionsAsFlags();
         String locale = Locale.getDefault().toLanguageTag();
         int responseCode = mKeyStore.presentConfirmationPrompt(
                 mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
@@ -277,7 +304,6 @@
      * @return true if confirmation prompts are supported by the device.
      */
     public static boolean isSupported() {
-        // TODO: read and return system property.
-        return true;
+        return KeyStore.getInstance().isConfirmationPromptSupported();
     }
 }
diff --git a/core/java/android/security/keystore/BadCertificateFormatException.java b/core/java/android/security/keystore/BadCertificateFormatException.java
index ddc7bd2..c51b773 100644
--- a/core/java/android/security/keystore/BadCertificateFormatException.java
+++ b/core/java/android/security/keystore/BadCertificateFormatException.java
@@ -17,8 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when the recovery agent supplies an invalid X509 certificate.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.BadCertificateFormatException}.
  * @hide
  */
 public class BadCertificateFormatException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/DecryptionFailedException.java b/core/java/android/security/keystore/DecryptionFailedException.java
index 945fcf6..c0b52f7 100644
--- a/core/java/android/security/keystore/DecryptionFailedException.java
+++ b/core/java/android/security/keystore/DecryptionFailedException.java
@@ -17,9 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key,
- * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.DecryptionFailedException}.
  * @hide
  */
 public class DecryptionFailedException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/InternalRecoveryServiceException.java b/core/java/android/security/keystore/InternalRecoveryServiceException.java
index 85829be..40076f7 100644
--- a/core/java/android/security/keystore/InternalRecoveryServiceException.java
+++ b/core/java/android/security/keystore/InternalRecoveryServiceException.java
@@ -17,11 +17,7 @@
 package android.security.keystore;
 
 /**
- * An error thrown when something went wrong internally in the recovery service.
- *
- * <p>This is an unexpected error, and indicates a problem with the service itself, rather than the
- * caller having performed some kind of illegal action.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.InternalRecoveryServiceException}.
  * @hide
  */
 public class InternalRecoveryServiceException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/KeyDerivationParams.java b/core/java/android/security/keystore/KeyDerivationParams.java
index b19cee2..e475dc3 100644
--- a/core/java/android/security/keystore/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/KeyDerivationParams.java
@@ -27,9 +27,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Collection of parameters which define a key derivation function.
- * Currently only supports salted SHA-256
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyDerivationParams}.
  * @hide
  */
 public final class KeyDerivationParams implements Parcelable {
diff --git a/core/java/android/security/keystore/KeychainProtectionParams.java b/core/java/android/security/keystore/KeychainProtectionParams.java
index a940fdc..19a087d 100644
--- a/core/java/android/security/keystore/KeychainProtectionParams.java
+++ b/core/java/android/security/keystore/KeychainProtectionParams.java
@@ -28,23 +28,7 @@
 import java.util.Arrays;
 
 /**
- * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This
- * class wraps all the data necessary to derive the same key on a recovering device:
- *
- * <ul>
- *     <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
- *         the recovering device can display the pattern UI to the user when asking them to enter
- *         the lock screen from their previous device.
- *     <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
- * </ul>
- *
- * <p>As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current
- * version of the keychain.
- *
- * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
- * user's lock screen. In the future, the keychain will support multiple layers of protection
- * (e.g. an additional keychain password, along with the lock screen).
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyChainProtectionParams}.
  * @hide
  */
 public final class KeychainProtectionParams implements Parcelable {
diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java
index 23aec25..cf18fd1 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.java
+++ b/core/java/android/security/keystore/KeychainSnapshot.java
@@ -25,21 +25,7 @@
 import java.util.List;
 
 /**
- * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
- *
- * <ul>
- *     <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
- *         protected the keychain, which is why this forces a new snapshot.)
- *     <li>A key is added to or removed from the recoverable keychain.
- * </ul>
- *
- * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
- * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
- * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
- * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
- * sends it to the framework, where it is decrypted using the user's lock screen from their previous
- * device.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyChainSnapshot}.
  * @hide
  */
 public final class KeychainSnapshot implements Parcelable {
diff --git a/core/java/android/security/keystore/LockScreenRequiredException.java b/core/java/android/security/keystore/LockScreenRequiredException.java
index b07fb9c..0970284 100644
--- a/core/java/android/security/keystore/LockScreenRequiredException.java
+++ b/core/java/android/security/keystore/LockScreenRequiredException.java
@@ -17,10 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when trying to generate keys for a profile that has no lock screen set.
- *
- * <p>A lock screen must be set, as the lock screen is used to encrypt the snapshot.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.LockScreenRequiredException}.
  * @hide
  */
 public class LockScreenRequiredException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/RecoveryClaim.java b/core/java/android/security/keystore/RecoveryClaim.java
index 6f566af..12be607 100644
--- a/core/java/android/security/keystore/RecoveryClaim.java
+++ b/core/java/android/security/keystore/RecoveryClaim.java
@@ -17,8 +17,7 @@
 package android.security.keystore;
 
 /**
- * An attempt to recover a keychain protected by remote secure hardware.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
  * @hide
  */
 public class RecoveryClaim {
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index 4a0de5f..145261e 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -31,22 +31,6 @@
 import java.util.Map;
 
 /**
- * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
- * other Android devices belonging to the user. The exported keychain is protected by the user's
- * lock screen.
- *
- * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
- * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
- * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
- * After  that number of incorrect guesses, the trusted hardware no longer allows access to the
- * key chain.
- *
- * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
- * recovery agent is itself the system app.
- *
- * <p>A recovery agent requires the privileged permission
- * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
- *
  * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
  * @hide
  */
diff --git a/core/java/android/security/keystore/RecoveryControllerException.java b/core/java/android/security/keystore/RecoveryControllerException.java
index 5b806b7..f990c23 100644
--- a/core/java/android/security/keystore/RecoveryControllerException.java
+++ b/core/java/android/security/keystore/RecoveryControllerException.java
@@ -19,8 +19,7 @@
 import java.security.GeneralSecurityException;
 
 /**
- * Base exception for errors thrown by {@link RecoveryController}.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
  * @hide
  */
 public abstract class RecoveryControllerException extends GeneralSecurityException {
diff --git a/core/java/android/security/keystore/RecoverySession.java b/core/java/android/security/keystore/RecoverySession.java
index ae8d91a..8a3e06b 100644
--- a/core/java/android/security/keystore/RecoverySession.java
+++ b/core/java/android/security/keystore/RecoverySession.java
@@ -19,9 +19,7 @@
 import java.security.SecureRandom;
 
 /**
- * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a
- * recovery agent.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
  * @hide
  */
 public class RecoverySession implements AutoCloseable {
diff --git a/core/java/android/security/keystore/SessionExpiredException.java b/core/java/android/security/keystore/SessionExpiredException.java
index f13e206..7c8d5e4 100644
--- a/core/java/android/security/keystore/SessionExpiredException.java
+++ b/core/java/android/security/keystore/SessionExpiredException.java
@@ -17,8 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when attempting to use a {@link RecoverySession} that has since expired.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.SessionExpiredException}.
  * @hide
  */
 public class SessionExpiredException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/WrappedApplicationKey.java b/core/java/android/security/keystore/WrappedApplicationKey.java
index 522bb95..2ce8c7d 100644
--- a/core/java/android/security/keystore/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/WrappedApplicationKey.java
@@ -23,16 +23,7 @@
 import com.android.internal.util.Preconditions;
 
 /**
- * Helper class with data necessary recover a single application key, given a recovery key.
- *
- * <ul>
- *   <li>Alias - Keystore alias of the key.
- *   <li>Encrypted key material.
- * </ul>
- *
- * Note that Application info is not included. Recovery Agent can only make its own keys
- * recoverable.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.WrappedApplicationKey}.
  * @hide
  */
 public final class WrappedApplicationKey implements Parcelable {
diff --git a/core/java/android/security/keystore/recovery/RecoveryClaim.java b/core/java/android/security/keystore/recovery/RecoveryClaim.java
deleted file mode 100644
index 45c6b4ff..0000000
--- a/core/java/android/security/keystore/recovery/RecoveryClaim.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 android.security.keystore.recovery;
-
-/**
- * An attempt to recover a keychain protected by remote secure hardware.
- *
- * @hide
- * Deprecated
- */
-public class RecoveryClaim {
-
-    private final RecoverySession mRecoverySession;
-    private final byte[] mClaimBytes;
-
-    RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) {
-        mRecoverySession = recoverySession;
-        mClaimBytes = claimBytes;
-    }
-
-    /**
-     * Returns the session associated with the recovery attempt. This is used to match the symmetric
-     * key, which remains internal to the framework, for decrypting the claim response.
-     *
-     * @return The session data.
-     */
-    public RecoverySession getRecoverySession() {
-        return mRecoverySession;
-    }
-
-    /**
-     * Returns the encrypted claim's bytes.
-     *
-     * <p>This should be sent by the recovery agent to the remote secure hardware, which will use
-     * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key
-     * to the device.
-     */
-    public byte[] getClaimBytes() {
-        return mClaimBytes;
-    }
-}
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 0683e02..426ca5c 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -113,6 +113,14 @@
      */
     public static final int ERROR_DECRYPTION_FAILED = 26;
 
+    /**
+     * Error thrown if the format of a given key is invalid. This might be because the key has a
+     * wrong length, invalid content, etc.
+     *
+     * @hide
+     */
+    public static final int ERROR_INVALID_KEY_FORMAT = 27;
+
 
     private final ILockSettings mBinder;
     private final KeyStore mKeyStore;
@@ -461,6 +469,7 @@
         }
     }
 
+    // TODO: Unhide the following APIs, generateKey(), importKey(), and getKey()
     /**
      * @deprecated Use {@link #generateKey(String)}.
      * @removed
@@ -503,6 +512,40 @@
     }
 
     /**
+     * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code
+     * keyBytes}.
+     *
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+     *     screen is required to generate recoverable keys.
+     *
+     * @hide
+     */
+    public Key importKey(@NonNull String alias, byte[] keyBytes)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        try {
+            String grantAlias = mBinder.importKey(alias, keyBytes);
+            if (grantAlias == null) {
+                throw new InternalRecoveryServiceException("Null grant alias");
+            }
+            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+                    mKeyStore,
+                    grantAlias,
+                    KeyStore.UID_SELF);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (UnrecoverableKeyException e) {
+            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_INSECURE_USER) {
+                throw new LockScreenRequiredException(e.getMessage());
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
      * Gets a key called {@code alias} from the recoverable key store.
      *
      * @param alias The key alias.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 422e36b..eebd22a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -85,7 +85,10 @@
  * or after {@link #onListenerDisconnected()}.
  * </p>
  * <p> Notification listeners cannot get notification access or be bound by the system on
- * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
+ * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices. The system also ignores
+ * notification listeners running in a work profile. A
+ * {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work
+ * profile.</p>
  */
 public abstract class NotificationListenerService extends Service {
 
@@ -1217,6 +1220,7 @@
                 // convert icon metadata to legacy format for older clients
                 createLegacyIconExtras(sbn.getNotification());
                 maybePopulateRemoteViews(sbn.getNotification());
+                maybePopulatePeople(sbn.getNotification());
             } catch (IllegalArgumentException e) {
                 // warn and drop corrupt notification
                 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1ead0b4..a5a7cbc 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,7 +37,6 @@
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
         DEFAULT_FLAGS = new HashMap<>();
-        DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
index 402bef9..cc9991a 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -110,9 +110,9 @@
         Drawable badgeColor = sysRes.getDrawable(
                 com.android.internal.R.drawable.ic_corp_icon_badge_color)
                 .getConstantState().newDrawable().mutate();
-        badgeColor.setTint(backgroundColor);
 
         Drawable badgeForeground = sysRes.getDrawable(foregroundRes);
+        badgeForeground.setTint(backgroundColor);
 
         Drawable[] drawables = base == null
                 ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 3350f3e..517b13b 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -18,8 +18,7 @@
 
 /**
  * StatsLog provides an API for developers to send events to statsd. The events can be used to
- * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
- * statsd.
+ * define custom metrics inside statsd.
  */
 public final class StatsLog extends StatsLogInternal {
     private static final String TAG = "StatsManager";
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index e9fe481..e0ccda9 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -205,25 +205,21 @@
         }
 
         PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager();
-        PackageInfo packageInfo;
+        String libraryFileName;
         try {
-            packageInfo = packageManager.getPackageInfo(packageName,
+            PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
                     PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+            libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(LOGTAG, "Couldn't find package " + packageName);
             return LIBLOAD_WRONG_PACKAGE_NAME;
         }
 
-        try {
-            int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, packageInfo);
-            // If we failed waiting for relro we want to return that fact even if we successfully
-            // load the relro file.
-            if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
-            return loadNativeRet;
-        } catch (MissingWebViewPackageException e) {
-            Log.e(LOGTAG, "Couldn't load native library: " + e);
-            return LIBLOAD_FAILED_TO_LOAD_LIBRARY;
-        }
+        int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, libraryFileName);
+        // If we failed waiting for relro we want to return that fact even if we successfully
+        // load the relro file.
+        if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
+        return loadNativeRet;
     }
 
     static WebViewFactoryProvider getProvider() {
@@ -454,7 +450,8 @@
                 ClassLoader clazzLoader = webViewContext.getClassLoader();
 
                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
-                WebViewLibraryLoader.loadNativeLibrary(clazzLoader, sPackageInfo);
+                WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
+                        getWebViewLibrary(sPackageInfo.applicationInfo));
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
 
                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index eb2b6bc..cabba06 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -234,17 +234,14 @@
      * <p class="note"><b>Note:</b> Assumes that we have waited for relro creation.
      *
      * @param clazzLoader class loader used to find the linker namespace to load the library into.
-     * @param packageInfo the package from which WebView is loaded.
+     * @param libraryFileName the filename of the library to load.
      */
-    static int loadNativeLibrary(ClassLoader clazzLoader, PackageInfo packageInfo)
-            throws WebViewFactory.MissingWebViewPackageException {
+    public static int loadNativeLibrary(ClassLoader clazzLoader, String libraryFileName) {
         if (!sAddressSpaceReserved) {
             Log.e(LOGTAG, "can't load with relro file; address space not reserved");
             return WebViewFactory.LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
         }
 
-        final String libraryFileName =
-                WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo);
         String relroPath = VMRuntime.getRuntime().is64Bit() ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
                                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_32;
         int result = nativeLoadWithRelroFile(libraryFileName, relroPath, clazzLoader);
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 63fbef3..4167ad4 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -169,6 +169,8 @@
             final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
                     TextUtils.join(File.pathSeparator, zipPaths);
 
+            String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo);
+
             // In the case where the ApplicationInfo has been modified by the stub WebView,
             // we need to use the original ApplicationInfo to determine what the original classpath
             // would have been to use as a cache key.
@@ -179,7 +181,7 @@
             ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
 
             Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
-            sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey,
+            sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey,
                                          Build.SUPPORTED_ABIS[0]);
         } catch (Exception e) {
             Log.e(LOGTAG, "Error connecting to webview zygote", e);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 6e87e23..85f68d7 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -319,6 +320,10 @@
      * producing a shakiness effect for the magnifier content.
      */
     private static class InternalPopupWindow {
+        // The alpha set on the magnifier's content, which defines how
+        // prominent the white background is.
+        private static final int CONTENT_BITMAP_ALPHA = 242;
+
         // Display associated to the view the magnifier is attached to.
         private final Display mDisplay;
         // The size of the content of the magnifier.
@@ -511,10 +516,13 @@
                 final DisplayListCanvas canvas =
                         mBitmapRenderNode.start(mContentWidth, mContentHeight);
                 try {
+                    canvas.drawColor(Color.WHITE);
+
                     final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
                     final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
                     final Paint paint = new Paint();
                     paint.setFilterBitmap(true);
+                    paint.setAlpha(CONTENT_BITMAP_ALPHA);
                     canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
                 } finally {
                     mBitmapRenderNode.end(canvas);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2cfdb76..50e6393 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -319,6 +319,11 @@
 
     // Enum for the "typeface" XML parameter.
     // TODO: How can we get this from the XML instead of hardcoding it here?
+    /** @hide */
+    @IntDef(value = {DEFAULT_TYPEFACE, SANS, SERIF, MONOSPACE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface XMLTypefaceAttr{}
+    private static final int DEFAULT_TYPEFACE = -1;
     private static final int SANS = 1;
     private static final int SERIF = 2;
     private static final int MONOSPACE = 3;
@@ -1976,33 +1981,52 @@
         }
     }
 
-    private void setTypefaceFromAttrs(Typeface fontTypeface, String familyName, int typefaceIndex,
-            int styleIndex) {
-        Typeface tf = fontTypeface;
-        if (tf == null && familyName != null) {
-            tf = Typeface.create(familyName, styleIndex);
-        } else if (tf != null && tf.getStyle() != styleIndex) {
-            tf = Typeface.create(tf, styleIndex);
+    /**
+     * Sets the Typeface taking into account the given attributes.
+     *
+     * @param typeface a typeface
+     * @param familyName family name string, e.g. "serif"
+     * @param typefaceIndex an index of the typeface enum, e.g. SANS, SERIF.
+     * @param style a typeface style
+     * @param weight a weight value for the Typeface or -1 if not specified.
+     */
+    private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
+            @XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
+            @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+        if (typeface == null && familyName != null) {
+            // Lookup normal Typeface from system font map.
+            final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
+            resolveStyleAndSetTypeface(normalTypeface, style, weight);
+        } else if (typeface != null) {
+            resolveStyleAndSetTypeface(typeface, style, weight);
+        } else {  // both typeface and familyName is null.
+            switch (typefaceIndex) {
+                case SANS:
+                    resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight);
+                    break;
+                case SERIF:
+                    resolveStyleAndSetTypeface(Typeface.SERIF, style, weight);
+                    break;
+                case MONOSPACE:
+                    resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight);
+                    break;
+                case DEFAULT_TYPEFACE:
+                default:
+                    resolveStyleAndSetTypeface(null, style, weight);
+                    break;
+            }
         }
-        if (tf != null) {
-            setTypeface(tf);
-            return;
+    }
+
+    private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
+            @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+        if (weight >= 0) {
+            weight = Math.min(Typeface.MAX_WEIGHT, weight);
+            final boolean italic = (style & Typeface.ITALIC) != 0;
+            setTypeface(Typeface.create(typeface, weight, italic));
+        } else {
+            setTypeface(Typeface.create(typeface, style));
         }
-        switch (typefaceIndex) {
-            case SANS:
-                tf = Typeface.SANS_SERIF;
-                break;
-
-            case SERIF:
-                tf = Typeface.SERIF;
-                break;
-
-            case MONOSPACE:
-                tf = Typeface.MONOSPACE;
-                break;
-        }
-
-        setTypeface(tf, styleIndex);
     }
 
     private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) {
@@ -3392,6 +3416,7 @@
         boolean mFontFamilyExplicit = false;
         int mTypefaceIndex = -1;
         int mStyleIndex = -1;
+        int mFontWeight = -1;
         boolean mAllCaps = false;
         int mShadowColor = 0;
         float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0;
@@ -3416,6 +3441,7 @@
                     + "    mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
                     + "    mTypefaceIndex:" + mTypefaceIndex + "\n"
                     + "    mStyleIndex:" + mStyleIndex + "\n"
+                    + "    mFontWeight:" + mFontWeight + "\n"
                     + "    mAllCaps:" + mAllCaps + "\n"
                     + "    mShadowColor:" + mShadowColor + "\n"
                     + "    mShadowDx:" + mShadowDx + "\n"
@@ -3451,6 +3477,8 @@
                 com.android.internal.R.styleable.TextAppearance_fontFamily);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle,
                 com.android.internal.R.styleable.TextAppearance_textStyle);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textFontWeight,
+                com.android.internal.R.styleable.TextAppearance_textFontWeight);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps,
                 com.android.internal.R.styleable.TextAppearance_textAllCaps);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor,
@@ -3536,6 +3564,9 @@
                 case com.android.internal.R.styleable.TextAppearance_textStyle:
                     attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex);
                     break;
+                case com.android.internal.R.styleable.TextAppearance_textFontWeight:
+                    attributes.mFontWeight = appearance.getInt(attr, attributes.mFontWeight);
+                    break;
                 case com.android.internal.R.styleable.TextAppearance_textAllCaps:
                     attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps);
                     break;
@@ -3598,7 +3629,7 @@
             attributes.mFontFamily = null;
         }
         setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily,
-                attributes.mTypefaceIndex, attributes.mStyleIndex);
+                attributes.mTypefaceIndex, attributes.mStyleIndex, attributes.mFontWeight);
 
         if (attributes.mShadowColor != 0) {
             setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy,
@@ -5938,15 +5969,19 @@
         boolean forceUpdate = false;
         if (isPassword) {
             setTransformationMethod(PasswordTransformationMethod.getInstance());
-            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
+            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE,
+                    Typeface.NORMAL, -1 /* weight, not specifeid */);
         } else if (isVisiblePassword) {
             if (mTransformation == PasswordTransformationMethod.getInstance()) {
                 forceUpdate = true;
             }
-            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
+            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE,
+                    Typeface.NORMAL, -1 /* weight, not specified */);
         } else if (wasPassword || wasVisiblePassword) {
             // not in password mode, clean up typeface and transformation
-            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, -1, -1);
+            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */,
+                    DEFAULT_TYPEFACE /* typeface index */, Typeface.NORMAL,
+                    -1 /* weight, not specified */);
             if (mTransformation == PasswordTransformationMethod.getInstance()) {
                 forceUpdate = true;
             }
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 32b580c..9f2434e 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewFactoryProvider;
+import android.webkit.WebViewLibraryLoader;
 
 import java.io.DataOutputStream;
 import java.io.File;
@@ -71,7 +72,8 @@
         }
 
         @Override
-        protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
+        protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
+                String cacheKey) {
             Log.i(TAG, "Beginning package preload");
             // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
             // our children will reuse the same classloader instead of creating their own.
@@ -80,6 +82,10 @@
             ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
                     packagePath, libsPath, cacheKey);
 
+            // Load the native library using WebViewLibraryLoader to share the RELRO data with other
+            // processes.
+            WebViewLibraryLoader.loadNativeLibrary(loader, libFileName);
+
             // Add the APK to the Zygote's list of allowed files for children.
             String[] packageList = TextUtils.split(packagePath, File.pathSeparator);
             for (String packageEntry : packageList) {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index a32fb43..cd83c57 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -155,7 +155,7 @@
 
         if (parsedArgs.preloadPackage != null) {
             handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
-                    parsedArgs.preloadPackageCacheKey);
+                    parsedArgs.preloadPackageLibFileName, parsedArgs.preloadPackageCacheKey);
             return null;
         }
 
@@ -290,7 +290,8 @@
         return mSocketOutStream;
     }
 
-    protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
+    protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
+            String cacheKey) {
         throw new RuntimeException("Zyogte does not support package preloading");
     }
 
@@ -402,10 +403,24 @@
         String appDataDir;
 
         /**
-         * Whether to preload a package, with the package path in the remainingArgs.
+         * The APK path of the package to preload, when using --preload-package.
          */
         String preloadPackage;
+
+        /**
+         * The native library path of the package to preload, when using --preload-package.
+         */
         String preloadPackageLibs;
+
+        /**
+         * The filename of the native library to preload, when using --preload-package.
+         */
+        String preloadPackageLibFileName;
+
+        /**
+         * The cache key under which to enter the preloaded package into the classloader cache,
+         * when using --preload-package.
+         */
         String preloadPackageCacheKey;
 
         /**
@@ -571,6 +586,7 @@
                 } else if (arg.equals("--preload-package")) {
                     preloadPackage = args[++curArg];
                     preloadPackageLibs = args[++curArg];
+                    preloadPackageLibFileName = args[++curArg];
                     preloadPackageCacheKey = args[++curArg];
                 } else if (arg.equals("--preload-default")) {
                     preloadDefault = true;
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index d3fc644..7c9cf7a 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -68,6 +68,7 @@
     KeyChainSnapshot getKeyChainSnapshot();
     byte[] generateAndStoreKey(String alias);
     String generateKey(String alias);
+    String importKey(String alias, in byte[] keyBytes);
     String getKey(String alias);
     void removeKey(String alias);
     void setSnapshotCreatedPendingIntent(in PendingIntent intent);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 61a22c1..6456fe6 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -282,7 +282,7 @@
 
         // compute the frame count
         size_t frameCount;
-        if (audio_is_linear_pcm(format)) {
+        if (audio_has_proportional_frames(format)) {
             const size_t bytesPerSample = audio_bytes_per_sample(format);
             frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
         } else {
diff --git a/core/proto/android/os/data.proto b/core/proto/android/os/data.proto
new file mode 100644
index 0000000..c06f318
--- /dev/null
+++ b/core/proto/android/os/data.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+// This file contains protobuf definitions used in incidentd directly.
+// The top level proto message must be used as a new SectionType in
+// incidentd.
+
+// Output of SECTION_GZIP section type, which reads a file, gzip it and attached
+// in incident report as a proto field, example is LAST_KMSG.
+// NOTE the content in the file must not contain sensitive PII otherwise
+// implement it with fine-grained proto definition.
+message GZippedFileProto {
+    optional string filename = 1;
+
+    optional bytes gzipped_data = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 9a53b89..be15597 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@
 import "frameworks/base/core/proto/android/os/batterytype.proto";
 import "frameworks/base/core/proto/android/os/cpufreq.proto";
 import "frameworks/base/core/proto/android/os/cpuinfo.proto";
+import "frameworks/base/core/proto/android/os/data.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
 import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
@@ -52,9 +53,8 @@
 
 package android.os;
 
-// privacy field options must not be set at this level because all
-// the sections are able to be controlled and configured by section ids.
-// Instead privacy field options need to be configured in each section proto message.
+// Privacy tag can be marked to override UNSET messages so generic
+// message type can be handled case by case, e.g. GZippedFileProto.
 message IncidentProto {
     reserved 1001;
 
@@ -151,6 +151,12 @@
         (section).args = "/sys/class/power_supply/bms/battery_type"
     ];
 
+    optional GZippedFileProto last_kmsg = 2007 [
+        (section).type = SECTION_GZIP,
+        (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg",
+        (privacy).dest = DEST_AUTOMATIC
+    ];
+
     // System Services
     optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
         (section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 788d901..5042ede 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -58,6 +58,9 @@
   optional KeyguardControllerProto keyguard_controller = 3;
   optional int32 focused_stack_id = 4;
   optional .com.android.server.wm.proto.IdentifierProto resumed_activity = 5;
+  // Whether or not the home activity is the recents activity. This is needed for the CTS tests to
+  // know what activity types to check for when invoking splitscreen multi-window.
+  optional bool is_home_recents_component = 6;
 }
 
 /* represents ActivityStackSupervisor.ActivityDisplay */
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index d1c5db6..b288c11 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -220,6 +220,8 @@
   optional int64 allow_while_idle_long_duration_ms = 5;
   // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
   optional int64 allow_while_idle_whitelist_duration_ms = 6;
+  // Maximum alarm recurrence interval.
+  optional int64 max_interval_duration_ms = 7;
 }
 
 // A com.android.server.AlarmManagerService.FilterStats object.
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 78cce58..915f5fb 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -1,12 +1,37 @@
+<?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.
+-->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="20dp"
         android:height="20dp"
-        android:viewportWidth="20.0"
-        android:viewportHeight="20.0">
+        android:viewportWidth="20"
+        android:viewportHeight="20">
+
     <path
-        android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
-        android:fillColor="#FF6D00"/>
+        android:fillColor="#fcfcfc"
+        android:pathData="M 10 0 C 15.5228474983 0 20 4.47715250169 20 10 C 20 15.5228474983 15.5228474983 20 10 20 C 4.47715250169 20 0 15.5228474983 0 10 C 0 4.47715250169 4.47715250169 0 10 0 Z" />
     <path
-        android:pathData="M14.67,6.5h-2.33V5.33c0,-0.65 -0.52,-1.17 -1.17,-1.17H8.83c-0.65,0 -1.17,0.52 -1.17,1.17V6.5H5.33c-0.65,0 -1.16,0.52 -1.16,1.17l-0.01,6.42c0,0.65 0.52,1.17 1.17,1.17h9.33c0.65,0 1.17,-0.52 1.17,-1.17V7.67C15.83,7.02 15.31,6.5 14.67,6.5zM10,11.75c-0.64,0 -1.17,-0.52 -1.17,-1.17c0,-0.64 0.52,-1.17 1.17,-1.17c0.64,0 1.17,0.52 1.17,1.17C11.17,11.22 10.64,11.75 10,11.75zM11.17,6.5H8.83V5.33h2.33V6.5z"
-        android:fillColor="#FFFFFF"/>
-</vector>
+        android:strokeColor="#e8eaed"
+        android:strokeWidth="0.25"
+        android:pathData="M 10 0.12 C 15.4565733283 0.12 19.88 4.54342667167 19.88 10 C 19.88 15.4565733283 15.4565733283 19.88 10 19.88 C 4.54342667167 19.88 0.12 15.4565733283 0.12 10 C 0.12 4.54342667167 4.54342667167 0.12 10 0.12 Z" />
+    <path
+        android:pathData="M 3.5 3.5 L 16.5 3.5 L 16.5 16.5 L 3.5 16.5 L 3.5 3.5 Z" />
+    <path
+        android:fillColor="#1a73e8"
+        android:pathData="M14.46,6.58H12.23V5.5a1.09,1.09,0,0,0-1.11-1.08H8.89A1.09,1.09,0,0,0,7.77,5.5V6.58H5.54A1.09,1.09,0,0,0,4.43,7.65v5.91a1.09,1.09,0,0,0,1.11,1.08h8.91a1.09,1.09,0,0,0,1.11-1.08V7.65A1.09,1.09,0,0,0,14.46,6.58ZM10,11.42a1.08,1.08,0,1,1,1.11-1.08A1.1,1.1,0,0,1,10,11.42Zm1.11-4.84H8.89V5.5h2.23Z" />
+    <path
+        android:pathData="M 0 0 H 20 V 20 H 0 V 0 Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_badge_case.xml b/core/res/res/drawable/ic_corp_badge_case.xml
index 2d11ee6..1cd995e 100644
--- a/core/res/res/drawable/ic_corp_badge_case.xml
+++ b/core/res/res/drawable/ic_corp_badge_case.xml
@@ -5,5 +5,5 @@
         android:viewportHeight="20.0">
     <path
         android:pathData="M14.67,6.5h-2.33V5.33c0,-0.65 -0.52,-1.17 -1.17,-1.17H8.83c-0.65,0 -1.17,0.52 -1.17,1.17V6.5H5.33c-0.65,0 -1.16,0.52 -1.16,1.17l-0.01,6.42c0,0.65 0.52,1.17 1.17,1.17h9.33c0.65,0 1.17,-0.52 1.17,-1.17V7.67C15.83,7.02 15.31,6.5 14.67,6.5zM10,11.75c-0.64,0 -1.17,-0.52 -1.17,-1.17c0,-0.64 0.52,-1.17 1.17,-1.17c0.64,0 1.17,0.52 1.17,1.17C11.17,11.22 10.64,11.75 10,11.75zM11.17,6.5H8.83V5.33h2.33V6.5z"
-        android:fillColor="#FFFFFF"/>
+        android:fillColor="#1A73E8"/>
 </vector>
diff --git a/core/res/res/drawable/ic_corp_badge_color.xml b/core/res/res/drawable/ic_corp_badge_color.xml
index b6c7969..4aef7d0 100644
--- a/core/res/res/drawable/ic_corp_badge_color.xml
+++ b/core/res/res/drawable/ic_corp_badge_color.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
 Copyright (C) 2016 The Android Open Source Project
 
@@ -20,5 +21,5 @@
         android:viewportHeight="20.0">
     <path
         android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0"
-        android:fillColor="#FFFFFF"/>
-</vector>
+        android:fillColor="#fcfcfc"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_icon_badge_case.xml b/core/res/res/drawable/ic_corp_icon_badge_case.xml
index dd653c6..50551d40 100644
--- a/core/res/res/drawable/ic_corp_icon_badge_case.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge_case.xml
@@ -1,9 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="64dp"
         android:height="64dp"
-        android:viewportWidth="64.0"
-        android:viewportHeight="64.0">
+        android:viewportWidth="64"
+        android:viewportHeight="64">
+
     <path
-        android:pathData="M55.67,44h-3.33v-1.67c0,-0.92 -0.74,-1.67 -1.67,-1.67h-3.33c-0.92,0 -1.67,0.74 -1.67,1.67V44h-3.33c-0.92,0 -1.66,0.74 -1.66,1.67l-0.01,9.17c0,0.93 0.74,1.67 1.67,1.67h13.33c0.92,0 1.67,-0.74 1.67,-1.67v-9.17C57.33,44.74 56.59,44 55.67,44zM49,51.5c-0.92,0 -1.67,-0.75 -1.67,-1.67c0,-0.92 0.75,-1.67 1.67,-1.67s1.67,0.75 1.67,1.67C50.67,50.75 49.92,51.5 49,51.5zM50.67,44h-3.33v-1.67h3.33V44z"
-        android:fillColor="#FFFFFF"/>
-</vector>
+        android:pathData="M 42 42 L 58 42 L 58 58 L 42 58 L 42 42 Z" />
+    <path
+        android:fillColor="#1A73E8"
+        android:pathData="M55.33,46H52.67V44.67a1.33,1.33,0,0,0-1.33-1.33H48.67a1.33,1.33,0,0,0-1.33,1.33V46H44.67a1.32,1.32,0,0,0-1.33,1.33v7.33A1.33,1.33,0,0,0,44.67,56H55.33a1.33,1.33,0,0,0,1.33-1.33V47.33A1.33,1.33,0,0,0,55.33,46ZM50,52a1.33,1.33,0,1,1,1.33-1.33A1.34,1.34,0,0,1,50,52Zm1.33-6H48.67V44.67h2.67Z" />
+    <path
+        android:pathData="M 0 0 H 64 V 64 H 0 V 0 Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_icon_badge_color.xml b/core/res/res/drawable/ic_corp_icon_badge_color.xml
index 3bc4e67..6dba277 100644
--- a/core/res/res/drawable/ic_corp_icon_badge_color.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge_color.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
 Copyright (C) 2016 The Android Open Source Project
 
@@ -14,11 +15,16 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64.0dp"
-        android:height="64.0dp"
-        android:viewportWidth="64.0"
-        android:viewportHeight="64.0">
+        android:width="64dp"
+        android:height="64dp"
+        android:viewportWidth="64"
+        android:viewportHeight="64">
+
     <path
-        android:pathData="M49.1,48.8m-13.9,0.0a13.9,13.9 0.0,1.0 1.0,27.8 0.0a13.9,13.9 0.0,1.0 1.0,-27.8 0.0"
-        android:fillColor="#FFFFFF"/>
-</vector>
+        android:fillColor="#fcfcfc"
+        android:strokeColor="#e8eaed"
+        android:strokeWidth="0.25"
+        android:pathData="M62,50A12,12,0,1,1,50,38,12,12,0,0,1,62,50" />
+    <path
+        android:pathData="M 0 0 H 64 V 64 H 0 V 0 Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_icon_badge_shadow.xml b/core/res/res/drawable/ic_corp_icon_badge_shadow.xml
index a546cdd..f33ed1f 100644
--- a/core/res/res/drawable/ic_corp_icon_badge_shadow.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge_shadow.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
 Copyright (C) 2016 The Android Open Source Project
 
@@ -14,16 +15,35 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64.0dp"
-        android:height="64.0dp"
-        android:viewportWidth="64.0"
-        android:viewportHeight="64.0">
+        android:width="64dp"
+        android:height="64dp"
+        android:viewportWidth="64"
+        android:viewportHeight="64">
+
     <path
-        android:fillColor="#FF000000"
-        android:pathData="M49.1,50.1m-13.9,0.0a13.9,13.9 0.0,1.0 1.0,27.8 0.0a13.9,13.9 0.0,1.0 1.0,-27.8 0.0"
-        android:fillAlpha="0.2"/>
+        android:fillColor="#000000"
+        android:fillAlpha="0.06"
+        android:strokeAlpha="0.06"
+        android:strokeWidth="1"
+        android:pathData="M62,51.25a12,12,0,1,1-12-12,12,12,0,0,1,12,12" />
     <path
-        android:fillColor="#FF000000"
-        android:pathData="M49.1,49.4m-13.9,0.0a13.9,13.9 0.0,1.0 1.0,27.8 0.0a13.9,13.9 0.0,1.0 1.0,-27.8 0.0"
-        android:fillAlpha="0.2"/>
-</vector>
+        android:pathData="M 0 0 H 64 V 64 H 0 V 0 Z" />
+    <path
+        android:fillColor="#000000"
+        android:fillAlpha="0.06"
+        android:strokeAlpha="0.06"
+        android:strokeWidth="1"
+        android:pathData="M62,52.28A12,12,0,1,1,50.53,39.76,12,12,0,0,1,62,52.28" />
+    <path
+        android:fillColor="#000000"
+        android:fillAlpha="0.06"
+        android:strokeAlpha="0.06"
+        android:strokeWidth="1"
+        android:pathData="M62,50.75a12,12,0,1,1-12-12,12,12,0,0,1,12,12" />
+    <path
+        android:fillColor="#000000"
+        android:fillAlpha="0.06"
+        android:strokeAlpha="0.06"
+        android:strokeWidth="1"
+        android:pathData="M62,50.25a12,12,0,1,1-12-12,12,12,0,0,1,12,12" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 96a83f8..22ab9c9 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4472,6 +4472,8 @@
         <attr name="textSize" />
         <!-- Style (normal, bold, italic, bold|italic) for the text. -->
         <attr name="textStyle" />
+        <!-- Weight for the font used in the TextView. -->
+        <attr name="textFontWeight" />
         <!-- Typeface (normal, sans, serif, monospace) for the text. -->
         <attr name="typeface" />
         <!-- Font family (named by string or as a font resource reference) for the text. -->
@@ -4561,6 +4563,8 @@
         <attr name="typeface" />
         <!-- Style (normal, bold, italic, bold|italic) for the text. -->
         <attr name="textStyle" />
+        <!-- Weight for the font used in the TextView. -->
+        <attr name="textFontWeight" />
         <!-- Font family (named by string or as a font resource reference) for the text. -->
         <attr name="fontFamily" />
         <!-- Text color for links. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index a078d8b..722102e 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -167,8 +167,8 @@
     <color name="user_icon_default_white">#ffffffff</color><!-- white -->
 
     <!-- Default profile badge colors -->
-    <color name="profile_badge_1">#ffff6d00</color><!-- Orange -->
-    <color name="profile_badge_2">#ff000000</color><!-- Black -->
+    <color name="profile_badge_1">#ff1A73E8</color><!-- Blue -->
+    <color name="profile_badge_2">#ffff6d00</color><!-- Orange -->
     <color name="profile_badge_3">#ff22f033</color><!-- Green -->
 
     <!-- Default instant app badge color -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2ce08eb..2918260 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -541,7 +541,7 @@
     <!-- Magnifier dimensions -->
     <dimen name="magnifier_width">100dp</dimen>
     <dimen name="magnifier_height">48dp</dimen>
-    <dimen name="magnifier_elevation">2dp</dimen>
+    <dimen name="magnifier_elevation">4dp</dimen>
     <dimen name="magnifier_offset">42dp</dimen>
     <item type="dimen" format="float" name="magnifier_zoom_scale">1.25</item>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a5ba4c6..c4006b3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2872,6 +2872,7 @@
       <public name="urlBarResourceId" />
       <!-- @hide @SystemApi -->
       <public name="userRestriction" />
+      <public name="textFontWeight" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5c9f863..c3ae5fa 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -379,7 +379,7 @@
     <!-- Text message in the factory reset warning dialog. This says that the the device admin app
          is missing or corrupted. As a result the device will be erased. [CHAR LIMIT=NONE]-->
     <string name="factory_reset_message">The admin app can\'t be used. Your device will now be
-        erased.\n\nIf you have questions, contact your organization's admin.</string>
+        erased.\n\nIf you have questions, contact your organization\'s admin.</string>
 
     <!-- A toast message displayed when printing is attempted but disabled by policy. -->
     <string name="printing_disabled_by">Printing disabled by <xliff:g id="owner_app">%s</xliff:g>.</string>
@@ -764,7 +764,7 @@
     <string name="capability_title_canCaptureFingerprintGestures">Fingerprint gestures</string>
     <!-- Description for the capability of an accessibility service to perform gestures. -->
     <string name="capability_desc_canCaptureFingerprintGestures">Can capture gestures performed on
-        the device's fingerprint sensor.</string>
+        the device\'s fingerprint sensor.</string>
 
     <!--  Permissions -->
 
@@ -3775,7 +3775,7 @@
     <!-- Notification title when data usage has exceeded warning threshold. [CHAR LIMIT=50] -->
     <string name="data_usage_warning_title">Data warning</string>
     <!-- Notification body when data usage has exceeded warning threshold. [CHAR LIMIT=32] -->
-    <string name="data_usage_warning_body">You've used <xliff:g id="app" example="3.8GB">%s</xliff:g> of data</string>
+    <string name="data_usage_warning_body">You\'ve used <xliff:g id="app" example="3.8GB">%s</xliff:g> of data</string>
 
     <!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=50] -->
     <string name="data_usage_mobile_limit_title">Mobile data limit reached</string>
@@ -3789,7 +3789,7 @@
     <!-- Notification title when Wi-Fi data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_wifi_limit_snoozed_title">Over your Wi-Fi data limit</string>
     <!-- Notification body when data usage has exceeded limit threshold. -->
-    <string name="data_usage_limit_snoozed_body">You've gone <xliff:g id="size" example="3.8GB">%s</xliff:g> over your set limit</string>
+    <string name="data_usage_limit_snoozed_body">You\'ve gone <xliff:g id="size" example="3.8GB">%s</xliff:g> over your set limit</string>
 
     <!-- Notification title when background data usage is limited. [CHAR LIMIT=32] -->
     <string name="data_usage_restricted_title">Background data restricted</string>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 8595165..38beebd 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -21,6 +21,7 @@
 import static android.content.res.FontResourcesParser.FontFileResourceEntry;
 import static android.content.res.FontResourcesParser.ProviderResourceEntry;
 
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -49,6 +50,8 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
@@ -117,6 +120,11 @@
      */
     public long native_instance;
 
+    /** @hide */
+    @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Style {}
+
     // Style
     public static final int NORMAL = 0;
     public static final int BOLD = 1;
@@ -124,8 +132,15 @@
     public static final int BOLD_ITALIC = 3;
     /** @hide */ public static final int STYLE_MASK = 0x03;
 
-    private int mStyle = 0;
-    private int mWeight = 0;
+    private @Style int mStyle = 0;
+
+    /**
+     * A maximum value for the weight value.
+     * @hide
+     */
+    public static final int MAX_WEIGHT = 1000;
+
+    private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0;
 
     // Value for weight and italic. Indicates the value is resolved by font metadata.
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -153,7 +168,7 @@
     }
 
     /** Returns the typeface's intrinsic style attributes */
-    public int getStyle() {
+    public @Style int getStyle() {
         return mStyle;
     }
 
@@ -659,7 +674,7 @@
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
      */
-    public static Typeface create(String familyName, int style) {
+    public static Typeface create(String familyName, @Style int style) {
         return create(sSystemFontMap.get(familyName), style);
     }
 
@@ -680,7 +695,7 @@
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
      */
-    public static Typeface create(Typeface family, int style) {
+    public static Typeface create(Typeface family, @Style int style) {
         if ((style & ~STYLE_MASK) != 0) {
             style = NORMAL;
         }
@@ -776,7 +791,7 @@
      *
      * @return the default typeface that corresponds to the style
      */
-    public static Typeface defaultFromStyle(int style) {
+    public static Typeface defaultFromStyle(@Style int style) {
         return sDefaults[style];
     }
 
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ded427e..1924bbe 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -784,6 +784,20 @@
     }
 
     /**
+     * Requests keystore to check if the confirmationui HAL is available.
+     *
+     * @return whether the confirmationUI HAL is available.
+     */
+    public boolean isConfirmationPromptSupported() {
+        try {
+            return mBinder.isConfirmationPromptSupported();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    /**
      * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
      * code.
      */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ab27a0d..cf29e43 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -165,7 +165,7 @@
         ALOGI("%s", ss.str().c_str());
         // Just so we have something that counts up, the value is largely irrelevant
         ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
-        android::util::stats_write(android::util::DAVEY_OCCURRED, ns2ms(totalDuration));
+        android::util::stats_write(android::util::DAVEY_OCCURRED, getuid(), ns2ms(totalDuration));
     }
 }
 
diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto
index 49bfe1e..ef6a8ff 100644
--- a/libs/incident/proto/android/section.proto
+++ b/libs/incident/proto/android/section.proto
@@ -40,6 +40,9 @@
 
     // incidentd calls logs for annotated field
     SECTION_LOG = 4;
+
+    // incidentd read file and gzip the data in bytes field
+    SECTION_GZIP = 5;
 }
 
 message SectionFlags {
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
index 0a8a5aa..bf698d4 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -154,7 +154,7 @@
     void editRawFixed32(size_t pos, uint32_t val);
 
     /**
-     * Copy _size_ bytes of data starting at __srcPos__ to wp.
+     * Copy _size_ bytes of data starting at __srcPos__ to wp, srcPos must be larger than wp.pos().
      */
     void copy(size_t srcPos, size_t size);
 
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 022198b..62030bb 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -36,7 +36,6 @@
 
 #include <gui/Surface.h>
 
-#include <media/ICrypto.h>
 #include <media/MediaCodecBuffer.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -46,6 +45,7 @@
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/PersistentSurface.h>
+#include <mediadrm/ICrypto.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include <system/window.h>
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index 1b3c24f..2d9051f 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -26,9 +26,9 @@
 
 #include <binder/IServiceManager.h>
 #include <cutils/properties.h>
-#include <media/ICrypto.h>
-#include <media/IMediaDrmService.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <mediadrm/ICrypto.h>
+#include <mediadrm/IMediaDrmService.h>
 
 namespace android {
 
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 3518392..4c20f05 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -31,10 +31,10 @@
 #include <binder/Parcel.h>
 #include <binder/PersistableBundle.h>
 #include <cutils/properties.h>
-#include <media/IDrm.h>
-#include <media/IMediaDrmService.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaErrors.h>
+#include <mediadrm/IDrm.h>
+#include <mediadrm/IMediaDrmService.h>
 
 using ::android::os::PersistableBundle;
 
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 8a48e7b..02d0d70 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -57,15 +57,15 @@
 
     <!-- When the lock screen is showing and the phone plugged in, and the battery
          is not fully charged, say that it's charging.  -->
-    <string name="keyguard_plugged_in">Charging</string>
+    <string name="keyguard_plugged_in"><xliff:g id="percentage">%s</xliff:g> • Charging</string>
 
     <!-- When the lock screen is showing and the phone plugged in, and the battery
          is not fully charged, and it's plugged into a fast charger, say that it's charging fast.  -->
-    <string name="keyguard_plugged_in_charging_fast">Charging rapidly</string>
+    <string name="keyguard_plugged_in_charging_fast"><xliff:g id="percentage">%s</xliff:g> • Charging rapidly</string>
 
     <!-- When the lock screen is showing and the phone plugged in, and the battery
          is not fully charged, and it's plugged into a slow charger, say that it's charging slowly.  -->
-    <string name="keyguard_plugged_in_charging_slowly">Charging slowly</string>
+    <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
 
     <!-- When the lock screen is showing and the battery is low, warn user to plug
          in the phone soon. -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index cf0659a..a444ff9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -124,7 +124,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,alarm
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
     </string>
 
     <!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 920dd98..9245ac1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -743,8 +743,6 @@
     <string name="quick_settings_wifi_on_label">Wi-Fi On</string>
     <!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
     <string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string>
-    <!-- QuickSettings: Alarm title [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_alarm_title">Alarm</string>
     <!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
     <string name="quick_settings_cast_title">Cast</string>
     <!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
@@ -949,13 +947,13 @@
     <string name="interruption_level_alarms_twoline">Alarms\nonly</string>
 
     <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
-    <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+    <string name="keyguard_indication_charging_time"><xliff:g id="percentage">%2$s</xliff:g> • Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
 
     <!-- Indication on the keyguard that is shown when the device is charging rapidly. Should match keyguard_plugged_in_charging_fast [CHAR LIMIT=40]-->
-    <string name="keyguard_indication_charging_time_fast">Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+    <string name="keyguard_indication_charging_time_fast"><xliff:g id="percentage">%2$s</xliff:g> • Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
 
     <!-- Indication on the keyguard that is shown when the device is charging slowly. Should match keyguard_plugged_in_charging_slowly [CHAR LIMIT=40]-->
-    <string name="keyguard_indication_charging_time_slowly">Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+    <string name="keyguard_indication_charging_time_slowly"><xliff:g id="percentage">%2$s</xliff:g> • Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
 
     <!-- Related to user switcher --><skip/>
 
@@ -2140,4 +2138,8 @@
     <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
     <string name="slice_permission_deny">Deny</string>
 
+    <!-- List of packages for which we don't want to show recents onboarding, add into overlay as needed. -->
+    <string-array name="recents_onboarding_blacklisted_packages" translatable="false">
+    </string-array>
+
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index b8319a8e..846aadd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -28,25 +28,30 @@
      * Proxies SurfaceControl.screenshotToBuffer().
      */
     GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
-            int maxLayer, boolean useIdentityTransform, int rotation);
+            int maxLayer, boolean useIdentityTransform, int rotation) = 0;
 
     /**
      * Begins screen pinning on the provided {@param taskId}.
      */
-    void startScreenPinning(int taskId);
+    void startScreenPinning(int taskId) = 1;
 
     /**
      * Called when the overview service has started the recents animation.
      */
-    void onRecentsAnimationStarted();
+    void onRecentsAnimationStarted() = 2;
 
     /**
      * Specifies the text to be shown for onboarding the new swipe-up gesture to access recents.
      */
-    void setRecentsOnboardingText(CharSequence text);
+    void setRecentsOnboardingText(CharSequence text) = 3;
 
     /**
      * Enables/disables launcher/overview interaction features {@link InteractionType}.
      */
-    void setInteractionState(int flags);
+    void setInteractionState(int flags) = 4;
+
+    /**
+    * Notifies SystemUI that split screen has been invoked.
+    */
+    void onSplitScreenInvoked() = 5;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 68400fc..5b49e67 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -101,4 +101,12 @@
             Log.w(TAG, "Failed to override pending app transition (remote): ", e);
         }
     }
+
+    public void endProlongedAnimations() {
+        try {
+            WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to end prolonged animations: ", e);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index b54d09a..4c2aa63 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -162,12 +162,13 @@
             mRow.addView(button);
 
             PendingIntent pendingIntent = null;
-            if (rc.getPrimaryAction() != null) {
-                pendingIntent = rc.getPrimaryAction().getAction();
+            if (rc.getContentIntent() != null) {
+                pendingIntent = rc.getContentIntent().getAction();
             }
             mClickActions.put(button, pendingIntent);
 
-            button.setText(rc.getTitleItem().getText());
+            final SliceItem titleItem = rc.getTitleItem();
+            button.setText(titleItem == null ? null : titleItem.getText());
 
             Drawable iconDrawable = null;
             SliceItem icon = SliceQuery.find(item.getSlice(),
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 1185f45..3c666e4 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -34,6 +34,8 @@
 import android.view.SurfaceControl;
 
 import com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.GraphicBufferCompat;
@@ -108,6 +110,15 @@
             }
         }
 
+        public void onSplitScreenInvoked() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         public void setRecentsOnboardingText(CharSequence text) {
             mOnboardingText = text;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index c7d276c..26618bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -27,6 +28,7 @@
 import android.icu.text.DisplayContext;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,6 +38,7 @@
 
 import java.util.Date;
 import java.util.Locale;
+import java.util.concurrent.TimeUnit;
 
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceProvider;
@@ -53,6 +56,12 @@
     public static final String KEYGUARD_NEXT_ALARM_URI =
             "content://com.android.systemui.keyguard/alarm";
 
+    /**
+     * Only show alarms that will ring within N hours.
+     */
+    @VisibleForTesting
+    static final int ALARM_VISIBILITY_HOURS = 12;
+
     private final Date mCurrentTime = new Date();
     protected final Uri mSliceUri;
     protected final Uri mDateUri;
@@ -65,6 +74,10 @@
     private boolean mRegisteredEveryMinute;
     private String mNextAlarm;
     private NextAlarmController mNextAlarmController;
+    protected AlarmManager mAlarmManager;
+    protected ContentResolver mContentResolver;
+    private AlarmManager.AlarmClockInfo mNextAlarmInfo;
+    private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
 
     /**
      * Receiver responsible for time ticking and updating the date format.
@@ -105,17 +118,26 @@
     public Slice onBindSlice(Uri sliceUri) {
         ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
         builder.addRow(new RowBuilder(builder, mDateUri).setTitle(mLastText));
-        if (!TextUtils.isEmpty(mNextAlarm)) {
-            Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
-            builder.addRow(new RowBuilder(builder, mAlarmUri)
-                    .setTitle(mNextAlarm).addEndItem(icon));
+        addNextAlarm(builder);
+        return builder.build();
+    }
+
+    protected void addNextAlarm(ListBuilder builder) {
+        if (TextUtils.isEmpty(mNextAlarm)) {
+            return;
         }
 
-        return builder.build();
+        Icon alarmIcon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
+        RowBuilder alarmRowBuilder = new RowBuilder(builder, mAlarmUri)
+                .setTitle(mNextAlarm)
+                .addEndItem(alarmIcon);
+        builder.addRow(alarmRowBuilder);
     }
 
     @Override
     public boolean onCreateSliceProvider() {
+        mAlarmManager = getContext().getSystemService(AlarmManager.class);
+        mContentResolver = getContext().getContentResolver();
         mNextAlarmController = new NextAlarmControllerImpl(getContext());
         mNextAlarmController.addCallback(this);
         mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
@@ -124,15 +146,25 @@
         return true;
     }
 
-    public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) {
-        if (info == null) {
-            return "";
+    private void updateNextAlarm() {
+        if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
+            String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
+                    ActivityManager.getCurrentUser()) ? "H:mm" : "h:mm";
+            mNextAlarm = android.text.format.DateFormat.format(pattern,
+                    mNextAlarmInfo.getTriggerTime()).toString();
+        } else {
+            mNextAlarm = "";
         }
-        String skeleton = android.text.format.DateFormat
-                .is24HourFormat(context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
-        String pattern = android.text.format.DateFormat
-                .getBestDateTimePattern(Locale.getDefault(), skeleton);
-        return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
+        mContentResolver.notifyChange(mSliceUri, null /* observer */);
+    }
+
+    private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
+        if (alarmClockInfo == null) {
+            return false;
+        }
+
+        long limit = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(hours);
+        return mNextAlarmInfo.getTriggerTime() <= limit;
     }
 
     /**
@@ -181,7 +213,7 @@
         final String text = getFormattedDate();
         if (!text.equals(mLastText)) {
             mLastText = text;
-            getContext().getContentResolver().notifyChange(mSliceUri, null /* observer */);
+            mContentResolver.notifyChange(mSliceUri, null /* observer */);
         }
     }
 
@@ -203,7 +235,15 @@
 
     @Override
     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
-        mNextAlarm = formatNextAlarm(getContext(), nextAlarm);
-        getContext().getContentResolver().notifyChange(mSliceUri, null /* observer */);
+        mNextAlarmInfo = nextAlarm;
+        mAlarmManager.cancel(mUpdateNextAlarm);
+
+        long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime()
+                - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS);
+        if (triggerAt > 0) {
+            mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
+                    mUpdateNextAlarm, mHandler);
+        }
+        updateNextAlarm();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index b22ea4c..2629f30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -24,7 +24,7 @@
     // The size of a single dot in relation to the whole animation.
     private static final float SINGLE_SCALE = .4f;
 
-    private static final float MINOR_ALPHA = .3f;
+    private static final float MINOR_ALPHA = .42f;
 
     private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
 
@@ -53,7 +53,7 @@
             removeViewAt(getChildCount() - 1);
         }
         TypedArray array = getContext().obtainStyledAttributes(
-                new int[]{android.R.attr.colorForeground});
+                new int[]{android.R.attr.colorControlActivated});
         int color = array.getColor(0, 0);
         array.recycle();
         while (numPages > getChildCount()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 78481d3..2151436 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -15,10 +15,10 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.content.Context;
 import android.content.Intent;
@@ -51,6 +51,8 @@
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 
+import java.util.Locale;
+
 /**
  * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
  * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
@@ -289,7 +291,7 @@
 
     @Override
     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
-        mNextAlarmText = nextAlarm != null ? formatNextAlarm(mContext, nextAlarm) : null;
+        mNextAlarmText = nextAlarm != null ? formatNextAlarm(nextAlarm) : null;
         if (mNextAlarmText != null) {
             hideLongPressTooltip(true /* shouldFadeInAlarmText */);
         } else {
@@ -430,4 +432,15 @@
     public void setCallback(Callback qsPanelCallback) {
         mHeaderQsPanel.setCallback(qsPanelCallback);
     }
+
+    private String formatNextAlarm(AlarmManager.AlarmClockInfo info) {
+        if (info == null) {
+            return "";
+        }
+        String skeleton = android.text.format.DateFormat
+                .is24HourFormat(mContext, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
+        String pattern = android.text.format.DateFormat
+                .getBestDateTimePattern(Locale.getDefault(), skeleton);
+        return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 409c753..47b0de9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -45,8 +45,10 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
@@ -100,6 +102,8 @@
     private static RecentsTaskLoader sTaskLoader;
     private static RecentsConfiguration sConfiguration;
 
+    private OverviewProxyService mOverviewProxyService;
+
     private Handler mHandler;
     private RecentsImpl mImpl;
     private int mDraggingInRecentsCurrentUser;
@@ -208,6 +212,7 @@
         sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
         mHandler = new Handler();
         mImpl = new RecentsImpl(mContext);
+        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
 
         // Register with the event bus
         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
@@ -247,6 +252,13 @@
             return;
         }
 
+        if (mOverviewProxyService.getProxy() != null) {
+            // TODO: Proxy to Launcher
+            if (!triggeredFromAltTab) {
+                return;
+            }
+        }
+
         ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
         int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
         int currentUser = sSystemServicesProxy.getCurrentUser();
@@ -282,6 +294,13 @@
             return;
         }
 
+        if (mOverviewProxyService.getProxy() != null) {
+            // TODO: Proxy to Launcher
+            if (!triggeredFromAltTab) {
+                return;
+            }
+        }
+
         int currentUser = sSystemServicesProxy.getCurrentUser();
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
@@ -313,6 +332,11 @@
             return;
         }
 
+        if (mOverviewProxyService.getProxy() != null) {
+            // TODO: Proxy to Launcher
+            return;
+        }
+
         int growTarget = getComponent(Divider.class).getView().growsRecents();
         int currentUser = sSystemServicesProxy.getCurrentUser();
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
@@ -345,6 +369,11 @@
             return;
         }
 
+        if (mOverviewProxyService.getProxy() != null) {
+            // TODO: Proxy to Launcher
+            return;
+        }
+
         int currentUser = sSystemServicesProxy.getCurrentUser();
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.preloadRecents();
@@ -373,6 +402,11 @@
             return;
         }
 
+        if (mOverviewProxyService.getProxy() != null) {
+            // TODO: Proxy to Launcher
+            return;
+        }
+
         int currentUser = sSystemServicesProxy.getCurrentUser();
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.cancelPreloadingRecents();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b0a2fad..95b311f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -96,6 +96,7 @@
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
+import com.android.systemui.shared.system.WindowManagerWrapper;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
@@ -836,12 +837,7 @@
         mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
         // We post to make sure that this information is delivered after this traversals is
         // finished.
-        mRecentsView.post(new Runnable() {
-            @Override
-            public void run() {
-                Recents.getSystemServices().endProlongedAnimations();
-            }
-        });
+        mRecentsView.post(() -> WindowManagerWrapper.getInstance().endProlongedAnimations());
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 26fac6c..127361a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -49,6 +49,10 @@
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Shows onboarding for the new recents interaction in P (codenamed quickstep).
  */
@@ -65,6 +69,7 @@
     private final Context mContext;
     private final WindowManager mWindowManager;
     private final OverviewProxyService mOverviewProxyService;
+    private Set<String> mBlacklistedPackages;
     private final View mLayout;
     private final TextView mTextView;
     private final ImageView mDismissView;
@@ -85,6 +90,10 @@
         public void onTaskStackChanged() {
             ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance()
                     .getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */);
+            if (mBlacklistedPackages.contains(info.baseActivity.getPackageName())) {
+                hide(true);
+                return;
+            }
             int activityType = info.configuration.windowConfiguration.getActivityType();
             int numAppsLaunched = Prefs.getInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
             if (activityType == ACTIVITY_TYPE_STANDARD) {
@@ -122,6 +131,9 @@
         mOverviewProxyService = overviewProxyService;
         final Resources res = context.getResources();
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        mBlacklistedPackages = new HashSet<>();
+        Collections.addAll(mBlacklistedPackages, res.getStringArray(
+                R.array.recents_onboarding_blacklisted_packages));
         mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null);
         mTextView = mLayout.findViewById(R.id.onboarding_text);
         mDismissView = mLayout.findViewById(R.id.dismiss);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 93fd34a..544d95c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -444,17 +444,6 @@
         }
     }
 
-    public void endProlongedAnimations() {
-        if (mWm == null) {
-            return;
-        }
-        try {
-            mIwm.endProlongedAnimations();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
     public void registerDockedStackListener(IDockedStackListener listener) {
         if (mWm == null) return;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 22e8909..bc14203 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -411,12 +411,14 @@
                 break;
         }
 
+        String percentage = NumberFormat.getPercentInstance()
+                .format(mBatteryLevel / 100f);
         if (hasChargingTime) {
             String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
                     mContext, chargingTimeRemaining);
-            return mContext.getResources().getString(chargingId, chargingTimeFormatted);
+            return mContext.getResources().getString(chargingId, chargingTimeFormatted, percentage);
         } else {
-            return mContext.getResources().getString(chargingId);
+            return mContext.getResources().getString(chargingId, percentage);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cad956c..4f09133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -698,7 +698,7 @@
         if (!hasOverflow) {
             // we have to ensure that adding the low priority notification won't lead to an
             // overflow
-            collapsedPadding -= (1.0f + OVERFLOW_EARLY_AMOUNT) * mCollapsedIcons.getIconSize();
+            collapsedPadding -= mCollapsedIcons.getNoOverflowExtraPadding();
         } else {
             // Partial overflow padding will fill enough space to add extra dots
             collapsedPadding -= mCollapsedIcons.getPartialOverflowExtraPadding();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index b220686..446a1d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -14,7 +14,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.app.AlarmManager.AlarmClockInfo;
 import android.content.Context;
 import android.os.Handler;
 import android.provider.Settings.Secure;
@@ -28,8 +27,6 @@
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 
 /**
  * Manages which tiles should be automatically added to QS.
@@ -40,7 +37,6 @@
     public static final String INVERSION = "inversion";
     public static final String WORK = "work";
     public static final String NIGHT = "night";
-    public static final String ALARM = "alarm";
 
     private final Context mContext;
     private final QSTileHost mHost;
@@ -87,9 +83,6 @@
             && ColorDisplayController.isAvailable(mContext)) {
             Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
         }
-        if (!mAutoTracker.isAdded(ALARM)) {
-            Dependency.get(NextAlarmController.class).addCallback(mNextAlarmChangeCallback);
-        }
     }
 
     public void destroy() {
@@ -101,7 +94,6 @@
         Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
         Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
         Dependency.get(ColorDisplayController.class).setListener(null);
-        Dependency.get(NextAlarmController.class).removeCallback(mNextAlarmChangeCallback);
     }
 
     private final ManagedProfileController.Callback mProfileCallback =
@@ -150,19 +142,6 @@
         }
     };
 
-    private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
-        @Override
-        public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
-            if (mAutoTracker.isAdded(ALARM)) return;
-            if (nextAlarm != null) {
-                mHost.addTile(ALARM);
-                mAutoTracker.setTileAdded(ALARM);
-                mHandler.post(() -> Dependency.get(NextAlarmController.class)
-                    .removeCallback(mNextAlarmChangeCallback));
-            }
-        }
-    };
-
     @VisibleForTesting
     final ColorDisplayController.Callback mColorDisplayCallback =
             new ColorDisplayController.Callback() {
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 f7f791e..f42473d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -51,6 +51,7 @@
 
     public static final String TAG = "CollapsedStatusBarFragment";
     private static final String EXTRA_PANEL_STATE = "panel_state";
+    public static final String STATUS_BAR_ICON_MANAGER_TAG = "status_bar_icon_manager";
     public static final int FADE_IN_DURATION = 320;
     public static final int FADE_IN_DELAY = 50;
     private PhoneStatusBarView mStatusBar;
@@ -94,6 +95,7 @@
             mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
         }
         mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
+        mDarkIconManager.setShouldLog(true);
         Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
         mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
         mClockView = mStatusBar.findViewById(R.id.clock);
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 c499619..edfd02b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.graphics.Rect;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -25,21 +26,27 @@
 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.StatusBarIconView;
+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;
 
-public class DemoStatusIcons extends LinearLayout implements DemoMode {
+public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver {
     private final LinearLayout mStatusIcons;
     private final int mIconSize;
 
     private boolean mDemoMode;
+    private int mColor;
 
     public DemoStatusIcons(LinearLayout statusIcons, int iconSize) {
         super(statusIcons.getContext());
         mStatusIcons = statusIcons;
         mIconSize = iconSize;
+        mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
 
         setLayoutParams(mStatusIcons.getLayoutParams());
         setOrientation(mStatusIcons.getOrientation());
@@ -48,6 +55,22 @@
         p.addView(this, p.indexOfChild(mStatusIcons));
     }
 
+    public void remove() {
+        ((ViewGroup) getParent()).removeView(this);
+    }
+
+    public void setColor(int color) {
+        mColor = color;
+        updateColors();
+    }
+
+    private void updateColors() {
+        for (int i = 0; i < getChildCount(); i++) {
+            StatusBarIconView child = (StatusBarIconView) getChildAt(i);
+            child.setStaticDrawableColor(mColor);
+        }
+    }
+
     @Override
     public void dispatchDemoCommand(String command, Bundle args) {
         if (!mDemoMode && command.equals(COMMAND_ENTER)) {
@@ -136,6 +159,7 @@
                     break;
                 } else {
                     StatusBarIcon icon = v.getStatusBarIcon();
+                    icon.visible = true;
                     icon.icon = Icon.createWithResource(icon.icon.getResPackage(), iconId);
                     v.set(icon);
                     v.updateDrawable();
@@ -150,9 +174,16 @@
             return;
         }
         StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.SYSTEM, iconId, 0, 0, "Demo");
+        icon.visible = true;
         StatusBarIconView v = new StatusBarIconView(getContext(), null, null);
         v.setTag(slot);
         v.set(icon);
+        v.setStaticDrawableColor(mColor);
         addView(v, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize));
     }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 5cf4c4c..5479dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -56,6 +56,7 @@
     private static final int NO_VALUE = Integer.MIN_VALUE;
     private static final String TAG = "NotificationIconContainer";
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_OVERFLOW = false;
     private static final int CANNED_ANIMATION_DURATION = 100;
     private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
         private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
@@ -107,6 +108,7 @@
     private final HashMap<View, IconState> mIconStates = new HashMap<>();
     private int mDotPadding;
     private int mStaticDotRadius;
+    private int mStaticDotDiameter;
     private int mActualLayoutWidth = NO_VALUE;
     private float mActualPaddingEnd = NO_VALUE;
     private float mActualPaddingStart = NO_VALUE;
@@ -122,17 +124,21 @@
     private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
     // Keep track of the last visible icon so collapsed container can report on its location
     private IconState mLastVisibleIconState;
+    private float mVisualOverflowStart;
+    // Keep track of overflow in range [0, 3]
+    private int mNumDots;
 
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
         initDimens();
-        setWillNotDraw(!DEBUG);
+        setWillNotDraw(!(DEBUG || DEBUG_OVERFLOW));
     }
 
     private void initDimens() {
         mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
         mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
+        mStaticDotDiameter = 2 * mStaticDotRadius;
     }
 
     @Override
@@ -142,6 +148,30 @@
         paint.setColor(Color.RED);
         paint.setStyle(Paint.Style.STROKE);
         canvas.drawRect(getActualPaddingStart(), 0, getLayoutEnd(), getHeight(), paint);
+
+        if (DEBUG_OVERFLOW) {
+            if (mLastVisibleIconState == null) {
+                return;
+            }
+
+            int height = getHeight();
+            int end = getFinalTranslationX();
+
+            // Visualize the "end" of the layout
+            paint.setColor(Color.BLUE);
+            canvas.drawLine(end, 0, end, height, paint);
+
+            paint.setColor(Color.BLACK);
+            int lastIcon = (int) mLastVisibleIconState.xTranslation;
+            canvas.drawLine(lastIcon, 0, lastIcon, height, paint);
+
+            paint.setColor(Color.RED);
+            canvas.drawLine(mVisualOverflowStart, 0, mVisualOverflowStart, height, paint);
+
+            paint.setColor(Color.YELLOW);
+            float overflow = getMaxOverflowStart();
+            canvas.drawLine(overflow, 0, overflow, height, paint);
+        }
     }
 
     @Override
@@ -282,7 +312,7 @@
     }
 
     /**
-     * Calulate the horizontal translations for each notification based on how much the icons
+     * Calculate the horizontal translations for each notification based on how much the icons
      * are inserted into the notification container.
      * If this is not a whole number, the fraction means by how much the icon is appearing.
      */
@@ -293,9 +323,9 @@
         int maxVisibleIcons = mDark ? MAX_VISIBLE_ICONS_WHEN_DARK :
                     mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
         float layoutEnd = getLayoutEnd();
-        float overflowStart = layoutEnd - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT);
+        float overflowStart = getMaxOverflowStart();
+        mVisualOverflowStart = 0;
         boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
-        float visualOverflowStart = 0;
         for (int i = 0; i < childCount; i++) {
             View view = getChildAt(i);
             IconState iconState = mIconStates.get(view);
@@ -310,45 +340,40 @@
                 noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
             }
             iconState.visibleState = StatusBarIconView.STATE_ICON;
-            if (firstOverflowIndex == -1 && (forceOverflow
-                    || (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)))) {
+
+            boolean isOverflowing =
+                    (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart));
+            if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
                 firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
-                int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding;
-                visualOverflowStart = overflowStart + mIconSize * (1 + OVERFLOW_EARLY_AMOUNT)
-                        - totalDotLength / 2
-                        - mIconSize * 0.5f + mStaticDotRadius;
+                mVisualOverflowStart = layoutEnd - mIconSize
+                        - 2 * (mStaticDotDiameter + mDotPadding);
                 if (forceOverflow) {
-                    visualOverflowStart = Math.min(translationX, visualOverflowStart
-                            + mStaticDotRadius * 2 + mDotPadding);
-                } else {
-                    visualOverflowStart += (translationX - overflowStart) / mIconSize
-                            * (mStaticDotRadius * 2 + mDotPadding);
+                    mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
                 }
             }
             translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
         }
+        mNumDots = 0;
         if (firstOverflowIndex != -1) {
-            int numDots = 1;
-            translationX = visualOverflowStart;
+            translationX = mVisualOverflowStart;
             for (int i = firstOverflowIndex; i < childCount; i++) {
                 View view = getChildAt(i);
                 IconState iconState = mIconStates.get(view);
                 int dotWidth = mStaticDotRadius * 2 + mDotPadding;
                 iconState.xTranslation = translationX;
-                if (numDots <= MAX_DOTS) {
-                    if (numDots == 1 && iconState.iconAppearAmount < 0.8f) {
+                if (mNumDots < MAX_DOTS) {
+                    if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) {
                         iconState.visibleState = StatusBarIconView.STATE_ICON;
-                        numDots--;
                     } else {
                         iconState.visibleState = StatusBarIconView.STATE_DOT;
+                        mNumDots++;
                     }
-                    translationX += (numDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
+                    translationX += (mNumDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
                             * iconState.iconAppearAmount;
                     mLastVisibleIconState = iconState;
                 } else {
                     iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
                 }
-                numDots++;
             }
         } else if (childCount > 0) {
             View lastChild = getChildAt(childCount - 1);
@@ -360,7 +385,7 @@
             if (firstOverflowIndex != -1) {
                 // If we have an overflow, only count those half for centering because the dots
                 // don't have a lot of visual weight.
-                float deltaIgnoringOverflow = (getLayoutEnd() - visualOverflowStart) / 2;
+                float deltaIgnoringOverflow = (getLayoutEnd() - mVisualOverflowStart) / 2;
                 delta = (deltaIgnoringOverflow + delta) / 2;
             }
             for (int i = 0; i < childCount; i++) {
@@ -440,7 +465,13 @@
             return 0;
         }
 
-        return (int) (mLastVisibleIconState.xTranslation + mIconSize * (1 + OVERFLOW_EARLY_AMOUNT));
+        int translation = (int) (mLastVisibleIconState.xTranslation + mIconSize);
+        // There's a chance that last translation goes beyond the edge maybe
+        return Math.min(getWidth(), translation);
+    }
+
+    private float getMaxOverflowStart() {
+        return getLayoutEnd() - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT);
     }
 
     public void setChangingViewPositions(boolean changingViewPositions) {
@@ -471,12 +502,7 @@
     }
 
     public boolean hasOverflow() {
-        if (mIsStaticLayout) {
-            return getChildCount() > MAX_STATIC_ICONS;
-        }
-
-        float width = (getChildCount() + OVERFLOW_EARLY_AMOUNT) * mIconSize;
-        return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0;
+        return mNumDots > 0;
     }
 
     /**
@@ -486,12 +512,7 @@
      * This method has no meaning for non-static containers
      */
     public boolean hasPartialOverflow() {
-        if (mIsStaticLayout) {
-            int count = getChildCount();
-            return count > MAX_STATIC_ICONS && count <= MAX_STATIC_ICONS + MAX_DOTS;
-        }
-
-        return false;
+        return mNumDots > 0 && mNumDots < MAX_DOTS;
     }
 
     /**
@@ -504,7 +525,30 @@
             return 0;
         }
 
-        return (MAX_STATIC_ICONS + MAX_DOTS - getChildCount()) * (mStaticDotRadius + mDotPadding);
+        int partialOverflowAmount = (MAX_DOTS - mNumDots) * (mStaticDotRadius * 2 + mDotPadding);
+
+        int adjustedWidth = getFinalTranslationX() + partialOverflowAmount;
+        // In case we actually give too much padding...
+        if (adjustedWidth > getWidth()) {
+            partialOverflowAmount = getWidth() - getFinalTranslationX();
+        }
+
+        return partialOverflowAmount;
+    }
+
+    // Give some extra room for btw notifications if we can
+    public int getNoOverflowExtraPadding() {
+        if (mNumDots != 0) {
+            return 0;
+        }
+
+        int collapsedPadding = (int) ((1.0f + OVERFLOW_EARLY_AMOUNT) * getIconSize());
+
+        if (collapsedPadding + getFinalTranslationX() > getWidth()) {
+            collapsedPadding = getWidth() - getFinalTranslationX();
+        }
+
+        return collapsedPadding;
     }
 
     public int getIconSize() {
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 933c952..a31727e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -210,7 +210,6 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -240,7 +239,6 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -4727,7 +4725,7 @@
 
         @Override
         public boolean isPowerSaveActive() {
-            return mBatteryController.isPowerSave();
+            return mBatteryController.isAodPowerSave();
         }
 
         @Override
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 07610ce..956bebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -18,6 +18,7 @@
 import static android.app.StatusBarManager.DISABLE_NONE;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -29,6 +30,7 @@
 import android.widget.LinearLayout.LayoutParams;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -109,6 +111,20 @@
             super.onSetIcon(viewIndex, icon);
             mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex));
         }
+
+        @Override
+        protected DemoStatusIcons createDemoStatusIcons() {
+            DemoStatusIcons icons = super.createDemoStatusIcons();
+            mDarkIconDispatcher.addDarkReceiver(icons);
+
+            return icons;
+        }
+
+        @Override
+        protected void exitDemoMode() {
+            mDarkIconDispatcher.removeDarkReceiver(mDemoStatusIcons);
+            super.exitDemoMode();
+        }
     }
 
     public static class TintedIconManager extends IconManager {
@@ -134,15 +150,28 @@
                 }
             }
         }
+
+        @Override
+        protected DemoStatusIcons createDemoStatusIcons() {
+            DemoStatusIcons icons = super.createDemoStatusIcons();
+            icons.setColor(mColor);
+            return icons;
+        }
     }
 
     /**
      * Turns info from StatusBarIconController into ImageViews in a ViewGroup.
      */
-    public static class IconManager {
+    public static class IconManager implements DemoMode {
         protected final ViewGroup mGroup;
         protected final Context mContext;
         protected final int mIconSize;
+        // Whether or not these icons show up in dumpsys
+        protected boolean mShouldLog = false;
+
+        // Enables SystemUI demo mode to take effect in this group
+        protected boolean mDemoable = true;
+        protected DemoStatusIcons mDemoStatusIcons;
 
         public IconManager(ViewGroup group) {
             mGroup = group;
@@ -159,6 +188,22 @@
             }
         }
 
+        public boolean isDemoable() {
+            return mDemoable;
+        }
+
+        public void setIsDemoable(boolean demoable) {
+            mDemoable = demoable;
+        }
+
+        public void setShouldLog(boolean should) {
+            mShouldLog = should;
+        }
+
+        public boolean shouldLog() {
+            return mShouldLog;
+        }
+
         protected void onIconAdded(int index, String slot, boolean blocked,
                 StatusBarIcon icon) {
             addIcon(index, slot, blocked, icon);
@@ -218,5 +263,31 @@
             StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);
             view.set(icon);
         }
+
+        @Override
+        public void dispatchDemoCommand(String command, Bundle args) {
+            if (!mDemoable) {
+                return;
+            }
+
+            if (mDemoStatusIcons != null && command.equals(COMMAND_EXIT)) {
+                mDemoStatusIcons.dispatchDemoCommand(command, args);
+                exitDemoMode();
+            } else {
+                if (mDemoStatusIcons == null) {
+                    mDemoStatusIcons = createDemoStatusIcons();
+                }
+                mDemoStatusIcons.dispatchDemoCommand(command, args);
+            }
+        }
+
+        protected void exitDemoMode() {
+            mDemoStatusIcons.remove();
+            mDemoStatusIcons = null;
+        }
+
+        protected DemoStatusIcons createDemoStatusIcons() {
+            return new DemoStatusIcons((LinearLayout) mGroup, mIconSize);
+        }
     }
 }
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 1c3ee75..8f5e705 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -41,6 +41,8 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.STATUS_BAR_ICON_MANAGER_TAG;
+
 /**
  * Receives the callbacks from CommandQueue related to icons and tracks the state of
  * all the icons. Dispatches this state to any IconManagers that are currently
@@ -48,6 +50,7 @@
  */
 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<>();
@@ -55,6 +58,7 @@
 
     private Context mContext;
     private DemoStatusIcons mDemoStatusIcons;
+    private IconManager mStatusBarIconManager;
 
     public StatusBarIconControllerImpl(Context context) {
         super(context.getResources().getStringArray(
@@ -197,26 +201,28 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        // TODO: Dump info about all icon groups?
-        ViewGroup statusIcons = mIconGroups.get(0).mGroup;
-        int N = statusIcons.getChildCount();
-        pw.println("  icon views: " + N);
-        for (int i = 0; i < N; i++) {
-            StatusBarIconView ic = (StatusBarIconView) statusIcons.getChildAt(i);
-            pw.println("    [" + i + "] icon=" + ic);
+        pw.println(TAG + " state:");
+        for (IconManager manager : mIconGroups) {
+            if (manager.shouldLog()) {
+                ViewGroup group = manager.mGroup;
+                int N = group.getChildCount();
+                pw.println("  icon views: " + N);
+                for (int i = 0; i < N; i++) {
+                    StatusBarIconView ic = (StatusBarIconView) group.getChildAt(i);
+                    pw.println("    [" + i + "] icon=" + ic);
+                }
+            }
         }
+
         super.dump(pw);
     }
 
     public void dispatchDemoCommand(String command, Bundle args) {
-        if (mDemoStatusIcons == null) {
-            // TODO: Rework how we handle demo mode.
-            int iconSize = mContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.status_bar_icon_size);
-            mDemoStatusIcons = new DemoStatusIcons((LinearLayout) mIconGroups.get(0).mGroup,
-                    iconSize);
+        for (IconManager manager : mIconGroups) {
+            if (manager.isDemoable()) {
+                manager.dispatchDemoCommand(command, args);
+            }
         }
-        mDemoStatusIcons.dispatchDemoCommand(command, args);
     }
 
     @Override
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 f600908..1aa3a43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -77,6 +77,7 @@
     }
 
     public void dump(PrintWriter pw) {
+        pw.println("StatusBarIconList state:");
         final int N = mSlots.size();
         pw.println("  icon slots: " + N);
         for (int i=0; i<N; i++) {
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 503a1b4..dba89479 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -14,12 +14,6 @@
  * limitations under the License.
  */
 
-/**
- * A container for Status bar system icons. Limits the number of system icons and handles overflow
- * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
- *
- * Children are expected to be of type StatusBarIconView.
- */
 package com.android.systemui.statusbar.phone;
 
 import android.annotation.Nullable;
@@ -33,6 +27,12 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.stack.ViewState;
 
+/**
+ * A container for Status bar system icons. Limits the number of system icons and handles overflow
+ * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
+ *
+ * Children are expected to be of type StatusBarIconView.
+ */
 public class StatusIconContainer extends AlphaOptimizedLinearLayout {
 
     private static final String TAG = "StatusIconContainer";
@@ -40,6 +40,10 @@
     private static final int MAX_ICONS = 5;
     private static final int MAX_DOTS = 3;
 
+    public StatusIconContainer(Context context) {
+        this(context, null);
+    }
+
     public StatusIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 641fe69..6f4026d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -41,6 +41,13 @@
     boolean isPowerSave();
 
     /**
+     * Returns {@code true} if AOD was disabled by power saving policies.
+     */
+    default boolean isAodPowerSave() {
+        return isPowerSave();
+    }
+
+    /**
      * A listener that will be notified whenever a change in battery level or power save mode
      * has occurred.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index e8d5af6..49f880c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -24,8 +24,10 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.os.PowerSaveState;
 import android.util.Log;
-import com.android.systemui.DemoMode;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -52,13 +54,19 @@
     protected boolean mCharging;
     protected boolean mCharged;
     protected boolean mPowerSave;
+    protected boolean mAodPowerSave;
     private boolean mTestmode = false;
     private boolean mHasReceivedBattery = false;
 
     public BatteryControllerImpl(Context context) {
+        this(context, context.getSystemService(PowerManager.class));
+    }
+
+    @VisibleForTesting
+    BatteryControllerImpl(Context context, PowerManager powerManager) {
         mContext = context;
         mHandler = new Handler();
-        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mPowerManager = powerManager;
 
         registerReceiver();
         updatePowerSave();
@@ -166,6 +174,11 @@
         return mPowerSave;
     }
 
+    @Override
+    public boolean isAodPowerSave() {
+        return mAodPowerSave;
+    }
+
     private void updatePowerSave() {
         setPowerSave(mPowerManager.isPowerSaveMode());
     }
@@ -173,6 +186,11 @@
     private void setPowerSave(boolean powerSave) {
         if (powerSave == mPowerSave) return;
         mPowerSave = powerSave;
+
+        // AOD power saving setting might be different from PowerManager power saving mode.
+        PowerSaveState state = mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD);
+        mAodPowerSave = state.batterySaverEnabled;
+
         if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
         firePowerSaveChanged();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index cd409d8..b6116e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -16,10 +16,17 @@
 
 package com.android.systemui.keyguard;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
 import androidx.app.slice.Slice;
+
+import android.app.AlarmManager;
+import android.content.ContentResolver;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Debug;
 import android.os.Handler;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -32,24 +39,31 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
 
 import androidx.app.slice.SliceItem;
 import androidx.app.slice.SliceProvider;
 import androidx.app.slice.SliceSpecs;
 import androidx.app.slice.core.SliceQuery;
-import androidx.app.slice.widget.SliceLiveData;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardSliceProviderTest extends SysuiTestCase {
 
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private AlarmManager mAlarmManager;
     private TestableKeyguardSliceProvider mProvider;
 
     @Before
     public void setup() {
+        MockitoAnnotations.initMocks(this);
         mProvider = new TestableKeyguardSliceProvider();
         mProvider.attachInfo(getContext(), null);
         SliceProvider.setSpecs(Arrays.asList(SliceSpecs.LIST));
@@ -70,7 +84,7 @@
 
     @Test
     public void returnsValidSlice() {
-        Slice slice = mProvider.onBindSlice(Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI));
+        Slice slice = mProvider.onBindSlice(mProvider.getUri());
         SliceItem text = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_TEXT,
                 android.app.slice.Slice.HINT_TITLE,
                 null /* nonHints */);
@@ -87,21 +101,52 @@
 
     @Test
     public void updatesClock() {
-        mProvider.mUpdateClockInvokations = 0;
         mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK));
         TestableLooper.get(this).processAllMessages();
-        Assert.assertEquals("Clock should have been updated.", 1 /* expected */,
-                mProvider.mUpdateClockInvokations);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+    }
+
+    @Test
+    public void schedulesAlarm12hBefore() {
+        long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(16);
+        AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null);
+        mProvider.onNextAlarmChanged(alarmClockInfo);
+
+        long twelveHours = TimeUnit.HOURS.toMillis(KeyguardSliceProvider.ALARM_VISIBILITY_HOURS);
+        long triggerAt = in16Hours - twelveHours;
+        verify(mAlarmManager).setExact(eq(AlarmManager.RTC), eq(triggerAt), anyString(), any(),
+                any());
+    }
+
+    @Test
+    public void updatingNextAlarmInvalidatesSlice() {
+        long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(8);
+        AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null);
+        mProvider.onNextAlarmChanged(alarmClockInfo);
+
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
     }
 
     private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
         int mCleanDateFormatInvokations;
-        int mUpdateClockInvokations;
+        private int mCounter;
 
         TestableKeyguardSliceProvider() {
             super(new Handler(TestableLooper.get(KeyguardSliceProviderTest.this).getLooper()));
         }
 
+        Uri getUri() {
+            return mSliceUri;
+        }
+
+        @Override
+        public boolean onCreateSliceProvider() {
+            super.onCreateSliceProvider();
+            mAlarmManager = KeyguardSliceProviderTest.this.mAlarmManager;
+            mContentResolver = KeyguardSliceProviderTest.this.mContentResolver;
+            return true;
+        }
+
         @Override
         void cleanDateFormat() {
             super.cleanDateFormat();
@@ -109,9 +154,8 @@
         }
 
         @Override
-        protected void updateClock() {
-            super.updateClock();
-            mUpdateClockInvokations++;
+        protected String getFormattedDate() {
+            return super.getFormattedDate() + mCounter++;
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 2d2db1b..a80b045 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -18,28 +18,21 @@
 
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
-import android.app.AlarmManager.AlarmClockInfo;
-import android.os.Handler;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import com.android.internal.app.ColorDisplayController;
 import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -47,19 +40,16 @@
 public class AutoTileManagerTest extends SysuiTestCase {
 
     @Mock private QSTileHost mQsTileHost;
-    @Mock private AutoAddTracker mAutoAddTracker;
-    @Captor private ArgumentCaptor<NextAlarmChangeCallback> mAlarmCallback;
 
     private AutoTileManager mAutoTileManager;
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mDependency.injectMockDependency(NextAlarmController.class);
-        mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker,
-            mQsTileHost, new Handler(TestableLooper.get(this).getLooper()));
-        verify(Dependency.get(NextAlarmController.class))
-            .addCallback(mAlarmCallback.capture());
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
+        Prefs.putBoolean(mContext, Prefs.Key.QS_NIGHTDISPLAY_ADDED, false);
+        mQsTileHost = Mockito.mock(QSTileHost.class);
+        mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
     }
 
     @Test
@@ -109,30 +99,4 @@
                 ColorDisplayController.AUTO_MODE_DISABLED);
         verify(mQsTileHost, never()).addTile("night");
     }
-
-    @Test
-    public void alarmTileAdded_whenAlarmSet() {
-        mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
-
-        verify(mQsTileHost).addTile("alarm");
-        verify(mAutoAddTracker).setTileAdded("alarm");
-    }
-
-    @Test
-    public void alarmTileNotAdded_whenAlarmNotSet() {
-        mAlarmCallback.getValue().onNextAlarmChanged(null);
-
-        verify(mQsTileHost, never()).addTile("alarm");
-        verify(mAutoAddTracker, never()).setTileAdded("alarm");
-    }
-
-    @Test
-    public void alarmTileNotAdded_whenAlreadyAdded() {
-        when(mAutoAddTracker.isAdded("alarm")).thenReturn(true);
-
-        mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
-
-        verify(mQsTileHost, never()).addTile("alarm");
-        verify(mAutoAddTracker, never()).setTileAdded("alarm");
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
new file mode 100644
index 0000000..d54c295
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.policy;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class BatteryControllerTest extends SysuiTestCase {
+
+    @Mock
+    private PowerManager mPowerManager;
+    private BatteryControllerImpl mBatteryController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mBatteryController = new BatteryControllerImpl(getContext(), mPowerManager);
+    }
+
+    @Test
+    public void testIndependentAODBatterySaver_true() {
+        PowerSaveState state = new PowerSaveState.Builder()
+                .setBatterySaverEnabled(true)
+                .build();
+        Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+        when(mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD)).thenReturn(state);
+        when(mPowerManager.isPowerSaveMode()).thenReturn(true);
+
+        mBatteryController.onReceive(getContext(), intent);
+
+        Assert.assertTrue(mBatteryController.isPowerSave());
+        Assert.assertTrue(mBatteryController.isAodPowerSave());
+    }
+
+    @Test
+    public void testIndependentAODBatterySaver_false() {
+        PowerSaveState state = new PowerSaveState.Builder()
+                .setBatterySaverEnabled(false)
+                .build();
+        Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+        when(mPowerManager.getPowerSaveState(PowerManager.ServiceType.AOD)).thenReturn(state);
+        when(mPowerManager.isPowerSaveMode()).thenReturn(true);
+
+        mBatteryController.onReceive(getContext(), intent);
+
+        Assert.assertTrue(mBatteryController.isPowerSave());
+        Assert.assertFalse(mBatteryController.isAodPowerSave());
+    }
+
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c56002e..b897c7c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5328,6 +5328,21 @@
     // OS: P
     PACKAGE_OPTIMIZATION_COMPILATION_REASON = 1321;
 
+    // FIELD: The camera API level used.
+    // CATEGORY: CAMERA
+    // OS: P
+    FIELD_CAMERA_API_LEVEL = 1322;
+
+    // OPEN: Settings > Battery > Battery tip > Battery tip Dialog
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_BATTERY_TIP_DIALOG = 1323;
+
+    // OPEN: Settings > Battery > Battery tip
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_BATTERY_TIP_SHOWN = 1324;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index d4ecc28..f7a4b73 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -356,7 +356,8 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (mDestroyed || !mBinding) {
-                mContext.unbindService(mServiceConnection);
+                // This is abnormal. Unbinding the connection has been requested already.
+                Slog.wtf(LOG_TAG, "onServiceConnected was dispatched after unbindService.");
                 return;
             }
             mBinding = false;
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 501ff29..c87d298 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.app.backup.BackupManager;
+import android.app.backup.BackupTransport;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -58,8 +59,6 @@
     @VisibleForTesting
     public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
 
-    private static final String EXTRA_TRANSPORT_REGISTRATION = "transport_registration";
-
     private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -587,7 +586,7 @@
         String callerLogString = "TransportManager.registerTransport()";
 
         Bundle extras = new Bundle();
-        extras.putBoolean(EXTRA_TRANSPORT_REGISTRATION, true);
+        extras.putBoolean(BackupTransport.EXTRA_TRANSPORT_REGISTRATION, true);
 
         TransportClient transportClient = mTransportClientManager.getTransportClient(
             transportComponent, extras, callerLogString);
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 3df6e47..136fada 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -302,7 +302,7 @@
                     sets = transport.getAvailableRestoreSets();
                     // cache the result in the active session
                     synchronized (params.session) {
-                        params.session.mRestoreSets = sets;
+                        params.session.setRestoreSets(sets);
                     }
                     if (sets == null) {
                         EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index e500d6e..5125b0d 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -16,6 +16,7 @@
 
 package com.android.server.backup.params;
 
+import android.annotation.Nullable;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IRestoreObserver;
 import android.content.pm.PackageInfo;
@@ -28,10 +29,10 @@
     public final IRestoreObserver observer;
     public final IBackupManagerMonitor monitor;
     public final long token;
-    public final PackageInfo packageInfo;
+    @Nullable public final PackageInfo packageInfo;
     public final int pmToken; // in post-install restore, the PM's token for this transaction
     public final boolean isSystemRestore;
-    public final String[] filterSet;
+    @Nullable public final String[] filterSet;
     public final OnTaskFinishedListener listener;
 
     /**
@@ -129,10 +130,10 @@
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
             long token,
-            PackageInfo packageInfo,
+            @Nullable PackageInfo packageInfo,
             int pmToken,
             boolean isSystemRestore,
-            String[] filterSet,
+            @Nullable String[] filterSet,
             OnTaskFinishedListener listener) {
         this.transportClient = transportClient;
         this.observer = observer;
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 238f7a0..140dded 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -22,6 +22,7 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
 
+import android.annotation.Nullable;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IRestoreObserver;
 import android.app.backup.IRestoreSession;
@@ -53,13 +54,15 @@
     private final TransportManager mTransportManager;
     private final String mTransportName;
     private final BackupManagerService mBackupManagerService;
-    private final String mPackageName;
+    @Nullable private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
     boolean mTimedOut = false;
 
-    public ActiveRestoreSession(BackupManagerService backupManagerService,
-            String packageName, String transportName) {
+    public ActiveRestoreSession(
+            BackupManagerService backupManagerService,
+            @Nullable String packageName,
+            String transportName) {
         mBackupManagerService = backupManagerService;
         mPackageName = packageName;
         mTransportManager = backupManagerService.getTransportManager();
@@ -360,6 +363,10 @@
         }
     }
 
+    public void setRestoreSets(RestoreSet[] restoreSets) {
+        mRestoreSets = restoreSets;
+    }
+
     /**
      * Returns 0 if operation sent or -1 otherwise.
      */
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 6eb9619..3caa1e7 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -30,6 +30,7 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
 
+import android.annotation.Nullable;
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
 import android.app.backup.BackupDataInput;
@@ -158,12 +159,18 @@
 
     private final int mEphemeralOpToken;
 
-    // Invariant: mWakelock is already held, and this task is responsible for
-    // releasing it at the end of the restore operation.
-    public PerformUnifiedRestoreTask(BackupManagerService backupManagerService,
-            TransportClient transportClient, IRestoreObserver observer,
-            IBackupManagerMonitor monitor, long restoreSetToken, PackageInfo targetPackage,
-            int pmToken, boolean isFullSystemRestore, String[] filterSet,
+    // This task can assume that the wakelock is properly held for it and doesn't have to worry
+    // about releasing it.
+    public PerformUnifiedRestoreTask(
+            BackupManagerService backupManagerService,
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long restoreSetToken,
+            @Nullable PackageInfo targetPackage,
+            int pmToken,
+            boolean isFullSystemRestore,
+            @Nullable String[] filterSet,
             OnTaskFinishedListener listener) {
         this.backupManagerService = backupManagerService;
         mTransportManager = backupManagerService.getTransportManager();
@@ -336,7 +343,7 @@
      *
      *   [ state change => FINAL ]
      *
-     * 7. t.finishRestore(), release wakelock, etc.
+     * 7. t.finishRestore(), call listeners, etc.
      *
      *
      */
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index c93f405..62a7b8f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -66,6 +66,7 @@
 import android.system.Os;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.KeyValueListParser;
@@ -268,6 +269,7 @@
         // Key names stored in the settings value.
         private static final String KEY_MIN_FUTURITY = "min_futurity";
         private static final String KEY_MIN_INTERVAL = "min_interval";
+        private static final String KEY_MAX_INTERVAL = "max_interval";
         private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
         private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
         private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
@@ -285,6 +287,7 @@
 
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
+        private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
@@ -303,6 +306,9 @@
         // Minimum alarm recurrence interval
         public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL;
 
+        // Maximum alarm recurrence interval
+        public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;
+
         // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
         public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;
 
@@ -361,6 +367,7 @@
 
                 MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
                 MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
+                MAX_INTERVAL = mParser.getLong(KEY_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
                 ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
                         DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
                 ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME,
@@ -391,6 +398,10 @@
             TimeUtils.formatDuration(MIN_INTERVAL, pw);
             pw.println();
 
+            pw.print("    "); pw.print(KEY_MAX_INTERVAL); pw.print("=");
+            TimeUtils.formatDuration(MAX_INTERVAL, pw);
+            pw.println();
+
             pw.print("    "); pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
             pw.println();
@@ -419,6 +430,7 @@
 
             proto.write(ConstantsProto.MIN_FUTURITY_DURATION_MS, MIN_FUTURITY);
             proto.write(ConstantsProto.MIN_INTERVAL_DURATION_MS, MIN_INTERVAL);
+            proto.write(ConstantsProto.MAX_INTERVAL_DURATION_MS, MAX_INTERVAL);
             proto.write(ConstantsProto.LISTENER_TIMEOUT_DURATION_MS, LISTENER_TIMEOUT);
             proto.write(ConstantsProto.ALLOW_WHILE_IDLE_SHORT_DURATION_MS,
                     ALLOW_WHILE_IDLE_SHORT_TIME);
@@ -481,7 +493,7 @@
 
         Batch(Alarm seed) {
             start = seed.whenElapsed;
-            end = seed.maxWhenElapsed;
+            end = clampPositive(seed.maxWhenElapsed);
             flags = seed.flags;
             alarms.add(seed);
             if (seed.operation == mTimeTickSender) {
@@ -737,7 +749,7 @@
         if (futurity < MIN_FUZZABLE_INTERVAL) {
             futurity = 0;
         }
-        return triggerAtTime + (long)(.75 * futurity);
+        return clampPositive(triggerAtTime + (long)(.75 * futurity));
     }
 
     // returns true if the batch was added at the head
@@ -913,7 +925,7 @@
             // the window based on the alarm's new futurity.  Note that this
             // reflects a policy of preferring timely to deferred delivery.
             maxElapsed = (a.windowLength > 0)
-                    ? (whenElapsed + a.windowLength)
+                    ? clampPositive(whenElapsed + a.windowLength)
                     : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
         }
         a.whenElapsed = whenElapsed;
@@ -921,6 +933,10 @@
         setImplLocked(a, true, doValidate);
     }
 
+    static long clampPositive(long val) {
+        return (val >= 0) ? val : Long.MAX_VALUE;
+    }
+
     /**
      * Sends alarms that were blocked due to user applied background restrictions - either because
      * the user lifted those or the uid came to foreground.
@@ -1421,13 +1437,18 @@
         }
 
         // Sanity check the recurrence interval.  This will catch people who supply
-        // seconds when the API expects milliseconds.
+        // seconds when the API expects milliseconds, or apps trying shenanigans
+        // around intentional period overflow, etc.
         final long minInterval = mConstants.MIN_INTERVAL;
         if (interval > 0 && interval < minInterval) {
             Slog.w(TAG, "Suspiciously short interval " + interval
                     + " millis; expanding to " + (minInterval/1000)
                     + " seconds");
             interval = minInterval;
+        } else if (interval > mConstants.MAX_INTERVAL) {
+            Slog.w(TAG, "Suspiciously long interval " + interval
+                    + " millis; clamping");
+            interval = mConstants.MAX_INTERVAL;
         }
 
         if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
@@ -3175,8 +3196,7 @@
             whenElapsed = _whenElapsed;
             expectedWhenElapsed = _whenElapsed;
             windowLength = _windowLength;
-            maxWhenElapsed = _maxWhen;
-            expectedMaxWhenElapsed = _maxWhen;
+            maxWhenElapsed = expectedMaxWhenElapsed = clampPositive(_maxWhen);
             repeatInterval = _interval;
             operation = _op;
             listener = _rec;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 56ed6c8..bac81e7 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -158,6 +158,8 @@
     }
 
     private void positionChildAt(ActivityStack stack, int position) {
+        // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
+        //       the position internally, also update the logic here
         mStacks.remove(stack);
         final int insertPosition = getTopInsertPosition(stack, position);
         mStacks.add(insertPosition, stack);
@@ -750,7 +752,15 @@
             return;
         }
 
-        positionChildAt(mHomeStack, Math.max(0, mStacks.indexOf(behindStack) - 1));
+        // Note that positionChildAt will first remove the given stack before inserting into the
+        // list, so we need to adjust the insertion index to account for the removed index
+        // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
+        //       position internally
+        final int homeStackIndex = mStacks.indexOf(mHomeStack);
+        final int behindStackIndex = mStacks.indexOf(behindStack);
+        final int insertIndex = homeStackIndex <= behindStackIndex
+                ? behindStackIndex - 1 : behindStackIndex;
+        positionChildAt(mHomeStack, Math.max(0, insertIndex));
     }
 
     boolean isSleeping() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index da88667..f1e3bfd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9410,6 +9410,25 @@
                     allowed = false;
                 }
             }
+            if (pi.pathPermissions != null) {
+                final int N = pi.pathPermissions.length;
+                for (int i=0; i<N; i++) {
+                    if (pi.pathPermissions[i] != null
+                            && pi.pathPermissions[i].match(grantUri.uri.getPath())) {
+                        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                            if (pi.pathPermissions[i].getReadPermission() != null) {
+                                allowed = false;
+                            }
+                        }
+                        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                            if (pi.pathPermissions[i].getWritePermission() != null) {
+                                allowed = false;
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
             if (allowed) {
                 return -1;
             }
@@ -26155,6 +26174,10 @@
             return getRecentTasks().isCallerRecents(callingUid);
         }
 
+        public boolean isRecentsComponentHomeActivity(int userId) {
+            return getRecentTasks().isRecentsComponentHomeActivity(userId);
+        }
+
         @Override
         public boolean isUidActive(int uid) {
             synchronized (ActivityManagerService.this) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 8cc9273..274a4b0 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1581,25 +1581,7 @@
     void setState(ActivityState state, String reason) {
         if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
                         + " to:" + state + " reason:" + reason);
-        final boolean stateChanged = mState != state;
         mState = state;
-
-        if (stateChanged && isState(DESTROYING, DESTROYED)) {
-            makeFinishingLocked();
-
-            // When moving to the destroyed state, immediately destroy the activity in the
-            // associated stack. Most paths for finishing an activity will handle an activity's path
-            // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities.
-            // However, moving to the destroyed state directly (as in the case of an app dying) and
-            // marking it as finished will lead to cleanup steps that will prevent later handling
-            // from happening.
-            if (isState(DESTROYED)) {
-                final ActivityStack stack = getStack();
-                if (stack != null) {
-                    stack.activityDestroyedLocked(this, reason);
-                }
-            }
-        }
     }
 
     ActivityState getState() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4987b33..2f6afd2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -603,6 +603,8 @@
                 // the one where the home stack is visible since recents isn't visible yet, but the
                 // divider will be off. I think we should just make the initial bounds that of home
                 // so that the divider matches and remove this logic.
+                // TODO: This is currently only called when entering split-screen while in another
+                // task, and from the tests
                 final ActivityStack recentStack = display.getOrCreateStack(
                         WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
                         true /* onTop */);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5577186..0157c7c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -93,6 +93,7 @@
 import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.proto.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
 import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
@@ -164,7 +165,6 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
-import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -2540,6 +2540,11 @@
         }
     }
 
+    void deferUpdateRecentsHomeStackBounds() {
+        deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        deferUpdateBounds(ACTIVITY_TYPE_HOME);
+    }
+
     void deferUpdateBounds(int activityType) {
         final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
         if (stack != null) {
@@ -2547,6 +2552,11 @@
         }
     }
 
+    void continueUpdateRecentsHomeStackBounds() {
+        continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        continueUpdateBounds(ACTIVITY_TYPE_HOME);
+    }
+
     void continueUpdateBounds(int activityType) {
         final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
         if (stack != null) {
@@ -2555,7 +2565,7 @@
     }
 
     void notifyAppTransitionDone() {
-        continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        continueUpdateRecentsHomeStackBounds();
         for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
             final int taskId = mResizingTasksDuringAnimation.valueAt(i);
             final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
@@ -3760,6 +3770,8 @@
                 pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
             }
         }
+        pw.print(prefix); pw.print("isHomeRecentsComponent=");
+        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
 
         mKeyguardController.dump(pw, prefix);
         mService.mLockTaskController.dump(pw, prefix);
@@ -3781,6 +3793,8 @@
         } else {
             proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
         }
+        proto.write(IS_HOME_RECENTS_COMPONENT,
+                mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
     }
 
     /**
@@ -4546,14 +4560,14 @@
                 // Defer updating the stack in which recents is until the app transition is done, to
                 // not run into issues where we still need to draw the task in recents but the
                 // docked stack is already created.
-                deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+                deferUpdateRecentsHomeStackBounds();
                 mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
             }
 
             task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
                     activityOptions, ON_TOP);
             if (task == null) {
-                continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+                continueUpdateRecentsHomeStackBounds();
                 mWindowManager.executeAppTransition();
                 throw new IllegalArgumentException(
                         "startActivityFromRecents: Task " + taskId + " not found.");
@@ -4611,6 +4625,11 @@
                     // window manager can correctly calculate the focus window that can receive
                     // input keys.
                     moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");
+
+                    // Immediately update the minimized docked stack mode, the upcoming animation
+                    // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
+                    // will do the animation to the target bounds
+                    mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
                 }
             }
             mWindowManager.continueSurfaceLayout();
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 2de84ab..5fd300c 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -279,6 +279,16 @@
     }
 
     /**
+     * @return whether the home app is also the active handler of recent tasks.
+     */
+    boolean isRecentsComponentHomeActivity(int userId) {
+        final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
+                .getDefaultHomeActivity(userId);
+        return defaultHomeActivity != null &&
+                defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
+    }
+
+    /**
      * @return the recents component.
      */
     ComponentName getRecentsComponent() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8afa540..ca22820 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1538,17 +1538,6 @@
         if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
             mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
 
-            // Check if volume update should be send to AVRCP
-            if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&
-                (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
-                (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
-                synchronized (mA2dpAvrcpLock) {
-                    if (mA2dp != null && mAvrcpAbsVolSupported) {
-                        mA2dp.adjustAvrcpAbsoluteVolume(direction);
-                    }
-                }
-            }
-
             if (isMuteAdjust) {
                 boolean state;
                 if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
@@ -1597,8 +1586,20 @@
                         0);
             }
 
-            // Check if volume update should be sent to Hdmi system audio.
             int newIndex = mStreamStates[streamType].getIndex(device);
+
+            // Check if volume update should be send to AVRCP
+            if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+                (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+                (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
+                synchronized (mA2dpAvrcpLock) {
+                    if (mA2dp != null && mAvrcpAbsVolSupported) {
+                        mA2dp.setAvrcpAbsoluteVolume(newIndex / 10);
+                    }
+                }
+            }
+
+            // Check if volume update should be sent to Hdmi system audio.
             if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                 setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
             }
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 3133a51..ca8823f 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -103,13 +103,15 @@
     private static class CameraUsageEvent {
         public final int mCameraFacing;
         public final String mClientName;
+        public final int mAPILevel;
 
         private boolean mCompleted;
         private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
 
-        public CameraUsageEvent(int facing, String clientName) {
+        public CameraUsageEvent(int facing, String clientName, int apiLevel) {
             mCameraFacing = facing;
             mClientName = clientName;
+            mAPILevel = apiLevel;
             mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
             mCompleted = false;
         }
@@ -168,13 +170,13 @@
 
         @Override
         public void notifyCameraState(String cameraId, int newCameraState, int facing,
-                String clientName) {
+                String clientName, int apiLevel) {
             String state = cameraStateToString(newCameraState);
             String facingStr = cameraFacingToString(facing);
             if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
-                    state + " for client " + clientName);
+                    state + " for client " + clientName + " API Level " + apiLevel);
 
-            updateActivityCount(cameraId, newCameraState, facing, clientName);
+            updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel);
         }
     };
 
@@ -293,6 +295,7 @@
                         .setType(MetricsEvent.TYPE_ACTION)
                         .setSubtype(subtype)
                         .setLatency(e.getDuration())
+                        .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel)
                         .setPackageName(e.mClientName);
                 mLogger.write(l);
             }
@@ -368,7 +371,8 @@
         return true;
     }
 
-    private void updateActivityCount(String cameraId, int newCameraState, int facing, String clientName) {
+    private void updateActivityCount(String cameraId, int newCameraState, int facing,
+            String clientName, int apiLevel) {
         synchronized(mLock) {
             // Update active camera list and notify NFC if necessary
             boolean wasEmpty = mActiveCameraUsage.isEmpty();
@@ -376,7 +380,7 @@
                 case ICameraServiceProxy.CAMERA_STATE_OPEN:
                     break;
                 case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
-                    CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName);
+                    CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
                     CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
                     if (oldEvent != null) {
                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 0c9d70a..776e93d 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.clipboard;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -24,9 +25,9 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ContentProvider;
+import android.content.Context;
 import android.content.IClipboard;
 import android.content.IOnPrimaryClipChangedListener;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -37,7 +38,6 @@
 import android.os.IBinder;
 import android.os.IUserManager;
 import android.os.Parcel;
-import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -49,14 +49,10 @@
 
 import com.android.server.SystemService;
 
-import java.util.HashSet;
-import java.util.List;
-
-import java.lang.Thread;
-import java.lang.Runnable;
-import java.lang.InterruptedException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.util.HashSet;
+import java.util.List;
 
 // The following class is Android Emulator specific. It is used to read and
 // write contents of the host system's clipboard.
@@ -182,7 +178,8 @@
                                          new String[]{"text/plain"},
                                          new ClipData.Item(contents));
                         synchronized(mClipboards) {
-                            setPrimaryClipInternal(getClipboard(0), clip);
+                            setPrimaryClipInternal(getClipboard(0), clip,
+                                    android.os.Process.SYSTEM_UID);
                         }
                     }
                 });
@@ -218,7 +215,10 @@
         final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
                 = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
 
+        /** Current primary clip. */
         ClipData primaryClip;
+        /** UID that set {@link #primaryClip}. */
+        int primaryClipUid = android.os.Process.NOBODY_UID;
 
         final HashSet<String> activePermissionOwners
                 = new HashSet<String>();
@@ -246,58 +246,28 @@
         @Override
         public void setPrimaryClip(ClipData clip, String callingPackage) {
             synchronized (this) {
-                if (clip != null && clip.getItemCount() <= 0) {
+                if (clip == null || clip.getItemCount() <= 0) {
                     throw new IllegalArgumentException("No items");
                 }
-                if (clip.getItemAt(0).getText() != null &&
-                    mHostClipboardMonitor != null) {
-                    mHostClipboardMonitor.setHostClipboard(
-                        clip.getItemAt(0).getText().toString());
-                }
                 final int callingUid = Binder.getCallingUid();
                 if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
                             callingUid)) {
                     return;
                 }
                 checkDataOwnerLocked(clip, callingUid);
-                final int userId = UserHandle.getUserId(callingUid);
-                PerUserClipboard clipboard = getClipboard(userId);
-                revokeUris(clipboard);
-                setPrimaryClipInternal(clipboard, clip);
-                List<UserInfo> related = getRelatedProfiles(userId);
-                if (related != null) {
-                    int size = related.size();
-                    if (size > 1) { // Related profiles list include the current profile.
-                        boolean canCopy = false;
-                        try {
-                            canCopy = !mUm.getUserRestrictions(userId).getBoolean(
-                                    UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "Remote Exception calling UserManager: " + e);
-                        }
-                        // Copy clip data to related users if allowed. If disallowed, then remove
-                        // primary clip in related users to prevent pasting stale content.
-                        if (!canCopy) {
-                            clip = null;
-                        } else {
-                            // We want to fix the uris of the related user's clip without changing the
-                            // uris of the current user's clip.
-                            // So, copy the ClipData, and then copy all the items, so that nothing
-                            // is shared in memmory.
-                            clip = new ClipData(clip);
-                            for (int i = clip.getItemCount() - 1; i >= 0; i--) {
-                                clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
-                            }
-                            clip.fixUrisLight(userId);
-                        }
-                        for (int i = 0; i < size; i++) {
-                            int id = related.get(i).id;
-                            if (id != userId) {
-                                setPrimaryClipInternal(getClipboard(id), clip);
-                            }
-                        }
-                    }
+                setPrimaryClipInternal(clip, callingUid);
+            }
+        }
+
+        @Override
+        public void clearPrimaryClip(String callingPackage) {
+            synchronized (this) {
+                final int callingUid = Binder.getCallingUid();
+                if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
+                        callingUid)) {
+                    return;
                 }
+                setPrimaryClipInternal(null, callingUid);
             }
         }
 
@@ -398,13 +368,75 @@
         return related;
     }
 
-    void setPrimaryClipInternal(PerUserClipboard clipboard, ClipData clip) {
+    void setPrimaryClipInternal(@Nullable ClipData clip, int callingUid) {
+        // Push clipboard to host, if any
+        if (mHostClipboardMonitor != null) {
+            if (clip == null) {
+                // Someone really wants the clipboard cleared, so push empty
+                mHostClipboardMonitor.setHostClipboard("");
+            } else if (clip.getItemCount() > 0) {
+                final CharSequence text = clip.getItemAt(0).getText();
+                if (text != null) {
+                    mHostClipboardMonitor.setHostClipboard(text.toString());
+                }
+            }
+        }
+
+        // Update this user
+        final int userId = UserHandle.getUserId(callingUid);
+        setPrimaryClipInternal(getClipboard(userId), clip, callingUid);
+
+        // Update related users
+        List<UserInfo> related = getRelatedProfiles(userId);
+        if (related != null) {
+            int size = related.size();
+            if (size > 1) { // Related profiles list include the current profile.
+                boolean canCopy = false;
+                try {
+                    canCopy = !mUm.getUserRestrictions(userId).getBoolean(
+                            UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+                }
+                // Copy clip data to related users if allowed. If disallowed, then remove
+                // primary clip in related users to prevent pasting stale content.
+                if (!canCopy) {
+                    clip = null;
+                } else {
+                    // We want to fix the uris of the related user's clip without changing the
+                    // uris of the current user's clip.
+                    // So, copy the ClipData, and then copy all the items, so that nothing
+                    // is shared in memmory.
+                    clip = new ClipData(clip);
+                    for (int i = clip.getItemCount() - 1; i >= 0; i--) {
+                        clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
+                    }
+                    clip.fixUrisLight(userId);
+                }
+                for (int i = 0; i < size; i++) {
+                    int id = related.get(i).id;
+                    if (id != userId) {
+                        setPrimaryClipInternal(getClipboard(id), clip, callingUid);
+                    }
+                }
+            }
+        }
+    }
+
+    void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
+            int callingUid) {
+        revokeUris(clipboard);
         clipboard.activePermissionOwners.clear();
         if (clip == null && clipboard.primaryClip == null) {
             return;
         }
         clipboard.primaryClip = clip;
         if (clip != null) {
+            clipboard.primaryClipUid = callingUid;
+        } else {
+            clipboard.primaryClipUid = android.os.Process.NOBODY_UID;
+        }
+        if (clip != null) {
             final ClipDescription description = clip.getDescription();
             if (description != null) {
                 description.setTimestamp(System.currentTimeMillis());
@@ -479,12 +511,12 @@
         }
     }
 
-    private final void grantUriLocked(Uri uri, String pkg, int userId) {
+    private final void grantUriLocked(Uri uri, int primaryClipUid, String pkg, int userId) {
         long ident = Binder.clearCallingIdentity();
         try {
             int sourceUserId = ContentProvider.getUserIdFromUri(uri, userId);
             uri = ContentProvider.getUriWithoutUserId(uri);
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg,
+            mAm.grantUriPermissionFromOwner(mPermissionOwner, primaryClipUid, pkg,
                     uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, userId);
         } catch (RemoteException e) {
         } finally {
@@ -492,13 +524,14 @@
         }
     }
 
-    private final void grantItemLocked(ClipData.Item item, String pkg, int userId) {
+    private final void grantItemLocked(ClipData.Item item, int primaryClipUid, String pkg,
+            int userId) {
         if (item.getUri() != null) {
-            grantUriLocked(item.getUri(), pkg, userId);
+            grantUriLocked(item.getUri(), primaryClipUid, pkg, userId);
         }
         Intent intent = item.getIntent();
         if (intent != null && intent.getData() != null) {
-            grantUriLocked(intent.getData(), pkg, userId);
+            grantUriLocked(intent.getData(), primaryClipUid, pkg, userId);
         }
     }
 
@@ -524,7 +557,8 @@
         if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
             final int N = clipboard.primaryClip.getItemCount();
             for (int i=0; i<N; i++) {
-                grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg, UserHandle.getUserId(uid));
+                grantItemLocked(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid, pkg,
+                        UserHandle.getUserId(uid));
             }
             clipboard.activePermissionOwners.add(pkg);
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9e00819..752ab8f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2079,6 +2079,11 @@
     }
 
     @Override
+    public String importKey(@NonNull String alias, byte[] keyBytes) throws RemoteException {
+        return mRecoverableKeyStoreManager.importKey(alias, keyBytes);
+    }
+
+    @Override
     public String getKey(@NonNull String alias) throws RemoteException {
         return mRecoverableKeyStoreManager.getKey(alias);
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
index 2fe3f4e..7ebe8bf 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
@@ -16,6 +16,8 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.annotation.NonNull;
+
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 
 import java.security.InvalidKeyException;
@@ -25,20 +27,24 @@
 
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
 
+// TODO: Rename RecoverableKeyGenerator to RecoverableKeyManager as it can import a key too now
 /**
- * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
+ * Generates/imports keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
  *
- * <p>Generates 256-bit AES keys, which can be used for encrypt / decrypt with AES/GCM/NoPadding.
+ * <p>Generates/imports 256-bit AES keys, which can be used for encrypt and decrypt with AES-GCM.
  * They are synced to disk wrapped by a platform key. This allows them to be exported to a remote
  * service.
  *
  * @hide
  */
 public class RecoverableKeyGenerator {
+
     private static final int RESULT_CANNOT_INSERT_ROW = -1;
-    private static final String KEY_GENERATOR_ALGORITHM = "AES";
-    private static final int KEY_SIZE_BITS = 256;
+    private static final String SECRET_KEY_ALGORITHM = "AES";
+
+    static final int KEY_SIZE_BITS = 256;
 
     /**
      * A new {@link RecoverableKeyGenerator} instance.
@@ -52,7 +58,7 @@
             throws NoSuchAlgorithmException {
         // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
         // material, so that it can be synced to disk in encrypted form.
-        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(SECRET_KEY_ALGORITHM);
         return new RecoverableKeyGenerator(keyGenerator, database);
     }
 
@@ -102,4 +108,41 @@
         mDatabase.setShouldCreateSnapshot(userId, uid, true);
         return key.getEncoded();
     }
+
+    /**
+     * Imports an AES key with the given alias.
+     *
+     * <p>Stores in the AndroidKeyStore, as well as persisting in wrapped form to disk. It is
+     * persisted to disk so that it can be synced remotely, and then recovered on another device.
+     * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding.
+     *
+     * @param platformKey The user's platform key, with which to wrap the generated key.
+     * @param userId The user ID of the profile to which the calling app belongs.
+     * @param uid The uid of the application that will own the key.
+     * @param alias The alias by which the key will be known in the recoverable key store.
+     * @param keyBytes The raw bytes of the AES key to be imported.
+     * @throws RecoverableKeyStorageException if there is some error persisting the key either to
+     *     the database.
+     * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
+     * @throws InvalidKeyException if the platform key cannot be used to wrap keys.
+     *
+     * @hide
+     */
+    public void importKey(
+            @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias,
+            @NonNull byte[] keyBytes)
+            throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
+        SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM);
+
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+        long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);
+
+        if (result == RESULT_CANNOT_INSERT_ROW) {
+            throw new RecoverableKeyStorageException(
+                    String.format(
+                            Locale.US, "Failed writing (%d, %s) to database.", uid, alias));
+        }
+
+        mDatabase.setShouldCreateSnapshot(userId, uid, true);
+    }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 72f72eb..da0b0d0 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -19,6 +19,7 @@
 import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
 import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
 import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
 import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
 import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
 import static android.security.keystore.recovery.RecoveryController.ERROR_SESSION_EXPIRED;
@@ -505,6 +506,7 @@
      *
      * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes.
      *
+     * @deprecated
      * @hide
      */
     public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
@@ -581,6 +583,57 @@
     }
 
     /**
+     * Imports a 256-bit AES-GCM key named {@code alias}. The key is stored in system service
+     * keystore namespace.
+     *
+     * @param alias the alias provided by caller as a reference to the key.
+     * @param keyBytes the raw bytes of the 256-bit AES key.
+     * @return grant alias, which caller can use to access the key.
+     * @throws RemoteException if the given key is invalid or some internal errors occur.
+     *
+     * @hide
+     */
+    public String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
+            throws RemoteException {
+        if (keyBytes == null ||
+                keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
+            Log.e(TAG, "The given key for import doesn't have the required length "
+                    + RecoverableKeyGenerator.KEY_SIZE_BITS);
+            throw new ServiceSpecificException(ERROR_INVALID_KEY_FORMAT,
+                    "The given key does not contain " + RecoverableKeyGenerator.KEY_SIZE_BITS
+                            + " bits.");
+        }
+
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        // TODO: Refactor RecoverableKeyGenerator to wrap the PlatformKey logic
+
+        PlatformEncryptionKey encryptionKey;
+        try {
+            encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
+        } catch (NoSuchAlgorithmException e) {
+            // Impossible: all algorithms must be supported by AOSP
+            throw new RuntimeException(e);
+        } catch (KeyStoreException | UnrecoverableKeyException e) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+        } catch (InsecureUserException e) {
+            throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
+        }
+
+        try {
+            // Wrap the key by the platform key and store the wrapped key locally
+            mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes);
+
+            // Import the key to Android KeyStore and get grant
+            mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
+            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+        } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    /**
      * Gets a key named {@code alias} in caller's namespace.
      *
      * @return grant alias, which caller can use to access the key.
@@ -630,14 +683,6 @@
         }
     }
 
-    private String constructLoggingMessage(String key, byte[] value) {
-        if (value == null) {
-            return key + " is null";
-        } else {
-            return key + ": " + HexDump.toHexString(value);
-        }
-    }
-
     /**
      * Uses {@code recoveryKey} to decrypt {@code applicationKeys}.
      *
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 1cb5d91..8983ec3 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -70,6 +70,122 @@
     }
 
     /**
+     * Table holding encrypted snapshots of the recoverable key store.
+     */
+    static class SnapshotsEntry implements BaseColumns {
+        static final String TABLE_NAME = "snapshots";
+
+        /**
+         * The version number of the snapshot.
+         */
+        static final String COLUMN_NAME_VERSION = "version";
+
+        /**
+         * The ID of the user whose keystore was snapshotted.
+         */
+        static final String COLUMN_NAME_USER_ID = "user_id";
+
+        /**
+         * The UID of the app that owns the snapshot (i.e., the recovery agent).
+         */
+        static final String COLUMN_NAME_UID = "uid";
+
+        /**
+         * The maximum number of attempts allowed to attempt to decrypt the recovery key.
+         */
+        static final String COLUMN_NAME_MAX_ATTEMPTS = "max_attempts";
+
+        /**
+         * The ID of the counter in the trusted hardware module.
+         */
+        static final String COLUMN_NAME_COUNTER_ID = "counter_id";
+
+        /**
+         * Server parameters used to help identify the device (during recovery).
+         */
+        static final String SERVER_PARAMS = "server_params";
+
+        /**
+         * The public key of the trusted hardware module. This key has been used to encrypt the
+         * snapshot, to ensure that it can only be read by the trusted module.
+         */
+        static final String TRUSTED_HARDWARE_PUBLIC_KEY = "thm_public_key";
+
+        /**
+         * {@link java.security.cert.CertPath} signing the trusted hardware module to whose public
+         * key this snapshot is encrypted.
+         */
+        static final String CERT_PATH = "cert_path";
+
+        /**
+         * The recovery key, encrypted with the user's lock screen and the trusted hardware module's
+         * public key.
+         */
+        static final String ENCRYPTED_RECOVERY_KEY = "encrypted_recovery_key";
+    }
+
+    /**
+     * Table holding encrypted keys belonging to a particular snapshot.
+     */
+    static class SnapshotKeysEntry implements BaseColumns {
+        static final String TABLE_NAME = "snapshot_keys";
+
+        /**
+         * ID of the associated snapshot entry in {@link SnapshotsEntry}.
+         */
+        static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
+
+        /**
+         * Alias of the key.
+         */
+        static final String COLUMN_NAME_ALIAS = "alias";
+
+        /**
+         * Key material, encrypted with the recovery key from the snapshot.
+         */
+        static final String COLUMN_NAME_ENCRYPTED_BYTES = "encrypted_key_bytes";
+    }
+
+    /**
+     * A layer of protection associated with a snapshot.
+     */
+    static class SnapshotProtectionParams implements BaseColumns {
+        static final String TABLE_NAME = "snapshot_protection_params";
+
+        /**
+         * ID of the associated snapshot entry in {@link SnapshotsEntry}.
+         */
+        static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
+
+        /**
+         * Type of secret used to generate recovery key. One of
+         * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
+         * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}.
+         */
+        static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
+
+        /**
+         * If a lock screen, the type of UI used. One of
+         * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PATTERN},
+         * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PIN}, or
+         * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PASSWORD}.
+         */
+        static final String COLUMN_NAME_LOCKSCREEN_UI_TYPE = "lock_screen_ui_type";
+
+        /**
+         * The algorithm used to derive cryptographic material from the key and salt. One of
+         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
+         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_ARGON2ID}.
+         */
+        static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
+
+        /**
+         * The salt used along with the secret to generate cryptographic material.
+         */
+        static final String COLUMN_NAME_KEY_DERIVATION_SALT = "key_derivation_salt";
+    }
+
+    /**
      * Recoverable KeyStore metadata for a specific user profile.
      */
     static class UserMetadataEntry implements BaseColumns {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 896480f..c0c66b2 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -45,8 +46,6 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-import android.os.SystemClock;
-
 /**
  * This {@link NotificationSignalExtractor} attempts to validate
  * people references. Also elevates the priority of real people.
@@ -231,7 +230,6 @@
 
     private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
             List<String> peopleOverride, float[] affinityOut) {
-        long start = SystemClock.elapsedRealtime();
         float affinity = NONE;
         if (extras == null) {
             return null;
@@ -239,7 +237,7 @@
         final Set<String> people = new ArraySet<>(peopleOverride);
         final String[] notificationPeople = getExtraPeople(extras);
         if (notificationPeople != null ) {
-            people.addAll(Arrays.asList(getExtraPeople(extras)));
+            people.addAll(Arrays.asList(notificationPeople));
         }
 
         if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
@@ -283,7 +281,31 @@
 
     // VisibleForTesting
     public static String[] getExtraPeople(Bundle extras) {
-        Object people = extras.get(Notification.EXTRA_PEOPLE_LIST);
+        String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST);
+        String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE);
+        return combineLists(legacyPeople, peopleList);
+    }
+
+    private static String[] combineLists(String[] first, String[] second) {
+        if (first == null) {
+            return second;
+        }
+        if (second == null) {
+            return first;
+        }
+        ArraySet<String> people = new ArraySet<>(first.length + second.length);
+        for (String person: first) {
+            people.add(person);
+        }
+        for (String person: second) {
+            people.add(person);
+        }
+        return (String[]) people.toArray();
+    }
+
+    @Nullable
+    private static String[] getExtraPeopleForKey(Bundle extras, String key) {
+        Object people = extras.get(key);
         if (people instanceof String[]) {
             return (String[]) people;
         }
@@ -458,7 +480,6 @@
 
         @Override
         public void work() {
-            long start = SystemClock.elapsedRealtime();
             if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
             long timeStartMs = System.currentTimeMillis();
             for (final String handle: mPendingLookups) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 256fb42..0c6e555 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23556,6 +23556,11 @@
         }
 
         @Override
+        public ComponentName getDefaultHomeActivity(int userId) {
+            return PackageManagerService.this.getDefaultHomeActivity(userId);
+        }
+
+        @Override
         public void setDeviceAndProfileOwnerPackages(
                 int deviceOwnerUserId, String deviceOwnerPackage,
                 SparseArray<String> profileOwnerPackages) {
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 08dc97e..16336b3 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -68,6 +68,7 @@
     private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
     private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
     private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
+    private static final String KEY_AOD_DISABLED = "aod_disabled";
 
     private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
     private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
@@ -200,11 +201,17 @@
     private boolean mForceBackgroundCheck;
 
     /**
-     * Weather to show non-essential sensors (e.g. edge sensors) or not.
+     * Whether to show non-essential sensors (e.g. edge sensors) or not.
      */
     @GuardedBy("mLock")
     private boolean mOptionalSensorsDisabled;
 
+    /**
+     * Whether AOD is enabled or not.
+     */
+    @GuardedBy("mLock")
+    private boolean mAodDisabled;
+
     @GuardedBy("mLock")
     private Context mContext;
 
@@ -339,6 +346,7 @@
         mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
         mForceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, true);
         mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
+        mAodDisabled = parser.getBoolean(KEY_AOD_DISABLED, true);
 
         // Get default value from Settings.Secure
         final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
@@ -375,6 +383,7 @@
 
         if (mLaunchBoostDisabled) sb.append("l");
         if (mOptionalSensorsDisabled) sb.append("S");
+        if (mAodDisabled) sb.append("o");
 
         sb.append(mGpsMode);
 
@@ -437,6 +446,9 @@
                 case ServiceType.OPTIONAL_SENSORS:
                     return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
                             .build();
+                case ServiceType.AOD:
+                    return builder.setBatterySaverEnabled(mAodDisabled)
+                            .build();
                 default:
                     return builder.setBatterySaverEnabled(realMode)
                             .build();
@@ -491,6 +503,7 @@
             pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
             pw.println("  " + KEY_FORCE_BACKGROUND_CHECK + "=" + mForceBackgroundCheck);
             pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
+            pw.println("  " + KEY_AOD_DISABLED + "=" + mAodDisabled);
             pw.println();
 
             pw.print("  Interactive File values:\n");
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 95c30d1..6e017cd 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -100,6 +100,7 @@
 
     private final PendingIntent mAnomalyAlarmIntent;
     private final PendingIntent mPullingAlarmIntent;
+    private final PendingIntent mPeriodicAlarmIntent;
     private final BroadcastReceiver mAppUpdateReceiver;
     private final BroadcastReceiver mUserUpdateReceiver;
     private final ShutdownEventReceiver mShutdownEventReceiver;
@@ -123,6 +124,8 @@
                 new Intent(mContext, AnomalyAlarmReceiver.class), 0);
         mPullingAlarmIntent = PendingIntent.getBroadcast(
                 mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
+        mPeriodicAlarmIntent = PendingIntent.getBroadcast(
+                mContext, 0, new Intent(mContext, PeriodicAlarmReceiver.class), 0);
         mAppUpdateReceiver = new AppUpdateReceiver();
         mUserUpdateReceiver = new BroadcastReceiver() {
             @Override
@@ -329,7 +332,28 @@
         }
     }
 
-    private final static class ShutdownEventReceiver extends BroadcastReceiver {
+    public final static class PeriodicAlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG)
+                Slog.d(TAG, "Time to poll something.");
+            synchronized (sStatsdLock) {
+                if (sStatsd == null) {
+                    Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
+                    return;
+                }
+                try {
+                    // Two-way call to statsd to retain AlarmManager wakelock
+                    sStatsd.informAlarmForSubscriberTriggeringFired();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
+                }
+            }
+            // AlarmManager releases its own wakelock here.
+        }
+    }
+
+    public final static class ShutdownEventReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             /**
@@ -385,6 +409,35 @@
     }
 
     @Override // Binder call
+    public void setAlarmForSubscriberTriggering(long timestampMs) {
+        enforceCallingPermission();
+        if (DEBUG)
+            Slog.d(TAG, "Setting periodic alarm at " + timestampMs);
+        final long callingToken = Binder.clearCallingIdentity();
+        try {
+            // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+            // only fire when it awakens.
+            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, timestampMs, mPeriodicAlarmIntent);
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+    }
+
+    @Override // Binder call
+    public void cancelAlarmForSubscriberTriggering() {
+        enforceCallingPermission();
+        if (DEBUG)
+            Slog.d(TAG, "Cancelling periodic alarm");
+        final long callingToken = Binder.clearCallingIdentity();
+        try {
+            mAlarmManager.cancel(mPeriodicAlarmIntent);
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+    }
+
+    @Override // Binder call
     public void setPullingAlarms(long timestampMs, long intervalMs) {
         enforceCallingPermission();
         if (DEBUG)
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 277a04b..c2cc7c9 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1620,7 +1620,15 @@
 
     @Override
     public SurfaceControl getAnimationLeashParent() {
-        return getAppAnimationLayer();
+        // All normal app transitions take place in an animation layer which is below the pinned
+        // stack but may be above the parent stacks of the given animating apps.
+        // For transitions in the pinned stack (menu activity) we just let them occur as a child
+        // of the pinned stack.
+        if (!inPinnedWindowingMode()) {
+            return getAppAnimationLayer();
+        } else {
+            return getStack().getSurfaceControl();
+        }
     }
 
     boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
@@ -1709,6 +1717,10 @@
                 frame.set(win.mFrame);
             } else if (win.isLetterboxedAppWindow()) {
                 frame.set(getTask().getBounds());
+            } else if (win.isDockedResizing()) {
+                // If we are animating while docked resizing, then use the stack bounds as the
+                // animation target (which will be different than the task bounds)
+                frame.set(getTask().getParent().getBounds());
             } else {
                 frame.set(win.mContainingFrame);
             }
@@ -1763,10 +1775,18 @@
 
     @Override
     public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
-
         // The leash is parented to the animation layer. We need to preserve the z-order by using
         // the prefix order index, but we boost if necessary.
-        int layer = getPrefixOrderIndex();
+        int layer = 0;
+        if (!inPinnedWindowingMode()) {
+            layer = getPrefixOrderIndex();
+        } else {
+            // Pinned stacks have animations take place within themselves rather than an animation
+            // layer so we need to preserve the order relative to the stack (e.g. the order of our
+            // task/parent).
+            layer = getParent().getPrefixOrderIndex();
+        }
+
         if (mNeedsZBoost) {
             layer += Z_BOOST_BASE;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 75a6338..19c634a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3583,7 +3583,7 @@
                     if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
                         t.setLayer(mSplitScreenDividerAnchor, layer++);
                     }
-                    if (s.isSelfOrChildAnimating()) {
+                    if (s.isAppAnimating() && state != ALWAYS_ON_TOP_STATE) {
                         // Ensure the animation layer ends up above the
                         // highest animating stack and no higher.
                         layerForAnimationLayer = layer++;
@@ -3632,6 +3632,11 @@
             super(name, service);
         }
 
+        @Override
+        void assignChildLayers(SurfaceControl.Transaction t) {
+            assignChildLayers(t, null /* imeContainer */);
+        }
+
         void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) {
             boolean needAssignIme = imeContainer != null
                     && imeContainer.getSurfaceControl() != null;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 0e12838..e3e4a46 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -50,7 +50,9 @@
             }
 
             if (mContainer == null) {
-                throw new IllegalArgumentException("Trying to add displayId=" + displayId);
+                throw new IllegalArgumentException("Trying to add displayId=" + displayId
+                        + " display=" + display
+                        + " dc=" + mRoot.getDisplayContent(displayId));
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 46c59c5..1f1efc4 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -620,7 +620,12 @@
         if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
                 && appTransition != TRANSIT_NONE &&
                 !AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
-            mService.showRecentApps();
+            if (mService.mAmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
+                // When the home activity is the recents component and we are already minimized,
+                // then there is nothing to do here since home is already visible
+            } else {
+                mService.showRecentApps();
+            }
         }
     }
 
@@ -641,7 +646,7 @@
         return mMinimizedDock;
     }
 
-    private void checkMinimizeChanged(boolean animate) {
+    void checkMinimizeChanged(boolean animate) {
         if (mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() == null) {
             return;
         }
@@ -693,7 +698,7 @@
         final boolean imeChanged = clearImeAdjustAnimation();
         boolean minimizedChange = false;
         if (isHomeStackResizable()) {
-            notifyDockedStackMinimizedChanged(minimizedDock, true /* animate */,
+            notifyDockedStackMinimizedChanged(minimizedDock, animate,
                     true /* isHomeStackResizable */);
             minimizedChange = true;
         } else {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1f7caff..42f6065 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -406,6 +406,10 @@
                 }
                 break;
             default:
+                // TODO: Removing the child before reinserting requires the caller to provide a
+                //       position that takes into account the removed child (if the index of the
+                //       child < position, then the position should be adjusted). We should consider
+                //       doing this adjustment here and remove any adjustments in the callers.
                 mChildren.remove(child);
                 mChildren.add(position, child);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d96b27a..8b8a6d3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2782,6 +2782,13 @@
         mDockedStackCreateBounds = bounds;
     }
 
+    public void checkSplitScreenMinimizedChanged(boolean animate) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
+            displayContent.getDockedDividerController().checkMinimizeChanged(animate);
+        }
+    }
+
     public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
         final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
         return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 66c7293..286cc49 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
@@ -608,6 +609,10 @@
         if (transit == TRANSIT_NONE) {
             return TRANSIT_NONE;
         }
+        // Never update the transition for the wallpaper if we are just docking from recents
+        if (transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+            return TRANSIT_DOCK_TASK_FROM_RECENTS;
+        }
 
         // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 3557dc9..4020a52 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -172,4 +172,8 @@
     public long forceSecurityLogs() {
         return 0;
     }
+
+    @Override
+    public void setDefaultSmsApplication(ComponentName admin, String packageName) {
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0a6ff6d..6a468b1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -160,6 +160,7 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -206,6 +207,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.telephony.SmsApplication;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
@@ -219,6 +221,7 @@
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.UserRestrictionsUtils;
+import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import com.google.android.collect.Sets;
 
@@ -8217,6 +8220,16 @@
     }
 
     @Override
+    public void setDefaultSmsApplication(ComponentName admin, String packageName) {
+        Preconditions.checkNotNull(admin, "ComponentName is null");
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+        mInjector.binderWithCleanCallingIdentity(() ->
+                SmsApplication.setDefaultApplication(packageName, mContext));
+    }
+
+    @Override
     public boolean setApplicationRestrictionsManagingPackage(ComponentName admin,
             String packageName) {
         try {
@@ -8873,13 +8886,40 @@
         final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
                 && UserManager.isDeviceInDemoMode(mContext);
         final boolean leaveAllSystemAppsEnabled = (flags & LEAVE_ALL_SYSTEM_APPS_ENABLED) != 0;
+        final int targetSdkVersion;
+
         // Create user.
         UserHandle user = null;
         synchronized (this) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
+            final int callingUid = mInjector.binderGetCallingUid();
             final long id = mInjector.binderClearCallingIdentity();
             try {
+                targetSdkVersion = mInjector.getPackageManagerInternal().getUidTargetSdkVersion(
+                        callingUid);
+
+                // Return detail error code for checks inside
+                // UserManagerService.createUserInternalUnchecked.
+                DeviceStorageMonitorInternal deviceStorageMonitorInternal =
+                        LocalServices.getService(DeviceStorageMonitorInternal.class);
+                if (deviceStorageMonitorInternal.isMemoryLow()) {
+                    if (targetSdkVersion >= Build.VERSION_CODES.P) {
+                        throw new ServiceSpecificException(
+                                UserManager.USER_OPERATION_ERROR_LOW_STORAGE, "low device storage");
+                    } else {
+                        return null;
+                    }
+                }
+                if (!mUserManager.canAddMoreUsers()) {
+                    if (targetSdkVersion >= Build.VERSION_CODES.P) {
+                        throw new ServiceSpecificException(
+                                UserManager.USER_OPERATION_ERROR_MAX_USERS, "user limit reached");
+                    } else {
+                        return null;
+                    }
+                }
+
                 int userInfoFlags = 0;
                 if (ephemeral) {
                     userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
@@ -8903,7 +8943,12 @@
             }
         }
         if (user == null) {
-            return null;
+            if (targetSdkVersion >= Build.VERSION_CODES.P) {
+                throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN,
+                        "failed to create user");
+            } else {
+                return null;
+            }
         }
 
         final int userHandle = user.getIdentifier();
@@ -8949,7 +8994,12 @@
             return user;
         } catch (Throwable re) {
             mUserManager.removeUser(userHandle);
-            return null;
+            if (targetSdkVersion >= Build.VERSION_CODES.P) {
+                throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN,
+                        re.getMessage());
+            } else {
+                return null;
+            }
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -9030,24 +9080,24 @@
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
             Log.w(LOG_TAG, "Managed profile cannot be started in background");
-            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
+            return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
                 Log.w(LOG_TAG, "Cannot start more users in background");
-                return DevicePolicyManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
+                return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
             }
 
             if (mInjector.getIActivityManager().startUserInBackground(userId)) {
-                return DevicePolicyManager.USER_OPERATION_SUCCESS;
+                return UserManager.USER_OPERATION_SUCCESS;
             } else {
-                return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+                return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
         } catch (RemoteException e) {
             // Same process, should not happen.
-            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+            return UserManager.USER_OPERATION_ERROR_UNKNOWN;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -9065,7 +9115,7 @@
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
             Log.w(LOG_TAG, "Managed profile cannot be stopped");
-            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
+            return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         return stopUserUnchecked(userId);
@@ -9086,7 +9136,7 @@
 
         if (isManagedProfile(callingUserId)) {
             Log.w(LOG_TAG, "Managed profile cannot be logout");
-            return DevicePolicyManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
+            return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
@@ -9094,11 +9144,11 @@
             if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
                 Log.w(LOG_TAG, "Failed to switch to primary user");
                 // This should never happen as target user is UserHandle.USER_SYSTEM
-                return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+                return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
         } catch (RemoteException e) {
             // Same process, should not happen.
-            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+            return UserManager.USER_OPERATION_ERROR_UNKNOWN;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -9111,15 +9161,15 @@
         try {
             switch (mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)) {
                 case ActivityManager.USER_OP_SUCCESS:
-                    return DevicePolicyManager.USER_OPERATION_SUCCESS;
+                    return UserManager.USER_OPERATION_SUCCESS;
                 case ActivityManager.USER_OP_IS_CURRENT:
-                    return DevicePolicyManager.USER_OPERATION_ERROR_CURRENT_USER;
+                    return UserManager.USER_OPERATION_ERROR_CURRENT_USER;
                 default:
-                    return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+                    return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
         } catch (RemoteException e) {
             // Same process, should not happen.
-            return DevicePolicyManager.USER_OPERATION_ERROR_UNKNOWN;
+            return UserManager.USER_OPERATION_ERROR_UNKNOWN;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 02514b8..6abd30c 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -42,6 +42,7 @@
 
 import android.annotation.Nullable;
 import android.app.backup.BackupManager;
+import android.app.backup.BackupTransport;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -86,12 +87,6 @@
     private static final String PACKAGE_A = "some.package.a";
     private static final String PACKAGE_B = "some.package.b";
 
-    /**
-     * GMSCore depends on this constant so we define it here on top of the definition in {@link
-     * TransportManager} to verify this extra is passed
-     */
-    private static final String EXTRA_TRANSPORT_REGISTRATION = "transport_registration";
-
     @Mock private OnTransportRegisteredListener mListener;
     @Mock private TransportClientManager mTransportClientManager;
     private TransportData mTransportA1;
@@ -210,7 +205,8 @@
         verify(mTransportClientManager)
                 .getTransportClient(
                         eq(mTransportA1.getTransportComponent()),
-                        argThat(bundle -> bundle.getBoolean(EXTRA_TRANSPORT_REGISTRATION)),
+                        argThat(bundle ->
+                                bundle.getBoolean(BackupTransport.EXTRA_TRANSPORT_REGISTRATION)),
                         anyString());
     }
 
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 4ac00f0..c6a4f57 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -25,8 +25,10 @@
 
 import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
@@ -39,6 +41,7 @@
 import android.app.backup.RestoreSet;
 import android.os.Looper;
 import android.os.PowerManager;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.EventLogTags;
@@ -51,7 +54,9 @@
 import com.android.server.testing.FrameworkRobolectricTestRunner;
 import com.android.server.testing.SystemLoaderPackages;
 import com.android.server.testing.shadows.ShadowEventLog;
+import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -62,8 +67,14 @@
 import org.robolectric.shadows.ShadowApplication;
 import org.robolectric.shadows.ShadowLooper;
 
+import java.util.ArrayDeque;
+
 @RunWith(FrameworkRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, sdk = 26, shadows = ShadowEventLog.class)
+@Config(
+    manifest = Config.NONE,
+    sdk = 26,
+    shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class}
+)
 @SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class ActiveRestoreSessionTest {
@@ -78,6 +89,8 @@
     private ShadowApplication mShadowApplication;
     private PowerManager.WakeLock mWakeLock;
     private TransportData mTransport;
+    private long mToken1;
+    private long mToken2;
     private RestoreSet mRestoreSet1;
     private RestoreSet mRestoreSet2;
 
@@ -87,8 +100,10 @@
 
         mTransport = backupTransport();
 
-        mRestoreSet1 = new RestoreSet("name1", "device1", 1L);
-        mRestoreSet2 = new RestoreSet("name2", "device2", 2L);
+        mToken1 = 1L;
+        mRestoreSet1 = new RestoreSet("name1", "device1", mToken1);
+        mToken2 = 2L;
+        mRestoreSet2 = new RestoreSet("name2", "device2", mToken2);
 
         Application application = RuntimeEnvironment.application;
         mShadowApplication = shadowOf(application);
@@ -106,6 +121,12 @@
                 application.getPackageManager(),
                 backupHandler,
                 mWakeLock);
+        when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        ShadowPerformUnifiedRestoreTask.reset();
     }
 
     @Test
@@ -193,12 +214,244 @@
         assertThat(mWakeLock.isHeld()).isFalse();
     }
 
+    @Test
+    public void testRestoreAll() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        doCallRealMethod().when(mBackupManagerService).setRestoreInProgress(anyBoolean());
+        when(mBackupManagerService.isRestoreInProgress()).thenCallRealMethod();
+        TransportMock transportMock = setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(0);
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+        assertThat(mWakeLock.isHeld()).isFalse();
+        assertThat(mBackupManagerService.isRestoreInProgress()).isFalse();
+        // Verify it created the task properly
+        ShadowPerformUnifiedRestoreTask shadowTask =
+                ShadowPerformUnifiedRestoreTask.getLastCreated();
+        assertThat(shadowTask.isFullSystemRestore()).isTrue();
+        assertThat(shadowTask.getFilterSet()).isNull();
+        assertThat(shadowTask.getPackage()).isNull();
+    }
+
+    @Test
+    public void testRestoreAll_whenNoRestoreSets() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
+
+        int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(-1);
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+    }
+
+    @Test
+    public void testRestoreAll_whenSinglePackageSession() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(PACKAGE_1, mTransport, mRestoreSet1);
+
+        int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(-1);
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+    }
+
+    @Test
+    public void testRestoreAll_whenSessionEnded() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+        restoreSession.endRestoreSession();
+        mShadowBackupLooper.runToEndOfTasks();
+
+        expectThrows(
+                IllegalStateException.class,
+                () -> restoreSession.restoreAll(mToken1, mObserver, mMonitor));
+    }
+
+    @Test
+    public void testRestoreAll_whenTransportNotRegistered() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport.unregistered());
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(-1);
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+    }
+
+    @Test
+    public void testRestoreAll_whenRestoreInProgress_addsToPendingRestores() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        when(mBackupManagerService.isRestoreInProgress()).thenReturn(true);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        int result = restoreSession.restoreAll(mToken1, mObserver, mMonitor);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(0);
+        assertThat(mBackupManagerService.getPendingRestores()).hasSize(1);
+    }
+
+    @Test
+    public void testRestoreSome_for2Packages() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        TransportMock transportMock = setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        int result =
+                restoreSession.restoreSome(
+                        mToken1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(0);
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+        assertThat(mWakeLock.isHeld()).isFalse();
+        assertThat(mBackupManagerService.isRestoreInProgress()).isFalse();
+        ShadowPerformUnifiedRestoreTask shadowTask =
+                ShadowPerformUnifiedRestoreTask.getLastCreated();
+        assertThat(shadowTask.getFilterSet()).asList().containsExactly(PACKAGE_1, PACKAGE_2);
+        assertThat(shadowTask.getPackage()).isNull();
+    }
+
+    @Test
+    public void testRestoreSome_for2Packages_createsSystemRestoreTask() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        restoreSession.restoreSome(
+                mToken1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated().isFullSystemRestore()).isTrue();
+    }
+
+    @Test
+    public void testRestoreSome_for1Package() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        ShadowPerformUnifiedRestoreTask shadowTask =
+                ShadowPerformUnifiedRestoreTask.getLastCreated();
+        assertThat(shadowTask.getFilterSet()).asList().containsExactly(PACKAGE_1);
+        assertThat(shadowTask.getPackage()).isNull();
+    }
+
+    @Test
+    public void testRestoreSome_for1Package_createsNonSystemRestoreTask() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated().isFullSystemRestore())
+                .isFalse();
+    }
+
+    @Test
+    public void testRestoreSome_whenNoRestoreSets() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
+
+        int result =
+                restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(-1);
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+    }
+
+    @Test
+    public void testRestoreSome_whenSinglePackageSession() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(PACKAGE_1, mTransport, mRestoreSet1);
+
+        int result =
+                restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_2});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(-1);
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+    }
+
+    @Test
+    public void testRestoreSome_whenSessionEnded() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport);
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+        restoreSession.endRestoreSession();
+        mShadowBackupLooper.runToEndOfTasks();
+
+        expectThrows(
+                IllegalStateException.class,
+                () ->
+                        restoreSession.restoreSome(
+                                mToken1, mObserver, mMonitor, new String[] {PACKAGE_1}));
+    }
+
+    @Test
+    public void testRestoreSome_whenTransportNotRegistered() throws Exception {
+        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpTransport(mTransport.unregistered());
+        IRestoreSession restoreSession =
+                createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1);
+
+        int result =
+                restoreSession.restoreSome(mToken1, mObserver, mMonitor, new String[] {PACKAGE_1});
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(-1);
+        assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated()).isNull();
+    }
+
     private IRestoreSession createActiveRestoreSession(
             String packageName, TransportData transport) {
         return new ActiveRestoreSession(
                 mBackupManagerService, packageName, transport.transportName);
     }
 
+    private IRestoreSession createActiveRestoreSessionWithRestoreSets(
+            String packageName, TransportData transport, RestoreSet... restoreSets)
+            throws RemoteException {
+        ActiveRestoreSession restoreSession =
+                new ActiveRestoreSession(
+                        mBackupManagerService, packageName, transport.transportName);
+        restoreSession.setRestoreSets(restoreSets);
+        return restoreSession;
+    }
+
     private TransportMock setUpTransport(TransportData transport) throws Exception {
         return TransportTestUtils.setUpTransport(mTransportManager, transport);
     }
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
index 565c7e6..c00a61d 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -115,6 +115,7 @@
                     .thenReturn(transportDirName);
             when(transportManager.getTransportDirName(eq(transportComponent)))
                     .thenReturn(transportDirName);
+            when(transportManager.isTransportRegistered(eq(transportName))).thenReturn(true);
             // TODO: Mock rest of description methods
         } else {
             // Transport not registered
@@ -127,6 +128,7 @@
                     .thenThrow(TransportNotRegisteredException.class);
             when(transportManager.getTransportDirName(eq(transportComponent)))
                     .thenThrow(TransportNotRegisteredException.class);
+            when(transportManager.isTransportRegistered(eq(transportName))).thenReturn(false);
         }
         return transportMock;
     }
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
new file mode 100644
index 0000000..0f93c7a
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing.shadows;
+
+import android.annotation.Nullable;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IRestoreObserver;
+import android.content.pm.PackageInfo;
+
+import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(PerformUnifiedRestoreTask.class)
+public class ShadowPerformUnifiedRestoreTask {
+    @Nullable private static ShadowPerformUnifiedRestoreTask sLastShadow;
+
+    /**
+     * Retrieves the shadow for the last {@link PerformUnifiedRestoreTask} object created.
+     *
+     * @return The shadow or {@code null} if no object created since last {@link #reset()}.
+     */
+    @Nullable
+    public static ShadowPerformUnifiedRestoreTask getLastCreated() {
+        return sLastShadow;
+    }
+
+    public static void reset() {
+        sLastShadow = null;
+    }
+
+    private BackupManagerService mBackupManagerService;
+    @Nullable private PackageInfo mPackage;
+    private boolean mIsFullSystemRestore;
+    @Nullable private String[] mFilterSet;
+    private OnTaskFinishedListener mListener;
+
+    @Implementation
+    public void __constructor__(
+            BackupManagerService backupManagerService,
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long restoreSetToken,
+            @Nullable PackageInfo targetPackage,
+            int pmToken,
+            boolean isFullSystemRestore,
+            @Nullable String[] filterSet,
+            OnTaskFinishedListener listener) {
+        mBackupManagerService = backupManagerService;
+        mPackage = targetPackage;
+        mIsFullSystemRestore = isFullSystemRestore;
+        mFilterSet = filterSet;
+        mListener = listener;
+        sLastShadow = this;
+    }
+
+    @Implementation
+    public void execute() {
+        mBackupManagerService.setRestoreInProgress(false);
+        mListener.onFinished("ShadowPerformUnifiedRestoreTask.execute()");
+    }
+
+    public PackageInfo getPackage() {
+        return mPackage;
+    }
+
+    public String[] getFilterSet() {
+        return mFilterSet;
+    }
+
+    public boolean isFullSystemRestore() {
+        return mIsFullSystemRestore;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index bfc3133..fb1595e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -204,20 +204,4 @@
         verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
                 anyInt(), anyInt(), eq(record.info));
     }
-
-    @Test
-    public void testFinishingAfterDestroying() throws Exception {
-        assertFalse(mActivity.finishing);
-        mActivity.setState(DESTROYING, "testFinishingAfterDestroying");
-        assertTrue(mActivity.isState(DESTROYING));
-        assertTrue(mActivity.finishing);
-    }
-
-    @Test
-    public void testFinishingAfterDestroyed() throws Exception {
-        assertFalse(mActivity.finishing);
-        mActivity.setState(DESTROYED, "testFinishingAfterDestroyed");
-        assertTrue(mActivity.isState(DESTROYED));
-        assertTrue(mActivity.finishing);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index ce3528b..c62820e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -408,6 +408,10 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(display,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
@@ -415,6 +419,10 @@
         assertTrue(display.getStackAboveHome() == fullscreenStack1);
         display.moveHomeStackBehindStack(fullscreenStack2);
         assertTrue(display.getStackAboveHome() == fullscreenStack2);
+        display.moveHomeStackBehindStack(fullscreenStack4);
+        assertTrue(display.getStackAboveHome() == fullscreenStack4);
+        display.moveHomeStackBehindStack(fullscreenStack2);
+        assertTrue(display.getStackAboveHome() == fullscreenStack2);
     }
 
     private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index 8a461ac..fd8b319 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -39,6 +39,7 @@
 import java.io.File;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyStore;
+import java.util.Random;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
@@ -51,7 +52,7 @@
     private static final int TEST_GENERATION_ID = 3;
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String KEY_ALGORITHM = "AES";
-    private static final int KEY_SIZE_BYTES = 32;
+    private static final int KEY_SIZE_BYTES = RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE;
     private static final String KEY_WRAP_ALGORITHM = "AES/GCM/NoPadding";
     private static final String TEST_ALIAS = "karlin";
     private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey";
@@ -71,7 +72,7 @@
         mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
         mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
 
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        AndroidKeyStoreSecretKey platformKey = generatePlatformKey();
         mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
         mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
         mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
@@ -117,7 +118,21 @@
         assertArrayEquals(rawMaterial, unwrappedMaterial);
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    @Test
+    public void importKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception {
+        byte[] rawMaterial = randomBytes(KEY_SIZE_BYTES);
+        mRecoverableKeyGenerator.importKey(
+                mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial);
+
+        WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
+        Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM);
+        cipher.init(Cipher.DECRYPT_MODE, mDecryptKey.getKey(),
+                new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+        byte[] unwrappedMaterial = cipher.doFinal(wrappedKey.getKeyMaterial());
+        assertArrayEquals(rawMaterial, unwrappedMaterial);
+    }
+
+    private AndroidKeyStoreSecretKey generatePlatformKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -132,4 +147,10 @@
     private static byte[] getUtf8Bytes(String s) {
         return s.getBytes(StandardCharsets.UTF_8);
     }
+
+    private static byte[] randomBytes(int n) {
+        byte[] bytes = new byte[n];
+        new Random().nextBytes(bytes);
+        return bytes;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b67659d..199410c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -132,6 +132,7 @@
     private static final String TEST_ALIAS = "nick";
     private static final String TEST_ALIAS2 = "bob";
     private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
+    private static final int APPLICATION_KEY_SIZE_BYTES = 32;
     private static final int GENERATION_ID = 1;
     private static final byte[] NONCE = getUtf8Bytes("nonce");
     private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
@@ -209,6 +210,39 @@
     }
 
     @Test
+    public void importKey_storesTheKey() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
+
+        mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
+
+        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
+    public void importKey_throwsIfInvalidLength() throws Exception {
+        byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
+        try {
+            mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("not contain 256 bits");
+        }
+    }
+
+    @Test
+    public void importKey_throwsIfNullKey() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("not contain 256 bits");
+        }
+    }
+
+    @Test
     public void removeKey_removesAKey() throws Exception {
         int uid = Binder.getCallingUid();
         mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ddb4f04..33da403 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -138,6 +138,7 @@
     }
 
     public void systemReady() {
+	mSystemReady = true;
         if (mProxy != null) {
             try {
                 mProxy.queryPortStatus();
@@ -146,7 +147,6 @@
                         "ServiceStart: Failed to query port status", e);
             }
         }
-        mSystemReady = true;
     }
 
     public UsbPort[] getPorts() {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 297a6ea..956efc0 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -380,11 +380,10 @@
                 if (DEBUG) {
                     Log.d(TAG, "  type:0x" + Integer.toHexString(type));
                 }
-                if ((type >= UsbTerminalTypes.TERMINAL_IN_UNDEFINED
-                            && type <= UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY)
-                        || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
-                            && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)
-                        || (type == UsbTerminalTypes.TERMINAL_USB_STREAMING)) {
+                int terminalCategory = type & ~0xFF;
+                if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED
+                        && terminalCategory != UsbTerminalTypes.TERMINAL_OUT_UNDEFINED) {
+                    // If not explicitly a USB connection or output, it could be an input.
                     hasInput = true;
                     break;
                 }
@@ -419,10 +418,10 @@
                 if (DEBUG) {
                     Log.d(TAG, "  type:0x" + Integer.toHexString(type));
                 }
-                if ((type >= UsbTerminalTypes.TERMINAL_OUT_UNDEFINED
-                            && type <= UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER)
-                        || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
-                            && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)) {
+                int terminalCategory = type & ~0xFF;
+                if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED
+                        && terminalCategory != UsbTerminalTypes.TERMINAL_IN_UNDEFINED) {
+                    // If not explicitly a USB connection or input, it could be an output.
                     hasOutput = true;
                     break;
                 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
index 9bd6cb9..cbb899e 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
@@ -24,6 +24,7 @@
     private static final String TAG = "UsbTerminalTypes";
 
     // USB
+    public static final int TERMINAL_USB_UNDEFINED   = 0x0100;
     public static final int TERMINAL_USB_STREAMING   = 0x0101;
 
     // Inputs
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6798a83..96eb23d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1815,7 +1815,14 @@
             "check_pricing_with_carrier_data_roaming_bool";
 
     /**
-     * List of thresholds of RSRP for determining the display level of LTE signal bar.
+     * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -140 and -44, as explained in
+     * TS 136.133 9.1.4 - RSRP Measurement Report Mapping.
+     * <p>
+     * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside
+     * these boundaries is considered invalid.
      * @hide
      */
     public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
@@ -2136,12 +2143,10 @@
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 new int[] {
-                        -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
                         -128, /* SIGNAL_STRENGTH_POOR */
                         -118, /* SIGNAL_STRENGTH_MODERATE */
                         -108, /* SIGNAL_STRENGTH_GOOD */
                         -98,  /* SIGNAL_STRENGTH_GREAT */
-                        -44
                 });
     }
 
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 778ca77..47a7d5f 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -62,7 +62,9 @@
      */
     public static final int INVALID = Integer.MAX_VALUE;
 
-    private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+    private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
+    private static final int MAX_LTE_RSRP = -44;
+    private static final int MIN_LTE_RSRP = -140;
 
     /** Parameters reported by the Radio */
     private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
@@ -86,7 +88,8 @@
                             // onSignalStrengthResult.
     private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
 
-    // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+    // The threshold of LTE RSRP for determining the display level of LTE signal bar. Note that the
+    // min and max are fixed at MIN_LTE_RSRP (-140) and MAX_LTE_RSRP (-44).
     private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
 
     /**
@@ -324,7 +327,8 @@
 
         // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
         mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
-        mLteRsrp = ((mLteRsrp >= 44) && (mLteRsrp <= 140)) ? -mLteRsrp : SignalStrength.INVALID;
+        mLteRsrp = ((-mLteRsrp >= MIN_LTE_RSRP) && (-mLteRsrp <= MAX_LTE_RSRP)) ? -mLteRsrp
+                                : SignalStrength.INVALID;
         mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
         mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
                 : SignalStrength.INVALID;
@@ -740,24 +744,29 @@
      */
     public int getLteLevel() {
         /*
-         * TS 36.214 Physical Layer Section 5.1.3 TS 36.331 RRC RSSI = received
-         * signal + noise RSRP = reference signal dBm RSRQ = quality of signal
-         * dB= Number of Resource blocksxRSRP/RSSI SNR = gain=signal/noise ratio
-         * = -10log P1/P2 dB
+         * TS 36.214 Physical Layer Section 5.1.3
+         * TS 36.331 RRC
+         *
+         * RSSI = received signal + noise
+         * RSRP = reference signal dBm
+         * RSRQ = quality of signal dB = Number of Resource blocks*RSRP/RSSI
+         * SNR = gain = signal/noise ratio = -10log P1/P2 dB
          */
         int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
 
-        if (mLteRsrp > mLteRsrpThresholds[5]) {
-            rsrpIconLevel = -1;
-        } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+        if (mLteRsrp > MAX_LTE_RSRP || mLteRsrp < MIN_LTE_RSRP) {
+            if (mLteRsrp != INVALID) {
+                Log.wtf(LOG_TAG, "getLteLevel - invalid lte rsrp: mLteRsrp=" + mLteRsrp);
+            }
         } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
         } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
         } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[0] - mLteRsrpBoost)) {
             rsrpIconLevel = SIGNAL_STRENGTH_POOR;
-        } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+        } else {
             rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cdc1ba9..e0cc6e1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7183,18 +7183,16 @@
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
-     * @throws IllegalStateException if telephony service is unavailable.
      */
     public int getAndroidCarrierIdForSubscription() {
         try {
             ITelephony service = getITelephony();
-            return service.getSubscriptionCarrierId(getSubId());
+            if (service != null) {
+                return service.getSubscriptionCarrierId(getSubId());
+            }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
             ex.rethrowAsRuntimeException();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing.
-            throw new IllegalStateException("Telephony service unavailable");
         }
         return UNKNOWN_CARRIER_ID;
     }
@@ -7210,18 +7208,16 @@
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
-     * @throws IllegalStateException if telephony service is unavailable.
      */
     public CharSequence getAndroidCarrierNameForSubscription() {
         try {
             ITelephony service = getITelephony();
-            return service.getSubscriptionCarrierName(getSubId());
+            if (service != null) {
+                return service.getSubscriptionCarrierName(getSubId());
+            }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
             ex.rethrowAsRuntimeException();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing.
-            throw new IllegalStateException("Telephony service unavailable");
         }
         return null;
     }
diff --git a/tests/UsbTests/res/raw/readme.txt b/tests/UsbTests/res/raw/readme.txt
new file mode 100644
index 0000000..62b673c
--- /dev/null
+++ b/tests/UsbTests/res/raw/readme.txt
@@ -0,0 +1,35 @@
+The usbdescriptors_ files contain raw USB descriptors from the Google
+USB-C to 3.5mm adapter, with different loads connected to the 3.5mm
+jack.
+
+usbdescriptors_nothing.bin:
+ - The descriptors when the jack is disconnected.
+
+usbdescriptors_headphones.bin:
+ - The descriptors when the jack is connected to 32-ohm headphones,
+   no microphone.
+   The relevant output terminal is:
+        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
+        bTerminalID            15
+        wTerminalType      0x0302 Headphones
+   
+usbdescriptors_lineout.bin:
+ - The descriptors when the jack is connected to a PC line-in jack.
+   The relevant output terminal is:
+        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
+        bTerminalID            15
+        wTerminalType      0x0603 Line Connector
+
+usbdescriptors_headset.bin:
+ - The descriptors when a headset with microphone and low-impedance
+   headphones are connected.
+   The relevant input terminal is:
+        bDescriptorSubtype      2 (INPUT_TERMINAL)
+        bTerminalID             1
+        wTerminalType      0x0201 Microphone
+   The relevant output terminal is:
+        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
+        bTerminalID            15
+        wTerminalType      0x0302 Headphones
+
+
diff --git a/tests/UsbTests/res/raw/usbdescriptors_headphones.bin b/tests/UsbTests/res/raw/usbdescriptors_headphones.bin
new file mode 100644
index 0000000..e8f2932
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_headphones.bin
Binary files differ
diff --git a/tests/UsbTests/res/raw/usbdescriptors_headset.bin b/tests/UsbTests/res/raw/usbdescriptors_headset.bin
new file mode 100644
index 0000000..30eef2a
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_headset.bin
Binary files differ
diff --git a/tests/UsbTests/res/raw/usbdescriptors_lineout.bin b/tests/UsbTests/res/raw/usbdescriptors_lineout.bin
new file mode 100644
index 0000000..d540d33
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_lineout.bin
Binary files differ
diff --git a/tests/UsbTests/res/raw/usbdescriptors_nothing.bin b/tests/UsbTests/res/raw/usbdescriptors_nothing.bin
new file mode 100644
index 0000000..c318abf
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_nothing.bin
Binary files differ
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
new file mode 100644
index 0000000..f323952
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.google.common.io.ByteStreams;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.Exception;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link com.android.server.usb.descriptors.UsbDescriptorParser}
+ */
+@RunWith(AndroidJUnit4.class)
+public class UsbDescriptorParserTests {
+
+    public UsbDescriptorParser loadParser(int resource) {
+        Context c = InstrumentationRegistry.getContext();
+        Resources res = c.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(resource);
+        } catch (NotFoundException e) {
+            fail("Failed to load resource.");
+        }
+
+        byte[] descriptors = null;
+        try {
+            descriptors = ByteStreams.toByteArray(is);
+        } catch (IOException e) {
+            fail("Failed to convert descriptor strema to bytearray.");
+        }
+
+        // Testing same codepath as UsbHostManager.java:usbDeviceAdded
+        UsbDescriptorParser parser = new UsbDescriptorParser("test-usb-addr");
+        if (!parser.parseDescriptors(descriptors)) {
+            fail("failed to parse descriptors.");
+        }
+        return parser;
+    }
+
+    // A Headset has a microphone and a speaker and is a headset.
+    @Test
+    @SmallTest
+    public void testHeadsetDescriptorParser() {
+        UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_headset);
+        assertTrue(parser.hasInput());
+        assertTrue(parser.hasOutput());
+        assertTrue(parser.isInputHeadset());
+        assertTrue(parser.isOutputHeadset());
+    }
+
+    // Headphones have no microphones but are considered a headset.
+    @Test
+    @SmallTest
+    public void testHeadphoneDescriptorParser() {
+        UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_headphones);
+        assertFalse(parser.hasInput());
+        assertTrue(parser.hasOutput());
+        assertFalse(parser.isInputHeadset());
+        assertTrue(parser.isOutputHeadset());
+    }
+
+    // Line out has no microphones and aren't considered a headset.
+    @Test
+    @SmallTest
+    public void testLineoutDescriptorParser() {
+        UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_lineout);
+        assertFalse(parser.hasInput());
+        assertTrue(parser.hasOutput());
+        assertFalse(parser.isInputHeadset());
+        assertFalse(parser.isOutputHeadset());
+    }
+
+    // An HID-only device shouldn't be considered anything at all.
+    @Test
+    @SmallTest
+    public void testNothingDescriptorParser() {
+        UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_nothing);
+        assertFalse(parser.hasInput());
+        assertFalse(parser.hasOutput());
+        assertFalse(parser.isInputHeadset());
+        assertFalse(parser.isOutputHeadset());
+    }
+
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 66e0955..3e1ff6d 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -281,6 +281,7 @@
                         anyInt());
     }
 
+    @Test
     public void testCreateTwoTransformsWithSameSpis() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 7cffeea..1b6f882 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -26,11 +26,14 @@
 #include "ResourceUtils.h"
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
+#include "text/Utf8Iterator.h"
 #include "util/ImmutableMap.h"
 #include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlPullParser.h"
 
+using ::aapt::ResourceUtils::StringBuilder;
+using ::aapt::text::Utf8Iterator;
 using ::android::StringPiece;
 
 namespace aapt {
@@ -169,114 +172,212 @@
       config_(config),
       options_(options) {}
 
-/**
- * Build a string from XML that converts nested elements into Span objects.
- */
+// Base class Node for representing the various Spans and UntranslatableSections of an XML string.
+// This will be used to traverse and flatten the XML string into a single std::string, with all
+// Span and Untranslatable data maintained in parallel, as indices into the string.
+class Node {
+ public:
+  virtual ~Node() = default;
+
+  // Adds the given child node to this parent node's set of child nodes, moving ownership to the
+  // parent node as well.
+  // Returns a pointer to the child node that was added as a convenience.
+  template <typename T>
+  T* AddChild(std::unique_ptr<T> node) {
+    T* raw_ptr = node.get();
+    children.push_back(std::move(node));
+    return raw_ptr;
+  }
+
+  virtual void Build(StringBuilder* builder) const {
+    for (const auto& child : children) {
+      child->Build(builder);
+    }
+  }
+
+  std::vector<std::unique_ptr<Node>> children;
+};
+
+// A chunk of text in the XML string. This lives between other tags, such as XLIFF tags and Spans.
+class SegmentNode : public Node {
+ public:
+  std::string data;
+
+  void Build(StringBuilder* builder) const override {
+    builder->AppendText(data);
+  }
+};
+
+// A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
+class SpanNode : public Node {
+ public:
+  std::string name;
+
+  void Build(StringBuilder* builder) const override {
+    StringBuilder::SpanHandle span_handle = builder->StartSpan(name);
+    Node::Build(builder);
+    builder->EndSpan(span_handle);
+  }
+};
+
+// An XLIFF 'g' tag, which marks a section of the string as untranslatable.
+class UntranslatableNode : public Node {
+ public:
+  void Build(StringBuilder* builder) const override {
+    StringBuilder::UntranslatableHandle handle = builder->StartUntranslatable();
+    Node::Build(builder);
+    builder->EndUntranslatable(handle);
+  }
+};
+
+// Build a string from XML that converts nested elements into Span objects.
 bool ResourceParser::FlattenXmlSubtree(
     xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
     std::vector<UntranslatableSection>* out_untranslatable_sections) {
-  // Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply.
-  // The stack elements refer to the indices in out_style_string->spans.
-  // By first adding to the out_style_string->spans vector, and then using the stack to refer
-  // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>.
-  std::vector<size_t> span_stack;
-
-  // Clear the output variables.
-  out_raw_string->clear();
-  out_style_string->spans.clear();
-  out_untranslatable_sections->clear();
-
-  // The StringBuilder will concatenate the various segments of text which are initially
-  // separated by tags. It also handles unicode escape codes and quotations.
-  util::StringBuilder builder;
+  std::string raw_string;
+  std::string current_text;
 
   // The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
   Maybe<size_t> untranslatable_start_depth;
 
+  Node root;
+  std::vector<Node*> node_stack;
+  node_stack.push_back(&root);
+
+  bool saw_span_node = false;
+  SegmentNode* first_segment = nullptr;
+  SegmentNode* last_segment = nullptr;
+
   size_t depth = 1;
-  while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
+  while (depth > 0 && xml::XmlPullParser::IsGoodEvent(parser->Next())) {
     const xml::XmlPullParser::Event event = parser->event();
 
-    if (event == xml::XmlPullParser::Event::kStartElement) {
-      if (parser->element_namespace().empty()) {
-        // This is an HTML tag which we encode as a span. Add it to the span stack.
-        std::string span_name = parser->element_name();
-        const auto end_attr_iter = parser->end_attributes();
-        for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter; ++attr_iter) {
-          span_name += ";";
-          span_name += attr_iter->name;
-          span_name += "=";
-          span_name += attr_iter->value;
+    // First take care of any SegmentNodes that should be created.
+    if (event == xml::XmlPullParser::Event::kStartElement ||
+        event == xml::XmlPullParser::Event::kEndElement) {
+      if (!current_text.empty()) {
+        std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
+        segment_node->data = std::move(current_text);
+        last_segment = node_stack.back()->AddChild(std::move(segment_node));
+        if (first_segment == nullptr) {
+          first_segment = last_segment;
         }
+        current_text = {};
+      }
+    }
 
-        // Make sure the string is representable in our binary format.
-        if (builder.Utf16Len() > std::numeric_limits<uint32_t>::max()) {
-          diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
-                       << "style string '" << builder.ToString() << "' is too long");
-          return false;
-        }
+    switch (event) {
+      case xml::XmlPullParser::Event::kText: {
+        current_text += parser->text();
+        raw_string += parser->text();
+      } break;
 
-        out_style_string->spans.push_back(
-            Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
-        span_stack.push_back(out_style_string->spans.size() - 1);
-      } else if (parser->element_namespace() == sXliffNamespaceUri) {
-        if (parser->element_name() == "g") {
-          if (untranslatable_start_depth) {
-            // We've already encountered an <xliff:g> tag, and nested <xliff:g> tags are illegal.
-            diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
-                         << "illegal nested XLIFF 'g' tag");
-            return false;
-          } else {
-            // Mark the start of an untranslatable section. Use UTF8 indices/lengths.
-            untranslatable_start_depth = depth;
-            const size_t current_idx = builder.ToString().size();
-            out_untranslatable_sections->push_back(UntranslatableSection{current_idx, current_idx});
+      case xml::XmlPullParser::Event::kStartElement: {
+        if (parser->element_namespace().empty()) {
+          // This is an HTML tag which we encode as a span. Add it to the span stack.
+          std::unique_ptr<SpanNode> span_node = util::make_unique<SpanNode>();
+          span_node->name = parser->element_name();
+          const auto end_attr_iter = parser->end_attributes();
+          for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter;
+               ++attr_iter) {
+            span_node->name += ";";
+            span_node->name += attr_iter->name;
+            span_node->name += "=";
+            span_node->name += attr_iter->value;
           }
+
+          node_stack.push_back(node_stack.back()->AddChild(std::move(span_node)));
+          saw_span_node = true;
+        } else if (parser->element_namespace() == sXliffNamespaceUri) {
+          // This is an XLIFF tag, which is not encoded as a span.
+          if (parser->element_name() == "g") {
+            // Check that an 'untranslatable' tag is not already being processed. Nested
+            // <xliff:g> tags are illegal.
+            if (untranslatable_start_depth) {
+              diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+                           << "illegal nested XLIFF 'g' tag");
+              return false;
+            } else {
+              // Mark the beginning of an 'untranslatable' section.
+              untranslatable_start_depth = depth;
+              node_stack.push_back(
+                  node_stack.back()->AddChild(util::make_unique<UntranslatableNode>()));
+            }
+          } else {
+            // Ignore unknown XLIFF tags, but don't warn.
+            node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
+          }
+        } else {
+          // Besides XLIFF, any other namespaced tag is unsupported and ignored.
+          diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
+                      << "ignoring element '" << parser->element_name()
+                      << "' with unknown namespace '" << parser->element_namespace() << "'");
+          node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
         }
-        // Ignore other xliff tags, they get handled by other tools.
 
-      } else {
-        // Besides XLIFF, any other namespaced tag is unsupported and ignored.
-        diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
-                    << "ignoring element '" << parser->element_name()
-                    << "' with unknown namespace '" << parser->element_namespace() << "'");
-      }
+        // Enter one level inside the element.
+        depth++;
+      } break;
 
-      // Enter one level inside the element.
-      depth++;
-    } else if (event == xml::XmlPullParser::Event::kText) {
-      // Record both the raw text and append to the builder to deal with escape sequences
-      // and quotations.
-      out_raw_string->append(parser->text());
-      builder.Append(parser->text());
-    } else if (event == xml::XmlPullParser::Event::kEndElement) {
-      // Return one level from within the element.
-      depth--;
-      if (depth == 0) {
+      case xml::XmlPullParser::Event::kEndElement: {
+        // Return one level from within the element.
+        depth--;
+        if (depth == 0) {
+          break;
+        }
+
+        node_stack.pop_back();
+        if (untranslatable_start_depth == make_value(depth)) {
+          // This is the end of an untranslatable section.
+          untranslatable_start_depth = {};
+        }
+      } break;
+
+      default:
+        // ignore.
         break;
-      }
-
-      if (parser->element_namespace().empty()) {
-        // This is an HTML tag which we encode as a span. Update the span
-        // stack and pop the top entry.
-        Span& top_span = out_style_string->spans[span_stack.back()];
-        top_span.last_char = builder.Utf16Len() - 1;
-        span_stack.pop_back();
-      } else if (untranslatable_start_depth == make_value(depth)) {
-        // This is the end of an untranslatable section. Use UTF8 indices/lengths.
-        UntranslatableSection& untranslatable_section = out_untranslatable_sections->back();
-        untranslatable_section.end = builder.ToString().size();
-        untranslatable_start_depth = {};
-      }
-    } else if (event == xml::XmlPullParser::Event::kComment) {
-      // Ignore.
-    } else {
-      LOG(FATAL) << "unhandled XML event";
     }
   }
 
-  CHECK(span_stack.empty()) << "spans haven't been fully processed";
-  out_style_string->str = builder.ToString();
+  // Sanity check to make sure we processed all the nodes.
+  CHECK(node_stack.size() == 1u);
+  CHECK(node_stack.back() == &root);
+
+  if (!saw_span_node) {
+    // If there were no spans, we must treat this string a little differently (according to AAPT).
+    // Find and strip the leading whitespace from the first segment, and the trailing whitespace
+    // from the last segment.
+    if (first_segment != nullptr) {
+      // Trim leading whitespace.
+      StringPiece trimmed = util::TrimLeadingWhitespace(first_segment->data);
+      if (trimmed.size() != first_segment->data.size()) {
+        first_segment->data = trimmed.to_string();
+      }
+    }
+
+    if (last_segment != nullptr) {
+      // Trim trailing whitespace.
+      StringPiece trimmed = util::TrimTrailingWhitespace(last_segment->data);
+      if (trimmed.size() != last_segment->data.size()) {
+        last_segment->data = trimmed.to_string();
+      }
+    }
+  }
+
+  // Have the XML structure flatten itself into the StringBuilder. The StringBuilder will take
+  // care of recording the correctly adjusted Spans and UntranslatableSections.
+  StringBuilder builder;
+  root.Build(&builder);
+  if (!builder) {
+    diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) << builder.GetError());
+    return false;
+  }
+
+  ResourceUtils::FlattenedXmlString flattened_string = builder.GetFlattenedString();
+  *out_raw_string = std::move(raw_string);
+  *out_untranslatable_sections = std::move(flattened_string.untranslatable_sections);
+  out_style_string->str = std::move(flattened_string.text);
+  out_style_string->spans = std::move(flattened_string.spans);
   return true;
 }
 
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 618c8ed..c98c0b9 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -95,6 +95,16 @@
   ASSERT_THAT(str, NotNull());
   EXPECT_THAT(*str, StrValueEq("  hey there "));
   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
+
+  ASSERT_TRUE(TestParse(R"(<string name="bar">Isn\'t it cool?</string>)"));
+  str = test::GetValue<String>(&table_, "string/bar");
+  ASSERT_THAT(str, NotNull());
+  EXPECT_THAT(*str, StrValueEq("Isn't it cool?"));
+
+  ASSERT_TRUE(TestParse(R"(<string name="baz">"Isn't it cool?"</string>)"));
+  str = test::GetValue<String>(&table_, "string/baz");
+  ASSERT_THAT(str, NotNull());
+  EXPECT_THAT(*str, StrValueEq("Isn't it cool?"));
 }
 
 TEST_F(ResourceParserTest, ParseEscapedString) {
@@ -126,16 +136,16 @@
   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
   ASSERT_THAT(str, NotNull());
 
-  EXPECT_THAT(str->value->value, Eq("This is my aunt\u2019s fickle string"));
+  EXPECT_THAT(str->value->value, StrEq("This is my aunt\u2019s fickle string"));
   EXPECT_THAT(str->value->spans, SizeIs(2));
   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
 
-  EXPECT_THAT(*str->value->spans[0].name, Eq("b"));
-  EXPECT_THAT(str->value->spans[0].first_char, Eq(17u));
+  EXPECT_THAT(*str->value->spans[0].name, StrEq("b"));
+  EXPECT_THAT(str->value->spans[0].first_char, Eq(18u));
   EXPECT_THAT(str->value->spans[0].last_char, Eq(30u));
 
-  EXPECT_THAT(*str->value->spans[1].name, Eq("small"));
-  EXPECT_THAT(str->value->spans[1].first_char, Eq(24u));
+  EXPECT_THAT(*str->value->spans[1].name, StrEq("small"));
+  EXPECT_THAT(str->value->spans[1].first_char, Eq(25u));
   EXPECT_THAT(str->value->spans[1].last_char, Eq(30u));
 }
 
@@ -144,7 +154,7 @@
 
   String* str = test::GetValue<String>(&table_, "string/foo");
   ASSERT_THAT(str, NotNull());
-  EXPECT_THAT(*str->value, Eq("This is what I think"));
+  EXPECT_THAT(*str->value, StrEq("This is what I think"));
   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
 
   ASSERT_TRUE(TestParse(R"(<string name="foo2">"  This is what  I think  "</string>)"));
@@ -154,6 +164,25 @@
   EXPECT_THAT(*str, StrValueEq("  This is what  I think  "));
 }
 
+TEST_F(ResourceParserTest, ParseStyledStringWithWhitespace) {
+  std::string input = R"(<string name="foo">  <b> My <i> favorite</i> string </b>  </string>)";
+  ASSERT_TRUE(TestParse(input));
+
+  StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
+  ASSERT_THAT(str, NotNull());
+  EXPECT_THAT(str->value->value, StrEq("  My  favorite string  "));
+  EXPECT_THAT(str->untranslatable_sections, IsEmpty());
+
+  ASSERT_THAT(str->value->spans, SizeIs(2u));
+  EXPECT_THAT(*str->value->spans[0].name, StrEq("b"));
+  EXPECT_THAT(str->value->spans[0].first_char, Eq(1u));
+  EXPECT_THAT(str->value->spans[0].last_char, Eq(21u));
+
+  EXPECT_THAT(*str->value->spans[1].name, StrEq("i"));
+  EXPECT_THAT(str->value->spans[1].first_char, Eq(5u));
+  EXPECT_THAT(str->value->spans[1].last_char, Eq(13u));
+}
+
 TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
   std::string input = R"(
       <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
@@ -182,12 +211,9 @@
   String* str = test::GetValue<String>(&table_, "string/foo");
   ASSERT_THAT(str, NotNull());
   EXPECT_THAT(*str, StrValueEq("There are %1$d apples"));
-  ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
 
-  // We expect indices and lengths that span to include the whitespace
-  // before %1$d. This is due to how the StringBuilder withholds whitespace unless
-  // needed (to deal with line breaks, etc.).
-  EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
+  ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
+  EXPECT_THAT(str->untranslatable_sections[0].start, Eq(10u));
   EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
 }
 
@@ -199,14 +225,16 @@
 
   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
   ASSERT_THAT(str, NotNull());
-  EXPECT_THAT(str->value->value, Eq("There are %1$d apples"));
-  ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
+  EXPECT_THAT(str->value->value, Eq(" There are %1$d apples"));
 
-  // We expect indices and lengths that span to include the whitespace
-  // before %1$d. This is due to how the StringBuilder withholds whitespace unless
-  // needed (to deal with line breaks, etc.).
-  EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
-  EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
+  ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
+  EXPECT_THAT(str->untranslatable_sections[0].start, Eq(11u));
+  EXPECT_THAT(str->untranslatable_sections[0].end, Eq(15u));
+
+  ASSERT_THAT(str->value->spans, SizeIs(1u));
+  EXPECT_THAT(*str->value->spans[0].name, StrEq("b"));
+  EXPECT_THAT(str->value->spans[0].first_char, Eq(11u));
+  EXPECT_THAT(str->value->spans[0].last_char, Eq(14u));
 }
 
 TEST_F(ResourceParserTest, ParseNull) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 628466d..8fc3d65 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -18,17 +18,23 @@
 
 #include <sstream>
 
+#include "android-base/stringprintf.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/ResourceUtils.h"
 
 #include "NameMangler.h"
 #include "SdkConstants.h"
 #include "format/binary/ResourceTypeExtensions.h"
+#include "text/Unicode.h"
+#include "text/Utf8Iterator.h"
 #include "util/Files.h"
 #include "util/Util.h"
 
+using ::aapt::text::IsWhitespace;
+using ::aapt::text::Utf8Iterator;
 using ::android::StringPiece;
 using ::android::StringPiece16;
+using ::android::base::StringPrintf;
 
 namespace aapt {
 namespace ResourceUtils {
@@ -750,5 +756,195 @@
   return util::make_unique<BinaryPrimitive>(res_value);
 }
 
+// Converts the codepoint to UTF-8 and appends it to the string.
+static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
+  ssize_t len = utf32_to_utf8_length(&codepoint, 1);
+  if (len < 0) {
+    return false;
+  }
+
+  const size_t start_append_pos = output->size();
+
+  // Make room for the next character.
+  output->resize(output->size() + len);
+
+  char* dst = &*(output->begin() + start_append_pos);
+  utf32_to_utf8(&codepoint, 1, dst, len + 1);
+  return true;
+}
+
+// Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
+// Unicode codepoint represented by the escape sequence to the string.
+static bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
+  char32_t code = 0;
+  for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
+    char32_t codepoint = iter->Next();
+    char32_t a;
+    if (codepoint >= U'0' && codepoint <= U'9') {
+      a = codepoint - U'0';
+    } else if (codepoint >= U'a' && codepoint <= U'f') {
+      a = codepoint - U'a' + 10;
+    } else if (codepoint >= U'A' && codepoint <= U'F') {
+      a = codepoint - U'A' + 10;
+    } else {
+      return {};
+    }
+    code = (code << 4) | a;
+  }
+  return AppendCodepointToUtf8String(code, output);
+}
+
+StringBuilder::StringBuilder(bool preserve_spaces)
+    : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
+}
+
+StringBuilder& StringBuilder::AppendText(const std::string& text) {
+  if (!error_.empty()) {
+    return *this;
+  }
+
+  const size_t previous_len = xml_string_.text.size();
+  Utf8Iterator iter(text);
+  while (iter.HasNext()) {
+    char32_t codepoint = iter.Next();
+    if (!quote_ && text::IsWhitespace(codepoint)) {
+      if (!last_codepoint_was_space_) {
+        // Emit a space if it's the first.
+        xml_string_.text += ' ';
+        last_codepoint_was_space_ = true;
+      }
+
+      // Keep eating spaces.
+      continue;
+    }
+
+    // This is not a space.
+    last_codepoint_was_space_ = false;
+
+    if (codepoint == U'\\') {
+      if (iter.HasNext()) {
+        codepoint = iter.Next();
+        switch (codepoint) {
+          case U't':
+            xml_string_.text += '\t';
+            break;
+
+          case U'n':
+            xml_string_.text += '\n';
+            break;
+
+          case U'#':
+          case U'@':
+          case U'?':
+          case U'"':
+          case U'\'':
+          case U'\\':
+            xml_string_.text += static_cast<char>(codepoint);
+            break;
+
+          case U'u':
+            if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
+              error_ =
+                  StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
+              return *this;
+            }
+            break;
+
+          default:
+            // Ignore the escape character and just include the codepoint.
+            AppendCodepointToUtf8String(codepoint, &xml_string_.text);
+            break;
+        }
+      }
+    } else if (!preserve_spaces_ && codepoint == U'"') {
+      // Only toggle the quote state when we are not preserving spaces.
+      quote_ = !quote_;
+
+    } else if (!quote_ && codepoint == U'\'') {
+      // This should be escaped.
+      error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
+      return *this;
+
+    } else {
+      AppendCodepointToUtf8String(codepoint, &xml_string_.text);
+    }
+  }
+
+  // Accumulate the added string's UTF-16 length.
+  const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
+  const size_t utf8_length = xml_string_.text.size();
+  ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
+  if (len < 0) {
+    error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
+    return *this;
+  }
+
+  utf16_len_ += static_cast<uint32_t>(len);
+  return *this;
+}
+
+StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
+  if (!error_.empty()) {
+    return 0u;
+  }
+
+  // When we start a span, all state associated with whitespace truncation and quotation is ended.
+  ResetTextState();
+  Span span;
+  span.name = name;
+  span.first_char = span.last_char = utf16_len_;
+  xml_string_.spans.push_back(std::move(span));
+  return xml_string_.spans.size() - 1;
+}
+
+void StringBuilder::EndSpan(SpanHandle handle) {
+  if (!error_.empty()) {
+    return;
+  }
+
+  // When we end a span, all state associated with whitespace truncation and quotation is ended.
+  ResetTextState();
+  xml_string_.spans[handle].last_char = utf16_len_ - 1u;
+}
+
+StringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
+  if (!error_.empty()) {
+    return 0u;
+  }
+
+  UntranslatableSection section;
+  section.start = section.end = xml_string_.text.size();
+  xml_string_.untranslatable_sections.push_back(section);
+  return xml_string_.untranslatable_sections.size() - 1;
+}
+
+void StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
+  if (!error_.empty()) {
+    return;
+  }
+  xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
+}
+
+FlattenedXmlString StringBuilder::GetFlattenedString() const {
+  return xml_string_;
+}
+
+std::string StringBuilder::to_string() const {
+  return xml_string_.text;
+}
+
+StringBuilder::operator bool() const {
+  return error_.empty();
+}
+
+std::string StringBuilder::GetError() const {
+  return error_;
+}
+
+void StringBuilder::ResetTextState() {
+  quote_ = preserve_spaces_;
+  last_codepoint_was_space_ = false;
+}
+
 }  // namespace ResourceUtils
 }  // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f83d49e..7af2fe0 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -224,6 +224,95 @@
                                           const android::Res_value& res_value,
                                           StringPool* dst_pool);
 
+// A string flattened from an XML hierarchy, which maintains tags and untranslatable sections
+// in parallel data structures.
+struct FlattenedXmlString {
+  std::string text;
+  std::vector<UntranslatableSection> untranslatable_sections;
+  std::vector<Span> spans;
+};
+
+// Flattens an XML hierarchy into a FlattenedXmlString, formatting the text, escaping characters,
+// and removing whitespace, all while keeping the untranslatable sections and spans in sync with the
+// transformations.
+//
+// Specifically, the StringBuilder will handle escaped characters like \t, \n, \\, \', etc.
+// Single quotes *must* be escaped, unless within a pair of double-quotes.
+// Pairs of double-quotes disable whitespace stripping of the enclosed text.
+// Unicode escape codes (\u0049) are interpreted and the represented Unicode character is inserted.
+//
+// A NOTE ON WHITESPACE:
+//
+// When preserve_spaces is false, and when text is not enclosed within double-quotes,
+// StringBuilder replaces a series of whitespace with a single space character. This happens at the
+// start and end of the string as well, so leading and trailing whitespace is possible.
+//
+// When a Span is started or stopped, the whitespace counter is reset, meaning if whitespace
+// is encountered directly after the span, it will be emitted. This leads to situations like the
+// following: "This <b> is </b> spaced" -> "This  is  spaced". Without spans, this would be properly
+// compressed: "This  is  spaced" -> "This is spaced".
+//
+// Untranslatable sections do not have the same problem:
+// "This <xliff:g> is </xliff:g> not spaced" -> "This is not spaced".
+//
+// NOTE: This is all the way it is because AAPT1 did it this way. Maintaining backwards
+// compatibility is important.
+//
+class StringBuilder {
+ public:
+  using SpanHandle = size_t;
+  using UntranslatableHandle = size_t;
+
+  // Creates a StringBuilder. If preserve_spaces is true, whitespace removal is not performed, and
+  // single quotations can be used without escaping them.
+  explicit StringBuilder(bool preserve_spaces = false);
+
+  // Appends a chunk of text.
+  StringBuilder& AppendText(const std::string& text);
+
+  // Starts a Span (tag) with the given name. The name is expected to be of the form:
+  //  "tag_name;attr1=value;attr2=value;"
+  // Which is how Spans are encoded in the ResStringPool.
+  // To end the span, pass back the SpanHandle received from this method to the EndSpan() method.
+  SpanHandle StartSpan(const std::string& name);
+
+  // Ends a Span (tag). Pass in the matching SpanHandle previously obtained from StartSpan().
+  void EndSpan(SpanHandle handle);
+
+  // Starts an Untranslatable section.
+  // To end the section, pass back the UntranslatableHandle received from this method to
+  // the EndUntranslatable() method.
+  UntranslatableHandle StartUntranslatable();
+
+  // Ends an Untranslatable section. Pass in the matching UntranslatableHandle previously obtained
+  // from StartUntranslatable().
+  void EndUntranslatable(UntranslatableHandle handle);
+
+  // Returns the flattened XML string, with all spans and untranslatable sections encoded as
+  // parallel data structures.
+  FlattenedXmlString GetFlattenedString() const;
+
+  // Returns just the flattened XML text, with no spans or untranslatable sections.
+  std::string to_string() const;
+
+  // Returns true if there was no error.
+  explicit operator bool() const;
+
+  std::string GetError() const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StringBuilder);
+
+  void ResetTextState();
+
+  std::string error_;
+  FlattenedXmlString xml_string_;
+  uint32_t utf16_len_ = 0u;
+  bool preserve_spaces_;
+  bool quote_;
+  bool last_codepoint_was_space_ = false;
+};
+
 }  // namespace ResourceUtils
 }  // namespace aapt
 
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index cb786d3..11f3fa3 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -212,4 +212,48 @@
               Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));
 }
 
+TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys ")
+                  .AppendText(" this is so cool ")
+                  .to_string(),
+              Eq(" hey guys this is so cool "));
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText(" \" wow,  so many \t ")
+                  .AppendText("spaces. \"what? ")
+                  .to_string(),
+              Eq("  wow,  so many \t spaces. what? "));
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("  where \t ")
+                  .AppendText(" \nis the pie?")
+                  .to_string(),
+              Eq(" where is the pie?"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderEscaping) {
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("hey guys\\n ")
+                  .AppendText(" this \\t is so\\\\ cool")
+                  .to_string(),
+              Eq("hey guys\n this \t is so\\ cool"));
+  EXPECT_THAT(ResourceUtils::StringBuilder().AppendText("\\@\\?\\#\\\\\\'").to_string(),
+              Eq("@?#\\\'"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderMisplacedQuote) {
+  ResourceUtils::StringBuilder builder;
+  EXPECT_FALSE(builder.AppendText("they're coming!"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderUnicodeCodes) {
+  EXPECT_THAT(ResourceUtils::StringBuilder().AppendText("\\u00AF\\u0AF0 woah").to_string(),
+              Eq("\u00AF\u0AF0 woah"));
+  EXPECT_FALSE(ResourceUtils::StringBuilder().AppendText("\\u00 yo"));
+}
+
+TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
+  EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
+              Eq("\""));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index 345cc95..781b9fe 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -25,13 +25,17 @@
 #include "androidfw/ResourceTypes.h"
 #include "utils/misc.h"
 
+#include "ResourceUtils.h"
 #include "SdkConstants.h"
+#include "ValueVisitor.h"
 #include "format/binary/ChunkWriter.h"
 #include "format/binary/ResourceTypeExtensions.h"
 #include "xml/XmlDom.h"
 
 using namespace android;
 
+using ::aapt::ResourceUtils::StringBuilder;
+
 namespace aapt {
 
 namespace {
@@ -88,9 +92,9 @@
     ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
 
     // Process plain strings to make sure they get properly escaped.
-    util::StringBuilder builder;
-    builder.Append(node->text);
-    AddString(builder.ToString(), kLowPriority, &flat_text->data);
+    StringBuilder builder;
+    builder.AppendText(node->text);
+    AddString(builder.to_string(), kLowPriority, &flat_text->data);
 
     writer.Finish();
   }
@@ -153,6 +157,9 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
 
+  // We are adding strings to a StringPool whose strings will be sorted and merged with other
+  // string pools. That means we can't encode the ID of a string directly. Instead, we defer the
+  // writing of the ID here, until after the StringPool is merged and sorted.
   void AddString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest,
                  bool treat_empty_string_as_null = false) {
     if (str.empty() && treat_empty_string_as_null) {
@@ -164,6 +171,9 @@
     }
   }
 
+  // We are adding strings to a StringPool whose strings will be sorted and merged with other
+  // string pools. That means we can't encode the ID of a string directly. Instead, we defer the
+  // writing of the ID here, until after the StringPool is merged and sorted.
   void AddString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
     string_refs.push_back(StringFlattenDest{ref, dest});
   }
@@ -248,30 +258,39 @@
         AddString(name_ref, &flat_attr->name);
       }
 
-      // Process plain strings to make sure they get properly escaped.
-      StringPiece raw_value = xml_attr->value;
-
-      util::StringBuilder str_builder(true /*preserve_spaces*/);
-      str_builder.Append(xml_attr->value);
-
-      if (!options_.keep_raw_values) {
-        raw_value = str_builder.ToString();
-      }
-
-      if (options_.keep_raw_values || !xml_attr->compiled_value) {
-        // Keep raw values if the value is not compiled or
-        // if we're building a static library (need symbols).
-        AddString(raw_value, kLowPriority, &flat_attr->rawValue);
-      }
-
-      if (xml_attr->compiled_value) {
-        CHECK(xml_attr->compiled_value->Flatten(&flat_attr->typedValue));
+      std::string processed_str;
+      Maybe<StringPiece> compiled_text;
+      if (xml_attr->compiled_value != nullptr) {
+        // Make sure we're not flattening a String. A String can be referencing a string from
+        // a different StringPool than we're using here to build the binary XML.
+        String* string_value = ValueCast<String>(xml_attr->compiled_value.get());
+        if (string_value != nullptr) {
+          // Mark the String's text as needing to be serialized.
+          compiled_text = StringPiece(*string_value->value);
+        } else {
+          // Serialize this compiled value safely.
+          CHECK(xml_attr->compiled_value->Flatten(&flat_attr->typedValue));
+        }
       } else {
-        // Flatten as a regular string type.
-        flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
+        // There is no compiled value, so treat the raw string as compiled, once it is processed to
+        // make sure escape sequences are properly interpreted.
+        processed_str =
+            StringBuilder(true /*preserve_spaces*/).AppendText(xml_attr->value).to_string();
+        compiled_text = StringPiece(processed_str);
+      }
 
-        AddString(str_builder.ToString(), kLowPriority,
-                  (ResStringPool_ref*)&flat_attr->typedValue.data);
+      if (compiled_text) {
+        // Write out the compiled text and raw_text.
+        flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
+        AddString(compiled_text.value(), kLowPriority,
+                  reinterpret_cast<ResStringPool_ref*>(&flat_attr->typedValue.data));
+        if (options_.keep_raw_values) {
+          AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
+        } else {
+          AddString(compiled_text.value(), kLowPriority, &flat_attr->rawValue);
+        }
+      } else if (options_.keep_raw_values && !xml_attr->value.empty()) {
+        AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
       }
 
       flat_attr->typedValue.size = util::HostToDevice16(sizeof(flat_attr->typedValue));
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index 0450f6c..08243fe 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -286,4 +286,92 @@
   EXPECT_THAT(tree.getText(&len), StrEq(u"\\d{5}"));
 }
 
+TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
+
+  // Raw values are kept when encoding an attribute with no compiled value, regardless of option.
+  XmlFlattenerOptions options;
+  options.keep_raw_values = false;
+
+  android::ResXMLTree tree;
+  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+  while (tree.next() != android::ResXMLTree::START_TAG) {
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+  }
+
+  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
+  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledStringValuePreservesRawValue) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
+  doc->root->attributes[0].compiled_value =
+      util::make_unique<String>(doc->string_pool.MakeRef("bar"));
+
+  // Raw values are kept when encoding a string anyways.
+  XmlFlattenerOptions options;
+  options.keep_raw_values = false;
+
+  android::ResXMLTree tree;
+  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+  while (tree.next() != android::ResXMLTree::START_TAG) {
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+  }
+
+  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
+  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
+  doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
+
+  XmlFlattenerOptions options;
+  options.keep_raw_values = false;
+
+  android::ResXMLTree tree;
+  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+  while (tree.next() != android::ResXMLTree::START_TAG) {
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+  }
+
+  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
+  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
+  doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
+
+  XmlFlattenerOptions options;
+  options.keep_raw_values = true;
+
+  android::ResXMLTree tree;
+  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+  while (tree.next() != android::ResXMLTree::START_TAG) {
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+  }
+
+  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
+
+  size_t len;
+  EXPECT_THAT(tree.getAttributeStringValue(0, &len), StrEq(u"true"));
+
+  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index b8f8804..9aaaa69 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -30,6 +30,7 @@
 #include "util/Util.h"
 #include "xml/XmlUtil.h"
 
+using ::aapt::ResourceUtils::StringBuilder;
 using ::android::StringPiece;
 
 namespace aapt {
@@ -133,10 +134,11 @@
 
       // If we could not parse as any specific type, try a basic STRING.
       if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
-        util::StringBuilder string_builder;
-        string_builder.Append(*raw_string->value);
+        StringBuilder string_builder;
+        string_builder.AppendText(*raw_string->value);
         if (string_builder) {
-          transformed = util::make_unique<String>(string_pool_->MakeRef(string_builder.ToString()));
+          transformed =
+              util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
         }
       }
 
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index e42145d..d1c9ca1 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -76,6 +76,34 @@
   return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
 }
 
+StringPiece TrimLeadingWhitespace(const StringPiece& str) {
+  if (str.size() == 0 || str.data() == nullptr) {
+    return str;
+  }
+
+  const char* start = str.data();
+  const char* end = start + str.length();
+
+  while (start != end && isspace(*start)) {
+    start++;
+  }
+  return StringPiece(start, end - start);
+}
+
+StringPiece TrimTrailingWhitespace(const StringPiece& str) {
+  if (str.size() == 0 || str.data() == nullptr) {
+    return str;
+  }
+
+  const char* start = str.data();
+  const char* end = start + str.length();
+
+  while (end != start && isspace(*(end - 1))) {
+    end--;
+  }
+  return StringPiece(start, end - start);
+}
+
 StringPiece TrimWhitespace(const StringPiece& str) {
   if (str.size() == 0 || str.data() == nullptr) {
     return str;
@@ -269,162 +297,6 @@
   return true;
 }
 
-static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
-  ssize_t len = utf32_to_utf8_length(&codepoint, 1);
-  if (len < 0) {
-    return false;
-  }
-
-  const size_t start_append_pos = output->size();
-
-  // Make room for the next character.
-  output->resize(output->size() + len);
-
-  char* dst = &*(output->begin() + start_append_pos);
-  utf32_to_utf8(&codepoint, 1, dst, len + 1);
-  return true;
-}
-
-static bool AppendUnicodeCodepoint(Utf8Iterator* iter, std::string* output) {
-  char32_t code = 0;
-  for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
-    char32_t codepoint = iter->Next();
-    char32_t a;
-    if (codepoint >= U'0' && codepoint <= U'9') {
-      a = codepoint - U'0';
-    } else if (codepoint >= U'a' && codepoint <= U'f') {
-      a = codepoint - U'a' + 10;
-    } else if (codepoint >= U'A' && codepoint <= U'F') {
-      a = codepoint - U'A' + 10;
-    } else {
-      return {};
-    }
-    code = (code << 4) | a;
-  }
-  return AppendCodepointToUtf8String(code, output);
-}
-
-static bool IsCodepointSpace(char32_t codepoint) {
-  if (static_cast<uint32_t>(codepoint) & 0xffffff00u) {
-    return false;
-  }
-  return isspace(static_cast<char>(codepoint));
-}
-
-StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces) {
-}
-
-StringBuilder& StringBuilder::Append(const StringPiece& str) {
-  if (!error_.empty()) {
-    return *this;
-  }
-
-  // Where the new data will be appended to.
-  const size_t new_data_index = str_.size();
-
-  Utf8Iterator iter(str);
-  while (iter.HasNext()) {
-    const char32_t codepoint = iter.Next();
-
-    if (last_char_was_escape_) {
-      switch (codepoint) {
-        case U't':
-          str_ += '\t';
-          break;
-
-        case U'n':
-          str_ += '\n';
-          break;
-
-        case U'#':
-        case U'@':
-        case U'?':
-        case U'"':
-        case U'\'':
-        case U'\\':
-          str_ += static_cast<char>(codepoint);
-          break;
-
-        case U'u':
-          if (!AppendUnicodeCodepoint(&iter, &str_)) {
-            error_ = "invalid unicode escape sequence";
-            return *this;
-          }
-          break;
-
-        default:
-          // Ignore the escape character and just include the codepoint.
-          AppendCodepointToUtf8String(codepoint, &str_);
-          break;
-      }
-      last_char_was_escape_ = false;
-
-    } else if (!preserve_spaces_ && codepoint == U'"') {
-      if (!quote_ && trailing_space_) {
-        // We found an opening quote, and we have trailing space, so we should append that
-        // space now.
-        if (trailing_space_) {
-          // We had trailing whitespace, so replace with a single space.
-          if (!str_.empty()) {
-            str_ += ' ';
-          }
-          trailing_space_ = false;
-        }
-      }
-      quote_ = !quote_;
-
-    } else if (!preserve_spaces_ && codepoint == U'\'' && !quote_) {
-      // This should be escaped.
-      error_ = "unescaped apostrophe";
-      return *this;
-
-    } else if (codepoint == U'\\') {
-      // This is an escape sequence, convert to the real value.
-      if (!quote_ && trailing_space_) {
-        // We had trailing whitespace, so
-        // replace with a single space.
-        if (!str_.empty()) {
-          str_ += ' ';
-        }
-        trailing_space_ = false;
-      }
-      last_char_was_escape_ = true;
-    } else {
-      if (preserve_spaces_ || quote_) {
-        // Quotes mean everything is taken, including whitespace.
-        AppendCodepointToUtf8String(codepoint, &str_);
-      } else {
-        // This is not quoted text, so we will accumulate whitespace and only emit a single
-        // character of whitespace if it is followed by a non-whitespace character.
-        if (IsCodepointSpace(codepoint)) {
-          // We found whitespace.
-          trailing_space_ = true;
-        } else {
-          if (trailing_space_) {
-            // We saw trailing space before, so replace all
-            // that trailing space with one space.
-            if (!str_.empty()) {
-              str_ += ' ';
-            }
-            trailing_space_ = false;
-          }
-          AppendCodepointToUtf8String(codepoint, &str_);
-        }
-      }
-    }
-  }
-
-  // Accumulate the added string's UTF-16 length.
-  ssize_t len = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str_.data()) + new_data_index,
-                                     str_.size() - new_data_index);
-  if (len < 0) {
-    error_ = "invalid unicode code point";
-    return *this;
-  }
-  utf16_len_ += len;
-  return *this;
-}
-
 std::u16string Utf8ToUtf16(const StringPiece& utf8) {
   ssize_t utf16_length = utf8_to_utf16_length(
       reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 7c949b90..0eb35d1 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -59,7 +59,15 @@
 // Returns true if the string ends with suffix.
 bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffix);
 
-// Creates a new StringPiece16 that points to a substring of the original string without leading or
+// Creates a new StringPiece that points to a substring of the original string without leading
+// whitespace.
+android::StringPiece TrimLeadingWhitespace(const android::StringPiece& str);
+
+// Creates a new StringPiece that points to a substring of the original string without trailing
+// whitespace.
+android::StringPiece TrimTrailingWhitespace(const android::StringPiece& str);
+
+// Creates a new StringPiece that points to a substring of the original string without leading or
 // trailing whitespace.
 android::StringPiece TrimWhitespace(const android::StringPiece& str);
 
@@ -141,9 +149,12 @@
 // break the string interpolation.
 bool VerifyJavaStringFormat(const android::StringPiece& str);
 
+bool AppendStyledString(const android::StringPiece& input, bool preserve_spaces,
+                        std::string* out_str, std::string* out_error);
+
 class StringBuilder {
  public:
-  explicit StringBuilder(bool preserve_spaces = false);
+  StringBuilder() = default;
 
   StringBuilder& Append(const android::StringPiece& str);
   const std::string& ToString() const;
@@ -158,7 +169,6 @@
   explicit operator bool() const;
 
  private:
-  bool preserve_spaces_;
   std::string str_;
   size_t utf16_len_ = 0;
   bool quote_ = false;
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 2d1242a..d4e3bec 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -41,45 +41,6 @@
   EXPECT_TRUE(util::StartsWith("hello.xml", "he"));
 }
 
-TEST(UtilTest, StringBuilderSplitEscapeSequence) {
-  EXPECT_THAT(util::StringBuilder().Append("this is a new\\").Append("nline.").ToString(),
-              Eq("this is a new\nline."));
-}
-
-TEST(UtilTest, StringBuilderWhitespaceRemoval) {
-  EXPECT_THAT(util::StringBuilder().Append("    hey guys ").Append(" this is so cool ").ToString(),
-              Eq("hey guys this is so cool"));
-  EXPECT_THAT(
-      util::StringBuilder().Append(" \" wow,  so many \t ").Append("spaces. \"what? ").ToString(),
-      Eq(" wow,  so many \t spaces. what?"));
-  EXPECT_THAT(util::StringBuilder().Append("  where \t ").Append(" \nis the pie?").ToString(),
-              Eq("where is the pie?"));
-}
-
-TEST(UtilTest, StringBuilderEscaping) {
-  EXPECT_THAT(util::StringBuilder()
-                  .Append("    hey guys\\n ")
-                  .Append(" this \\t is so\\\\ cool ")
-                  .ToString(),
-              Eq("hey guys\n this \t is so\\ cool"));
-  EXPECT_THAT(util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString(), Eq("@?#\\\'"));
-}
-
-TEST(UtilTest, StringBuilderMisplacedQuote) {
-  util::StringBuilder builder;
-  EXPECT_FALSE(builder.Append("they're coming!"));
-}
-
-TEST(UtilTest, StringBuilderUnicodeCodes) {
-  EXPECT_THAT(util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString(),
-              Eq("\u00AF\u0AF0 woah"));
-  EXPECT_FALSE(util::StringBuilder().Append("\\u00 yo"));
-}
-
-TEST(UtilTest, StringBuilderPreserveSpaces) {
-  EXPECT_THAT(util::StringBuilder(true /*preserve_spaces*/).Append("\"").ToString(), Eq("\""));
-}
-
 TEST(UtilTest, TokenizeInput) {
   auto tokenizer = util::Tokenize(StringPiece("this| is|the|end"), '|');
   auto iter = tokenizer.begin();
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 7b748ce..b6cd086 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -248,8 +248,14 @@
 
       android::Res_value res_value;
       if (parser->getAttributeValue(i, &res_value) > 0) {
-        attr.compiled_value = ResourceUtils::ParseBinaryResValue(
-            ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+        // Only compile the value if it is not a string, or it is a string that differs from
+        // the raw attribute value.
+        int32_t raw_value_idx = parser->getAttributeValueStringID(i);
+        if (res_value.dataType != android::Res_value::TYPE_STRING || raw_value_idx < 0 ||
+            static_cast<uint32_t>(raw_value_idx) != res_value.data) {
+          attr.compiled_value = ResourceUtils::ParseBinaryResValue(
+              ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+        }
       }
 
       el->attributes.push_back(std::move(attr));
@@ -262,8 +268,8 @@
   // an enum, which causes errors when qualifying it with android::
   using namespace android;
 
-  StringPool string_pool;
-  std::unique_ptr<Element> root;
+  std::unique_ptr<XmlResource> xml_resource = util::make_unique<XmlResource>();
+
   std::stack<Element*> node_stack;
   std::unique_ptr<Element> pending_element;
 
@@ -322,12 +328,12 @@
         }
 
         Element* this_el = el.get();
-        CopyAttributes(el.get(), &tree, &string_pool);
+        CopyAttributes(el.get(), &tree, &xml_resource->string_pool);
 
         if (!node_stack.empty()) {
           node_stack.top()->AppendChild(std::move(el));
         } else {
-          root = std::move(el);
+          xml_resource->root = std::move(el);
         }
         node_stack.push(this_el);
         break;
@@ -359,7 +365,7 @@
         break;
     }
   }
-  return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
+  return xml_resource;
 }
 
 std::unique_ptr<XmlResource> XmlResource::Clone() const {
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index e7b269a..9183918 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -411,6 +411,11 @@
             case SECTION_LOG:
                 printf("    new LogSection(%d, %s),\n", field->number(), s.args().c_str());
                 break;
+            case SECTION_GZIP:
+                printf("    new GZipSection(%d,", field->number());
+                splitAndPrint(s.args());
+                printf(" NULL),\n");
+                break;
         }
     }
     printf("    NULL };\n");