Merge "Resupport long compare"
diff --git a/Android.mk b/Android.mk
index 3b8d6a8..58e21ff 100644
--- a/Android.mk
+++ b/Android.mk
@@ -793,6 +793,8 @@
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto) \
     $(call all-proto-files-under, cmds/statsd/src)
+# b/72714520
+LOCAL_ERROR_PRONE_FLAGS := -Xep:MissingOverride:OFF
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 # ====  java proto device library (for test only)  ==============================
diff --git a/api/current.txt b/api/current.txt
index df18f10..9bc3150 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36640,6 +36640,7 @@
     field public static final deprecated java.lang.String RADIO_NFC = "nfc";
     field public static final deprecated java.lang.String RADIO_WIFI = "wifi";
     field public static final java.lang.String RINGTONE = "ringtone";
+    field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode";
     field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness";
     field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
     field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1
@@ -38449,6 +38450,7 @@
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isStrongBoxBacked();
     method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38476,6 +38478,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -38567,6 +38570,8 @@
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
+    method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38585,6 +38590,8 @@
     method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -40645,6 +40652,7 @@
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+    field public static final int PROPERTY_RTT = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
@@ -49795,6 +49803,8 @@
     method public boolean isFieldClassificationEnabled();
     method public void notifyValueChanged(android.view.View);
     method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+    method public void notifyViewClicked(android.view.View);
+    method public void notifyViewClicked(android.view.View, int);
     method public void notifyViewEntered(android.view.View);
     method public void notifyViewEntered(android.view.View, int, android.graphics.Rect);
     method public void notifyViewExited(android.view.View);
@@ -50756,41 +50766,37 @@
   }
 
   public class TracingConfig {
-    ctor public TracingConfig(int);
-    ctor public TracingConfig(int, java.lang.String, int);
-    method public java.lang.String getCustomCategoryPattern();
-    method public int getPresetCategories();
+    method public java.util.List<java.lang.String> getCustomIncludedCategories();
+    method public int getPredefinedCategories();
     method public int getTracingMode();
-    field public static final int CATEGORIES_FRAME_VIEWER = 4; // 0x4
-    field public static final int CATEGORIES_INPUT_LATENCY = 1; // 0x1
-    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3; // 0x3
-    field public static final int CATEGORIES_NONE = -1; // 0xffffffff
-    field public static final int CATEGORIES_RENDERING = 2; // 0x2
-    field public static final int CATEGORIES_WEB_DEVELOPER = 0; // 0x0
+    field public static final int CATEGORIES_ALL = 1; // 0x1
+    field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+    field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+    field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+    field public static final int CATEGORIES_NONE = 0; // 0x0
+    field public static final int CATEGORIES_RENDERING = 16; // 0x10
+    field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
     field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
-    field public static final int RECORD_TO_CONSOLE = 3; // 0x3
     field public static final int RECORD_UNTIL_FULL = 0; // 0x0
     field public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; // 0x2
   }
 
+  public static class TracingConfig.Builder {
+    ctor public TracingConfig.Builder();
+    method public android.webkit.TracingConfig.Builder addCategories(int...);
+    method public android.webkit.TracingConfig.Builder addCategories(java.lang.String...);
+    method public android.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String>);
+    method public android.webkit.TracingConfig build();
+    method public android.webkit.TracingConfig.Builder setTracingMode(int);
+  }
+
   public abstract class TracingController {
     ctor public TracingController();
     method public static android.webkit.TracingController getInstance();
     method public abstract boolean isTracing();
-    method public abstract boolean start(android.webkit.TracingConfig);
-    method public abstract boolean stop();
-    method public abstract boolean stopAndFlush(android.webkit.TracingController.TracingOutputStream, android.os.Handler);
-  }
-
-  public static abstract interface TracingController.TracingOutputStream {
-    method public abstract void complete();
-    method public abstract void write(byte[]);
-  }
-
-  public class TracingFileOutputStream implements android.webkit.TracingController.TracingOutputStream {
-    ctor public TracingFileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
-    method public void complete();
-    method public void write(byte[]);
+    method public abstract void start(android.webkit.TracingConfig);
+    method public abstract boolean stop(java.io.OutputStream, java.util.concurrent.Executor);
   }
 
   public final class URLUtil {
diff --git a/api/system-current.txt b/api/system-current.txt
index 44349dc..8001ee3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4164,6 +4164,8 @@
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
     field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
     field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
+    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";
     field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
     field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
diff --git a/api/test-current.txt b/api/test-current.txt
index 9bfc105..2e47e00 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1061,6 +1061,7 @@
 
   public final class AutofillId implements android.os.Parcelable {
     ctor public AutofillId(int);
+    ctor public AutofillId(android.view.autofill.AutofillId, int);
   }
 
 }
diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS
new file mode 100644
index 0000000..1a68a32
--- /dev/null
+++ b/cmds/incident_helper/OWNERS
@@ -0,0 +1,2 @@
+jinyithu@google.com
+kwekua@google.com
diff --git a/cmds/incidentd/.clang-format b/cmds/incidentd/.clang-format
new file mode 100644
index 0000000..6fa5b47
--- /dev/null
+++ b/cmds/incidentd/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+  - Regex:    '^"Log\.h"'
+    Priority:    -1
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 2b00d9e..d2d24c8 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -32,7 +32,7 @@
         src/Privacy.cpp \
         src/Reporter.cpp \
         src/Section.cpp \
-        src/io_util.cpp \
+        src/incidentd_util.cpp \
         src/main.cpp \
         src/report_directory.cpp
 
@@ -56,6 +56,7 @@
         libcutils \
         libincident \
         liblog \
+        libprotobuf-cpp-lite \
         libprotoutil \
         libselinux \
         libservices \
@@ -115,7 +116,7 @@
     src/Privacy.cpp \
     src/Reporter.cpp \
     src/Section.cpp \
-    src/io_util.cpp \
+    src/incidentd_util.cpp \
     src/report_directory.cpp \
     tests/section_list.cpp \
     tests/PrivacyBuffer_test.cpp \
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
new file mode 100644
index 0000000..1a68a32
--- /dev/null
+++ b/cmds/incidentd/OWNERS
@@ -0,0 +1,2 @@
+jinyithu@google.com
+kwekua@google.com
diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md
index 71c6deb..1730a640 100644
--- a/cmds/incidentd/README.md
+++ b/cmds/incidentd/README.md
@@ -20,4 +20,8 @@
 
 ```
 root$ atest incidentd_test
-```
\ No newline at end of file
+```
+
+Use clang-format to style the file
+
+clang-format -style=file -i <file list>
\ No newline at end of file
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 0fff4e6..883924c8 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "FdBuffer.h"
 
@@ -26,30 +25,16 @@
 #include <unistd.h>
 #include <wait.h>
 
-const bool DEBUG = false;
-const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
-const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
+const ssize_t BUFFER_SIZE = 16 * 1024;  // 16 KB
+const ssize_t MAX_BUFFER_COUNT = 256;   // 4 MB max
 
 FdBuffer::FdBuffer()
-    :mBuffer(BUFFER_SIZE),
-     mStartTime(-1),
-     mFinishTime(-1),
-     mTimedOut(false),
-     mTruncated(false)
-{
-}
+    : mBuffer(BUFFER_SIZE), mStartTime(-1), mFinishTime(-1), mTimedOut(false), mTruncated(false) {}
 
-FdBuffer::~FdBuffer()
-{
-}
+FdBuffer::~FdBuffer() {}
 
-status_t
-FdBuffer::read(int fd, int64_t timeout)
-{
-    struct pollfd pfds = {
-        .fd = fd,
-        .events = POLLIN
-    };
+status_t FdBuffer::read(int fd, int64_t timeout) {
+    struct pollfd pfds = {.fd = fd, .events = POLLIN};
     mStartTime = uptimeMillis();
 
     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
@@ -63,22 +48,22 @@
 
         int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
         if (remainingTime <= 0) {
-            if (DEBUG) ALOGD("timed out due to long read");
+            VLOG("timed out due to long read");
             mTimedOut = true;
             break;
         }
 
         int count = poll(&pfds, 1, remainingTime);
         if (count == 0) {
-            if (DEBUG) ALOGD("timed out due to block calling poll");
+            VLOG("timed out due to block calling poll");
             mTimedOut = true;
             break;
         } else if (count < 0) {
-            if (DEBUG) ALOGD("poll failed: %s", strerror(errno));
+            VLOG("poll failed: %s", strerror(errno));
             return -errno;
         } else {
             if ((pfds.revents & POLLERR) != 0) {
-                if (DEBUG) ALOGD("return event has error %s", strerror(errno));
+                VLOG("return event has error %s", strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             } else {
                 ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
@@ -86,7 +71,7 @@
                     if (errno == EAGAIN || errno == EWOULDBLOCK) {
                         continue;
                     } else {
-                        if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno));
+                        VLOG("Fail to read %d: %s", fd, strerror(errno));
                         return -errno;
                     }
                 } else if (amt == 0) {
@@ -100,13 +85,12 @@
     return NO_ERROR;
 }
 
-status_t
-FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs)
-{
+status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs,
+                                             const bool isSysfs) {
     struct pollfd pfds[] = {
-        { .fd = fd,     .events = POLLIN  },
-        { .fd = toFd,   .events = POLLOUT },
-        { .fd = fromFd, .events = POLLIN  },
+            {.fd = fd, .events = POLLIN},
+            {.fd = toFd, .events = POLLOUT},
+            {.fd = fromFd, .events = POLLIN},
     };
 
     mStartTime = uptimeMillis();
@@ -131,7 +115,7 @@
 
         int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
         if (remainingTime <= 0) {
-            if (DEBUG) ALOGD("timed out due to long read");
+            VLOG("timed out due to long read");
             mTimedOut = true;
             break;
         }
@@ -139,11 +123,11 @@
         // wait for any pfds to be ready to perform IO
         int count = poll(pfds, 3, remainingTime);
         if (count == 0) {
-            if (DEBUG) ALOGD("timed out due to block calling poll");
+            VLOG("timed out due to block calling poll");
             mTimedOut = true;
             break;
         } else if (count < 0) {
-            if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno));
+            VLOG("Fail to poll: %s", strerror(errno));
             return -errno;
         }
 
@@ -151,10 +135,10 @@
         for (int i = 0; i < 3; ++i) {
             if ((pfds[i].revents & POLLERR) != 0) {
                 if (i == 0 && isSysfs) {
-                    if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd);
+                    VLOG("fd %d is sysfs, ignore its POLLERR return value", fd);
                     continue;
                 }
-                if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
+                VLOG("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             }
         }
@@ -169,9 +153,9 @@
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                    if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno));
+                    VLOG("Fail to read fd %d: %s", fd, strerror(errno));
                     return -errno;
-                } // otherwise just continue
+                }                   // 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;
@@ -191,9 +175,9 @@
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                    if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno));
+                    VLOG("Fail to write toFd %d: %s", toFd, strerror(errno));
                     return -errno;
-                } // otherwise just continue
+                }  // otherwise just continue
             } else {
                 wpos += amt;
                 cirSize -= amt;
@@ -218,9 +202,9 @@
         ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
         if (amt < 0) {
             if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno));
+                VLOG("Fail to read fromFd %d: %s", fromFd, strerror(errno));
                 return -errno;
-            } // otherwise just continue
+            }  // otherwise just continue
         } else if (amt == 0) {
             break;
         } else {
@@ -232,14 +216,6 @@
     return NO_ERROR;
 }
 
-size_t
-FdBuffer::size() const
-{
-    return mBuffer.size();
-}
+size_t FdBuffer::size() const { return mBuffer.size(); }
 
-EncodedBuffer::iterator
-FdBuffer::data() const
-{
-    return mBuffer.begin();
-}
+EncodedBuffer::iterator FdBuffer::data() const { return mBuffer.begin(); }
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 48dc855..5bfa093 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef FD_BUFFER_H
 #define FD_BUFFER_H
@@ -27,8 +28,7 @@
 /**
  * Reads a file into a buffer, and then writes that data to an FdSet.
  */
-class FdBuffer
-{
+class FdBuffer {
 public:
     FdBuffer();
     ~FdBuffer();
@@ -50,7 +50,8 @@
      *
      * Poll will return POLLERR if fd is from sysfs, handle this edge case.
      */
-    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false);
+    status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs,
+                                       const bool isSysfs = false);
 
     /**
      * Whether we timed out.
@@ -90,4 +91,4 @@
     bool mTruncated;
 };
 
-#endif // FD_BUFFER_H
+#endif  // FD_BUFFER_H
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 654036e..9ae6240 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -13,15 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "IncidentService.h"
 
+#include "FdBuffer.h"
+#include "PrivacyBuffer.h"
 #include "Reporter.h"
+#include "incidentd_util.h"
+#include "section_list.h"
 
 #include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
 #include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
 #include <cutils/log.h>
 #include <private/android_filesystem_config.h>
 #include <utils/Looper.h>
@@ -29,11 +34,9 @@
 #include <unistd.h>
 
 using namespace android;
+using namespace android::base;
 
-enum {
-    WHAT_RUN_REPORT = 1,
-    WHAT_SEND_BACKLOG_TO_DROPBOX = 2
-};
+enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 };
 
 //#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
 #define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
@@ -42,9 +45,7 @@
 String16 const DUMP_PERMISSION("android.permission.DUMP");
 String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
 
-static Status
-checkIncidentPermissions(const IncidentReportArgs& args)
-{
+static Status checkIncidentPermissions(const IncidentReportArgs& args) {
     uid_t callingUid = IPCThreadState::self()->getCallingUid();
     pid_t callingPid = IPCThreadState::self()->getCallingPid();
     if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
@@ -55,14 +56,16 @@
     // checking calling permission.
     if (!checkCallingPermission(DUMP_PERMISSION)) {
         ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
-                callingPid, callingUid);
-        return Status::fromExceptionCode(Status::EX_SECURITY,
+              callingPid, callingUid);
+        return Status::fromExceptionCode(
+                Status::EX_SECURITY,
                 "Calling process does not have permission: android.permission.DUMP");
     }
     if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
         ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
-                callingPid, callingUid);
-        return Status::fromExceptionCode(Status::EX_SECURITY,
+              callingPid, callingUid);
+        return Status::fromExceptionCode(
+                Status::EX_SECURITY,
                 "Calling process does not have permission: android.permission.USAGE_STATS");
     }
 
@@ -71,40 +74,34 @@
         case DEST_LOCAL:
             if (callingUid != AID_SHELL && callingUid != AID_ROOT) {
                 ALOGW("Calling pid %d and uid %d does not have permission to get local data.",
-                        callingPid, callingUid);
-                return Status::fromExceptionCode(Status::EX_SECURITY,
-                    "Calling process does not have permission to get local data.");
+                      callingPid, callingUid);
+                return Status::fromExceptionCode(
+                        Status::EX_SECURITY,
+                        "Calling process does not have permission to get local data.");
             }
         case DEST_EXPLICIT:
-            if (callingUid != AID_SHELL && callingUid != AID_ROOT &&
-                callingUid != AID_STATSD && callingUid != AID_SYSTEM) {
+            if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD &&
+                callingUid != AID_SYSTEM) {
                 ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.",
-                        callingPid, callingUid);
-                return Status::fromExceptionCode(Status::EX_SECURITY,
-                    "Calling process does not have permission to get explicit data.");
+                      callingPid, callingUid);
+                return Status::fromExceptionCode(
+                        Status::EX_SECURITY,
+                        "Calling process does not have permission to get explicit data.");
             }
     }
     return Status::ok();
 }
 // ================================================================================
-ReportRequestQueue::ReportRequestQueue()
-{
-}
+ReportRequestQueue::ReportRequestQueue() {}
 
-ReportRequestQueue::~ReportRequestQueue()
-{
-}
+ReportRequestQueue::~ReportRequestQueue() {}
 
-void
-ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
-{
+void ReportRequestQueue::addRequest(const sp<ReportRequest>& request) {
     unique_lock<mutex> lock(mLock);
     mQueue.push_back(request);
 }
 
-sp<ReportRequest>
-ReportRequestQueue::getNextRequest()
-{
+sp<ReportRequest> ReportRequestQueue::getNextRequest() {
     unique_lock<mutex> lock(mLock);
     if (mQueue.empty()) {
         return NULL;
@@ -115,22 +112,13 @@
     }
 }
 
-
 // ================================================================================
 ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
-    :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
-     mHandlerLooper(handlerLooper),
-     mQueue(queue)
-{
-}
+    : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), mHandlerLooper(handlerLooper), mQueue(queue) {}
 
-ReportHandler::~ReportHandler()
-{
-}
+ReportHandler::~ReportHandler() {}
 
-void
-ReportHandler::handleMessage(const Message& message)
-{
+void ReportHandler::handleMessage(const Message& message) {
     switch (message.what) {
         case WHAT_RUN_REPORT:
             run_report();
@@ -141,33 +129,24 @@
     }
 }
 
-void
-ReportHandler::scheduleRunReport(const sp<ReportRequest>& request)
-{
+void ReportHandler::scheduleRunReport(const sp<ReportRequest>& request) {
     mQueue->addRequest(request);
     mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT);
     mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT));
 }
 
-void
-ReportHandler::scheduleSendBacklogToDropbox()
-{
+void ReportHandler::scheduleSendBacklogToDropbox() {
     unique_lock<mutex> lock(mLock);
     mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
     schedule_send_backlog_to_dropbox_locked();
 }
 
-void
-ReportHandler::schedule_send_backlog_to_dropbox_locked()
-{
+void ReportHandler::schedule_send_backlog_to_dropbox_locked() {
     mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX);
-    mHandlerLooper->sendMessageDelayed(mBacklogDelay, this,
-            Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
+    mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
 }
 
-void
-ReportHandler::run_report()
-{
+void ReportHandler::run_report() {
     sp<Reporter> reporter = new Reporter();
 
     // Merge all of the requests into one that has all of the
@@ -190,15 +169,13 @@
     }
 }
 
-void
-ReportHandler::send_backlog_to_dropbox()
-{
+void ReportHandler::send_backlog_to_dropbox() {
     if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) {
         // There was a failure. Exponential backoff.
         unique_lock<mutex> lock(mLock);
         mBacklogDelay *= 2;
         ALOGI("Error sending to dropbox. Trying again in %lld minutes",
-                (mBacklogDelay / (1000000000LL * 60)));
+              (mBacklogDelay / (1000000000LL * 60)));
         schedule_send_backlog_to_dropbox_locked();
     } else {
         mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
@@ -207,18 +184,13 @@
 
 // ================================================================================
 IncidentService::IncidentService(const sp<Looper>& handlerLooper)
-    :mQueue(new ReportRequestQueue())
-{
+    : mQueue(new ReportRequestQueue()) {
     mHandler = new ReportHandler(handlerLooper, mQueue);
 }
 
-IncidentService::~IncidentService()
-{
-}
+IncidentService::~IncidentService() {}
 
-Status
-IncidentService::reportIncident(const IncidentReportArgs& args)
-{
+Status IncidentService::reportIncident(const IncidentReportArgs& args) {
     ALOGI("reportIncident");
 
     Status status = checkIncidentPermissions(args);
@@ -231,10 +203,9 @@
     return Status::ok();
 }
 
-Status
-IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
-            const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream)
-{
+Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
+                                               const sp<IIncidentReportStatusListener>& listener,
+                                               const unique_fd& stream) {
     ALOGI("reportIncidentToStream");
 
     Status status = checkIncidentPermissions(args);
@@ -252,12 +223,10 @@
     return Status::ok();
 }
 
-Status
-IncidentService::systemRunning()
-{
+Status IncidentService::systemRunning() {
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
         return Status::fromExceptionCode(Status::EX_SECURITY,
-                "Only system uid can call systemRunning");
+                                         "Only system uid can call systemRunning");
     }
 
     // When system_server is up and running, schedule the dropbox task to run.
@@ -266,3 +235,120 @@
     return Status::ok();
 }
 
+/**
+ * Implement our own because the default binder implementation isn't
+ * properly handling SHELL_COMMAND_TRANSACTION.
+ */
+status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                     uint32_t flags) {
+    status_t err;
+
+    switch (code) {
+        case SHELL_COMMAND_TRANSACTION: {
+            int in = data.readFileDescriptor();
+            int out = data.readFileDescriptor();
+            int err = data.readFileDescriptor();
+            int argc = data.readInt32();
+            Vector<String8> args;
+            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+                args.add(String8(data.readString16()));
+            }
+            sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
+            sp<IResultReceiver> resultReceiver =
+                    IResultReceiver::asInterface(data.readStrongBinder());
+
+            FILE* fin = fdopen(in, "r");
+            FILE* fout = fdopen(out, "w");
+            FILE* ferr = fdopen(err, "w");
+
+            if (fin == NULL || fout == NULL || ferr == NULL) {
+                resultReceiver->send(NO_MEMORY);
+            } else {
+                err = command(fin, fout, ferr, args);
+                resultReceiver->send(err);
+            }
+
+            if (fin != NULL) {
+                fflush(fin);
+                fclose(fin);
+            }
+            if (fout != NULL) {
+                fflush(fout);
+                fclose(fout);
+            }
+            if (fout != NULL) {
+                fflush(ferr);
+                fclose(ferr);
+            }
+
+            return NO_ERROR;
+        }
+        default: { return BnIncidentManager::onTransact(code, data, reply, flags); }
+    }
+}
+
+status_t IncidentService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+    const int argCount = args.size();
+
+    if (argCount >= 1) {
+        if (!args[0].compare(String8("privacy"))) {
+            return cmd_privacy(in, out, err, args);
+        }
+    }
+    return cmd_help(out);
+}
+
+status_t IncidentService::cmd_help(FILE* out) {
+    fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
+    fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
+    fprintf(out, "    Prints/parses for the section id.\n");
+    return NO_ERROR;
+}
+
+static void printPrivacy(const Privacy* p, FILE* out, String8 indent) {
+    if (p == NULL) return;
+    fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->dest);
+    if (p->children == NULL) return;
+    for (int i = 0; p->children[i] != NULL; i++) {  // NULL-terminated.
+        printPrivacy(p->children[i], out, indent + "  ");
+    }
+}
+
+status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+    const int argCount = args.size();
+    if (argCount >= 3) {
+        String8 opt = args[1];
+        int sectionId = atoi(args[2].string());
+
+        const Privacy* p = get_privacy_of_section(sectionId);
+        if (p == NULL) {
+            fprintf(err, "Can't find section id %d\n", sectionId);
+            return NO_ERROR;
+        }
+        fprintf(err, "Get privacy for %d\n", sectionId);
+        if (opt == "print") {
+            printPrivacy(p, out, String8(""));
+        } else if (opt == "parse") {
+            FdBuffer buf;
+            status_t error = buf.read(fileno(in), 60000);
+            if (error != NO_ERROR) {
+                fprintf(err, "Error reading from stdin\n");
+                return error;
+            }
+            fprintf(err, "Read %zu bytes\n", buf.size());
+            auto data = buf.data();
+            PrivacyBuffer pBuf(p, data);
+
+            PrivacySpec spec = PrivacySpec::new_spec(argCount > 3 ? atoi(args[3]) : -1);
+            error = pBuf.strip(spec);
+            if (error != NO_ERROR) {
+                fprintf(err, "Error strip pii fields with spec %d\n", spec.dest);
+                return error;
+            }
+            return pBuf.flush(fileno(out));
+        }
+    } else {
+        return cmd_help(out);
+    }
+    return NO_ERROR;
+}
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index d6f33df..3c66507 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef INCIDENT_SERVICE_H
 #define INCIDENT_SERVICE_H
@@ -32,8 +33,7 @@
 using namespace std;
 
 // ================================================================================
-class ReportRequestQueue : public virtual RefBase
-{
+class ReportRequestQueue : public virtual RefBase {
 public:
     ReportRequestQueue();
     virtual ~ReportRequestQueue();
@@ -46,10 +46,8 @@
     deque<sp<ReportRequest> > mQueue;
 };
 
-
 // ================================================================================
-class ReportHandler : public MessageHandler
-{
+class ReportHandler : public MessageHandler {
 public:
     ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue);
     virtual ~ReportHandler();
@@ -89,7 +87,6 @@
     void send_backlog_to_dropbox();
 };
 
-
 // ================================================================================
 class IncidentService : public BnIncidentManager {
 public:
@@ -99,14 +96,29 @@
     virtual Status reportIncident(const IncidentReportArgs& args);
 
     virtual Status reportIncidentToStream(const IncidentReportArgs& args,
-            const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream);
+                                          const sp<IIncidentReportStatusListener>& listener,
+                                          const unique_fd& stream);
 
     virtual Status systemRunning();
 
+    // Implement commands for debugging purpose.
+    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                uint32_t flags) override;
+    virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+
 private:
     sp<ReportRequestQueue> mQueue;
     sp<ReportHandler> mHandler;
+
+    /**
+     * Commands print out help.
+     */
+    status_t cmd_help(FILE* out);
+
+    /**
+     * Commands related to privacy filtering.
+     */
+    status_t cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
 };
 
-
-#endif // INCIDENT_SERVICE_H
+#endif  // INCIDENT_SERVICE_H
diff --git a/cmds/incidentd/src/Log.h b/cmds/incidentd/src/Log.h
new file mode 100644
index 0000000..46efbd1
--- /dev/null
+++ b/cmds/incidentd/src/Log.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file must be included at the top of the file. Other header files
+ * occasionally include log.h, and if LOG_TAG isn't set when that happens
+ * we'll get a preprocesser error when we try to define it here.
+ */
+
+#pragma once
+
+#define LOG_TAG "incidentd"
+#define DEBUG false
+
+#include <log/log.h>
+
+// Use the local value to turn on/off debug logs instead of using log.tag.properties.
+// The advantage is that in production compiler can remove the logging code if the local
+// DEBUG/VERBOSE is false.
+#define VLOG(...) \
+    if (DEBUG) ALOGD(__VA_ARGS__);
\ No newline at end of file
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 3f0e331..c42a87b 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -21,10 +21,9 @@
 
 uint64_t encode_field_id(const Privacy* p) { return (uint64_t)p->type << 32 | p->field_id; }
 
-const Privacy* lookup(const Privacy* p, uint32_t fieldId)
-{
+const Privacy* lookup(const Privacy* p, uint32_t fieldId) {
     if (p->children == NULL) return NULL;
-    for (int i=0; p->children[i] != NULL; i++) { // NULL-terminated.
+    for (int i = 0; p->children[i] != NULL; i++) {  // NULL-terminated.
         if (p->children[i]->field_id == fieldId) return p->children[i];
         // Incident section gen tool guarantees field ids in ascending order.
         if (p->children[i]->field_id > fieldId) return NULL;
@@ -32,52 +31,37 @@
     return NULL;
 }
 
-static bool allowDest(const uint8_t dest, const uint8_t policy)
-{
+static bool allowDest(const uint8_t dest, const uint8_t policy) {
     switch (policy) {
-    case android::os::DEST_LOCAL:
-        return dest == android::os::DEST_LOCAL;
-    case android::os::DEST_EXPLICIT:
-    case DEST_UNSET:
-        return dest == android::os::DEST_LOCAL ||
-            dest == android::os::DEST_EXPLICIT ||
-            dest == DEST_UNSET;
-    case android::os::DEST_AUTOMATIC:
-        return true;
-    default:
-        return false;
+        case android::os::DEST_LOCAL:
+            return dest == android::os::DEST_LOCAL;
+        case android::os::DEST_EXPLICIT:
+        case DEST_UNSET:
+            return dest == android::os::DEST_LOCAL || dest == android::os::DEST_EXPLICIT ||
+                   dest == DEST_UNSET;
+        case android::os::DEST_AUTOMATIC:
+            return true;
+        default:
+            return false;
     }
 }
 
-bool
-PrivacySpec::operator<(const PrivacySpec& other) const
-{
-  return dest < other.dest;
-}
+bool PrivacySpec::operator<(const PrivacySpec& other) const { return dest < other.dest; }
 
-bool
-PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const
-{
+bool PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const {
     uint8_t policy = privacy != NULL ? privacy->dest : defaultDest;
     return allowDest(dest, policy);
 }
 
-bool
-PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; }
+bool PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; }
 
-PrivacySpec PrivacySpec::new_spec(int dest)
-{
+PrivacySpec PrivacySpec::new_spec(int dest) {
     switch (dest) {
         case android::os::DEST_AUTOMATIC:
         case android::os::DEST_EXPLICIT:
         case android::os::DEST_LOCAL:
             return PrivacySpec(dest);
         default:
-            return PrivacySpec();
+            return PrivacySpec(android::os::DEST_AUTOMATIC);
     }
 }
-
-PrivacySpec PrivacySpec::get_default_dropbox_spec()
-{
-    return PrivacySpec(android::os::DEST_AUTOMATIC);
-}
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index 4f3db67..6b6de9c 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef PRIVACY_H
 #define PRIVACY_H
@@ -20,7 +21,7 @@
 #include <stdint.h>
 
 // This is the default value of DEST enum, sync with privacy.proto
-const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident
+const uint8_t DEST_UNSET = 255;  // DEST_UNSET is not exposed to libincident
 const uint8_t DEST_DEFAULT_VALUE = DEST_UNSET;
 
 /*
@@ -68,16 +69,17 @@
     bool operator<(const PrivacySpec& other) const;
 
     // check permission of a policy, if returns true, don't strip the data.
-    bool CheckPremission(const Privacy* privacy, const uint8_t defaultDest = DEST_DEFAULT_VALUE) const;
+    bool CheckPremission(const Privacy* privacy,
+                         const uint8_t defaultDest = DEST_DEFAULT_VALUE) const;
 
     // if returns true, no data need to be stripped.
     bool RequireAll() const;
 
     // Constructs spec using static methods below.
     static PrivacySpec new_spec(int dest);
-    static PrivacySpec get_default_dropbox_spec();
+
 private:
     PrivacySpec(uint8_t dest) : dest(dest) {}
 };
 
-#endif // PRIVACY_H
+#endif  // PRIVACY_H
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index f53befe..e4128f4 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -13,29 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "PrivacyBuffer.h"
-#include "io_util.h"
+#include "incidentd_util.h"
 
+#include <android-base/file.h>
 #include <android/util/protobuf.h>
 #include <cutils/log.h>
 
 using namespace android::util;
 
-const bool DEBUG = false;
-
 /**
  * Write the field to buf based on the wire type, iterator will point to next field.
  * If skip is set to true, no data will be written to buf. Return number of bytes written.
  */
-void
-PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip)
-{
-    if (DEBUG) ALOGD("%s field %d (wiretype = %d)", skip ? "skip" : "write",
-        read_field_id(fieldTag), read_wire_type(fieldTag));
-
+void PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) {
     uint8_t wireType = read_wire_type(fieldTag);
     size_t bytesToWrite = 0;
     uint32_t varint = 0;
@@ -54,18 +47,17 @@
             break;
         case WIRE_TYPE_LENGTH_DELIMITED:
             bytesToWrite = mData.readRawVarint();
-            if(!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite);
+            if (!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite);
             break;
         case WIRE_TYPE_FIXED32:
             if (!skip) mProto.writeRawVarint(fieldTag);
             bytesToWrite = 4;
             break;
     }
-    if (DEBUG) ALOGD("%s %d bytes of data", skip ? "skip" : "write", (int)bytesToWrite);
     if (skip) {
         mData.rp()->move(bytesToWrite);
     } else {
-        for (size_t i=0; i<bytesToWrite; i++) {
+        for (size_t i = 0; i < bytesToWrite; i++) {
             mProto.writeRawByte(mData.next());
         }
     }
@@ -78,28 +70,29 @@
  * The iterator must point to the head of a protobuf formatted field for successful operation.
  * After exit with NO_ERROR, iterator points to the next protobuf field's head.
  */
-status_t
-PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec)
-{
+status_t PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec,
+                                   int depth /* use as a counter for this recusive method. */) {
     if (!mData.hasNext() || parentPolicy == NULL) return BAD_VALUE;
     uint32_t fieldTag = mData.readRawVarint();
-    const Privacy* policy = lookup(parentPolicy, read_field_id(fieldTag));
+    uint32_t fieldId = read_field_id(fieldTag);
+    const Privacy* policy = lookup(parentPolicy, fieldId);
 
+    VLOG("[Depth %2d]Try to strip id %d, wiretype %d", depth, fieldId, read_wire_type(fieldTag));
     if (policy == NULL || policy->children == NULL) {
-        if (DEBUG) ALOGD("Not a message field %d: dest(%d)", read_field_id(fieldTag),
-            policy != NULL ? policy->dest : parentPolicy->dest);
-
         bool skip = !spec.CheckPremission(policy, parentPolicy->dest);
         // iterator will point to head of next field
+        size_t currentAt = mData.rp()->pos();
         writeFieldOrSkip(fieldTag, skip);
+        VLOG("[Depth %2d]Field %d %ss %d bytes", depth, fieldId, skip ? "skip" : "write",
+             (int)(get_varint_size(fieldTag) + mData.rp()->pos() - currentAt));
         return NO_ERROR;
     }
     // current field is message type and its sub-fields have extra privacy policies
     uint32_t msgSize = mData.readRawVarint();
-    EncodedBuffer::Pointer start = mData.rp()->copy();
+    size_t start = mData.rp()->pos();
     long long token = mProto.start(encode_field_id(policy));
-    while (mData.rp()->pos() - start.pos() != msgSize) {
-        status_t err = stripField(policy, spec);
+    while (mData.rp()->pos() - start != msgSize) {
+        status_t err = stripField(policy, spec, depth + 1);
         if (err != NO_ERROR) return err;
     }
     mProto.end(token);
@@ -108,53 +101,39 @@
 
 // ================================================================================
 PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data)
-        :mPolicy(policy),
-         mData(data),
-         mProto(),
-         mSize(0)
-{
-}
+    : mPolicy(policy), mData(data), mProto(), mSize(0) {}
 
-PrivacyBuffer::~PrivacyBuffer()
-{
-}
+PrivacyBuffer::~PrivacyBuffer() {}
 
-status_t
-PrivacyBuffer::strip(const PrivacySpec& spec)
-{
-    if (DEBUG) ALOGD("Strip with spec %d", spec.dest);
+status_t PrivacyBuffer::strip(const PrivacySpec& spec) {
+    VLOG("Strip with spec %d", spec.dest);
     // optimization when no strip happens
     if (mPolicy == NULL || mPolicy->children == NULL || spec.RequireAll()) {
         if (spec.CheckPremission(mPolicy)) mSize = mData.size();
         return NO_ERROR;
     }
     while (mData.hasNext()) {
-        status_t err = stripField(mPolicy, spec);
+        status_t err = stripField(mPolicy, spec, 0);
         if (err != NO_ERROR) return err;
     }
     if (mData.bytesRead() != mData.size()) return BAD_VALUE;
     mSize = mProto.size();
-    mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip.
+    mData.rp()->rewind();  // rewind the read pointer back to beginning after the strip.
     return NO_ERROR;
 }
 
-void
-PrivacyBuffer::clear()
-{
+void PrivacyBuffer::clear() {
     mSize = 0;
     mProto.clear();
 }
 
-size_t
-PrivacyBuffer::size() const { return mSize; }
+size_t PrivacyBuffer::size() const { return mSize; }
 
-status_t
-PrivacyBuffer::flush(int fd)
-{
+status_t PrivacyBuffer::flush(int fd) {
     status_t err = NO_ERROR;
     EncodedBuffer::iterator iter = size() == mData.size() ? mData : mProto.data();
     while (iter.readBuffer() != NULL) {
-        err = write_all(fd, iter.readBuffer(), iter.currentToRead());
+        err = WriteFully(fd, iter.readBuffer(), iter.currentToRead()) ? NO_ERROR : -errno;
         iter.rp()->move(iter.currentToRead());
         if (err != NO_ERROR) return err;
     }
diff --git a/cmds/incidentd/src/PrivacyBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h
index c9ca9a7..92e1a25 100644
--- a/cmds/incidentd/src/PrivacyBuffer.h
+++ b/cmds/incidentd/src/PrivacyBuffer.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef PRIVACY_BUFFER_H
 #define PRIVACY_BUFFER_H
@@ -31,14 +32,14 @@
  * PrivacyBuffer holds the original protobuf data and strips PII-sensitive fields
  * based on the request and holds stripped data in its own buffer for output.
  */
-class PrivacyBuffer
-{
+class PrivacyBuffer {
 public:
     PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data);
     ~PrivacyBuffer();
 
     /**
-     * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds.
+     * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip
+     * succeeds.
      */
     status_t strip(const PrivacySpec& spec);
 
@@ -64,8 +65,8 @@
     ProtoOutputStream mProto;
     size_t mSize;
 
-    status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec);
+    status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec, int depth);
     void writeFieldOrSkip(uint32_t fieldTag, bool skip);
 };
 
-#endif // PRIVACY_BUFFER_H
\ No newline at end of file
+#endif  // PRIVACY_BUFFER_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index b9f479b..c0b5358 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "Reporter.h"
 
+#include "Privacy.h"
 #include "report_directory.h"
 #include "section_list.h"
 
@@ -25,11 +25,11 @@
 #include <private/android_filesystem_config.h>
 #include <utils/SystemClock.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <dirent.h>
-#include <fcntl.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 /**
  * The directory where the incident reports are stored.
@@ -38,72 +38,67 @@
 
 // ================================================================================
 ReportRequest::ReportRequest(const IncidentReportArgs& a,
-            const sp<IIncidentReportStatusListener> &l, int f)
-    :args(a),
-     listener(l),
-     fd(f),
-     err(NO_ERROR)
-{
-}
+                             const sp<IIncidentReportStatusListener>& l, int f)
+    : args(a), listener(l), fd(f), err(NO_ERROR) {}
 
-ReportRequest::~ReportRequest()
-{
+ReportRequest::~ReportRequest() {
     if (fd >= 0) {
         // clean up the opened file descriptor
         close(fd);
     }
 }
 
-bool
-ReportRequest::ok()
-{
-    return fd >= 0 && err == NO_ERROR;
-}
+bool ReportRequest::ok() { return fd >= 0 && err == NO_ERROR; }
 
 // ================================================================================
 ReportRequestSet::ReportRequestSet()
-    :mRequests(),
-     mSections(),
-     mMainFd(-1),
-     mMainDest(-1)
-{
-}
+    : mRequests(), mSections(), mMainFd(-1), mMainDest(-1), mMetadata(), mSectionStats() {}
 
-ReportRequestSet::~ReportRequestSet()
-{
-}
+ReportRequestSet::~ReportRequestSet() {}
 
 // TODO: dedup on exact same args and fd, report the status back to listener!
-void
-ReportRequestSet::add(const sp<ReportRequest>& request)
-{
+void ReportRequestSet::add(const sp<ReportRequest>& request) {
     mRequests.push_back(request);
     mSections.merge(request->args);
+    mMetadata.set_request_size(mMetadata.request_size() + 1);
 }
 
-void
-ReportRequestSet::setMainFd(int fd)
-{
+void ReportRequestSet::setMainFd(int fd) {
     mMainFd = fd;
+    mMetadata.set_use_dropbox(fd > 0);
 }
 
-void
-ReportRequestSet::setMainDest(int dest)
-{
+void ReportRequestSet::setMainDest(int dest) {
     mMainDest = dest;
+    PrivacySpec spec = PrivacySpec::new_spec(dest);
+    switch (spec.dest) {
+        case android::os::DEST_AUTOMATIC:
+            mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC);
+            break;
+        case android::os::DEST_EXPLICIT:
+            mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT);
+            break;
+        case android::os::DEST_LOCAL:
+            mMetadata.set_dest(IncidentMetadata_Destination_LOCAL);
+            break;
+    }
 }
 
-bool
-ReportRequestSet::containsSection(int id) {
-    return mSections.containsSection(id);
+bool ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); }
+
+IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) {
+    if (mSectionStats.find(id) == mSectionStats.end()) {
+        auto stats = mMetadata.add_sections();
+        stats->set_id(id);
+        mSectionStats[id] = stats;
+    }
+    return mSectionStats[id];
 }
 
 // ================================================================================
 Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
 
-Reporter::Reporter(const char* directory)
-    :batch()
-{
+Reporter::Reporter(const char* directory) : batch() {
     char buf[100];
 
     // TODO: Make the max size smaller for user builds.
@@ -121,22 +116,18 @@
     mFilename = mIncidentDirectory + buf;
 }
 
-Reporter::~Reporter()
-{
-}
+Reporter::~Reporter() {}
 
-Reporter::run_report_status_t
-Reporter::runReport()
-{
-
+Reporter::run_report_status_t Reporter::runReport() {
     status_t err = NO_ERROR;
     bool needMainFd = false;
     int mainFd = -1;
     int mainDest = -1;
     HeaderSection headers;
+    MetadataSection metadataSection;
 
     // See if we need the main file
-    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+    for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
         if ((*it)->fd < 0 && mainFd < 0) {
             needMainFd = true;
             mainDest = (*it)->args.dest();
@@ -167,7 +158,7 @@
     }
 
     // Tell everyone that we're starting.
-    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+    for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
         if ((*it)->listener != NULL) {
             (*it)->listener->onReportStarted();
         }
@@ -178,31 +169,36 @@
 
     // For each of the report fields, see if we need it, and if so, execute the command
     // and report to those that care that we're doing it.
-    for (const Section** section=SECTION_LIST; *section; section++) {
+    for (const Section** section = SECTION_LIST; *section; section++) {
         const int id = (*section)->id;
         if (this->batch.containsSection(id)) {
             ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
-            // Notify listener of starting
-            for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+            for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
                 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
-                    (*it)->listener->onReportSectionStatus(id,
-                            IIncidentReportStatusListener::STATUS_STARTING);
+                    (*it)->listener->onReportSectionStatus(
+                            id, IIncidentReportStatusListener::STATUS_STARTING);
                 }
             }
 
             // Execute - go get the data and write it into the file descriptors.
+            auto stats = batch.sectionStats(id);
+            int64_t startTime = uptimeMillis();
             err = (*section)->Execute(&batch);
+            int64_t endTime = uptimeMillis();
+
+            stats->set_success(err == NO_ERROR);
+            stats->set_exec_duration_ms(endTime - startTime);
             if (err != NO_ERROR) {
                 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
-                        (*section)->name.string(), id, strerror(-err));
+                      (*section)->name.string(), id, strerror(-err));
                 goto DONE;
             }
 
             // Notify listener of starting
-            for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+            for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
                 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
-                    (*it)->listener->onReportSectionStatus(id,
-                            IIncidentReportStatusListener::STATUS_FINISHED);
+                    (*it)->listener->onReportSectionStatus(
+                            id, IIncidentReportStatusListener::STATUS_FINISHED);
                 }
             }
             ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
@@ -210,13 +206,16 @@
     }
 
 DONE:
+    // Reports the metdadata when taking the incident report.
+    if (!isTest) metadataSection.Execute(&batch);
+
     // Close the file.
     if (mainFd >= 0) {
         close(mainFd);
     }
 
     // Tell everyone that we're done.
-    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+    for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
         if ((*it)->listener != NULL) {
             if (err == NO_ERROR) {
                 (*it)->listener->onReportFinished();
@@ -238,7 +237,7 @@
         // If the status was ok, delete the file. If not, leave it around until the next
         // boot or the next checkin. If the directory gets too big older files will
         // be rotated out.
-        if(!isTest) unlink(mFilename.c_str());
+        if (!isTest) unlink(mFilename.c_str());
     }
 
     return REPORT_FINISHED;
@@ -247,9 +246,7 @@
 /**
  * Create our output file and set the access permissions to -rw-rw----
  */
-status_t
-Reporter::create_file(int* fd)
-{
+status_t Reporter::create_file(int* fd) {
     const char* filename = mFilename.c_str();
 
     *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
@@ -271,9 +268,7 @@
     return NO_ERROR;
 }
 
-Reporter::run_report_status_t
-Reporter::upload_backlog()
-{
+Reporter::run_report_status_t Reporter::upload_backlog() {
     DIR* dir;
     struct dirent* entry;
     struct stat st;
@@ -324,4 +319,3 @@
 
     return REPORT_FINISHED;
 }
-
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index f30ecf0..0f3f221 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -13,13 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef REPORTER_H
 #define REPORTER_H
 
+#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
+
 #include <android/os/IIncidentReportStatusListener.h>
 #include <android/os/IncidentReportArgs.h>
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -30,23 +34,21 @@
 using namespace std;
 
 // ================================================================================
-struct ReportRequest : public virtual RefBase
-{
+struct ReportRequest : public virtual RefBase {
     IncidentReportArgs args;
     sp<IIncidentReportStatusListener> listener;
     int fd;
     status_t err;
 
-    ReportRequest(const IncidentReportArgs& args,
-            const sp<IIncidentReportStatusListener> &listener, int fd);
+    ReportRequest(const IncidentReportArgs& args, const sp<IIncidentReportStatusListener>& listener,
+                  int fd);
     virtual ~ReportRequest();
 
-    bool ok(); // returns true if the request is ok for write.
+    bool ok();  // returns true if the request is ok for write.
 };
 
 // ================================================================================
-class ReportRequestSet
-{
+class ReportRequestSet {
 public:
     ReportRequestSet();
     ~ReportRequestSet();
@@ -61,28 +63,31 @@
     iterator end() { return mRequests.end(); }
 
     int mainFd() { return mMainFd; }
-    bool containsSection(int id);
     int mainDest() { return mMainDest; }
+    IncidentMetadata& metadata() { return mMetadata; }
+
+    bool containsSection(int id);
+    IncidentMetadata::SectionStats* sectionStats(int id);
+
 private:
     vector<sp<ReportRequest>> mRequests;
     IncidentReportArgs mSections;
     int mMainFd;
     int mMainDest;
+
+    IncidentMetadata mMetadata;
+    map<int, IncidentMetadata::SectionStats*> mSectionStats;
 };
 
 // ================================================================================
-class Reporter : public virtual RefBase
-{
+class Reporter : public virtual RefBase {
 public:
-    enum run_report_status_t {
-        REPORT_FINISHED = 0,
-        REPORT_NEEDS_DROPBOX = 1
-    };
+    enum run_report_status_t { REPORT_FINISHED = 0, REPORT_NEEDS_DROPBOX = 1 };
 
     ReportRequestSet batch;
 
-    Reporter();
-    Reporter(const char* directory);
+    Reporter();                       // PROD must use this constructor.
+    Reporter(const char* directory);  // For testing purpose only.
     virtual ~Reporter();
 
     // Run the report as described in the batch and args parameters.
@@ -100,8 +105,7 @@
 
     status_t create_file(int* fd);
 
-    bool isTest = true; // default to true for testing
+    bool isTest = true;  // default to true for testing
 };
 
-
-#endif // REPORTER_H
+#endif  // REPORTER_H
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index faeab87..2e4e980 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "Section.h"
 
@@ -26,42 +25,41 @@
 #include <memory>
 #include <mutex>
 
+#include <android-base/file.h>
 #include <android/util/protobuf.h>
 #include <binder/IServiceManager.h>
 #include <log/log_event_list.h>
-#include <log/logprint.h>
 #include <log/log_read.h>
+#include <log/logprint.h>
 #include <private/android_logger.h>
 
 #include "FdBuffer.h"
-#include "frameworks/base/core/proto/android/util/log.proto.h"
-#include "io_util.h"
 #include "Privacy.h"
 #include "PrivacyBuffer.h"
-#include "section_list.h"
+#include "frameworks/base/core/proto/android/util/log.proto.h"
+#include "incidentd_util.h"
 
+using namespace android::base;
 using namespace android::util;
 using namespace std;
 
 // special section ids
 const int FIELD_ID_INCIDENT_HEADER = 1;
+const int FIELD_ID_INCIDENT_METADATA = 2;
 
 // incident section parameters
-const int   WAIT_MAX = 5;
+const int WAIT_MAX = 5;
 const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
 const char INCIDENT_HELPER[] = "/system/bin/incident_helper";
 
-static pid_t
-fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
-{
-    const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
+static pid_t fork_execute_incident_helper(const int id, const char* name, 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()) {
+        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);
         }
@@ -72,7 +70,7 @@
         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
+        _exit(EXIT_FAILURE);  // always exits with failure if any
     }
     // close the fds used in incident helper
     close(p2cPipe.readFd());
@@ -83,18 +81,18 @@
 // ================================================================================
 static status_t statusCode(int status) {
     if (WIFSIGNALED(status)) {
-      ALOGD("return by signal: %s", strerror(WTERMSIG(status)));
-      return -WTERMSIG(status);
+        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
+        return -WTERMSIG(status);
     } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
-      ALOGD("return by exit: %s", strerror(WEXITSTATUS(status)));
-      return -WEXITSTATUS(status);
+        VLOG("return by exit: %s", strerror(WEXITSTATUS(status)));
+        return -WEXITSTATUS(status);
     }
     return NO_ERROR;
 }
 
 static status_t kill_child(pid_t pid) {
     int status;
-    ALOGD("try to kill child process %d", pid);
+    VLOG("try to kill child process %d", pid);
     kill(pid, SIGKILL);
     if (waitpid(pid, &status, 0) == -1) return -1;
     return statusCode(status);
@@ -104,7 +102,7 @@
     int status;
     bool died = false;
     // wait for child to report status up to 1 seconds
-    for(int loop = 0; !died && loop < WAIT_MAX; loop++) {
+    for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
         if (waitpid(pid, &status, WNOHANG) == pid) died = true;
         // sleep for 0.2 second
         nanosleep(&WAIT_INTERVAL_NS, NULL);
@@ -113,42 +111,24 @@
     return statusCode(status);
 }
 // ================================================================================
-static const Privacy*
-get_privacy_of_section(int id)
-{
-    int l = 0;
-    int r = PRIVACY_POLICY_COUNT - 1;
-    while (l <= r) {
-        int mid = (l + r) >> 1;
-        const Privacy* p = PRIVACY_POLICY_LIST[mid];
-
-        if (p->field_id < (uint32_t)id) {
-            l = mid + 1;
-        } else if (p->field_id > (uint32_t)id) {
-            r = mid - 1;
-        } else {
-            return p;
-        }
-    }
-    return NULL;
-}
-
-// ================================================================================
-static status_t
-write_section_header(int fd, int sectionId, size_t size)
-{
+static status_t write_section_header(int fd, int sectionId, size_t size) {
     uint8_t buf[20];
-    uint8_t *p = write_length_delimited_tag_header(buf, sectionId, size);
-    return write_all(fd, buf, p-buf);
+    uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size);
+    return WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno;
 }
 
-static status_t
-write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
-{
+static status_t write_report_requests(const int id, const FdBuffer& buffer,
+                                      ReportRequestSet* requests) {
     status_t err = -EBADF;
     EncodedBuffer::iterator data = buffer.data();
     PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
     int writeable = 0;
+    auto stats = requests->sectionStats(id);
+
+    stats->set_dump_size_bytes(data.size());
+    stats->set_dump_duration_ms(buffer.durationMs());
+    stats->set_timed_out(buffer.timedOut());
+    stats->set_is_truncated(buffer.truncated());
 
     // The streaming ones, group requests by spec in order to save unnecessary strip operations
     map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
@@ -164,38 +144,49 @@
     for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
         PrivacySpec spec = mit->first;
         err = privacyBuffer.strip(spec);
-        if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted.
+        if (err != NO_ERROR) return err;  // it means the privacyBuffer data is corrupted.
         if (privacyBuffer.size() == 0) continue;
 
         for (auto it = mit->second.begin(); it != mit->second.end(); it++) {
             sp<ReportRequest> request = *it;
             err = write_section_header(request->fd, id, privacyBuffer.size());
-            if (err != NO_ERROR) { request->err = err; continue; }
+            if (err != NO_ERROR) {
+                request->err = err;
+                continue;
+            }
             err = privacyBuffer.flush(request->fd);
-            if (err != NO_ERROR) { request->err = err; continue; }
+            if (err != NO_ERROR) {
+                request->err = err;
+                continue;
+            }
             writeable++;
-            ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id,
-                  privacyBuffer.size(), request->fd, spec.dest);
+            VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(),
+                 request->fd, spec.dest);
         }
         privacyBuffer.clear();
     }
 
     // The dropbox file
     if (requests->mainFd() >= 0) {
-        PrivacySpec spec = requests->mainDest() < 0 ?
-                PrivacySpec::get_default_dropbox_spec() :
-                PrivacySpec::new_spec(requests->mainDest());
+        PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest());
         err = privacyBuffer.strip(spec);
-        if (err != NO_ERROR) return err; // the buffer data is corrupted.
+        if (err != NO_ERROR) return err;  // the buffer data is corrupted.
         if (privacyBuffer.size() == 0) goto DONE;
 
         err = write_section_header(requests->mainFd(), id, privacyBuffer.size());
-        if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
+        if (err != NO_ERROR) {
+            requests->setMainFd(-1);
+            goto DONE;
+        }
         err = privacyBuffer.flush(requests->mainFd());
-        if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
+        if (err != NO_ERROR) {
+            requests->setMainFd(-1);
+            goto DONE;
+        }
         writeable++;
-        ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id,
-              privacyBuffer.size(), requests->mainFd(), spec.dest);
+        VLOG("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(),
+             requests->mainFd(), spec.dest);
+        stats->set_report_size_bytes(privacyBuffer.size());
     }
 
 DONE:
@@ -204,67 +195,78 @@
 }
 
 // ================================================================================
-Section::Section(int i, const int64_t timeoutMs)
-    :id(i),
-     timeoutMs(timeoutMs)
-{
-}
+Section::Section(int i, const int64_t timeoutMs) : id(i), timeoutMs(timeoutMs) {}
 
-Section::~Section()
-{
-}
+Section::~Section() {}
 
 // ================================================================================
-HeaderSection::HeaderSection()
-    :Section(FIELD_ID_INCIDENT_HEADER, 0)
-{
-}
+HeaderSection::HeaderSection() : Section(FIELD_ID_INCIDENT_HEADER, 0) {}
 
-HeaderSection::~HeaderSection()
-{
-}
+HeaderSection::~HeaderSection() {}
 
-status_t
-HeaderSection::Execute(ReportRequestSet* requests) const
-{
-    for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
+status_t HeaderSection::Execute(ReportRequestSet* requests) const {
+    for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
         const sp<ReportRequest> request = *it;
         const vector<vector<uint8_t>>& headers = request->args.headers();
 
-        for (vector<vector<uint8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
+        for (vector<vector<uint8_t>>::const_iterator buf = headers.begin(); buf != headers.end();
+             buf++) {
             if (buf->empty()) continue;
 
             // So the idea is only requests with negative fd are written to dropbox file.
             int fd = request->fd >= 0 ? request->fd : requests->mainFd();
-            write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size());
-            write_all(fd, (uint8_t const*)buf->data(), buf->size());
+            write_section_header(fd, id, buf->size());
+            WriteFully(fd, (uint8_t const*)buf->data(), buf->size());
             // If there was an error now, there will be an error later and we will remove
             // it from the list then.
         }
     }
     return NO_ERROR;
 }
+// ================================================================================
+MetadataSection::MetadataSection() : Section(FIELD_ID_INCIDENT_METADATA, 0) {}
 
+MetadataSection::~MetadataSection() {}
+
+status_t MetadataSection::Execute(ReportRequestSet* requests) const {
+    std::string metadataBuf;
+    requests->metadata().SerializeToString(&metadataBuf);
+    for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
+        const sp<ReportRequest> request = *it;
+        if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) {
+            continue;
+        }
+        write_section_header(request->fd, id, metadataBuf.size());
+        if (!WriteFully(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size())) {
+            ALOGW("Failed to write metadata to fd %d", request->fd);
+            // we don't fail if we can't write to a single request's fd.
+        }
+    }
+    if (requests->mainFd() >= 0 && !metadataBuf.empty()) {
+        write_section_header(requests->mainFd(), id, metadataBuf.size());
+        if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size())) {
+            ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd());
+            return -1;
+        }
+    }
+    return NO_ERROR;
+}
 // ================================================================================
 FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
-    :Section(id, timeoutMs),
-     mFilename(filename)
-{
+    : Section(id, timeoutMs), mFilename(filename) {
     name = filename;
     mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
 }
 
 FileSection::~FileSection() {}
 
-status_t
-FileSection::Execute(ReportRequestSet* requests) const
-{
+status_t FileSection::Execute(ReportRequestSet* requests) const {
     // read from mFilename first, make sure the file is available
     // add O_CLOEXEC to make sure it is closed when exec incident helper
     int fd = open(mFilename, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
-       ALOGW("FileSection '%s' failed to open file", this->name.string());
-       return -errno;
+        ALOGW("FileSection '%s' failed to open file", this->name.string());
+        return -errno;
     }
 
     FdBuffer buffer;
@@ -284,22 +286,23 @@
 
     // parent process
     status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
-            this->timeoutMs, mIsSysfs);
+                                                           this->timeoutMs, mIsSysfs);
     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");
+              this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
         kill_child(pid);
         return readStatus;
     }
 
     status_t ihStatus = wait_child(pid);
     if (ihStatus != NO_ERROR) {
-        ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus));
+        ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(),
+              strerror(-ihStatus));
         return ihStatus;
     }
 
-    ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
-            (int)buffer.durationMs());
+    VLOG("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
+         (int)buffer.durationMs());
     status_t err = write_report_requests(this->id, buffer, requests);
     if (err != NO_ERROR) {
         ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
@@ -310,8 +313,7 @@
 }
 
 // ================================================================================
-struct WorkerThreadData : public virtual RefBase
-{
+struct WorkerThreadData : public virtual RefBase {
     const WorkerThreadSection* section;
     int fds[2];
 
@@ -328,31 +330,19 @@
 };
 
 WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
-    :section(sec),
-     workerDone(false),
-     workerError(NO_ERROR)
-{
+    : section(sec), workerDone(false), workerError(NO_ERROR) {
     fds[0] = -1;
     fds[1] = -1;
 }
 
-WorkerThreadData::~WorkerThreadData()
-{
-}
+WorkerThreadData::~WorkerThreadData() {}
 
 // ================================================================================
-WorkerThreadSection::WorkerThreadSection(int id)
-    :Section(id)
-{
-}
+WorkerThreadSection::WorkerThreadSection(int id) : Section(id) {}
 
-WorkerThreadSection::~WorkerThreadSection()
-{
-}
+WorkerThreadSection::~WorkerThreadSection() {}
 
-static void*
-worker_thread_func(void* cookie)
-{
+static void* worker_thread_func(void* cookie) {
     WorkerThreadData* data = (WorkerThreadData*)cookie;
     status_t err = data->section->BlockingCall(data->writeFd());
 
@@ -368,9 +358,7 @@
     return NULL;
 }
 
-status_t
-WorkerThreadSection::Execute(ReportRequestSet* requests) const
-{
+status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const {
     status_t err = NO_ERROR;
     pthread_t thread;
     pthread_attr_t attr;
@@ -413,7 +401,7 @@
     if (err != NO_ERROR) {
         // TODO: Log this error into the incident report.
         ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
-                strerror(-err));
+              strerror(-err));
     }
 
     // Done with the read fd. The worker thread closes the write one so
@@ -432,7 +420,7 @@
                 err = data->workerError;
                 // TODO: Log this error into the incident report.
                 ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(),
-                        strerror(-err));
+                      strerror(-err));
             }
         }
     }
@@ -450,13 +438,13 @@
     // just exit with a log messasge.
     if (err != NO_ERROR) {
         ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(),
-                strerror(-err));
+              strerror(-err));
         return NO_ERROR;
     }
 
     // Write the data that was collected
-    ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
-            (int)buffer.durationMs());
+    VLOG("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
+         (int)buffer.durationMs());
     err = write_report_requests(this->id, buffer, requests);
     if (err != NO_ERROR) {
         ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
@@ -467,14 +455,12 @@
 }
 
 // ================================================================================
-void
-CommandSection::init(const char* command, va_list args)
-{
+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) {
+    while (va_arg(copied_args, const char*) != NULL) {
         numOfArgs++;
     }
     va_end(copied_args);
@@ -484,41 +470,33 @@
 
     mCommand[0] = command;
     name = command;
-    for (int i=0; i<numOfArgs; i++) {
+    for (int i = 0; i < numOfArgs; i++) {
         const char* arg = va_arg(args, const char*);
-        mCommand[i+1] = arg;
+        mCommand[i + 1] = arg;
         name += " ";
         name += arg;
     }
-    mCommand[numOfArgs+1] = NULL;
+    mCommand[numOfArgs + 1] = NULL;
 }
 
 CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
-    :Section(id, timeoutMs)
-{
+    : Section(id, timeoutMs) {
     va_list args;
     va_start(args, command);
     init(command, args);
     va_end(args);
 }
 
-CommandSection::CommandSection(int id, const char* command, ...)
-    :Section(id)
-{
+CommandSection::CommandSection(int id, const char* command, ...) : Section(id) {
     va_list args;
     va_start(args, command);
     init(command, args);
     va_end(args);
 }
 
-CommandSection::~CommandSection()
-{
-    free(mCommand);
-}
+CommandSection::~CommandSection() { free(mCommand); }
 
-status_t
-CommandSection::Execute(ReportRequestSet* requests) const
-{
+status_t CommandSection::Execute(ReportRequestSet* requests) const {
     FdBuffer buffer;
     Fpipe cmdPipe;
     Fpipe ihPipe;
@@ -537,13 +515,15 @@
     if (cmdPid == 0) {
         // replace command's stdout with ihPipe's write Fd
         if (dup2(cmdPipe.writeFd(), STDOUT_FILENO) != 1 || !ihPipe.close() || !cmdPipe.close()) {
-            ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno));
+            ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(),
+                  strerror(errno));
             _exit(EXIT_FAILURE);
         }
-        execvp(this->mCommand[0], (char *const *) this->mCommand);
-        int err = errno; // record command error code
-        ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno));
-        _exit(err); // exit with command error code
+        execvp(this->mCommand[0], (char* const*)this->mCommand);
+        int err = errno;  // record command error code
+        ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(),
+              strerror(errno));
+        _exit(err);  // exit with command error code
     }
     pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe);
     if (ihPid == -1) {
@@ -555,24 +535,26 @@
     status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s",
-            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+              this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
         kill_child(cmdPid);
         kill_child(ihPid);
         return readStatus;
     }
 
-    // TODO: wait for command here has one trade-off: the failed status of command won't be detected until
+    // 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.
     status_t cmdStatus = wait_child(cmdPid);
-    status_t ihStatus  = wait_child(ihPid);
+    status_t ihStatus = wait_child(ihPid);
     if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
-        ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s",
-            this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
+        ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident "
+              "helper: %s",
+              this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
         return cmdStatus != NO_ERROR ? cmdStatus : ihStatus;
     }
 
-    ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
-            (int)buffer.durationMs());
+    VLOG("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
+         (int)buffer.durationMs());
     status_t err = write_report_requests(this->id, buffer, requests);
     if (err != NO_ERROR) {
         ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err));
@@ -583,9 +565,7 @@
 
 // ================================================================================
 DumpsysSection::DumpsysSection(int id, const char* service, ...)
-    :WorkerThreadSection(id),
-     mService(service)
-{
+    : WorkerThreadSection(id), mService(service) {
     name = "dumpsys ";
     name += service;
 
@@ -603,13 +583,9 @@
     va_end(args);
 }
 
-DumpsysSection::~DumpsysSection()
-{
-}
+DumpsysSection::~DumpsysSection() {}
 
-status_t
-DumpsysSection::BlockingCall(int pipeWriteFd) const
-{
+status_t DumpsysSection::BlockingCall(int pipeWriteFd) const {
     // checkService won't wait for the service to show up like getService will.
     sp<IBinder> service = defaultServiceManager()->checkService(mService);
 
@@ -633,30 +609,23 @@
 // initialization only once in Section.cpp.
 map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
 
-LogSection::LogSection(int id, log_id_t logID)
-    :WorkerThreadSection(id),
-     mLogID(logID)
-{
+LogSection::LogSection(int id, log_id_t logID) : WorkerThreadSection(id), mLogID(logID) {
     name += "logcat ";
     name += android_log_id_to_name(logID);
     switch (logID) {
-    case LOG_ID_EVENTS:
-    case LOG_ID_STATS:
-    case LOG_ID_SECURITY:
-        mBinary = true;
-        break;
-    default:
-        mBinary = false;
+        case LOG_ID_EVENTS:
+        case LOG_ID_STATS:
+        case LOG_ID_SECURITY:
+            mBinary = true;
+            break;
+        default:
+            mBinary = false;
     }
 }
 
-LogSection::~LogSection()
-{
-}
+LogSection::~LogSection() {}
 
-static size_t
-trimTail(char const* buf, size_t len)
-{
+static size_t trimTail(char const* buf, size_t len) {
     while (len > 0) {
         char c = buf[len - 1];
         if (c == '\0' || c == ' ' || c == '\n' || c == '\r' || c == ':') {
@@ -672,17 +641,15 @@
     return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
 }
 
-status_t
-LogSection::BlockingCall(int pipeWriteFd) const
-{
+status_t LogSection::BlockingCall(int pipeWriteFd) const {
     status_t err = NO_ERROR;
     // Open log buffer and getting logs since last retrieved time if any.
     unique_ptr<logger_list, void (*)(logger_list*)> loggers(
-        gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() ?
-        android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) :
-        android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
-            gLastLogsRetrieved[mLogID], 0),
-        android_logger_list_free);
+            gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end()
+                    ? android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0)
+                    : android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                                                     gLastLogsRetrieved[mLogID], 0),
+            android_logger_list_free);
 
     if (android_logger_open(loggers.get(), mLogID) == NULL) {
         ALOGW("LogSection %s: Can't get logger.", this->name.string());
@@ -693,7 +660,7 @@
     log_time lastTimestamp(0);
 
     ProtoOutputStream proto;
-    while (true) { // keeps reading until logd buffer is fully read.
+    while (true) {  // keeps reading until logd buffer is fully read.
         status_t err = android_logger_list_read(loggers.get(), &msg);
         // err = 0 - no content, unexpected connection drop or EOF.
         // err = +ive number - size of retrieved data from logger
@@ -708,7 +675,8 @@
         if (mBinary) {
             // remove the first uint32 which is tag's index in event log tags
             android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t),
-                    msg.len() - sizeof(uint32_t));;
+                                                                    msg.len() - sizeof(uint32_t));
+            ;
             android_log_list_element elem;
 
             lastTimestamp.tv_sec = msg.entry_v1.sec;
@@ -718,38 +686,46 @@
             long long token = proto.start(LogProto::BINARY_LOGS);
             proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec);
             proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec);
-            proto.write(BinaryLogEntry::UID, (int) msg.entry_v4.uid);
+            proto.write(BinaryLogEntry::UID, (int)msg.entry_v4.uid);
             proto.write(BinaryLogEntry::PID, msg.entry_v1.pid);
             proto.write(BinaryLogEntry::TID, msg.entry_v1.tid);
-            proto.write(BinaryLogEntry::TAG_INDEX, get4LE(reinterpret_cast<uint8_t const*>(msg.msg())));
+            proto.write(BinaryLogEntry::TAG_INDEX,
+                        get4LE(reinterpret_cast<uint8_t const*>(msg.msg())));
             do {
                 elem = android_log_read_next(context);
                 long long elemToken = proto.start(BinaryLogEntry::ELEMS);
                 switch (elem.type) {
                     case EVENT_TYPE_INT:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_INT);
-                        proto.write(BinaryLogEntry::Elem::VAL_INT32, (int) elem.data.int32);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_INT);
+                        proto.write(BinaryLogEntry::Elem::VAL_INT32, (int)elem.data.int32);
                         break;
                     case EVENT_TYPE_LONG:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LONG);
-                        proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long) elem.data.int64);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_LONG);
+                        proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long)elem.data.int64);
                         break;
                     case EVENT_TYPE_STRING:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_STRING);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_STRING);
                         proto.write(BinaryLogEntry::Elem::VAL_STRING, elem.data.string, elem.len);
                         break;
                     case EVENT_TYPE_FLOAT:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_FLOAT);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_FLOAT);
                         proto.write(BinaryLogEntry::Elem::VAL_FLOAT, elem.data.float32);
                         break;
                     case EVENT_TYPE_LIST:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_LIST);
                         break;
                     case EVENT_TYPE_LIST_STOP:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP);
                         break;
                     case EVENT_TYPE_UNKNOWN:
-                        proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN);
+                        proto.write(BinaryLogEntry::Elem::TYPE,
+                                    BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN);
                         break;
                 }
                 proto.end(elemToken);
@@ -777,7 +753,8 @@
             proto.write(TextLogEntry::PID, entry.pid);
             proto.write(TextLogEntry::TID, entry.tid);
             proto.write(TextLogEntry::TAG, entry.tag, trimTail(entry.tag, entry.tagLen));
-            proto.write(TextLogEntry::LOG, entry.message, trimTail(entry.message, entry.messageLen));
+            proto.write(TextLogEntry::LOG, entry.message,
+                        trimTail(entry.message, entry.messageLen));
             proto.end(token);
         }
     }
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index d440ee9..d644681 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -13,31 +13,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef SECTIONS_H
 #define SECTIONS_H
 
 #include "Reporter.h"
 
-#include <map>
 #include <stdarg.h>
+#include <map>
 
-#include <utils/String8.h>
 #include <utils/String16.h>
+#include <utils/String8.h>
 #include <utils/Vector.h>
 
 using namespace android;
 
-const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
+const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000;  // 10 seconds
 
 /**
  * Base class for sections
  */
-class Section
-{
+class Section {
 public:
     const int id;
-    const int64_t timeoutMs; // each section must have a timeout
+    const int64_t timeoutMs;  // each section must have a timeout
     String8 name;
 
     Section(int id, const int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS);
@@ -49,8 +49,7 @@
 /**
  * Section that generates incident headers.
  */
-class HeaderSection : public Section
-{
+class HeaderSection : public Section {
 public:
     HeaderSection();
     virtual ~HeaderSection();
@@ -59,10 +58,20 @@
 };
 
 /**
+ * Section that generates incident metadata.
+ */
+class MetadataSection : public Section {
+public:
+    MetadataSection();
+    virtual ~MetadataSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+};
+
+/**
  * Section that reads in a file.
  */
-class FileSection : public Section
-{
+class FileSection : public Section {
 public:
     FileSection(int id, const char* filename, const int64_t timeoutMs = 5000 /* 5 seconds */);
     virtual ~FileSection();
@@ -71,14 +80,13 @@
 
 private:
     const char* mFilename;
-    bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately
+    bool mIsSysfs;  // sysfs files are pollable but return POLLERR by default, handle it separately
 };
 
 /**
  * Base class for sections that call a command that might need a timeout.
  */
-class WorkerThreadSection : public Section
-{
+class WorkerThreadSection : public Section {
 public:
     WorkerThreadSection(int id);
     virtual ~WorkerThreadSection();
@@ -91,8 +99,7 @@
 /**
  * Section that forks and execs a command, and puts stdout as the section.
  */
-class CommandSection : public Section
-{
+class CommandSection : public Section {
 public:
     CommandSection(int id, const int64_t timeoutMs, const char* command, ...);
 
@@ -111,8 +118,7 @@
 /**
  * Section that calls dumpsys on a system service.
  */
-class DumpsysSection : public WorkerThreadSection
-{
+class DumpsysSection : public WorkerThreadSection {
 public:
     DumpsysSection(int id, const char* service, ...);
     virtual ~DumpsysSection();
@@ -127,8 +133,7 @@
 /**
  * Section that reads from logd.
  */
-class LogSection : public WorkerThreadSection
-{
+class LogSection : public WorkerThreadSection {
     // global last log retrieved timestamp for each log_id_t.
     static map<log_id_t, log_time> gLastLogsRetrieved;
 
@@ -143,5 +148,4 @@
     bool mBinary;
 };
 
-#endif // SECTIONS_H
-
+#endif  // SECTIONS_H
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
new file mode 100644
index 0000000..2415860
--- /dev/null
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "incidentd_util.h"
+
+#include "section_list.h"
+
+const Privacy* get_privacy_of_section(int id) {
+    int l = 0;
+    int r = PRIVACY_POLICY_COUNT - 1;
+    while (l <= r) {
+        int mid = (l + r) >> 1;
+        const Privacy* p = PRIVACY_POLICY_LIST[mid];
+
+        if (p->field_id < (uint32_t)id) {
+            l = mid + 1;
+        } else if (p->field_id > (uint32_t)id) {
+            r = mid - 1;
+        } else {
+            return p;
+        }
+    }
+    return NULL;
+}
+
+// ================================================================================
+Fpipe::Fpipe() : mRead(), mWrite() {}
+
+Fpipe::~Fpipe() { close(); }
+
+bool Fpipe::close() {
+    mRead.reset();
+    mWrite.reset();
+    return true;
+}
+
+bool Fpipe::init() { return Pipe(&mRead, &mWrite); }
+
+int Fpipe::readFd() const { return mRead.get(); }
+
+int Fpipe::writeFd() const { return mWrite.get(); }
\ No newline at end of file
diff --git a/cmds/incidentd/src/io_util.h b/cmds/incidentd/src/incidentd_util.h
similarity index 74%
rename from cmds/incidentd/src/io_util.h
rename to cmds/incidentd/src/incidentd_util.h
index 320dd6c..09aa040 100644
--- a/cmds/incidentd/src/io_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -13,16 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-#ifndef IO_UTIL_H
-#define IO_UTIL_H
+#ifndef INCIDENTD_UTIL_H
+#define INCIDENTD_UTIL_H
 
-#include <stdint.h>
-#include <utils/Errors.h>
+#include <android-base/unique_fd.h>
 
-using namespace android;
+#include "Privacy.h"
 
-status_t write_all(int fd, uint8_t const* buf, size_t size);
+using namespace android::base;
+
+const Privacy* get_privacy_of_section(int id);
 
 class Fpipe {
 public:
@@ -35,7 +37,8 @@
     int writeFd() const;
 
 private:
-    int mFds[2];
+    unique_fd mRead;
+    unique_fd mWrite;
 };
 
-#endif // IO_UTIL_H
\ No newline at end of file
+#endif  // INCIDENTD_UTIL_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
deleted file mode 100644
index 90f543e..0000000
--- a/cmds/incidentd/src/io_util.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "incidentd"
-
-#include "io_util.h"
-
-#include <unistd.h>
-
-status_t write_all(int fd, uint8_t const* buf, size_t size)
-{
-    while (size > 0) {
-        ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size));
-        if (amt < 0) {
-            return -errno;
-        }
-        size -= amt;
-        buf += amt;
-    }
-    return NO_ERROR;
-}
-
-Fpipe::Fpipe() {}
-
-Fpipe::~Fpipe() { close(); }
-
-bool Fpipe::close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
-
-bool Fpipe::init() { return pipe(mFds) != -1; }
-
-int Fpipe::readFd() const { return mFds[0]; }
-
-int Fpipe::writeFd() const { return mFds[1]; }
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
index 3a7511d..38b7449 100644
--- a/cmds/incidentd/src/main.cpp
+++ b/cmds/incidentd/src/main.cpp
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "IncidentService.h"
 
@@ -23,25 +22,22 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <binder/Status.h>
-#include <cutils/log.h>
 #include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 
-#include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 
 using namespace android;
 
 // ================================================================================
-int
-main(int /*argc*/, char** /*argv*/)
-{
+int main(int /*argc*/, char** /*argv*/) {
     // Set up the looper
     sp<Looper> looper(Looper::prepare(0 /* opts */));
 
     // Set up the binder
     sp<ProcessState> ps(ProcessState::self());
-    ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram
+    ps->setThreadPoolMaxThreadCount(1);  // everything is oneway, let it queue and save ram
     ps->startThreadPool();
     ps->giveThreadPoolName();
     IPCThreadState::self()->disableBackgroundScheduling(true);
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index 20111d8..b71c066 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -13,19 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "report_directory.h"
 
-#include <cutils/log.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <dirent.h>
 #include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <vector>
@@ -33,9 +31,7 @@
 using namespace android;
 using namespace std;
 
-status_t
-create_directory(const char* directory)
-{
+status_t create_directory(const char* directory) {
     struct stat st;
     status_t err = NO_ERROR;
     char* dir = strdup(directory);
@@ -75,14 +71,14 @@
         goto done;
     }
     if ((st.st_mode & 0777) != 0770) {
-        ALOGE("No incident reports today. Mode is %0o on report directory %s",
-                st.st_mode, directory);
+        ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode,
+              directory);
         err = BAD_VALUE;
         goto done;
     }
     if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) {
         ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
-                st.st_uid, st.st_gid, directory);
+              st.st_uid, st.st_gid, directory);
         err = BAD_VALUE;
         goto done;
     }
@@ -92,20 +88,17 @@
     return err;
 }
 
-static bool
-stat_mtime_cmp(const pair<String8,struct stat>& a, const pair<String8,struct stat>& b)
-{
+static bool stat_mtime_cmp(const pair<String8, struct stat>& a,
+                           const pair<String8, struct stat>& b) {
     return a.second.st_mtime < b.second.st_mtime;
 }
 
-void
-clean_directory(const char* directory, off_t maxSize, size_t maxCount)
-{
+void clean_directory(const char* directory, off_t maxSize, size_t maxCount) {
     DIR* dir;
     struct dirent* entry;
     struct stat st;
 
-    vector<pair<String8,struct stat>> files;
+    vector<pair<String8, struct stat>> files;
 
     if ((dir = opendir(directory)) == NULL) {
         ALOGE("Couldn't open incident directory: %s", directory);
@@ -131,7 +124,7 @@
         if (!S_ISREG(st.st_mode)) {
             continue;
         }
-        files.push_back(pair<String8,struct stat>(filename, st));
+        files.push_back(pair<String8, struct stat>(filename, st));
 
         totalSize += st.st_size;
         totalCount++;
@@ -148,8 +141,8 @@
     sort(files.begin(), files.end(), stat_mtime_cmp);
 
     // Remove files until we're under our limits.
-    for (vector<pair<String8,struct stat>>::iterator it = files.begin();
-            it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) {
+    for (vector<pair<String8, struct stat>>::iterator it = files.begin();
+         it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) {
         remove(it->first.string());
         totalSize -= it->second.st_size;
         totalCount--;
diff --git a/cmds/incidentd/src/report_directory.h b/cmds/incidentd/src/report_directory.h
index bed4f86..2a3cf4c 100644
--- a/cmds/incidentd/src/report_directory.h
+++ b/cmds/incidentd/src/report_directory.h
@@ -13,17 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef DIRECTORY_CLEANER_H
 #define DIRECTORY_CLEANER_H
 
+#include <sys/types.h>
 #include <utils/Errors.h>
 
-#include <sys/types.h>
-
-using namespace android;
-
-status_t create_directory(const char* directory);
+android::status_t create_directory(const char* directory);
 void clean_directory(const char* directory, off_t maxSize, size_t maxCount);
 
-#endif // DIRECTORY_CLEANER_H
+#endif  // DIRECTORY_CLEANER_H
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
index ddc0505..697e66f 100644
--- a/cmds/incidentd/src/section_list.h
+++ b/cmds/incidentd/src/section_list.h
@@ -13,11 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
 #ifndef SECTION_LIST_H
 #define SECTION_LIST_H
 
-#include <log/log_event_list.h> // include log_id_t enums.
+#include <log/log_event_list.h>  // include log_id_t enums.
 
 #include "Privacy.h"
 #include "Section.h"
@@ -36,5 +37,4 @@
 
 extern const int PRIVACY_POLICY_COUNT;
 
-#endif // SECTION_LIST_H
-
+#endif  // SECTION_LIST_H
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 3fd2ed8..956c8d3 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -11,11 +11,10 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "FdBuffer.h"
-#include "io_util.h"
+#include "incidentd_util.h"
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
@@ -48,7 +47,7 @@
     }
 
     void AssertBufferContent(const char* expected) {
-        int i=0;
+        int i = 0;
         EncodedBuffer::iterator it = buffer.data();
         while (it.hasNext()) {
             ASSERT_EQ(it.next(), expected[i++]);
@@ -100,7 +99,7 @@
     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
     ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
 
-    int i=0;
+    int i = 0;
     EncodedBuffer::iterator it = buffer.data();
     while (it.hasNext()) {
         EXPECT_EQ(it.next(), (uint8_t)testdata[i++]);
@@ -118,7 +117,7 @@
 
     if (pid == 0) {
         close(c2pPipe.readFd());
-        while(true) {
+        while (true) {
             write(c2pPipe.writeFd(), "poo", 3);
             sleep(1);
         }
@@ -130,7 +129,7 @@
         ASSERT_EQ(NO_ERROR, status);
         EXPECT_TRUE(buffer.timedOut());
 
-        kill(pid, SIGKILL); // reap the child process
+        kill(pid, SIGKILL);  // reap the child process
     }
 }
 
@@ -155,8 +154,8 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
-            p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(),
+                                                             c2pPipe.readFd(), READ_TIMEOUT));
         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
         AssertBufferContent(expected.c_str());
         wait(&pid);
@@ -187,8 +186,8 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
-            p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(),
+                                                             c2pPipe.readFd(), READ_TIMEOUT));
         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
         AssertBufferContent(expected.c_str());
         wait(&pid);
@@ -212,8 +211,8 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
-            p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(),
+                                                             c2pPipe.readFd(), READ_TIMEOUT));
         AssertBufferReadSuccessful(0);
         AssertBufferContent("");
         wait(&pid);
@@ -222,7 +221,7 @@
 
 TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) {
     const std::string testFile = kTestDataPath + "morethan4MB.txt";
-    size_t fourMB = (size_t) 4 * 1024 * 1024;
+    size_t fourMB = (size_t)4 * 1024 * 1024;
     int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC);
     ASSERT_NE(fd, -1);
     int pid = fork();
@@ -239,8 +238,8 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd,
-            p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(),
+                                                             c2pPipe.readFd(), READ_TIMEOUT));
         EXPECT_EQ(buffer.size(), fourMB);
         EXPECT_FALSE(buffer.timedOut());
         EXPECT_TRUE(buffer.truncated());
@@ -276,9 +275,9 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
-            p2cPipe.writeFd(), c2pPipe.readFd(), QUICK_TIMEOUT_MS));
+        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(),
+                                                             c2pPipe.readFd(), QUICK_TIMEOUT_MS));
         EXPECT_TRUE(buffer.timedOut());
-        kill(pid, SIGKILL); // reap the child process
+        kill(pid, SIGKILL);  // reap the child process
     }
 }
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index c7bfe55..7ea9bbf 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -11,8 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "FdBuffer.h"
 #include "PrivacyBuffer.h"
@@ -37,13 +36,12 @@
 const uint8_t STRING_TYPE = 9;
 const uint8_t MESSAGE_TYPE = 11;
 const string STRING_FIELD_0 = "\x02\viamtestdata";
-const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150
+const string VARINT_FIELD_1 = "\x08\x96\x01";  // 150
 const string STRING_FIELD_2 = "\x12\vwhatthefuck";
-const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
-const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1
+const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff";  // -1
+const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff";                  // -1
 const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2;
 
-
 class PrivacyBufferTest : public Test {
 public:
     virtual ~PrivacyBufferTest() {
@@ -55,9 +53,7 @@
         }
     }
 
-    virtual void SetUp() override {
-        ASSERT_NE(tf.fd, -1);
-    }
+    virtual void SetUp() override { ASSERT_NE(tf.fd, -1); }
 
     void writeToFdBuffer(string str) {
         ASSERT_TRUE(WriteStringToFile(str, tf.path));
@@ -81,11 +77,11 @@
     }
 
     void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) {
-        Privacy* list[size+1];
+        Privacy* list[size + 1];
         list[0] = privacy;
         va_list args;
         va_start(args, privacy);
-        for (int i=1; i<size; i++) {
+        for (int i = 1; i < size; i++) {
             Privacy* p = va_arg(args, Privacy*);
             list[i] = p;
         }
@@ -115,13 +111,14 @@
     }
 
     FdBuffer buffer;
+
 private:
     TemporaryFile tf;
     // Littering this code with unique_ptr (or similar) is ugly, so we just
     // mass-free everything after the test completes.
-    std::vector<Privacy *> privacies;
+    std::vector<Privacy*> privacies;
 
-    Privacy *new_uninit_privacy() {
+    Privacy* new_uninit_privacy() {
         Privacy* p = new Privacy;
         privacies.push_back(p);
         return p;
@@ -165,63 +162,69 @@
 
 TEST_F(PrivacyBufferTest, NoStripVarintField) {
     writeToFdBuffer(VARINT_FIELD_1);
-    assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1,
+                        create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) {
     writeToFdBuffer(STRING_FIELD_2);
-    assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, DEST_AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1,
+                        create_privacy(2, STRING_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripFixed64Field) {
     writeToFdBuffer(FIX64_FIELD_3);
-    assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1,
+                        create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripFixed32Field) {
     writeToFdBuffer(FIX32_FIELD_4);
-    assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1,
+                        create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) {
     writeToFdBuffer(MESSAGE_FIELD_5);
-    assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1,
+                        create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintAndString) {
-    writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
-            + FIX64_FIELD_3 + FIX32_FIELD_4);
+    writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 +
+                    FIX32_FIELD_4);
     string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
-    assertStripByFields(DEST_EXPLICIT, expected, 2,
-            create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(2, STRING_TYPE, DEST_LOCAL));
+    assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL),
+                        create_privacy(2, STRING_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintAndFixed64) {
-    writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
-            + FIX64_FIELD_3 + FIX32_FIELD_4);
+    writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 +
+                    FIX32_FIELD_4);
     string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
-    assertStripByFields(DEST_EXPLICIT, expected, 2,
-            create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(3, OTHER_TYPE, DEST_LOCAL));
+    assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL),
+                        create_privacy(3, OTHER_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) {
     writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
+    Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL};
     string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
     assertStripByFields(DEST_EXPLICIT, expected, 1, create_message_privacy(5, list));
 }
 
 TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) {
     writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
+    Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL};
     string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
-    assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), create_message_privacy(5, list));
+    assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL),
+                        create_message_privacy(5, list));
 }
 
 TEST_F(PrivacyBufferTest, ClearAndStrip) {
     string data = STRING_FIELD_0 + VARINT_FIELD_1;
     writeToFdBuffer(data);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
+    Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL};
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
     PrivacySpec spec1 = PrivacySpec::new_spec(DEST_EXPLICIT);
@@ -235,7 +238,7 @@
 
 TEST_F(PrivacyBufferTest, BadDataInFdBuffer) {
     writeToFdBuffer("iambaddata");
-    Privacy* list[] = { create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL };
+    Privacy* list[] = {create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL};
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
     PrivacySpec spec;
@@ -244,8 +247,8 @@
 
 TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
     writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
-    Privacy* field5[] = { create_message_privacy(5, list), NULL };
+    Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL};
+    Privacy* field5[] = {create_message_privacy(5, list), NULL};
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData);
     PrivacySpec spec;
@@ -256,7 +259,7 @@
     string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5;
     writeToFdBuffer(input);
     Privacy* field5 = create_message_privacy(5, NULL);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL };
+    Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL};
     field5->children = list;
     string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2;
     assertStrip(DEST_EXPLICIT, expected, field5);
@@ -264,7 +267,7 @@
 
 TEST_F(PrivacyBufferTest, AutoMessage) {
     writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
+    Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL};
     Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC);
     autoMsg->children = list;
     string expected = "\x2a\xd" + STRING_FIELD_2;
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index c494bd6..bd359ac 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -11,8 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "Reporter.h"
 
@@ -26,7 +25,6 @@
 #include <gtest/gtest.h>
 #include <string.h>
 
-
 using namespace android;
 using namespace android::base;
 using namespace android::binder;
@@ -35,8 +33,7 @@
 using ::testing::StrEq;
 using ::testing::Test;
 
-class TestListener : public IIncidentReportStatusListener
-{
+class TestListener : public IIncidentReportStatusListener {
 public:
     int startInvoked;
     int finishInvoked;
@@ -44,8 +41,8 @@
     map<int, int> startSections;
     map<int, int> finishSections;
 
-    TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0) {};
-    virtual ~TestListener() {};
+    TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0){};
+    virtual ~TestListener(){};
 
     virtual Status onReportStarted() {
         startInvoked++;
@@ -53,16 +50,14 @@
     };
     virtual Status onReportSectionStatus(int section, int status) {
         switch (status) {
-        case IIncidentReportStatusListener::STATUS_STARTING:
-            if (startSections.count(section) == 0)
-                startSections[section] = 0;
-            startSections[section] = startSections[section] + 1;
-            break;
-        case IIncidentReportStatusListener::STATUS_FINISHED:
-            if (finishSections.count(section) == 0)
-                finishSections[section] = 0;
-            finishSections[section] = finishSections[section] + 1;
-            break;
+            case IIncidentReportStatusListener::STATUS_STARTING:
+                if (startSections.count(section) == 0) startSections[section] = 0;
+                startSections[section] = startSections[section] + 1;
+                break;
+            case IIncidentReportStatusListener::STATUS_FINISHED:
+                if (finishSections.count(section) == 0) finishSections[section] = 0;
+                finishSections[section] = finishSections[section] + 1;
+                break;
         }
         return Status::ok();
     };
@@ -156,7 +151,8 @@
 
     string result;
     ReadFileToString(tf.path, &result);
-    EXPECT_THAT(result, StrEq("\n\x2" "\b\f"));
+    EXPECT_THAT(result, StrEq("\n\x2"
+                              "\b\f"));
 
     EXPECT_EQ(l->startInvoked, 2);
     EXPECT_EQ(l->finishInvoked, 2);
@@ -178,5 +174,24 @@
     ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
     vector<string> results = InspectFiles();
     ASSERT_EQ((int)results.size(), 1);
-    EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd");
+    EXPECT_EQ(results[0],
+              "\n\x2"
+              "\b\f\n\x6"
+              "\x12\x4"
+              "abcd");
 }
+
+TEST_F(ReporterTest, ReportMetadata) {
+    IncidentReportArgs args;
+    args.addSection(1);
+    args.setDest(android::os::DEST_EXPLICIT);
+    sp<ReportRequest> r = new ReportRequest(args, l, -1);
+    reporter->batch.add(r);
+
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    auto metadata = reporter->batch.metadata();
+    EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest());
+    EXPECT_EQ(1, metadata.request_size());
+    EXPECT_TRUE(metadata.use_dropbox());
+    EXPECT_EQ(0, metadata.sections_size());
+}
\ No newline at end of file
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 2cfd7df..a1f4fdc 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -11,13 +11,13 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#define LOG_TAG "incidentd"
+#include "Log.h"
 
 #include "Section.h"
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
+#include <android/os/IncidentReportArgs.h>
 #include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -29,9 +29,9 @@
 
 const int QUICK_TIMEOUT_MS = 100;
 
-const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150
+const string VARINT_FIELD_1 = "\x08\x96\x01";  // 150
 const string STRING_FIELD_2 = "\x12\vwhatthefuck";
-const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
+const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff";  // -1
 
 using namespace android::base;
 using namespace android::binder;
@@ -43,11 +43,10 @@
 
 // NOTICE: this test requires /system/bin/incident_helper is installed.
 
-class SimpleListener : public IIncidentReportStatusListener
-{
+class SimpleListener : public IIncidentReportStatusListener {
 public:
-    SimpleListener() {};
-    virtual ~SimpleListener() {};
+    SimpleListener(){};
+    virtual ~SimpleListener(){};
 
     virtual Status onReportStarted() { return Status::ok(); };
     virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); };
@@ -83,12 +82,26 @@
     string content;
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
-    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup"));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5"
+                                           "\x12\x3"
+                                           "axe\n\x05\x12\x03pup"));
 
     EXPECT_TRUE(ReadFileToString(output2.path, &content));
     EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
 }
 
+TEST(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"));
+}
+
 TEST(SectionTest, FileSection) {
     TemporaryFile tf;
     FileSection fs(REVERSE_PARSER, tf.path);
@@ -243,8 +256,9 @@
 
     IncidentReportArgs args1, args2, args3;
     args1.setAll(true);
-    args1.setDest(0); // LOCAL
-    args2.setAll(true); // default to explicit
+    args1.setDest(android::os::DEST_LOCAL);
+    args2.setAll(true);
+    args2.setDest(android::os::DEST_EXPLICIT);
     sp<SimpleListener> l = new SimpleListener();
     requests.add(new ReportRequest(args1, l, output1.fd));
     requests.add(new ReportRequest(args2, l, output2.fd));
@@ -257,12 +271,12 @@
 
     string content, expect;
     expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3;
-    char c = (char) expect.size();
+    char c = (char)expect.size();
     EXPECT_TRUE(ReadFileToString(output1.path, &content));
     EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
 
     expect = STRING_FIELD_2 + FIX64_FIELD_3;
-    c = (char) expect.size();
+    c = (char)expect.size();
     EXPECT_TRUE(ReadFileToString(output2.path, &content));
     EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
 
@@ -283,10 +297,12 @@
 
     ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
 
-    IncidentReportArgs args1, args2, args3, args4;
+    IncidentReportArgs args1, args2, args3;
     args1.setAll(true);
+    args1.setDest(android::os::DEST_EXPLICIT);
     args2.setAll(true);
-    args4.setAll(true);
+    args2.setDest(android::os::DEST_EXPLICIT);
+    args3.setAll(true);
     sp<SimpleListener> l = new SimpleListener();
     requests.add(new ReportRequest(args1, l, output1.fd));
     requests.add(new ReportRequest(args2, l, output2.fd));
@@ -299,7 +315,7 @@
 
     string content, expect;
     expect = STRING_FIELD_2 + FIX64_FIELD_3;
-    char c = (char) expect.size();
+    char c = (char)expect.size();
 
     // output1 and output2 are the same
     EXPECT_TRUE(ReadFileToString(output1.path, &content));
@@ -307,7 +323,8 @@
     EXPECT_TRUE(ReadFileToString(output2.path, &content));
     EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
 
-    // because args3 doesn't set section, so it should receive nothing
+    // output3 has only auto field
+    c = (char)STRING_FIELD_2.size();
     EXPECT_TRUE(ReadFileToString(output3.path, &content));
-    EXPECT_THAT(content, StrEq(""));
+    EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2));
 }
\ No newline at end of file
diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp
index 1d6213f..bd2d15c 100644
--- a/cmds/incidentd/tests/section_list.cpp
+++ b/cmds/incidentd/tests/section_list.cpp
@@ -1,25 +1,17 @@
 // This file is a dummy section_list.cpp used for test only.
 #include "section_list.h"
 
-const Section* SECTION_LIST[] = {
-    NULL
-};
+const Section* SECTION_LIST[] = {NULL};
 
-Privacy sub_field_1 { 1, 1, NULL, DEST_LOCAL, NULL };
-Privacy sub_field_2 { 2, 9, NULL, DEST_AUTOMATIC, NULL };
+Privacy sub_field_1{1, 1, NULL, DEST_LOCAL, NULL};
+Privacy sub_field_2{2, 9, NULL, DEST_AUTOMATIC, NULL};
 
-Privacy* list[] = {
-    &sub_field_1,
-    &sub_field_2,
-    NULL };
+Privacy* list[] = {&sub_field_1, &sub_field_2, NULL};
 
-Privacy field_0 { 0, 11, list, DEST_EXPLICIT, NULL };
-Privacy field_1 { 1, 9, NULL, DEST_AUTOMATIC, NULL };
+Privacy field_0{0, 11, list, DEST_EXPLICIT, NULL};
+Privacy field_1{1, 9, NULL, DEST_AUTOMATIC, NULL};
 
-Privacy* final_list[] = {
-    &field_0,
-    &field_1
-};
+Privacy* final_list[] = {&field_0, &field_1};
 
 const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(final_list);
 
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
new file mode 100644
index 0000000..362d411
--- /dev/null
+++ b/cmds/statsd/OWNERS
@@ -0,0 +1,6 @@
+bookatz@google.com
+jinyithu@google.com
+kwekua@google.com
+stlafon@google.com
+yaochen@google.com
+yanglu@google.com
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0a4e412..791fb14 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -571,8 +571,8 @@
             good = true;
         } else {
             fprintf(out,
-                    "Selecting a UID for writing Appbreadcrumb can only be dumped for other UIDs on eng"
-                            " or userdebug builds.\n");
+                    "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
+                            "on eng or userdebug builds.\n");
         }
     }
     if (good) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index fd65869..9690de7 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -193,8 +193,8 @@
     status_t cmd_write_data_to_disk(FILE* out);
 
     /**
-     * Write an AppBreadcrumbReported event to the StatsLog buffer, as though StatsLog.write
-     * (APP_BREADCRUMB_REPORTED).
+     * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling
+     * StatsLog.write(APP_BREADCRUMB_REPORTED).
      */
     status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args);
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1224504..85e209b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1471,7 +1471,7 @@
  */
 message CpuActiveTime {
     optional uint64 uid = 1;
-    optional uint32 cluster_number =2;
+    optional uint32 cluster_number = 2;
     optional uint64 idx = 3;
     optional uint64 time_millis = 4;
 }
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index fbb0fdd..d0f55ab 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -59,45 +59,71 @@
 }
 
 void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
+    lock_guard<mutex> lock(mMutex);
     mListeners.push_back(listener);
 }
 
 void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) {
-    // Add to set
-    mConfigs.insert(key);
+    vector<sp<ConfigListener>> broadcastList;
+    {
+        lock_guard <mutex> lock(mMutex);
 
-    // Save to disk
-    update_saved_configs(key, config);
+        // Add to set
+        mConfigs.insert(key);
+
+        // Save to disk
+        update_saved_configs_locked(key, config);
+
+        for (sp<ConfigListener> listener : mListeners) {
+            broadcastList.push_back(listener);
+        }
+    }
 
     // Tell everyone
-    for (auto& listener : mListeners) {
+    for (sp<ConfigListener> listener:broadcastList) {
         listener->OnConfigUpdated(key, config);
     }
 }
 
 void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+    lock_guard<mutex> lock(mMutex);
     mConfigReceivers[key] = intentSender;
 }
 
 void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
+    lock_guard<mutex> lock(mMutex);
     mConfigReceivers.erase(key);
 }
 
 void ConfigManager::RemoveConfig(const ConfigKey& key) {
-    auto it = mConfigs.find(key);
-    if (it != mConfigs.end()) {
-        // Remove from map
-        mConfigs.erase(it);
+    vector<sp<ConfigListener>> broadcastList;
+    {
+        lock_guard <mutex> lock(mMutex);
 
-        // Tell everyone
-        for (auto& listener : mListeners) {
-            listener->OnConfigRemoved(key);
+        auto it = mConfigs.find(key);
+        if (it != mConfigs.end()) {
+            // Remove from map
+            mConfigs.erase(it);
+
+            for (sp<ConfigListener> listener : mListeners) {
+                broadcastList.push_back(listener);
+            }
         }
+
+        auto itReceiver = mConfigReceivers.find(key);
+        if (itReceiver != mConfigReceivers.end()) {
+            // Remove from map
+            mConfigReceivers.erase(itReceiver);
+        }
+
+        // Remove from disk. There can still be a lingering file on disk so we check
+        // whether or not the config was on memory.
+        remove_saved_configs(key);
     }
 
-    // Remove from disk. There can still be a lingering file on disk so we check
-    // whether or not the config was on memory.
-    remove_saved_configs(key);
+    for (sp<ConfigListener> listener:broadcastList) {
+        listener->OnConfigRemoved(key);
+    }
 }
 
 void ConfigManager::remove_saved_configs(const ConfigKey& key) {
@@ -107,23 +133,32 @@
 
 void ConfigManager::RemoveConfigs(int uid) {
     vector<ConfigKey> removed;
+    vector<sp<ConfigListener>> broadcastList;
+    {
+        lock_guard <mutex> lock(mMutex);
 
-    for (auto it = mConfigs.begin(); it != mConfigs.end();) {
-        // Remove from map
-        if (it->GetUid() == uid) {
-            remove_saved_configs(*it);
-            removed.push_back(*it);
-            mConfigReceivers.erase(*it);
-            it = mConfigs.erase(it);
-        } else {
-            it++;
+
+        for (auto it = mConfigs.begin(); it != mConfigs.end();) {
+            // Remove from map
+            if (it->GetUid() == uid) {
+                remove_saved_configs(*it);
+                removed.push_back(*it);
+                mConfigReceivers.erase(*it);
+                it = mConfigs.erase(it);
+            } else {
+                it++;
+            }
+        }
+
+        for (sp<ConfigListener> listener : mListeners) {
+            broadcastList.push_back(listener);
         }
     }
 
     // Remove separately so if they do anything in the callback they can't mess up our iteration.
     for (auto& key : removed) {
         // Tell everyone
-        for (auto& listener : mListeners) {
+        for (sp<ConfigListener> listener:broadcastList) {
             listener->OnConfigRemoved(key);
         }
     }
@@ -131,27 +166,38 @@
 
 void ConfigManager::RemoveAllConfigs() {
     vector<ConfigKey> removed;
+    vector<sp<ConfigListener>> broadcastList;
+    {
+        lock_guard <mutex> lock(mMutex);
 
-    for (auto it = mConfigs.begin(); it != mConfigs.end();) {
-        // Remove from map
-        removed.push_back(*it);
-        auto receiverIt = mConfigReceivers.find(*it);
-        if (receiverIt != mConfigReceivers.end()) {
-            mConfigReceivers.erase(*it);
+
+        for (auto it = mConfigs.begin(); it != mConfigs.end();) {
+            // Remove from map
+            removed.push_back(*it);
+            auto receiverIt = mConfigReceivers.find(*it);
+            if (receiverIt != mConfigReceivers.end()) {
+                mConfigReceivers.erase(*it);
+            }
+            it = mConfigs.erase(it);
         }
-        it = mConfigs.erase(it);
+
+        for (sp<ConfigListener> listener : mListeners) {
+            broadcastList.push_back(listener);
+        }
     }
 
     // Remove separately so if they do anything in the callback they can't mess up our iteration.
     for (auto& key : removed) {
         // Tell everyone
-        for (auto& listener : mListeners) {
+        for (sp<ConfigListener> listener:broadcastList) {
             listener->OnConfigRemoved(key);
         }
     }
 }
 
 vector<ConfigKey> ConfigManager::GetAllConfigKeys() const {
+    lock_guard<mutex> lock(mMutex);
+
     vector<ConfigKey> ret;
     for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
         ret.push_back(*it);
@@ -160,6 +206,8 @@
 }
 
 const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+    lock_guard<mutex> lock(mMutex);
+
     auto it = mConfigReceivers.find(key);
     if (it == mConfigReceivers.end()) {
         return nullptr;
@@ -169,6 +217,8 @@
 }
 
 void ConfigManager::Dump(FILE* out) {
+    lock_guard<mutex> lock(mMutex);
+
     fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
     fprintf(out, "     uid name\n");
     for (const auto& key : mConfigs) {
@@ -180,7 +230,7 @@
     }
 }
 
-void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) {
+void ConfigManager::update_saved_configs_locked(const ConfigKey& key, const StatsdConfig& config) {
     // If there is a pre-existing config with same key we should first delete it.
     remove_saved_configs(key);
 
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index a2b2a0c..a0c1c1c 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -21,6 +21,7 @@
 #include "config/ConfigListener.h"
 
 #include <map>
+#include <mutex>
 #include <set>
 #include <string>
 
@@ -109,10 +110,12 @@
     void Dump(FILE* out);
 
 private:
+    mutable std::mutex mMutex;
+
     /**
      * Save the configs to disk.
      */
-    void update_saved_configs(const ConfigKey& key, const StatsdConfig& config);
+    void update_saved_configs_locked(const ConfigKey& key, const StatsdConfig& config);
 
     /**
      * Remove saved configs from disk.
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3b7936d..9c65371 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -111,12 +111,6 @@
 
 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
-        ALOGW("invalid alert: threshold (%f) > possible recordable value (%d x %lld)",
-              alert.trigger_if_sum_gt(), alert.num_buckets(),
-              (long long)mBucketSizeNs);
-        return nullptr;
-    }
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, mConfigKey);
     if (anomalyTracker != nullptr) {
         mAnomalyTrackers.push_back(anomalyTracker);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index ddc05c6..e75b710 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -190,8 +190,9 @@
         return;
     }
 
-    if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { // Check that app hook fields are valid.
-        // TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
+    if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
+        // Check that app breadcrumb reported fields are valid.
+        // TODO: Find a way to make these checks easier to maintain.
         status_t err = NO_ERROR;
 
         // Uid is 3rd from last field and must match the caller's uid,
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index b822f2c..a7d07e3 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -21,6 +21,7 @@
 Landroid/app/ActivityManager;->isLowRamDeviceStatic()Z
 Landroid/app/ActivityManager;->isUserRunning(I)Z
 Landroid/app/ActivityManager;->mContext:Landroid/content/Context;
+Landroid/app/ActivityManagerNative;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
 Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;
 Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
 Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
@@ -40,6 +41,7 @@
 Landroid/app/Activity;->mWindow:Landroid/view/Window;
 Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
 Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
+Landroid/app/Activity;->setPersistent(Z)V
 Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
 Landroid/app/ActivityThread$ActivityClientRecord;->activity:Landroid/app/Activity;
 Landroid/app/ActivityThread$ActivityClientRecord;->compatInfo:Landroid/content/res/CompatibilityInfo;
@@ -52,6 +54,7 @@
 Landroid/app/ActivityThread$AppBindData;->appInfo:Landroid/content/pm/ApplicationInfo;
 Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
 Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle;
+Landroid/app/ActivityThread$AppBindData;->processName:Ljava/lang/String;
 Landroid/app/ActivityThread$AppBindData;->providers:Ljava/util/List;
 Landroid/app/ActivityThread$BindServiceData;->intent:Landroid/content/Intent;
 Landroid/app/ActivityThread$BindServiceData;->token:Landroid/os/IBinder;
@@ -87,15 +90,20 @@
 Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList;
 Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData;
+Landroid/app/ActivityThread;->mDensityCompatMode:Z
 Landroid/app/ActivityThread;->mH:Landroid/app/ActivityThread$H;
 Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application;
 Landroid/app/ActivityThread;->mInstrumentation:Landroid/app/Instrumentation;
+Landroid/app/ActivityThread;->mLocalProvidersByName:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mNumVisibleActivities:I
 Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mProviderMap:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->performNewIntents(Landroid/os/IBinder;Ljava/util/List;Z)V
 Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
+Landroid/app/ActivityThread$ProviderClientRecord;->mHolder:Landroid/app/ContentProviderHolder;
 Landroid/app/ActivityThread$ProviderClientRecord;->mLocalProvider:Landroid/content/ContentProvider;
+Landroid/app/ActivityThread$ProviderClientRecord;->mProvider:Landroid/content/IContentProvider;
 Landroid/app/ActivityThread$ReceiverData;->compatInfo:Landroid/content/res/CompatibilityInfo;
 Landroid/app/ActivityThread$ReceiverData;->info:Landroid/content/pm/ActivityInfo;
 Landroid/app/ActivityThread$ReceiverData;->intent:Landroid/content/Intent;
@@ -104,6 +112,7 @@
 Landroid/app/ActivityThread$ServiceArgsData;->args:Landroid/content/Intent;
 Landroid/app/ActivityThread$ServiceArgsData;->token:Landroid/os/IBinder;
 Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
+Landroid/app/ActivityThread;->startActivityNow(Landroid/app/Activity;Ljava/lang/String;Landroid/content/Intent;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/Activity$NonConfigurationInstances;)Landroid/app/Activity;
 Landroid/app/admin/DevicePolicyManager;->getDeviceOwnerComponentOnAnyUser()Landroid/content/ComponentName;
 Landroid/app/admin/DevicePolicyManager;->getDeviceOwner()Ljava/lang/String;
 Landroid/app/admin/DevicePolicyManager;->getProfileOwner()Landroid/content/ComponentName;
@@ -143,6 +152,7 @@
 Landroid/app/ApplicationLoaders;->mLoaders:Landroid/util/ArrayMap;
 Landroid/app/Application;->mComponentCallbacks:Ljava/util/ArrayList;
 Landroid/app/Application;->mLoadedApk:Landroid/app/LoadedApk;
+Landroid/app/ApplicationPackageManager;->configurationChanged()V
 Landroid/app/ApplicationPackageManager;->deletePackage(Ljava/lang/String;Landroid/content/pm/IPackageDeleteObserver;I)V
 Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/String;ILandroid/content/pm/IPackageStatsObserver;)V
 Landroid/app/ApplicationPackageManager;-><init>(Landroid/app/ContextImpl;Landroid/content/pm/IPackageManager;)V
@@ -200,11 +210,15 @@
 Landroid/app/backup/RestoreSession;->restoreAll(JLandroid/app/backup/RestoreObserver;)I
 Landroid/app/backup/RestoreSet;-><init>(Ljava/lang/String;Ljava/lang/String;J)V
 Landroid/app/backup/SelectBackupTransportCallback;-><init>()V
+Landroid/app/ContentProviderHolder;->info:Landroid/content/pm/ProviderInfo;
+Landroid/app/ContentProviderHolder;-><init>(Landroid/content/pm/ProviderInfo;)V
 Landroid/app/ContentProviderHolder;->provider:Landroid/content/IContentProvider;
 Landroid/app/ContextImpl;->createActivityContext(Landroid/app/ActivityThread;Landroid/app/LoadedApk;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;ILandroid/content/res/Configuration;)Landroid/app/ContextImpl;
 Landroid/app/ContextImpl;->getActivityToken()Landroid/os/IBinder;
 Landroid/app/ContextImpl;->getDisplay()Landroid/view/Display;
 Landroid/app/ContextImpl;->getPreferencesDir()Ljava/io/File;
+Landroid/app/ContextImpl;->getReceiverRestrictedContext()Landroid/content/Context;
+Landroid/app/ContextImpl;->mBasePackageName:Ljava/lang/String;
 Landroid/app/ContextImpl;->mContentResolver:Landroid/app/ContextImpl$ApplicationContentResolver;
 Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
 Landroid/app/ContextImpl;->mOpPackageName:Ljava/lang/String;
@@ -216,6 +230,7 @@
 Landroid/app/ContextImpl;->mTheme:Landroid/content/res/Resources$Theme;
 Landroid/app/ContextImpl;->scheduleFinalCleanup(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V
+Landroid/app/ContextImpl;->sSharedPrefsCache:Landroid/util/ArrayMap;
 Landroid/app/DatePickerDialog;->mDatePicker:Landroid/widget/DatePicker;
 Landroid/app/Dialog;->CANCEL:I
 Landroid/app/Dialog;->dismissDialog()V
@@ -227,16 +242,23 @@
 Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
 Landroid/app/FragmentManagerImpl;->mAdded:Ljava/util/ArrayList;
 Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
+Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
+Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
+Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
 Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
 Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
 Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
+Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V
+Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
 Landroid/app/IActivityManager;->resumeAppSwitches()V
 Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
+Landroid/app/IActivityManager;->setTaskResizeable(II)V
 Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
 Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J
 Landroid/app/IActivityManager$Stub$Proxy;->isAppForeground(I)Z
 Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/app/IActivityManager;->unbindService(Landroid/app/IServiceConnection;)Z
 Landroid/app/IAlarmManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
@@ -256,6 +278,8 @@
 Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
 Landroid/app/job/IJobScheduler$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/job/IJobScheduler;
 Landroid/app/job/IJobScheduler$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/app/LoadedApk;->getClassLoader()Ljava/lang/ClassLoader;
+Landroid/app/LoadedApk;->getDataDirFile()Ljava/io/File;
 Landroid/app/LoadedApk;->mActivityThread:Landroid/app/ActivityThread;
 Landroid/app/LoadedApk;->makeApplication(ZLandroid/app/Instrumentation;)Landroid/app/Application;
 Landroid/app/LoadedApk;->mAppDir:Ljava/lang/String;
@@ -263,7 +287,9 @@
 Landroid/app/LoadedApk;->mApplication:Landroid/app/Application;
 Landroid/app/LoadedApk;->mBaseClassLoader:Ljava/lang/ClassLoader;
 Landroid/app/LoadedApk;->mClassLoader:Ljava/lang/ClassLoader;
+Landroid/app/LoadedApk;->mDataDirFile:Ljava/io/File;
 Landroid/app/LoadedApk;->mDataDir:Ljava/lang/String;
+Landroid/app/LoadedApk;->mDisplayAdjustments:Landroid/view/DisplayAdjustments;
 Landroid/app/LoadedApk;->mPackageName:Ljava/lang/String;
 Landroid/app/LoadedApk;->mReceivers:Landroid/util/ArrayMap;
 Landroid/app/LoadedApk;->mResDir:Ljava/lang/String;
@@ -276,6 +302,7 @@
 Landroid/app/NativeActivity;->setWindowFormat(I)V
 Landroid/app/NativeActivity;->showIme(I)V
 Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList;
+Landroid/app/Notification$Builder;->setChannel(Ljava/lang/String;)Landroid/app/Notification$Builder;
 Landroid/app/Notification;->EXTRA_SUBSTITUTE_APP_NAME:Ljava/lang/String;
 Landroid/app/Notification;->isGroupSummary()Z
 Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager;
@@ -310,6 +337,7 @@
 Landroid/app/StatusBarManager;->expandNotificationsPanel()V
 Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V
 Landroid/app/StatusBarManager;->expandSettingsPanel()V
+Landroid/app/StatusBarManager;->getService()Lcom/android/internal/statusbar/IStatusBarService;
 Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
 Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/usage/UsageStatsManager;->getAppStandbyBuckets()Ljava/util/Map;
@@ -361,6 +389,8 @@
 Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
 Landroid/content/BroadcastReceiver$PendingResult;-><init>(ILjava/lang/String;Landroid/os/Bundle;IZZLandroid/os/IBinder;II)V
 Landroid/content/BroadcastReceiver;->setPendingResult(Landroid/content/BroadcastReceiver$PendingResult;)V
+Landroid/content/ContentProviderClient;->mContentProvider:Landroid/content/IContentProvider;
+Landroid/content/ContentProviderClient;->mPackageName:Ljava/lang/String;
 Landroid/content/ContentProvider;->mContext:Landroid/content/Context;
 Landroid/content/ContentProvider;->mPathPermissions:[Landroid/content/pm/PathPermission;
 Landroid/content/ContentProvider;->mReadPermission:Ljava/lang/String;
@@ -394,9 +424,11 @@
 Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal;
 Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver;
 Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
+Landroid/content/IContentProvider;->call(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;
 Landroid/content/IContentService;->cancelSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/content/ComponentName;)V
 Landroid/content/IContentService;->getMasterSyncAutomatically()Z
 Landroid/content/IContentService;->setMasterSyncAutomatically(Z)V
+Landroid/content/IContentService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IContentService;
 Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
 Landroid/content/IntentFilter;->mActions:Ljava/util/ArrayList;
@@ -429,6 +461,7 @@
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
 Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
+Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
 Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
 Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
 Landroid/content/pm/PackageInstaller$SessionParams;->setAllocateAggressive(Z)V
@@ -570,6 +603,7 @@
 Landroid/content/SyncStatusInfo;->lastSuccessTime:J
 Landroid/database/AbstractCursor;->mExtras:Landroid/os/Bundle;
 Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
+Landroid/database/AbstractCursor;->mRowIdColumnIndex:I
 Landroid/database/CursorWindow;->mWindowPtr:J
 Landroid/database/CursorWindow;->sCursorWindowSize:I
 Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray;
@@ -1100,6 +1134,7 @@
 Landroid/net/wifi/WifiEnterpriseConfig;->getCaCertificateAlias()Ljava/lang/String;
 Landroid/net/wifi/WifiEnterpriseConfig;->getClientCertificateAlias()Ljava/lang/String;
 Landroid/net/wifi/WifiInfo;->getMeteredHint()Z
+Landroid/net/wifi/WifiInfo;->mMacAddress:Ljava/lang/String;
 Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V
 Landroid/net/wifi/WifiManager;->connect(ILandroid/net/wifi/WifiManager$ActionListener;)V
 Landroid/net/wifi/WifiManager;->connect(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
@@ -1513,6 +1548,13 @@
 Landroid/telephony/SignalStrength;->getLteRsrq()I
 Landroid/telephony/SignalStrength;->getLteRssnr()I
 Landroid/telephony/SignalStrength;->getLteSignalStrength()I
+Landroid/telephony/SignalStrength;->mGsmBitErrorRate:I
+Landroid/telephony/SignalStrength;->mGsmSignalStrength:I
+Landroid/telephony/SignalStrength;->mLteCqi:I
+Landroid/telephony/SignalStrength;->mLteRsrp:I
+Landroid/telephony/SignalStrength;->mLteRsrq:I
+Landroid/telephony/SignalStrength;->mLteRssnr:I
+Landroid/telephony/SignalStrength;->mLteSignalStrength:I
 Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_GOOD:I
 Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_GREAT:I
 Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_MODERATE:I
@@ -1564,6 +1606,7 @@
 Landroid/telephony/TelephonyManager;-><init>()V
 Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
 Landroid/telephony/TelephonyManager;->isNetworkRoaming(I)Z
+Landroid/telephony/TelephonyManager;->isVolteAvailable()Z
 Landroid/telephony/TelephonyManager;->setDataEnabled(IZ)V
 Landroid/text/AndroidBidi;->bidi(I[C[B)I
 Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V
@@ -1650,6 +1693,7 @@
 Landroid/view/Choreographer;->removeCallbacks(ILjava/lang/Runnable;Ljava/lang/Object;)V
 Landroid/view/Choreographer;->scheduleVsyncLocked()V
 Landroid/view/Choreographer;->USE_VSYNC:Z
+Landroid/view/ContextThemeWrapper;->getThemeResId()I
 Landroid/view/ContextThemeWrapper;->mResources:Landroid/content/res/Resources;
 Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme;
 Landroid/view/ContextThemeWrapper;->mThemeResource:I
@@ -1675,6 +1719,7 @@
 Landroid/view/InputEventReceiver;->dispatchBatchedInputEventPending()V
 Landroid/view/InputEventReceiver;->dispatchInputEvent(ILandroid/view/InputEvent;I)V
 Landroid/view/InputEventSender;->dispatchInputEventFinished(IZ)V
+Landroid/view/inputmethod/InputMethodInfo;->mSubtypes:Landroid/view/inputmethod/InputMethodSubtypeArray;
 Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
 Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
 Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
@@ -1688,6 +1733,7 @@
 Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V
 Landroid/view/inputmethod/InputMethodManager;->sInstance:Landroid/view/inputmethod/InputMethodManager;
 Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
+Landroid/view/inputmethod/InputMethodSubtypeArray;-><init>(Ljava/util/List;)V
 Landroid/view/InputQueue;->finishInputEvent(JZ)V
 Landroid/view/IWindowManager;->getAnimationScale(I)F
 Landroid/view/IWindowManager;->hasNavigationBar()Z
@@ -1881,6 +1927,7 @@
 Landroid/view/WindowManagerGlobal;->getRootView(Ljava/lang/String;)Landroid/view/View;
 Landroid/view/WindowManagerGlobal;->getViewRootNames()[Ljava/lang/String;
 Landroid/view/WindowManagerGlobal;->getWindowManagerService()Landroid/view/IWindowManager;
+Landroid/view/WindowManagerGlobal;->initialize()V
 Landroid/view/WindowManagerGlobal;->mLock:Ljava/lang/Object;
 Landroid/view/WindowManagerGlobal;->mParams:Ljava/util/ArrayList;
 Landroid/view/WindowManagerGlobal;->mRoots:Ljava/util/ArrayList;
@@ -2087,6 +2134,7 @@
 Landroid/widget/PopupWindow;->mLastWidth:I
 Landroid/widget/PopupWindow;->mOnScrollChangedListener:Landroid/view/ViewTreeObserver$OnScrollChangedListener;
 Landroid/widget/PopupWindow;->mOverlapAnchor:Z
+Landroid/widget/PopupWindow;->mTouchInterceptor:Landroid/view/View$OnTouchListener;
 Landroid/widget/PopupWindow;->mWidthMode:I
 Landroid/widget/PopupWindow;->mWindowLayoutType:I
 Landroid/widget/PopupWindow;->preparePopup(Landroid/view/WindowManager$LayoutParams;)V
@@ -2109,10 +2157,14 @@
 Landroid/widget/RemoteViews$Action;->mergeBehavior()I
 Landroid/widget/RemoteViews$Action;->viewId:I
 Landroid/widget/RemoteViews$BitmapCache;->mBitmaps:Ljava/util/ArrayList;
+Landroid/widget/RemoteViews$BitmapReflectionAction;->bitmap:Landroid/graphics/Bitmap;
+Landroid/widget/RemoteViews$BitmapReflectionAction;->methodName:Ljava/lang/String;
+Landroid/widget/RemoteViews;->estimateMemoryUsage()I
 Landroid/widget/RemoteViews;->mActions:Ljava/util/ArrayList;
 Landroid/widget/RemoteViews;->mApplication:Landroid/content/pm/ApplicationInfo;
 Landroid/widget/RemoteViews;->mBitmapCache:Landroid/widget/RemoteViews$BitmapCache;
 Landroid/widget/RemoteViews;->mergeRemoteViews(Landroid/widget/RemoteViews;)V
+Landroid/widget/RemoteViews;->mPortrait:Landroid/widget/RemoteViews;
 Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String;
 Landroid/widget/ScrollBarDrawable;->mVerticalThumb:Landroid/graphics/drawable/Drawable;
 Landroid/widget/ScrollBarDrawable;->setHorizontalThumbDrawable(Landroid/graphics/drawable/Drawable;)V
@@ -2145,6 +2197,7 @@
 Landroid/widget/TextView;->mMaximum:I
 Landroid/widget/TextView;->mMaxMode:I
 Landroid/widget/TextView;->mSingleLine:Z
+Landroid/widget/TextView;->mTextPaint:Landroid/text/TextPaint;
 Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V
 Landroid/widget/Toast;->getService()Landroid/app/INotificationManager;
 Landroid/widget/Toast;->sService:Landroid/app/INotificationManager;
@@ -2154,6 +2207,7 @@
 Landroid/widget/VideoView;->mUri:Landroid/net/Uri;
 Landroid/widget/VideoView;->mVideoHeight:I
 Landroid/widget/VideoView;->mVideoWidth:I
+Lcom/android/internal/app/IAppOpsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IAppOpsService;
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lang/String;)I
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
@@ -2244,6 +2298,13 @@
 Lcom/android/internal/R$integer;->config_screenBrightnessDim:I
 Lcom/android/internal/R$integer;->config_toastDefaultGravity:I
 Lcom/android/internal/R$layout;->screen_title:I
+Lcom/android/internal/R$styleable;->AccountAuthenticator_accountPreferences:I
+Lcom/android/internal/R$styleable;->AccountAuthenticator_accountType:I
+Lcom/android/internal/R$styleable;->AccountAuthenticator_customTokens:I
+Lcom/android/internal/R$styleable;->AccountAuthenticator:[I
+Lcom/android/internal/R$styleable;->AccountAuthenticator_icon:I
+Lcom/android/internal/R$styleable;->AccountAuthenticator_label:I
+Lcom/android/internal/R$styleable;->AccountAuthenticator_smallIcon:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_enabled:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_hardwareAccelerated:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication:[I
@@ -2274,6 +2335,14 @@
 Lcom/android/internal/R$styleable;->ImageView_src:I
 Lcom/android/internal/R$styleable;->ScrollView_fillViewport:I
 Lcom/android/internal/R$styleable;->ScrollView:[I
+Lcom/android/internal/R$styleable;->SyncAdapter_accountType:I
+Lcom/android/internal/R$styleable;->SyncAdapter_allowParallelSyncs:I
+Lcom/android/internal/R$styleable;->SyncAdapter_contentAuthority:I
+Lcom/android/internal/R$styleable;->SyncAdapter:[I
+Lcom/android/internal/R$styleable;->SyncAdapter_isAlwaysSyncable:I
+Lcom/android/internal/R$styleable;->SyncAdapter_settingsActivity:I
+Lcom/android/internal/R$styleable;->SyncAdapter_supportsUploading:I
+Lcom/android/internal/R$styleable;->SyncAdapter_userVisible:I
 Lcom/android/internal/R$styleable;->TabWidget:[I
 Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
 Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
@@ -2289,7 +2358,11 @@
 Lcom/android/internal/R$styleable;->ViewStub:[I
 Lcom/android/internal/R$styleable;->ViewStub_inflatedId:I
 Lcom/android/internal/R$styleable;->ViewStub_layout:I
+Lcom/android/internal/R$styleable;->Window:[I
 Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$styleable;->Window_windowIsFloating:I
+Lcom/android/internal/R$styleable;->Window_windowIsTranslucent:I
+Lcom/android/internal/R$styleable;->Window_windowShowWallpaper:I
 Lcom/android/internal/R$style;->Theme:I
 Lcom/android/internal/R$xml;->power_profile:I
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
@@ -2339,6 +2412,7 @@
 Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
 Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
 Ldalvik/system/CloseGuard;->warnIfOpen()V
+Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
 Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
 Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
 Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
@@ -2350,10 +2424,12 @@
 Ldalvik/system/DexPathList$Element;-><init>(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V
 Ldalvik/system/DexPathList;-><init>(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V
 Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList;->makeInMemoryDexElements([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
 Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
 Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
 Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
 Ldalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V
+Ldalvik/system/DexPathList$NativeLibraryElement;->path:Ljava/io/File;
 Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
 Ldalvik/system/DexPathList;->splitPaths(Ljava/lang/String;Z)Ljava/util/List;
 Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
@@ -2369,6 +2445,7 @@
 Ldalvik/system/VMRuntime;->runFinalization(J)V
 Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
 Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
+Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
 Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
 Ldalvik/system/VMRuntime;->trackExternalFree(J)V
 Ldalvik/system/VMRuntime;->vmInstructionSet()Ljava/lang/String;
@@ -2620,8 +2697,10 @@
 Ljava/util/concurrent/ThreadLocalRandom;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Date;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/Date;->writeObject(Ljava/io/ObjectOutputStream;)V
+Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
 Ljava/util/EnumMap;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/EnumMap;->writeObject(Ljava/io/ObjectOutputStream;)V
+Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
 Ljava/util/EnumSet;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/GregorianCalendar;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/HashMap$HashIterator;->hasNext()Z
diff --git a/core/java/android/annotation/OWNERS b/core/java/android/annotation/OWNERS
new file mode 100644
index 0000000..d6bb71b
--- /dev/null
+++ b/core/java/android/annotation/OWNERS
@@ -0,0 +1 @@
+tnorbye@google.com
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6c3d248..8a9efe8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+
 import static java.lang.Character.MIN_VALUE;
 
 import android.annotation.CallSuper;
@@ -135,6 +136,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 
@@ -1898,7 +1900,7 @@
 
         if (isFinishing()) {
             if (mAutoFillResetNeeded) {
-                getAutofillManager().onActivityFinished();
+                getAutofillManager().onActivityFinishing();
             } else if (mIntent != null
                     && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
                 // Activity was launched when user tapped a link in the Autofill Save UI - since
@@ -7689,6 +7691,9 @@
                 }
             }
         }
+        if (android.view.autofill.Helper.sVerbose) {
+            Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible));
+        }
         return visible;
     }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4c9fb74..1026550 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -543,7 +543,6 @@
             OP_CAMERA,
             // Body sensors
             OP_BODY_SENSORS,
-            OP_REQUEST_DELETE_PACKAGES,
 
             // APPOP PERMISSIONS
             OP_ACCESS_NOTIFICATIONS,
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index a788989..b072ee6 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1000,7 +1000,7 @@
      */
     @Override
     public boolean isAutofillCompatibilityEnabled() {
-        return mBase.isAutofillCompatibilityEnabled();
+        return mBase != null && mBase.isAutofillCompatibilityEnabled();
     }
 
     /**
@@ -1008,6 +1008,8 @@
      */
     @Override
     public void setAutofillCompatibilityEnabled(boolean  autofillCompatEnabled) {
-        mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+        if (mBase != null) {
+            mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+        }
     }
 }
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index cf01451..424fa83 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -27,9 +27,11 @@
 import android.annotation.StyleableRes;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
+import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Configuration.NativeConfig;
 import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -752,6 +754,26 @@
     }
 
     /**
+     * Loads a Drawable from an encoded image stream, or null.
+     *
+     * This call will handle closing ais.
+     */
+    private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
+            @NonNull Resources wrapper, @NonNull TypedValue value) {
+        ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
+                            wrapper, value);
+        try {
+            return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (IOException ioe) {
+            // This is okay. This may be something that ImageDecoder does not
+            // support, like SVG.
+            return null;
+        }
+    }
+
+    /**
      * Loads a drawable from XML or resources stream.
      */
     @NonNull
@@ -811,8 +833,8 @@
                 } else {
                     final InputStream is = mAssets.openNonAsset(
                             value.assetCookie, file, AssetManager.ACCESS_STREAMING);
-                    dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
-                    is.close();
+                    AssetInputStream ais = (AssetInputStream) is;
+                    dr = decodeImageDrawable(ais, wrapper, value);
                 }
             } finally {
                 stack.pop();
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 931b5c9..f08e1cc 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -242,6 +242,9 @@
 
     /**
      * Returns the number of physical cameras available on this device.
+     * The return value of this method might change dynamically if the device
+     * supports external cameras and an external camera is connected or
+     * disconnected.
      *
      * @return total number of accessible camera devices, or 0 if there are no
      *   cameras or an error was encountered enumerating them.
@@ -3542,8 +3545,8 @@
         /**
          * Gets the horizontal angle of view in degrees.
          *
-         * @return horizontal angle of view. This method will always return a
-         *         valid value.
+         * @return horizontal angle of view. Returns -1.0 when the device
+         *         doesn't report view angle information.
          */
         public float getHorizontalViewAngle() {
             return Float.parseFloat(get(KEY_HORIZONTAL_VIEW_ANGLE));
@@ -3552,8 +3555,8 @@
         /**
          * Gets the vertical angle of view in degrees.
          *
-         * @return vertical angle of view. This method will always return a
-         *         valid value.
+         * @return vertical angle of view. Returns -1.0 when the device
+         *         doesn't report view angle information.
          */
         public float getVerticalViewAngle() {
             return Float.parseFloat(get(KEY_VERTICAL_VIEW_ANGLE));
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 732f6a5..e558b7e 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -829,19 +829,24 @@
      * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
      * </ul>
      * </li>
+     * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
+     *   the same.</li>
      * <li>The logical camera device must be LIMITED or higher device.</li>
      * </ul>
      * <p>Both the logical camera device and its underlying physical devices support the
      * mandatory stream combinations required for their device levels.</p>
      * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
      * <ul>
-     * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+     * <li>For each guaranteed stream combination, the logical camera supports replacing one
+     *   logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
      *   or raw stream with two physical streams of the same size and format, each from a
      *   separate physical camera, given that the size and format are supported by both
      *   physical cameras.</li>
-     * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
-     *   advertise RAW capability, but the underlying physical cameras do. This is usually
-     *   the case when the physical cameras have different sensor sizes.</li>
+     * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
+     *   cameras do, the logical camera will support guaranteed stream combinations for RAW
+     *   capability, except that the RAW streams will be physical streams, each from a separate
+     *   physical camera. This is usually the case when the physical cameras have different
+     *   sensor sizes.</li>
      * </ul>
      * <p>Using physical streams in place of a logical stream of the same size and format will
      * not slow down the frame rate of the capture, as long as the minimum frame duration
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index b205e2c..a040a09 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,11 +82,9 @@
  *
  * </ul>
  *
- * <p>Please note that surface sharing is currently only enabled for outputs that use the
- * {@link ImageFormat#PRIVATE} format. This includes surface sources like
- * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
- * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
- * the aforementioned format.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
+ * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
+ * format may be used.</p>
  *
  * @see CameraDevice#createCaptureSessionByOutputConfigurations
  *
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7dde2ed..91c99be 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2662,7 +2662,7 @@
      * A {@code NetworkCallback} is registered by calling
      * {@link #requestNetwork(NetworkRequest, NetworkCallback)},
      * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)},
-     * or {@link #registerDefaultNetworkCallback(NetworkCallback). A {@code NetworkCallback} is
+     * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is
      * unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}.
      * A {@code NetworkCallback} should be registered at most once at any time.
      * A {@code NetworkCallback} that has been unregistered can be registered again.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5d7cf1e..7cd58e8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2240,12 +2240,16 @@
     public static final int DATA_CONNECTION_LTE = 13;
     public static final int DATA_CONNECTION_EHRPD = 14;
     public static final int DATA_CONNECTION_HSPAP = 15;
-    public static final int DATA_CONNECTION_OTHER = 16;
+    public static final int DATA_CONNECTION_GSM = 16;
+    public static final int DATA_CONNECTION_TD_SCDMA = 17;
+    public static final int DATA_CONNECTION_IWLAN = 18;
+    public static final int DATA_CONNECTION_LTE_CA = 19;
+    public static final int DATA_CONNECTION_OTHER = 20;
 
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
-        "ehrpd", "hspap", "other"
+        "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
     };
 
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 630b63f..c53d005 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3692,18 +3692,15 @@
                 new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
 
         /**
-         * User-selected RTT mode
+         * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
+         * calls when supported by the device and carrier. Boolean value.
          * 0 = OFF
-         * 1 = FULL
-         * 2 = VCO
-         * 3 = HCO
-         * Uses the same constants as TTY (e.g. {@link android.telecom.TelecomManager#TTY_MODE_OFF})
-         * @hide
+         * 1 = ON
          */
         public static final String RTT_CALLING_MODE = "rtt_calling_mode";
 
         /** @hide */
-        public static final Validator RTT_CALLING_MODE_VALIDATOR = TTY_MODE_VALIDATOR;
+        public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
@@ -12081,6 +12078,28 @@
                 "enable_gnss_raw_meas_full_tracking";
 
         /**
+         * Whether the notification should be ongoing (persistent) when a carrier app install is
+         * required.
+         *
+         * The value is a boolean (1 or 0).
+         * @hide
+         */
+        @SystemApi
+        public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
+                "install_carrier_app_notification_persistent";
+
+        /**
+         * The amount of time (ms) to hide the install carrier app notification after the user has
+         * ignored it. After this time passes, the notification will be shown again
+         *
+         * The value is a long
+         * @hide
+         */
+        @SystemApi
+        public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
+                "install_carrier_app_notification_sleep_millis";
+
+        /**
          * Whether we've enabled zram on this device. Takes effect on
          * reboot. The value "1" enables zram; "0" disables it, and
          * everything else is unspecified.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 1d13335..f4dcce1 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -75,6 +75,7 @@
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
     public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
     public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
+    public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -216,6 +217,7 @@
     public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
     public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
     public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
+    public static final int KM_ERROR_DEVICE_LOCKED = -72;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -262,6 +264,7 @@
         sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
                 "Invalid MAC or authentication tag length");
         sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
+        sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index 9f2182e..e561371 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -1,3 +1,5 @@
+set noparent
+
 siyamed@google.com
 nona@google.com
 clarabayarri@google.com
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 3fd4696..8cb46b7 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -23,6 +23,10 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -396,6 +400,33 @@
         return true;
     }
 
+    /**
+     *  Get the Bitmap from the Drawable.
+     *
+     *  If the Bitmap needed to be scaled up to account for density, BitmapDrawable
+     *  handles this at draw time. But this class doesn't actually draw the Bitmap;
+     *  it is just a holder for native code to access its SkBitmap. So this needs to
+     *  get a version that is scaled to account for density.
+     */
+    private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
+        Bitmap bitmap = bitmapDrawable.getBitmap();
+        final int scaledWidth  = bitmapDrawable.getIntrinsicWidth();
+        final int scaledHeight = bitmapDrawable.getIntrinsicHeight();
+        if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) {
+            return bitmap;
+        }
+
+        Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        RectF dst = new RectF(0, 0, scaledWidth, scaledHeight);
+
+        Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig());
+        Canvas canvas = new Canvas(scaled);
+        Paint paint = new Paint();
+        paint.setFilterBitmap(true);
+        canvas.drawBitmap(bitmap, src, dst, paint);
+        return scaled;
+    }
+
     private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
         final XmlResourceParser parser = resources.getXml(resourceId);
         final int bitmapRes;
@@ -452,7 +483,8 @@
                                 + "is different. All frames should have the exact same size and "
                                 + "share the same hotspot.");
                     }
-                    mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
+                    BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
+                    mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
                 }
             }
         }
@@ -461,7 +493,8 @@
                     + "refer to a bitmap drawable.");
         }
 
-        final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+        final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
         validateHotSpot(bitmap, hotSpotX, hotSpotY);
         // Set the properties now that we have successfully loaded the icon.
         mBitmap = bitmap;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fc78211..bd7f8e5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -152,7 +152,6 @@
     private static native void nativeSeverChildren(long transactionObj, long nativeObject);
     private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
             int scalingMode);
-    private static native void nativeDestroy(long transactionObj, long nativeObject);
     private static native IBinder nativeGetHandle(long nativeObject);
     private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
 
@@ -1571,16 +1570,6 @@
             return this;
         }
 
-        /**
-         * Same as {@link #destroy()} except this is invoked in a transaction instead of
-         * immediately.
-         */
-        public Transaction destroy(SurfaceControl sc) {
-            sc.checkNotReleased();
-            nativeDestroy(mNativeObject, sc.mNativeObject);
-            return this;
-        }
-
         public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
             if (displayToken == null) {
                 throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2af2467..3ff3c97 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7272,7 +7272,7 @@
                         // becomes true where it should issue notifyViewEntered().
                         afm.notifyViewEntered(this);
                     }
-                } else if (!isFocused()) {
+                } else if (!enter && !isFocused()) {
                     afm.notifyViewExited(this);
                 }
             }
@@ -8010,6 +8010,9 @@
      *   <li>Call {@link
      *    android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)}
      *       when the visibility of a virtual child changed.
+     *   <li>Call
+     *    {@link android.view.autofill.AutofillManager#notifyViewClicked(View, int)} when a virtual
+     *       child is clicked.
      *   <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure
      *       changed and the current context should be committed (for example, when the user tapped
      *       a {@code SUBMIT} button in an HTML page).
@@ -8807,6 +8810,9 @@
     /**
      * Computes whether this virtual autofill view is visible to the user.
      *
+     * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy
+     * view must override it.
+     *
      * @return Whether the view is visible on the screen.
      */
     public boolean isVisibleToUserForAutofill(int virtualId) {
@@ -8819,7 +8825,7 @@
                 }
             }
         }
-        return false;
+        return true;
     }
 
     /**
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 5ce2421..cb1d89c 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -38,6 +38,7 @@
     }
 
     /** @hide */
+    @TestApi
     public AutofillId(AutofillId parent, int virtualChildId) {
         mVirtual = true;
         mViewId = parent.mViewId;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 41d05a5..134dc1f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -71,10 +71,9 @@
 import java.util.List;
 import java.util.Objects;
 
+//TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
 
-// TODO: use java.lang.ref.Cleaner once Android supports Java 9
-
 /**
  * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
  * Autofill Framework lifecycle.
@@ -356,6 +355,13 @@
     @GuardedBy("mLock")
     @Nullable private ArraySet<AutofillId> mFillableIds;
 
+    /**
+     * Views that were already "entered" - if they're entered again when the session is not active,
+     * they're ignored
+     * */
+    @GuardedBy("mLock")
+    @Nullable private ArraySet<AutofillId> mEnteredIds;
+
     /** If set, session is commited when the field is clicked. */
     @GuardedBy("mLock")
     @Nullable private AutofillId mSaveTriggerId;
@@ -711,17 +717,29 @@
     }
 
     @GuardedBy("mLock")
-    private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
+    private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
         if (isDisabledByServiceLocked()) {
             if (sVerbose) {
-                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
-                        + ") on state " + getStateAsStringLocked());
+                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                        + ") on state " + getStateAsStringLocked() + " because disabled by svc");
             }
             return true;
         }
-        if (sVerbose && isFinishedLocked()) {
-            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
-                    + ") on state " + getStateAsStringLocked());
+        if (isFinishedLocked()) {
+            // Session already finished: ignore if automatic request and view already entered
+            if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
+                    && mEnteredIds.contains(id)) {
+                if (sVerbose) {
+                    Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                            + ") on state " + getStateAsStringLocked()
+                            + " because view was already entered: " + mEnteredIds);
+                }
+                return true;
+            }
+        }
+        if (sVerbose) {
+            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                    + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
         }
         return false;
     }
@@ -753,7 +771,8 @@
     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
     @GuardedBy("mLock")
     private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
-        if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+        final AutofillId id = getAutofillId(view);
+        if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
 
         AutofillCallback callback = null;
 
@@ -766,7 +785,6 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view);
                 final AutofillValue value = view.getAutofillValue();
 
                 if (!isActiveLocked()) {
@@ -776,6 +794,7 @@
                     // Update focus on existing session.
                     updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
                 }
+                addEnteredIdLocked(id);
             }
         }
         return callback;
@@ -900,8 +919,9 @@
     @GuardedBy("mLock")
     private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
                                                      int flags) {
+        final AutofillId id = getAutofillId(view, virtualId);
         AutofillCallback callback = null;
-        if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+        if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
 
         ensureServiceClientAddedIfNeededLocked();
 
@@ -912,8 +932,6 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view, virtualId);
-
                 if (!isActiveLocked()) {
                     // Starts new session.
                     startSessionLocked(id, bounds, null, flags);
@@ -921,11 +939,20 @@
                     // Update focus on existing session.
                     updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
                 }
+                addEnteredIdLocked(id);
             }
         }
         return callback;
     }
 
+    @GuardedBy("mLock")
+    private void addEnteredIdLocked(@NonNull AutofillId id) {
+        if (mEnteredIds == null) {
+            mEnteredIds = new ArraySet<>(1);
+        }
+        mEnteredIds.add(id);
+    }
+
     /**
      * Called when a virtual view that supports autofill is exited.
      *
@@ -992,9 +1019,9 @@
             }
 
             if (!mEnabled || !isActiveLocked()) {
-                if (sVerbose && mEnabled) {
-                    Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
-                            + getStateAsStringLocked());
+                if (sVerbose) {
+                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+                            + "): ignoring on state " + getStateAsStringLocked());
                 }
                 return;
             }
@@ -1024,6 +1051,10 @@
         }
         synchronized (mLock) {
             if (!mEnabled || !isActiveLocked()) {
+                if (sVerbose) {
+                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
+                            + "): ignoring on state " + getStateAsStringLocked());
+                }
                 return;
             }
 
@@ -1032,18 +1063,35 @@
         }
     }
 
+    /**
+     * Called to indicate a {@link View} is clicked.
+     *
+     * @param view view that has been clicked.
+     */
+    public void notifyViewClicked(@NonNull View view) {
+        notifyViewClicked(view.getAutofillId());
+    }
 
     /**
-     * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
+     * Called to indicate a virtual view has been clicked.
      *
-     * @hide
+     * @param view the virtual view parent.
+     * @param virtualId id identifying the virtual child inside the parent view.
      */
-    public void notifyViewClicked(View view) {
-        final AutofillId id = view.getAutofillId();
+    public void notifyViewClicked(@NonNull View view, int virtualId) {
+        notifyViewClicked(getAutofillId(view, virtualId));
+    }
 
+    private void notifyViewClicked(AutofillId id) {
+        if (!hasAutofillFeature()) {
+            return;
+        }
         if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
 
         synchronized (mLock) {
+            if (!mEnabled || !isActiveLocked()) {
+                return;
+            }
             if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
                 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
                 commitLocked();
@@ -1058,16 +1106,16 @@
      *
      * @hide
      */
-    public void onActivityFinished() {
+    public void onActivityFinishing() {
         if (!hasAutofillFeature()) {
             return;
         }
         synchronized (mLock) {
             if (mSaveOnFinish) {
-                if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
+                if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
                 commitLocked();
             } else {
-                if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
+                if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
                 cancelLocked();
             }
         }
@@ -1088,6 +1136,7 @@
         if (!hasAutofillFeature()) {
             return;
         }
+        if (sVerbose) Log.v(TAG, "commit() called by app");
         synchronized (mLock) {
             commitLocked();
         }
@@ -1392,7 +1441,8 @@
         if (sVerbose) {
             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
                     + ", flags=" + flags + ", state=" + getStateAsStringLocked()
-                    + ", compatMode=" + isCompatibilityModeEnabledLocked());
+                    + ", compatMode=" + isCompatibilityModeEnabledLocked()
+                    + ", enteredIds=" + mEnteredIds);
         }
         if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
             if (sVerbose) {
@@ -1430,7 +1480,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        resetSessionLocked();
+        resetSessionLocked(/* resetEnteredIds= */ true);
     }
 
     @GuardedBy("mLock")
@@ -1445,22 +1495,25 @@
             throw e.rethrowFromSystemServer();
         }
 
-        resetSessionLocked();
+        resetSessionLocked(/* resetEnteredIds= */ true);
     }
 
     @GuardedBy("mLock")
-    private void resetSessionLocked() {
+    private void resetSessionLocked(boolean resetEnteredIds) {
         mSessionId = NO_SESSION;
         mState = STATE_UNKNOWN;
         mTrackedViews = null;
         mFillableIds = null;
         mSaveTriggerId = null;
+        if (resetEnteredIds) {
+            mEnteredIds = null;
+        }
     }
 
     @GuardedBy("mLock")
     private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
             int flags) {
-        if (sVerbose && action != ACTION_VIEW_EXITED) {
+        if (sVerbose) {
             Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
                     + ", value=" + value + ", action=" + action + ", flags=" + flags);
         }
@@ -1630,7 +1683,7 @@
             mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
             if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
                 // Reset the session state
-                resetSessionLocked();
+                resetSessionLocked(/* resetEnteredIds= */ true);
             }
             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
                 // Reset connection to system
@@ -1826,7 +1879,7 @@
     private void setSessionFinished(int newState) {
         synchronized (mLock) {
             if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
-            resetSessionLocked();
+            resetSessionLocked(/* resetEnteredIds= */ false);
             mState = newState;
         }
     }
@@ -1954,6 +2007,7 @@
             pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
         }
         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
+        pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
         pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
@@ -2296,6 +2350,7 @@
                 final boolean[] isVisible;
 
                 if (client.autofillClientIsVisibleForAutofill()) {
+                    if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
                     isVisible = client.autofillClientGetViewVisibility(trackedIds);
                 } else {
                     // All false
@@ -2315,7 +2370,7 @@
             }
 
             if (sVerbose) {
-                Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
+                Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
                         + " mVisibleTrackedIds=" + mVisibleTrackedIds
                         + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
             }
@@ -2421,6 +2476,9 @@
             }
 
             if (mVisibleTrackedIds == null) {
+                if (sVerbose) {
+                    Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
+                }
                 finishSessionLocked();
             }
         }
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index e80fdd9..1da998d 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -46,6 +46,7 @@
 
     private final WindowPresenter mWindowPresenter;
     private WindowManager.LayoutParams mWindowLayoutParams;
+    private boolean mFullScreen;
 
     private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
             new View.OnAttachStateChangeListener() {
@@ -104,12 +105,17 @@
      */
     public void update(View anchor, int offsetX, int offsetY, int width, int height,
             Rect virtualBounds) {
+        mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
         // If we are showing the popup for a virtual view we use a fake view which
         // delegates to the anchor but present itself with the same bounds as the
         // virtual view. This ensures that the location logic in popup works
         // symmetrically when the dropdown is below and above the anchor.
         final View actualAnchor;
-        if (virtualBounds != null) {
+        if (mFullScreen) {
+            offsetX = 0;
+            offsetY = 0;
+            actualAnchor = anchor;
+        } else if (virtualBounds != null) {
             final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
             actualAnchor = new View(anchor.getContext()) {
                 @Override
@@ -209,6 +215,17 @@
     }
 
     @Override
+    protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
+            int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
+        if (mFullScreen) {
+            // Do not patch LayoutParams if force full screen
+            return false;
+        }
+        return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
+                width, height, gravity, allowScroll);
+    }
+
+    @Override
     public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
         if (sVerbose) {
             Log.v(TAG, "showAsDropDown(): anchor=" + anchor + ", xoff=" + xoff + ", yoff=" + yoff
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
index 75e2bf7..68badec 100644
--- a/core/java/android/webkit/TracingConfig.java
+++ b/core/java/android/webkit/TracingConfig.java
@@ -21,61 +21,76 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 /**
  * Holds tracing configuration information and predefined settings.
  */
 public class TracingConfig {
 
-    private final String mCustomCategoryPattern;
-    private final @PresetCategories int mPresetCategories;
+    private @PredefinedCategories int mPredefinedCategories;
+    private final List<String> mCustomIncludedCategories = new ArrayList<String>();
     private @TracingMode int mTracingMode;
 
     /** @hide */
-    @IntDef({CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY,
-            CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
+    @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER,
+            CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING,
+            CATEGORIES_FRAME_VIEWER})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PresetCategories {}
+    public @interface PredefinedCategories {}
 
     /**
-     * Indicates that there are no preset categories.
+     * Indicates that there are no predefined categories.
      */
-    public static final int CATEGORIES_NONE = -1;
+    public static final int CATEGORIES_NONE = 0;
 
     /**
-     * Predefined categories typically useful for web developers.
+     * Predefined set of categories, includes all categories enabled by default in chromium.
+     * Use with caution: this setting may produce large trace output.
+     */
+    public static final int CATEGORIES_ALL = 1 << 0;
+
+    /**
+     * Predefined set of categories typically useful for analyzing WebViews.
+     * Typically includes android_webview and Java.
+     */
+    public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1;
+
+    /**
+     * Predefined set of categories typically useful for web developers.
      * Typically includes blink, compositor, renderer.scheduler and v8 categories.
      */
-    public static final int CATEGORIES_WEB_DEVELOPER = 0;
+    public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2;
 
     /**
-     * Predefined categories for analyzing input latency issues.
+     * Predefined set of categories for analyzing input latency issues.
      * Typically includes input, renderer.scheduler categories.
      */
-    public static final int CATEGORIES_INPUT_LATENCY = 1;
+    public static final int CATEGORIES_INPUT_LATENCY = 1 << 3;
 
     /**
-     * Predefined categories for analyzing rendering issues.
+     * Predefined set of categories for analyzing rendering issues.
      * Typically includes blink, compositor and gpu categories.
      */
-    public static final int CATEGORIES_RENDERING = 2;
+    public static final int CATEGORIES_RENDERING = 1 << 4;
 
     /**
-     * Predefined categories for analyzing javascript and rendering issues.
-     * Typically includes blink, compositor, gpu, renderer.schduler and v8 categories.
+     * Predefined set of categories for analyzing javascript and rendering issues.
+     * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories.
      */
-    public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3;
+    public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5;
 
     /**
-     * Predefined categories for studying difficult rendering performance problems.
+     * Predefined set of categories for studying difficult rendering performance problems.
      * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
      * some other compositor categories which are disabled by default.
      */
-    public static final int CATEGORIES_FRAME_VIEWER = 4;
+    public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
 
     /** @hide */
-    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER,
-            RECORD_TO_CONSOLE})
+    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TracingMode {}
 
@@ -97,99 +112,38 @@
 
     /**
      * Record trace events using a larger internal tracing buffer until it is full.
-     * Uses more memory than the other modes and may not be suitable on devices
-     * with smaller RAM. Depending on the implementation typically allows up to
-     * 512 million events to be stored.
+     * Uses significantly more memory than {@link #RECORD_UNTIL_FULL} and may not be
+     * suitable on devices with smaller RAM.
      */
     public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2;
 
     /**
-     * Record trace events to console (logcat). The events are discarded and nothing
-     * is sent back to the caller. Uses the least memory as compared to the other modes.
+     * @hide
      */
-    public static final int RECORD_TO_CONSOLE = 3;
-
-    /**
-     * Create config with the preset categories.
-     * <p>
-     * Example:
-     *    TracingConfig(CATEGORIES_WEB_DEVELOPER) -- records trace events from the "web developer"
-     *                                               preset categories.
-     *
-     * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
-     *                    {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
-     *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
-     *                    {@link #CATEGORIES_FRAME_VIEWER}.
-     *
-     * Note: for specifying custom categories without presets use
-     * {@link #TracingConfig(int, String, int)}.
-     *
-     */
-    public TracingConfig(@PresetCategories int presetCategories) {
-        this(presetCategories, "", RECORD_UNTIL_FULL);
+    public TracingConfig(@PredefinedCategories int predefinedCategories,
+            @NonNull List<String> customIncludedCategories,
+            @TracingMode int tracingMode) {
+        mPredefinedCategories = predefinedCategories;
+        mCustomIncludedCategories.addAll(customIncludedCategories);
+        mTracingMode = tracingMode;
     }
 
     /**
-     * Create a configuration with both preset categories and custom categories.
-     * Also allows to specify the tracing mode.
-     *
-     * Note that the categories are defined by the currently-in-use version of WebView. They live
-     * in chromium code and are not part of the Android API. See
-     * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
-     * chromium documentation on tracing</a> for more details.
-     *
-     * <p>
-     * Examples:
-     *
-     *  Preset category with a specified trace mode:
-     *    TracingConfig(CATEGORIES_WEB_DEVELOPER, "", RECORD_UNTIL_FULL_LARGE_BUFFER);
-     *  Custom categories:
-     *    TracingConfig(CATEGORIES_NONE, "browser", RECORD_UNTIL_FULL)
-     *      -- records only the trace events from the "browser" category.
-     *    TraceConfig(CATEGORIES_NONE, "-input,-gpu", RECORD_UNTIL_FULL)
-     *      -- records all trace events excluding the events from the "input" and 'gpu' categories.
-     *    TracingConfig(CATEGORIES_NONE, "blink*,devtools*", RECORD_UNTIL_FULL)
-     *      -- records only the trace events matching the "blink*" and "devtools*" patterns
-     *         (e.g. "blink_gc" and "devtools.timeline" categories).
-     *
-     *  Combination of preset and additional custom categories:
-     *    TracingConfig(CATEGORIES_WEB_DEVELOPER, "memory-infra", RECORD_CONTINUOUSLY)
-     *      -- records events from the "web developer" categories and events from the "memory-infra"
-     *         category to understand where memory is being used.
-     *
-     * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
-     *                    {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
-     *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
-     *                    {@link #CATEGORIES_FRAME_VIEWER}.
-     * @param customCategories a comma-delimited list of category wildcards. A category can
-     *                         have an optional '-' prefix to make it an excluded category.
-     * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
-     *                    {@link #RECORD_CONTINUOUSLY}, {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}
-     *                    or {@link #RECORD_TO_CONSOLE}.
+     * Returns a bitmask of the predefined categories values of this configuration.
      */
-    public TracingConfig(@PresetCategories int presetCategories,
-            @NonNull String customCategories, @TracingMode int tracingMode) {
-        mPresetCategories = presetCategories;
-        mCustomCategoryPattern = customCategories;
-        mTracingMode = RECORD_UNTIL_FULL;
+    @PredefinedCategories
+    public int getPredefinedCategories() {
+        return mPredefinedCategories;
     }
 
     /**
-     * Returns the custom category pattern for this configuration.
+     * Returns the list of included custom category patterns for this configuration.
      *
-     * @return empty string if no custom category pattern is specified.
+     * @return empty list if no custom category patterns are specified.
      */
     @NonNull
-    public String getCustomCategoryPattern() {
-        return mCustomCategoryPattern;
-    }
-
-    /**
-     * Returns the preset categories value of this configuration.
-     */
-    @PresetCategories
-    public int getPresetCategories() {
-        return mPresetCategories;
+    public List<String> getCustomIncludedCategories() {
+        return mCustomIncludedCategories;
     }
 
     /**
@@ -200,4 +154,111 @@
         return mTracingMode;
     }
 
+    /**
+     * Builder used to create {@link TracingConfig} objects.
+     *
+     * Examples:
+     *   new TracingConfig.Builder().build()
+     *       -- creates a configuration with default options: {@link #CATEGORIES_NONE},
+     *          {@link #RECORD_UNTIL_FULL}.
+     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()
+     *       -- records trace events from the "web developer" predefined category sets.
+     *   new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
+     *                                             CATEGORIES_INPUT_LATENCY).build()
+     *       -- records trace events from the "rendering" and "input latency" predefined
+     *          category sets.
+     *   new TracingConfig.Builder().addCategories("browser").build()
+     *       -- records only the trace events from the "browser" category.
+     *   new TracingConfig.Builder().addCategories("blink*","renderer*").build()
+     *       -- records only the trace events matching the "blink*" and "renderer*" patterns
+     *          (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
+     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
+     *                              .addCategories("disabled-by-default-v8.gc")
+     *                              .setTracingMode(RECORD_CONTINUOUSLY).build()
+     *       -- records events from the "web developer" predefined category set and events from
+     *          the "disabled-by-default-v8.gc" category to understand where garbage collection
+     *          is being triggered. Uses a ring buffer for internal storage during tracing.
+     */
+    public static class Builder {
+        private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
+        private final List<String> mCustomIncludedCategories = new ArrayList<String>();
+        private @TracingMode int mTracingMode = RECORD_UNTIL_FULL;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Build {@link TracingConfig} using the current settings.
+         */
+        public TracingConfig build() {
+            return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories,
+                    mTracingMode);
+        }
+
+        /**
+         * Adds categories from a predefined set of categories to be included in the trace output.
+         *
+         * @param predefinedCategories list or bitmask of predefined category sets to use:
+         *                    {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
+         *                    {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY},
+         *                    {@link #CATEGORIES_RENDERING},
+         *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+         *                    {@link #CATEGORIES_FRAME_VIEWER}.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addCategories(@PredefinedCategories int... predefinedCategories) {
+            for (int categorySet : predefinedCategories) {
+                mPredefinedCategories |= categorySet;
+            }
+            return this;
+        }
+
+        /**
+         * Adds custom categories to be included in trace output.
+         *
+         * Note that the categories are defined by the currently-in-use version of WebView. They
+         * live in chromium code and are not part of the Android API. See
+         * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
+         * chromium documentation on tracing</a> for more details.
+         *
+         * @param categories a list of category patterns. A category pattern can contain wilcards,
+         *        e.g. "blink*" or full category name e.g. "renderer.scheduler".
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addCategories(String... categories) {
+            for (String category: categories) {
+                mCustomIncludedCategories.add(category);
+            }
+            return this;
+        }
+
+        /**
+         * Adds custom categories to be included in trace output.
+         *
+         * Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter.
+         *
+         * @param categories a list of category patters.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addCategories(Collection<String> categories) {
+            mCustomIncludedCategories.addAll(categories);
+            return this;
+        }
+
+        /**
+         * Sets the tracing mode for this configuration.
+         *
+         * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
+         *                    {@link #RECORD_CONTINUOUSLY} or
+         *                    {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder setTracingMode(@TracingMode int tracingMode) {
+            mTracingMode = tracingMode;
+            return this;
+        }
+    }
+
 }
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index cadb8a1..7871021 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -16,9 +16,12 @@
 
 package android.webkit;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Handler;
+
+import java.io.OutputStream;
+import java.util.concurrent.Executor;
 
 /**
  * Manages tracing of WebViews. In particular provides functionality for the app
@@ -29,40 +32,22 @@
  * The resulting trace data is sent back as a byte sequence in json format. This
  * file can be loaded in "chrome://tracing" for further analysis.
  * <p>
- * Note: All methods in this class must be called on the UI thread. All callbacks
- * are also called on the UI thread.
- * <p>
  * Example usage:
  * <pre class="prettyprint">
  * TracingController tracingController = TracingController.getInstance();
- * tracingController.start(new TraceConfig(CATEGORIES_WEB_DEVELOPER));
+ * tracingController.start(new TraceConfig.Builder()
+ *                  .addCategories(CATEGORIES_WEB_DEVELOPER).build());
  * [..]
- * tracingController.stopAndFlush(new TraceFileOutput("trace.json"), null);
+ * tracingController.stop(new FileOutputStream("trace.json"),
+ *                        Executors.newSingleThreadExecutor());
  * </pre></p>
  */
 public abstract class TracingController {
 
     /**
-     * Interface for capturing tracing data.
-     */
-    public interface TracingOutputStream {
-        /**
-         * Will be called to return tracing data in chunks.
-         * Tracing data is returned in json format an array of bytes.
-         */
-        void write(byte[] chunk);
-
-        /**
-         * Called when tracing is finished and the data collection is over.
-         * There will be no calls to #write after #complete is called.
-         */
-        void complete();
-    }
-
-    /**
      * Returns the default TracingController instance. At present there is
      * only one TracingController instance for all WebView instances,
-     * however this restriction may be relaxed in the future.
+     * however this restriction may be relaxed in a future Android release.
      *
      * @return the default TracingController instance
      */
@@ -72,55 +57,38 @@
     }
 
     /**
-     * Starts tracing all webviews. Depeding on the trace mode in traceConfig
+     * Starts tracing all webviews. Depending on the trace mode in traceConfig
      * specifies how the trace events are recorded.
      *
      * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL},
      * {@link TracingConfig#RECORD_CONTINUOUSLY} and
      * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded
      * using an internal buffer and flushed to the outputStream when
-     * {@link #stopAndFlush(TracingOutputStream, Handler)} is called.
+     * {@link #stop(OutputStream, Executor)} is called.
      *
      * @param tracingConfig configuration options to use for tracing
-     * @return false if the system is already tracing, true otherwise.
+     * @throws IllegalStateException if the system is already tracing.
      */
-    public abstract boolean start(TracingConfig tracingConfig);
+    public abstract void start(@NonNull TracingConfig tracingConfig);
 
     /**
-     * Stops tracing and discards all tracing data.
+     * Stops tracing and flushes tracing data to the specified outputStream.
      *
-     * This method is particularly useful in conjunction with the
-     * {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode because tracing data is logged to
-     * console and not sent to an outputStream as with
-     * {@link #stopAndFlush(TracingOutputStream, Handler)}.
+     * The data is sent to the specified output stream in json format typically
+     * in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion
+     * the {@link java.io.OutputStream#close()} method is called.
      *
+     * @param outputStream the output steam the tracing data will be sent to. If null
+     *                     the tracing data will be discarded.
+     * @param executor the {@link java.util.concurrent.Executor} on which the
+     *        outputStream #write and #close methods will be invoked.
      * @return false if the system was not tracing at the time of the call, true
      *         otherwise.
      */
-    public abstract boolean stop();
-
-    /**
-     * Stops tracing and flushes tracing data to the specifid outputStream.
-     *
-     * Note that if the {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode is used
-     * nothing will be sent to the outputStream and no TracingOuputStream methods will be
-     * called. In that case it is more convenient to just use {@link #stop()} instead.
-     *
-     * @param outputStream the output steam the tracing data will be sent to.
-     * @param handler the {@link android.os.Handler} on which the outputStream callbacks
-     *                will be invoked. If the handler is null the current thread's Looper
-     *                will be used.
-     * @return false if the system was not tracing at the time of the call, true
-     *         otherwise.
-     */
-    public abstract boolean stopAndFlush(TracingOutputStream outputStream,
-            @Nullable Handler handler);
+    public abstract boolean stop(@Nullable OutputStream outputStream,
+            @NonNull @CallbackExecutor Executor executor);
 
     /** True if the system is tracing */
     public abstract boolean isTracing();
 
-    // TODO: consider adding getTraceBufferUsage, percentage and approx event count.
-    // TODO: consider adding String getCategories(), for obtaining the actual list
-    // of categories used (given that presets are ints).
-
 }
diff --git a/core/java/android/webkit/TracingFileOutputStream.java b/core/java/android/webkit/TracingFileOutputStream.java
deleted file mode 100644
index 8a5fa36..0000000
--- a/core/java/android/webkit/TracingFileOutputStream.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.annotation.NonNull;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Simple TracingOutputStream implementation which writes the trace data from
- * {@link TracingController} to a new file.
- *
- */
-public class TracingFileOutputStream implements TracingController.TracingOutputStream {
-
-    private FileOutputStream mFileOutput;
-
-    public TracingFileOutputStream(@NonNull String filename) throws FileNotFoundException {
-        mFileOutput = new FileOutputStream(filename);
-    }
-
-    /**
-     * Writes bytes chunk to the file.
-     */
-    public void write(byte[] chunk) {
-        try {
-            mFileOutput.write(chunk);
-        } catch (IOException e) {
-            onIOException(e);
-        }
-    }
-
-    /**
-     * Closes the file.
-     */
-    public void complete() {
-        try {
-            mFileOutput.close();
-        } catch (IOException e) {
-            onIOException(e);
-        }
-    }
-
-    private void onIOException(IOException e) {
-        throw new RuntimeException(e);
-    }
-}
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 2789bae..e4b2930 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -1,9 +1,11 @@
 per-file TextView.java = siyamed@google.com
 per-file TextView.java = nona@google.com
 per-file TextView.java = clarabayarri@google.com
+
 per-file EditText.java = siyamed@google.com
 per-file EditText.java = nona@google.com
 per-file EditText.java = clarabayarri@google.com
+
 per-file Editor.java = siyamed@google.com
 per-file Editor.java = nona@google.com
 per-file Editor.java = clarabayarri@google.com
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index e91db13..7217def 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1583,7 +1583,7 @@
      *
      * @hide
      */
-    protected final boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+    protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
             int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
         final int anchorHeight = anchor.getHeight();
         final int anchorWidth = anchor.getWidth();
diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
index 500c028..bf151c3 100644
--- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
+++ b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
@@ -57,6 +57,8 @@
     private int mMainColor;
     private int mSecondaryColor;
     private ValueAnimator mColorAnimation;
+    private int mMainColorTo;
+    private int mSecondaryColorTo;
 
     public GradientDrawable(@NonNull Context context) {
         mDensity = context.getResources().getDisplayMetrics().density;
@@ -76,7 +78,7 @@
     }
 
     public void setColors(int mainColor, int secondaryColor, boolean animated) {
-        if (mainColor == mMainColor && secondaryColor == mSecondaryColor) {
+        if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) {
             return;
         }
 
@@ -84,6 +86,9 @@
             mColorAnimation.cancel();
         }
 
+        mMainColorTo = mainColor;
+        mSecondaryColorTo = mainColor;
+
         if (animated) {
             final int mainFrom = mMainColor;
             final int secFrom = mSecondaryColor;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6a56f45..4c5991e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5286,6 +5286,18 @@
                 case TelephonyManager.NETWORK_TYPE_HSPAP:
                     bin = DATA_CONNECTION_HSPAP;
                     break;
+                case TelephonyManager.NETWORK_TYPE_GSM:
+                    bin = DATA_CONNECTION_GSM;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                    bin = DATA_CONNECTION_TD_SCDMA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_IWLAN:
+                    bin = DATA_CONNECTION_IWLAN;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_LTE_CA:
+                    bin = DATA_CONNECTION_LTE_CA;
+                    break;
                 default:
                     bin = DATA_CONNECTION_OTHER;
                     break;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9c89976..30d81a7 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -576,7 +576,8 @@
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
                             instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
                             uuid, classLoaderContext, seInfo, false /* downgrade */,
-                            targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
+                            targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
+                            "server-dexopt");
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 1916c11..f44a1d1 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -213,10 +213,9 @@
         PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
         if (mediaSize != null) {
             writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize);
+            proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
         }
 
-        proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
-
         PrintAttributes.Resolution res = attributes.getResolution();
         if (res != null) {
             writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res);
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index d6f8dc4..61d5031 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -604,6 +604,7 @@
     CameraInfo cameraInfo;
     status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo);
     if (rc != NO_ERROR) {
+        ALOGE("%s: getCameraInfo error: %d", __FUNCTION__, rc);
         return rc;
     }
     int defaultOrientation = 0;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8ca5062..0ef5445 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -846,14 +846,6 @@
     transaction->setOverrideScalingMode(ctrl, scalingMode);
 }
 
-static void nativeDestroyInTransaction(JNIEnv* env, jclass clazz,
-                                       jlong transactionObj,
-                                       jlong nativeObject) {
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-    auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
-    transaction->destroySurface(ctrl);
-}
-
 static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     return javaObjectForIBinder(env, ctrl->getHandle());
@@ -1005,8 +997,6 @@
             (void*)nativeSeverChildren } ,
     {"nativeSetOverrideScalingMode", "(JJI)V",
             (void*)nativeSetOverrideScalingMode },
-    {"nativeDestroy", "(JJ)V",
-            (void*)nativeDestroyInTransaction },
     {"nativeGetHandle", "(J)Landroid/os/IBinder;",
             (void*)nativeGetHandle },
     {"nativeScreenshotToBuffer",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index c0950bfc..9a53b89 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -46,18 +46,12 @@
 import "frameworks/base/core/proto/android/util/event_log_tags.proto";
 import "frameworks/base/core/proto/android/util/log.proto";
 import "frameworks/base/libs/incident/proto/android/os/header.proto";
+import "frameworks/base/libs/incident/proto/android/os/metadata.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
 
 package android.os;
 
-// This field contains internal metadata associated with an incident report,
-// such as the section ids and privacy policy specs from caller as well as how long
-// and how many bytes a section takes, etc.
-message IncidentMetadata {
-
-}
-
 // 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.
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 02fc4da..6b92aa8 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -422,6 +422,8 @@
     optional SettingProto enable_deletion_helper_no_threshold_toggle = 340 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto notification_snooze_options = 341 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto enable_gnss_raw_meas_full_tracking = 346 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto install_carrier_app_notification_persistent = 356 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto install_carrier_app_notification_sleep_millis = 357 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto zram_enabled = 347 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto smart_replies_in_notifications_flags = 348 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/res/drawable/ic_signal_cellular_alt_24px.xml b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml
new file mode 100644
index 0000000..29f1f43
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml
@@ -0,0 +1,40 @@
+<?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="21dp"
+    android:viewportWidth="20"
+    android:viewportHeight="21">
+
+    <group
+            android:translateX="-31.000000"
+            android:translateY="-77.000000">
+        <group
+                android:translateX="24.000000"
+                android:translateY="72.000000">
+            <path
+                android:fillType="evenOdd"
+                android:strokeWidth="1"
+                android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z" />
+            <path
+                android:fillColor="#4285F4"
+                android:strokeWidth="1"
+                android:pathData="M23,5 L27,5 L27,26 L23,26 L23,5 Z M7,18.125 L11,18.125 L11,26 L7,26 L7,18.125 Z
+M15,11.5625 L19,11.5625 L19,26 L15,26 L15,11.5625 Z" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
new file mode 100644
index 0000000..07298c1
--- /dev/null
+++ b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
@@ -0,0 +1,48 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/autofill_dataset_picker"
+    style="@style/AutofillDatasetPicker"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/autofill_window_title"
+        android:layout_above="@+id/autofill_dataset_container"
+        android:layout_alignStart="@+id/autofill_dataset_container"
+        android:textSize="16sp"/>
+
+    <!-- autofill_container is the common parent for inserting authentication item or
+         autofill_dataset_list-->
+    <FrameLayout
+        android:id="@+id/autofill_dataset_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true">
+        <ListView
+            android:id="@+id/autofill_dataset_list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:clickable="true"
+            android:divider="@null"
+            android:drawSelectorOnTop="true"
+            android:visibility="gone"/>
+    </FrameLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 9a3d736..8774334 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -36,7 +36,7 @@
 
     <!-- Values for GPS configuration (Telus) -->
     <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.telusmobility.com</item>
+        <item>SUPL_HOST=supl.google.com</item>
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index 007fd04..05896b1 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -34,7 +34,7 @@
 
     <!-- Values for GPS configuration (Telus) -->
     <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.telusmobility.com</item>
+        <item>SUPL_HOST=supl.google.com</item>
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
diff --git a/core/res/res/values-television/dimens.xml b/core/res/res/values-television/dimens.xml
index 4c25225..aa56251 100644
--- a/core/res/res/values-television/dimens.xml
+++ b/core/res/res/values-television/dimens.xml
@@ -20,4 +20,8 @@
      <item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item>
      <item type="dimen" format="float" name="spot_shadow_alpha">0.3</item>
 
+     <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
+     <dimen name="autofill_dataset_picker_max_width">60%</dimen>
+     <dimen name="autofill_dataset_picker_max_height">70%</dimen>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1f4425f..0911298 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -999,7 +999,7 @@
     <integer name="config_shortPressOnSleepBehavior">0</integer>
 
     <!-- Time to wait while a button is pressed before triggering a very long press. -->
-    <integer name="config_veryLongPressTimeout">6000</integer>
+    <integer name="config_veryLongPressTimeout">3500</integer>
 
     <!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] -->
     <string name="widget_default_package_name" translatable="false"></string>
@@ -2328,6 +2328,10 @@
     <string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false"
             >com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string>
 
+    <!-- Name of the dialog that is used to install the carrier app when the SIM is inserted -->
+    <string name="config_carrierAppInstallDialogComponent" translatable="false"
+            >com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string>
+
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0411c6e..cfaab6a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -626,7 +626,8 @@
     <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
 
     <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
-    <dimen name="autofill_dataset_picker_max_size">90%</dimen>
+    <dimen name="autofill_dataset_picker_max_width">90%</dimen>
+    <dimen name="autofill_dataset_picker_max_height">90%</dimen>
 
     <!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height -->
     <dimen name="autofill_save_custom_subtitle_max_height">20%</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59c742e..837113d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -182,6 +182,8 @@
     <string name="notification_channel_voice_mail">Voicemail messages</string>
     <!-- Telephony notification channel name for a channel containing wifi calling status notifications. -->
     <string name="notification_channel_wfc">Wi-Fi calling</string>
+    <!-- Telephony notification channel name for a channel containing SIM notifications -->
+    <string name="notification_channel_sim">SIM status</string>
 
     <!-- Displayed to tell the user that peer changed TTY mode -->
     <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
@@ -2169,6 +2171,9 @@
     <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] -->
     <string name="setup_autofill">Set up Autofill</string>
 
+    <!-- Title of fullscreen autofill window [CHAR-LIMIT=80] -->
+    <string name="autofill_window_title">Autofill</string>
+
     <!-- String used to separate FirstName and LastName when writing out a local name
          e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
     <string name="autofill_address_name_separator">\u0020</string>
@@ -3194,10 +3199,12 @@
     <!-- See SIM_ADDED_DIALOG.  This is the button of that dialog. -->
     <string name="sim_restart_button">Restart</string>
     <!-- See Carrier_App_Dialog. This is the message of that dialog. -->
-    <string name="carrier_app_dialog_message">To get your new SIM working properly, you\'ll need to install and open an app from your carrier.</string>
-    <!-- See Carrier_App_Dialog. This is the button of that dialog. -->
-    <string name="carrier_app_dialog_button">GET THE APP</string>
-    <string name="carrier_app_dialog_not_now">NOT NOW</string>
+    <string name="install_carrier_app_notification_title">Activate mobile service</string>
+    <string name="install_carrier_app_notification_text">
+        Download the carrier app to activate your new SIM
+    </string>
+    <!-- See Carrier_App_Notification. This is the button of that dialog. -->
+    <string name="install_carrier_app_notification_button">Download app</string>
     <!-- See carrier_app_notification. This is the headline. -->
     <string name="carrier_app_notification_title">New SIM inserted</string>
     <string name="carrier_app_notification_text">Tap to set it up</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b69ded8..9995642 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,7 @@
   <java-symbol type="string" name="notification_channel_sms" />
   <java-symbol type="string" name="notification_channel_voice_mail" />
   <java-symbol type="string" name="notification_channel_wfc" />
+  <java-symbol type="string" name="notification_channel_sim" />
   <java-symbol type="string" name="SetupCallDefault" />
   <java-symbol type="string" name="accept" />
   <java-symbol type="string" name="activity_chooser_view_see_all" />
@@ -1383,6 +1384,7 @@
 
   <java-symbol type="drawable" name="ic_sim_card_multi_24px_clr" />
   <java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" />
+  <java-symbol type="drawable" name="ic_signal_cellular_alt_24px" />
 
   <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
   <java-symbol type="drawable" name="autofilled_highlight"/>
@@ -2072,6 +2074,7 @@
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
   <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
   <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
+  <java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
   <java-symbol type="string" name="config_persistentDataPackageName" />
 
@@ -2771,9 +2774,9 @@
   <java-symbol type="array" name="resolver_target_actions_pin" />
   <java-symbol type="array" name="resolver_target_actions_unpin" />
 
-  <java-symbol type="string" name="carrier_app_dialog_message" />
-  <java-symbol type="string" name="carrier_app_dialog_button" />
-  <java-symbol type="string" name="carrier_app_dialog_not_now" />
+  <java-symbol type="string" name="install_carrier_app_notification_title" />
+  <java-symbol type="string" name="install_carrier_app_notification_text" />
+  <java-symbol type="string" name="install_carrier_app_notification_button" />
   <java-symbol type="string" name="carrier_app_notification_title" />
   <java-symbol type="string" name="carrier_app_notification_text" />
   <java-symbol type="string" name="negative_duration" />
@@ -2987,6 +2990,8 @@
   <!-- com.android.server.autofill -->
   <java-symbol type="layout" name="autofill_save"/>
   <java-symbol type="layout" name="autofill_dataset_picker"/>
+  <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
+  <java-symbol type="id" name="autofill_dataset_container"/>
   <java-symbol type="id" name="autofill_dataset_list"/>
   <java-symbol type="id" name="autofill_dataset_picker"/>
   <java-symbol type="id" name="autofill" />
@@ -3015,7 +3020,8 @@
   <java-symbol type="drawable" name="autofill_dataset_picker_background" />
   <java-symbol type="style" name="AutofillDatasetPicker" />
   <java-symbol type="style" name="AutofillSaveAnimation" />
-  <java-symbol type="dimen" name="autofill_dataset_picker_max_size"/>
+  <java-symbol type="dimen" name="autofill_dataset_picker_max_width"/>
+  <java-symbol type="dimen" name="autofill_dataset_picker_max_height"/>
   <java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/>
   <java-symbol type="dimen" name="autofill_save_icon_max_size"/>
 
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9c3d8eb..f6a11bd 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -451,8 +451,8 @@
         <item name="tooltipBackgroundColor">@color/tooltip_background_light</item>
 
         <!-- Autofill: max width/height of the dataset picker as a fraction of screen size -->
-        <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_size</item>
-        <item name="autofillDatasetPickerMaxHeight">@dimen/autofill_dataset_picker_max_size</item>
+        <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_width</item>
+        <item name="autofillDatasetPickerMaxHeight">@dimen/autofill_dataset_picker_max_height</item>
 
         <!-- Autofill: max height of custom save subtitle as a fraction of screen size -->
         <item name="autofillSaveCustomSubtitleMaxHeight">@dimen/autofill_save_custom_subtitle_max_height</item>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index b367f29..67c9754 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -402,6 +402,8 @@
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
+                    Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
+                    Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS
index 9f2182e..a35c604 100644
--- a/core/tests/coretests/src/android/text/OWNERS
+++ b/core/tests/coretests/src/android/text/OWNERS
@@ -1,3 +1,5 @@
+set noparent
+
 siyamed@google.com
 nona@google.com
-clarabayarri@google.com
+clarabayarri@google.com
\ No newline at end of file
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index ee7abc5..3cca47b 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -25,7 +25,7 @@
 import android.annotation.RawRes;
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
+import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.Drawable;
@@ -263,6 +263,63 @@
         }
     }
 
+    /**
+     * Takes ownership of the AssetInputStream.
+     *
+     * @hide
+     */
+    public static class AssetInputStreamSource extends Source {
+        public AssetInputStreamSource(@NonNull AssetInputStream ais,
+                @NonNull Resources res, @NonNull TypedValue value) {
+            mAssetInputStream = ais;
+            mResources = res;
+
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                mDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                mDensity = value.density;
+            } else {
+                mDensity = Bitmap.DENSITY_NONE;
+            }
+        }
+
+        private AssetInputStream mAssetInputStream;
+        private final Resources  mResources;
+        private final int        mDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() {
+            return mDensity;
+        }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            ImageDecoder decoder = null;
+            synchronized (this) {
+                if (mAssetInputStream == null) {
+                    throw new IOException("Cannot reuse AssetInputStreamSource");
+                }
+                AssetInputStream ais = mAssetInputStream;
+                mAssetInputStream = null;
+                try {
+                    long asset = ais.getNativeAsset();
+                    decoder = nCreate(asset);
+                } finally {
+                    if (decoder == null) {
+                        IoUtils.closeQuietly(ais);
+                    } else {
+                        decoder.mInputStream = ais;
+                        decoder.mOwnsInputStream = true;
+                    }
+                }
+                return decoder;
+            }
+        }
+    }
+
     private static class ResourceSource extends Source {
         ResourceSource(@NonNull Resources res, int resId) {
             mResources = res;
@@ -296,11 +353,7 @@
                     mResDensity = value.density;
                 }
 
-                if (!(is instanceof AssetManager.AssetInputStream)) {
-                    // This should never happen.
-                    throw new RuntimeException("Resource is not an asset?");
-                }
-                long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
+                long asset = ((AssetInputStream) is).getNativeAsset();
                 decoder = nCreate(asset);
             } finally {
                 if (decoder == null) {
@@ -444,6 +497,7 @@
     private boolean mPreferRamOverQuality = false;
     private boolean mAsAlphaMask = false;
     private Rect    mCropRect;
+    private Rect    mOutPaddingRect;
     private Source  mSource;
 
     private PostProcessor          mPostProcessor;
@@ -782,6 +836,18 @@
     }
 
     /**
+     *  Set a Rect for retrieving nine patch padding.
+     *
+     *  If the image is a nine patch, this Rect will be set to the padding
+     *  rectangle during decode. Otherwise it will not be modified.
+     *
+     *  @hide
+     */
+    public void setOutPaddingRect(@NonNull Rect outPadding) {
+        mOutPaddingRect = outPadding;
+    }
+
+    /**
      *  Specify whether the {@link Bitmap} should be mutable.
      *
      *  <p>By default, a {@link Bitmap} created will be immutable, but that can
@@ -892,7 +958,6 @@
                 postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
                 mMutable, mAllocator, mRequireUnpremultiplied,
                 mPreferRamOverQuality, mAsAlphaMask);
-
     }
 
     private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -965,7 +1030,10 @@
             if (np != null && NinePatch.isNinePatchChunk(np)) {
                 Rect opticalInsets = new Rect();
                 bm.getOpticalInsets(opticalInsets);
-                Rect padding = new Rect();
+                Rect padding = decoder.mOutPaddingRect;
+                if (padding == null) {
+                    padding = new Rect();
+                }
                 nGetPadding(decoder.mNativePtr, padding);
                 return new NinePatchDrawable(res, bm, np, padding,
                         opticalInsets, null);
@@ -1008,6 +1076,15 @@
             final int srcDensity = computeDensity(src, decoder);
             Bitmap bm = decoder.decodeBitmap();
             bm.setDensity(srcDensity);
+
+            Rect padding = decoder.mOutPaddingRect;
+            if (padding != null) {
+                byte[] np = bm.getNinePatchChunk();
+                if (np != null && NinePatch.isNinePatchChunk(np)) {
+                    nGetPadding(decoder.mNativePtr, padding);
+                }
+            }
+
             return bm;
         }
     }
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 7ad062a..44b783b 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Outline;
@@ -49,6 +50,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -111,7 +113,7 @@
      */
     @Deprecated
     public BitmapDrawable() {
-        mBitmapState = new BitmapState((Bitmap) null);
+        init(new BitmapState((Bitmap) null), null);
     }
 
     /**
@@ -124,8 +126,7 @@
     @SuppressWarnings("unused")
     @Deprecated
     public BitmapDrawable(Resources res) {
-        mBitmapState = new BitmapState((Bitmap) null);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState((Bitmap) null), res);
     }
 
     /**
@@ -135,7 +136,7 @@
      */
     @Deprecated
     public BitmapDrawable(Bitmap bitmap) {
-        this(new BitmapState(bitmap), null);
+        init(new BitmapState(bitmap), null);
     }
 
     /**
@@ -143,8 +144,7 @@
      * the display metrics of the resources.
      */
     public BitmapDrawable(Resources res, Bitmap bitmap) {
-        this(new BitmapState(bitmap), res);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState(bitmap), res);
     }
 
     /**
@@ -154,10 +154,7 @@
      */
     @Deprecated
     public BitmapDrawable(String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
-        }
+        this(null, filepath);
     }
 
     /**
@@ -165,10 +162,21 @@
      */
     @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+        Bitmap bitmap = null;
+        try (FileInputStream stream = new FileInputStream(filepath)) {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+            }
         }
     }
 
@@ -179,10 +187,7 @@
      */
     @Deprecated
     public BitmapDrawable(java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
-        }
+        this(null, is);
     }
 
     /**
@@ -190,10 +195,21 @@
      */
     @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+        Bitmap bitmap = null;
+        try {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+            }
         }
     }
 
@@ -812,9 +828,19 @@
                 }
             }
 
+            int density = Bitmap.DENSITY_NONE;
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                density = value.density;
+            }
+
             Bitmap bitmap = null;
             try (InputStream is = r.openRawResource(srcResId, value)) {
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
             } catch (Exception e) {
                 // Do nothing and pick up the error below.
             }
@@ -1013,14 +1039,21 @@
         }
     }
 
+    private BitmapDrawable(BitmapState state, Resources res) {
+        init(state, res);
+    }
+
     /**
-     * The one constructor to rule them all. This is called by all public
+     * The one helper to rule them all. This is called by all public & private
      * constructors to set the state and initialize local properties.
      */
-    private BitmapDrawable(BitmapState state, Resources res) {
+    private void init(BitmapState state, Resources res) {
         mBitmapState = state;
-
         updateLocalState(res);
+
+        if (mBitmapState != null && res != null) {
+            mBitmapState.mTargetDensity = mTargetDensity;
+        }
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 05533d7..8af2fd8 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -50,11 +51,13 @@
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.StateSet;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.View;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -1179,6 +1182,10 @@
             return null;
         }
 
+        if (opts == null) {
+            return getBitmapDrawable(res, value, is);
+        }
+
         /*  ugh. The decodeStream contract is that we have already allocated
             the pad rect, but if the bitmap does not had a ninepatch chunk,
             then the pad will be ignored. If we could change this to lazily
@@ -1194,7 +1201,6 @@
         // an application in compatibility mode, without scaling those down
         // to the compatibility density only to have them scaled back up when
         // drawn to the screen.
-        if (opts == null) opts = new BitmapFactory.Options();
         opts.inScreenDensity = Drawable.resolveDensity(res, 0);
         Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
         if (bm != null) {
@@ -1211,6 +1217,33 @@
         return null;
     }
 
+    private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+        try {
+            ImageDecoder.Source source = null;
+            if (value != null) {
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                source = ImageDecoder.createSource(res, is, density);
+            } else {
+                source = ImageDecoder.createSource(res, is);
+            }
+
+            return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (IOException e) {
+            /*  do nothing.
+                If the exception happened on decode, the drawable will be null.
+            */
+            Log.e("Drawable", "Unable to decode stream: " + e);
+        }
+        return null;
+    }
+
     /**
      * Create a drawable from an XML document. For more information on how to
      * create resources in XML, see
@@ -1310,11 +1343,10 @@
         }
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
-        try {
-            Bitmap bm = BitmapFactory.decodeFile(pathName);
-            if (bm != null) {
-                return drawableFromBitmap(null, bm, null, null, null, pathName);
-            }
+        try (FileInputStream stream = new FileInputStream(pathName)) {
+            return getBitmapDrawable(null, null, stream);
+        } catch(IOException e) {
+            // Do nothing; we will just return null if the FileInputStream had an error
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 1790020..66f2a31 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -24,9 +24,9 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -211,7 +211,8 @@
             restoreAlpha = -1;
         }
 
-        final boolean needsDensityScaling = canvas.getDensity() == 0;
+        final boolean needsDensityScaling = canvas.getDensity() == 0
+                && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
         if (needsDensityScaling) {
             restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
 
@@ -421,10 +422,6 @@
 
         final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
         if (srcResId != 0) {
-            final BitmapFactory.Options options = new BitmapFactory.Options();
-            options.inDither = !state.mDither;
-            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
             final Rect padding = new Rect();
             final Rect opticalInsets = new Rect();
             Bitmap bitmap = null;
@@ -433,7 +430,17 @@
                 final TypedValue value = new TypedValue();
                 final InputStream is = r.openRawResource(srcResId, value);
 
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setOutPaddingRect(padding);
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
 
                 is.close();
             } catch (IOException e) {
@@ -660,8 +667,9 @@
             return;
         }
 
-        final int sourceDensity = ninePatch.getDensity();
         final int targetDensity = mTargetDensity;
+        final int sourceDensity = ninePatch.getDensity() == Bitmap.DENSITY_NONE ?
+            targetDensity : ninePatch.getDensity();
 
         final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets;
         if (sourceOpticalInsets != Insets.NONE) {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ded427e..e2aba04 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -545,7 +545,9 @@
         try {
             args = args != null ? args : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
-            return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
+            OperationResult res = mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
+            // This result is -26 (KEY_USER_NOT_AUTHENTICATED) but why??
+            return res;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
@@ -563,7 +565,8 @@
         try {
             arguments = arguments != null ? arguments : new KeymasterArguments();
             input = input != null ? input : new byte[0];
-            return mBinder.update(token, arguments, input);
+            OperationResult res = mBinder.update(token, arguments, input);
+            return res;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
@@ -618,9 +621,9 @@
      * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
      * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
      */
-    public int addAuthToken(byte[] authToken) {
+    public int addAuthToken(byte[] authToken, int userId) {
         try {
-            return mBinder.addAuthToken(authToken);
+            return mBinder.addAuthToken(authToken, userId);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
@@ -832,14 +835,14 @@
     public InvalidKeyException getInvalidKeyException(
             String keystoreKeyAlias, int uid, KeyStoreException e) {
         switch (e.getErrorCode()) {
-            case LOCKED:
+            case LOCKED: // 2
                 return new UserNotAuthenticatedException();
-            case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
+            case KeymasterDefs.KM_ERROR_KEY_EXPIRED: // -25
                 return new KeyExpiredException();
-            case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID:
+            case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: // -2
                 return new KeyNotYetValidException();
-            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
-            case OP_AUTH_NEEDED:
+            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: // -26
+            case OP_AUTH_NEEDED: // 15
             {
                 // We now need to determine whether the key/operation can become usable if user
                 // authentication is performed, or whether it can never become usable again.
@@ -879,7 +882,7 @@
                 // None of the key's SIDs can ever be authenticated
                 return new KeyPermanentlyInvalidatedException();
             }
-            case UNINITIALIZED:
+            case UNINITIALIZED: // 3
                 return new KeyPermanentlyInvalidatedException();
             default:
                 return new InvalidKeyException("Keystore operation failed", e);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 09b3b9b..419eb24 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,13 +243,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                        spec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -285,16 +279,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-        KeymasterUtils.addUserAuthArgs(args,
-                spec.isUserAuthenticationRequired(),
-                spec.getUserAuthenticationValidityDurationSeconds(),
-                spec.isUserAuthenticationValidWhileOnBody(),
-                spec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                spec.isUserConfirmationRequired());
-        if (spec.isTrustedUserPresenceRequired()) {
-            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
-        }
+        KeymasterUtils.addUserAuthArgs(args, spec);
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index e33e3cd..d68a33d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,13 +344,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        mSpec.isUserAuthenticationRequired(),
-                        mSpec.getUserAuthenticationValidityDurationSeconds(),
-                        mSpec.isUserAuthenticationValidWhileOnBody(),
-                        mSpec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                        mSpec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -541,13 +535,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        KeymasterUtils.addUserAuthArgs(args,
-                mSpec.isUserAuthenticationRequired(),
-                mSpec.getUserAuthenticationValidityDurationSeconds(),
-                mSpec.isUserAuthenticationValidWhileOnBody(),
-                mSpec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                mSpec.isUserConfirmationRequired());
+        KeymasterUtils.addUserAuthArgs(args, mSpec);
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 05cc74a..fc86ca0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -497,13 +497,7 @@
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
-                KeymasterUtils.addUserAuthArgs(importArgs,
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        spec.getBoundToSpecificSecureUserId(),
-                        spec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(importArgs, spec);
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -700,13 +694,7 @@
             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                     params.getEncryptionPaddings());
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
-            KeymasterUtils.addUserAuthArgs(args,
-                    params.isUserAuthenticationRequired(),
-                    params.getUserAuthenticationValidityDurationSeconds(),
-                    params.isUserAuthenticationValidWhileOnBody(),
-                    params.isInvalidatedByBiometricEnrollment(),
-                    params.getBoundToSpecificSecureUserId(),
-                    params.isUserConfirmationRequired());
+            KeymasterUtils.addUserAuthArgs(args, params);
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index da23c70..d0814c6 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
 
@@ -232,7 +233,7 @@
  * key = (SecretKey) keyStore.getKey("key2", null);
  * }</pre>
  */
-public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
 
     private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
@@ -265,6 +266,7 @@
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mIsStrongBoxBacked;
     private final boolean mUserConfirmationRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     /**
      * @hide should be built with Builder
@@ -295,7 +297,8 @@
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             boolean isStrongBoxBacked,
-            boolean userConfirmationRequired) {
+            boolean userConfirmationRequired,
+            boolean unlockedDeviceRequired) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -344,6 +347,7 @@
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mIsStrongBoxBacked = isStrongBoxBacked;
         mUserConfirmationRequired = userConfirmationRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -669,6 +673,22 @@
     }
 
     /**
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     *
+     * @see Builder#SetUnlockedDeviceRequired(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
+     * @hide
+     */
+    public long getBoundToSpecificSecureUserId() {
+        return GateKeeper.INVALID_SECURE_USER_ID;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -699,6 +719,7 @@
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mIsStrongBoxBacked = false;
         private boolean mUserConfirmationRequired;
+        private boolean mUnlockedDeviceRequired = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1267,6 +1288,18 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
+         * while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1297,7 +1330,8 @@
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mIsStrongBoxBacked,
-                    mUserConfirmationRequired);
+                    mUserConfirmationRequired,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index b5b3281..7f8259b 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
  * ...
  * }</pre>
  */
-public final class KeyProtection implements ProtectionParameter {
+public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
@@ -229,6 +229,8 @@
     private final long mBoundToSecureUserId;
     private final boolean mCriticalToDeviceEncryption;
     private final boolean mUserConfirmationRequired;
+    private final boolean mTrustedUserPresenceRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -242,11 +244,13 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            boolean trustedUserPresenceRequired,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
             boolean criticalToDeviceEncryption,
-            boolean userConfirmationRequired) {
+            boolean userConfirmationRequired,
+            boolean unlockedDeviceRequired) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -265,6 +269,8 @@
         mBoundToSecureUserId = boundToSecureUserId;
         mCriticalToDeviceEncryption = criticalToDeviceEncryption;
         mUserConfirmationRequired = userConfirmationRequired;
+        mTrustedUserPresenceRequired = trustedUserPresenceRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -437,6 +443,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     */
+    public boolean isTrustedUserPresenceRequired() {
+        return mTrustedUserPresenceRequired;
+    }
+
+    /**
      * Returns {@code true} if the key will be de-authorized when the device is removed from the
      * user's body.  This option has no effect on keys that don't have an authentication validity
      * duration, and has no effect if the device lacks an on-body sensor.
@@ -494,6 +508,15 @@
     }
 
     /**
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     *
+     * @see Builder#SetRequireDeviceUnlocked(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -512,6 +535,9 @@
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mUserConfirmationRequired;
+        private boolean mTrustedUserPresenceRequired = false;
+        private boolean mUnlockedDeviceRequired = false;
+
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
 
@@ -811,6 +837,16 @@
         }
 
         /**
+         * Sets whether a test of user presence is required to be performed between the
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         */
+        @NonNull
+        public Builder setTrustedUserPresenceRequired(boolean required) {
+            mTrustedUserPresenceRequired = required;
+            return this;
+        }
+
+        /**
          * Sets whether the key will remain authorized only until the device is removed from the
          * user's body up to the limit of the authentication validity period (see
          * {@link #setUserAuthenticationValidityDurationSeconds} and
@@ -892,6 +928,18 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
+         * while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -910,11 +958,13 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mTrustedUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
                     mCriticalToDeviceEncryption,
-                    mUserConfirmationRequired);
+                    mUserConfirmationRequired,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 4e28601..5bd0e74 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -18,6 +18,7 @@
 
 import android.util.Log;
 import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
 import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keymaster.KeymasterArguments;
@@ -101,22 +102,27 @@
      *         require user authentication.
      */
     public static void addUserAuthArgs(KeymasterArguments args,
-            boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment,
-            long boundToSpecificSecureUserId,
-            boolean userConfirmationRequired) {
-        if (userConfirmationRequired) {
+            UserAuthArgs spec) {
+        if (spec.isTrustedUserPresenceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+        }
+
+        if (spec.isUserConfirmationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
         }
 
-        if (!userAuthenticationRequired) {
+        if (spec.isUnlockedDeviceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
+            // Once keymaster is properly ignoring this tag, it should be added to every auth list
+            args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, UserHandle.getCallingUserId());
+        }
+
+        if (!spec.isUserAuthenticationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
         }
 
-        if (userAuthenticationValidityDurationSeconds == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint-only auth.
             FingerprintManager fingerprintManager =
@@ -132,9 +138,9 @@
             }
 
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
-            } else if (invalidatedByBiometricEnrollment) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
+            } else if (spec.isInvalidatedByBiometricEnrollment()) {
                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
                 // enrolled fingerprints, invalidating the key.
                 sid = fingerprintOnlySid;
@@ -147,14 +153,14 @@
             args.addUnsignedLong(
                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
-            if (userAuthenticationValidWhileOnBody) {
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
             } else {
                 // The key is authorized for use for the specified amount of time after the user has
                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -165,8 +171,8 @@
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
-                    userAuthenticationValidityDurationSeconds);
-            if (userAuthenticationValidWhileOnBody) {
+                    spec.getUserAuthenticationValidityDurationSeconds());
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
             }
         }
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
new file mode 100644
index 0000000..3a7017e
--- /dev/null
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * @hide
+ *
+ * This is an interface to encapsulate the user authentication arguments that
+ * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent
+ * authorization characteristics for new or imported keys can implement this
+ * interface to be passed to that method.
+ */
+public interface UserAuthArgs {
+
+    boolean isUserAuthenticationRequired();
+    int getUserAuthenticationValidityDurationSeconds();
+    boolean isUserAuthenticationValidWhileOnBody();
+    boolean isInvalidatedByBiometricEnrollment();
+    boolean isTrustedUserPresenceRequired();
+    boolean isUnlockedDeviceRequired();
+    boolean isUserConfirmationRequired();
+    long getBoundToSpecificSecureUserId();
+
+}
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
index b63400f..08c8346 100644
--- a/libs/incident/Android.mk
+++ b/libs/incident/Android.mk
@@ -33,6 +33,7 @@
         ../../core/java/android/os/IIncidentManager.aidl \
         ../../core/java/android/os/IIncidentReportStatusListener.aidl \
         proto/android/os/header.proto \
+        proto/android/os/metadata.proto \
         src/IncidentReportArgs.cpp
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/incident/proto/android/os/metadata.proto b/libs/incident/proto/android/os/metadata.proto
new file mode 100644
index 0000000..a1e89b0
--- /dev/null
+++ b/libs/incident/proto/android/os/metadata.proto
@@ -0,0 +1,63 @@
+/*
+ * 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 field contains internal metadata associated with an incident report,
+// such as the section ids and privacy policy specs from caller as well as how long
+// and how many bytes a section takes, etc.
+message IncidentMetadata {
+
+    // privacy level of the incident report.
+    enum Destination {
+        AUTOMATIC = 0;
+        EXPLICIT = 1;
+        LOCAL = 2;
+    }
+    optional Destination dest = 1;
+
+    optional int32 request_size = 2;
+
+    optional bool use_dropbox = 3;
+
+    // stats of each section taken in this incident report.
+    message SectionStats {
+        // section id.
+        optional int32 id = 1;
+        // if the section is successfully taken.
+        optional bool success = 2;
+        // number of bytes in the report after filtering.
+        optional int32 report_size_bytes = 3;
+        // the total duration to execute the section.
+        optional int64 exec_duration_ms = 4;
+
+        // number of bytes dumped from the section directly.
+        optional int32 dump_size_bytes = 5;
+        // duration of the dump takes.
+        optional int64 dump_duration_ms = 6;
+        // true if the section timed out.
+        optional bool timed_out = 7;
+        // true if the section is truncated.
+        optional bool is_truncated = 8;
+
+        // Next Tag: 9
+    }
+    repeated SectionStats sections = 4;
+}
+
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 73fad7a..6dff07f 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -30,6 +30,8 @@
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.CookieHandler;
+import java.net.CookieManager;
 import java.net.HttpCookie;
 
 import java.util.ArrayList;
@@ -433,10 +435,22 @@
          * @param cookies the cookies to be sent together with the request
          * @return the same Builder instance.
          * @throws NullPointerException if context or uri is null.
+         * @throws IllegalArgumentException if the cookie handler is not of CookieManager type
+         *                                  when cookies are provided.
          */
         public Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
                 @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
+            Preconditions.checkNotNull(context, "context cannot be null");
             Preconditions.checkNotNull(uri);
+            if (cookies != null) {
+                CookieHandler cookieHandler = CookieHandler.getDefault();
+                if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
+                    throw new IllegalArgumentException(
+                            "The cookie handler has to be of CookieManager type "
+                            + "when cookies are provided.");
+                }
+            }
+
             resetDataSource();
             mType = TYPE_URI;
             mUri = uri;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index e331b2c..2f3d972 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -804,19 +804,6 @@
     public abstract DataSourceDesc editPlaylistItem(int index, DataSourceDesc dsd);
 
     /**
-     * Prepares the player for playback, synchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
-     * which blocks until MediaPlayer2 is ready for playback.
-     *
-     * @throws IOException if source can not be accessed
-     * @throws IllegalStateException if it is called in an invalid state
-     * @hide
-     */
-    public void prepare() throws IOException { }
-
-    /**
      * Prepares the player for playback, asynchronously.
      *
      * After setting the datasource and the display surface, you need to
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index b805eb4..1b21b5b 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -137,7 +137,7 @@
  *         {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
  *         {@link #setLooping(boolean)},
  *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()},
- *         {@link #seekTo(long, int)}, {@link #prepare()} or
+ *         {@link #seekTo(long, int)} or
  *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
  *         methods is called right after a MediaPlayer2 object is constructed,
  *         the user supplied callback method OnErrorListener.onError() won't be
@@ -184,7 +184,7 @@
  *         register a OnErrorListener to look out for error notifications from
  *         the internal player engine.</li>
  *         <li>IllegalStateException is
- *         thrown to prevent programming errors such as calling {@link #prepare()},
+ *         thrown to prevent programming errors such as calling
  *         {@link #prepareAsync()}, {@link #setDataSource(DataSourceDesc)}, or
  *         {@code setPlaylist} methods in an invalid state. </li>
  *         </ul>
@@ -206,15 +206,11 @@
  *     <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
  *         before playback can be started.
  *         <ul>
- *         <li>There are two ways (synchronous vs.
- *         asynchronous) that the <em>Prepared</em> state can be reached:
- *         either a call to {@link #prepare()} (synchronous) which
- *         transfers the object to the <em>Prepared</em> state once the method call
- *         returns, or a call to {@link #prepareAsync()} (asynchronous) which
- *         first transfers the object to the <em>Preparing</em> state after the
+ *         <li>{@link #prepareAsync()} first transfers the object to the
+ *         <em>Preparing</em> state after the
  *         call returns (which occurs almost right way) while the internal
  *         player engine continues working on the rest of preparation work
- *         until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
+ *         until the preparation work completes. When the preparation completes,
  *         the internal player engine then calls a user supplied callback method,
  *         onPrepared() of the EventCallback interface, if an
  *         EventCallback is registered beforehand via {@link
@@ -224,7 +220,7 @@
  *         of calling any method with side effect while a MediaPlayer2 object is
  *         in the <em>Preparing</em> state is undefined.</li>
  *         <li>An IllegalStateException is
- *         thrown if {@link #prepare()} or {@link #prepareAsync()} is called in
+ *         thrown if {@link #prepareAsync()} is called in
  *         any other state.</li>
  *         <li>While in the <em>Prepared</em> state, properties
  *         such as audio/sound volume, screenOnWhilePlaying, looping can be
@@ -395,7 +391,7 @@
  *     <td>{Error}</p></td>
  *     <td>Successful invoke of this method does not change the state. In order for the
  *         target audio attributes type to become effective, this method must be called before
- *         prepare() or prepareAsync().</p></td></tr>
+ *         prepareAsync().</p></td></tr>
  * <tr><td>setAudioSessionId </p></td>
  *     <td>{Idle} </p></td>
  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
@@ -409,7 +405,7 @@
  *     <td>{Error}</p></td>
  *     <td>Successful invoke of this method does not change the state. In order for the
  *         target audio stream type to become effective, this method must be called before
- *         prepare() or prepareAsync().</p></td></tr>
+ *         prepareAsync().</p></td></tr>
  * <tr><td>setAuxEffectSendLevel </p></td>
  *     <td>any</p></td>
  *     <td>{} </p></td>
@@ -561,9 +557,12 @@
     private boolean mBypassInterruptionPolicy;
     private final CloseGuard mGuard = CloseGuard.get();
 
+    private final Object mPlLock = new Object();
     private List<DataSourceDesc> mPlaylist;
-    private int mPLCurrentIndex = 0;
-    private int mPLNextIndex = -1;
+    private int mPlCurrentIndex = 0;
+    private int mPlNextIndex = -1;
+    private int mPlNextSourceState = NEXT_SOURCE_STATE_INIT;
+    private boolean mPlNextSourcePlayPending = false;
     private int mLoopingMode = LOOPING_MODE_NONE;
 
     // Modular DRM
@@ -605,6 +604,11 @@
         native_setup(new WeakReference<MediaPlayer2Impl>(this));
     }
 
+    private static final int NEXT_SOURCE_STATE_ERROR = -1;
+    private static final int NEXT_SOURCE_STATE_INIT = 0;
+    private static final int NEXT_SOURCE_STATE_PREPARING = 1;
+    private static final int NEXT_SOURCE_STATE_PREPARED = 2;
+
     /*
      * Update the MediaPlayer2Impl SurfaceTexture.
      * Call after setting a new display surface.
@@ -774,10 +778,13 @@
     @Override
     public void setDataSource(@NonNull DataSourceDesc dsd) throws IOException {
         Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-        mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>(1));
-        mPlaylist.add(dsd);
-        mPLCurrentIndex = 0;
-        setDataSourcePriv(dsd);
+        synchronized (mPlLock) {
+            mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>(1));
+            mPlaylist.add(dsd);
+            mPlCurrentIndex = 0;
+            mPlNextIndex = -1;
+            handleDataSource(true /* isCurrent */, dsd);
+        }
     }
 
     /**
@@ -787,10 +794,12 @@
      */
     @Override
     public DataSourceDesc getCurrentDataSource() {
-        if (mPlaylist == null) {
-            return null;
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                return null;
+            }
+            return mPlaylist.get(mPlCurrentIndex);
         }
-        return mPlaylist.get(mPLCurrentIndex);
     }
 
     /**
@@ -826,11 +835,14 @@
             startIndex = pl.size() - 1;
         }
 
-        mPlaylist = Collections.synchronizedList(new ArrayList(pl));
-        mPLCurrentIndex = startIndex;
-        setDataSourcePriv(mPlaylist.get(startIndex));
-        // TODO: handle the preparation of next source in the play list.
-        // It should be processed after current source is prepared.
+        synchronized (mPlLock) {
+            mPlaylist = Collections.synchronizedList(new ArrayList(pl));
+            mPlCurrentIndex = startIndex;
+            handleDataSource(true /* isCurrent */, mPlaylist.get(startIndex));
+            // TODO: handle the preparation of next source in the play list.
+            // It should be processed after current source is prepared.
+            mPlNextIndex = getNextIndex_l();
+        }
     }
 
     /**
@@ -840,10 +852,12 @@
      */
     @Override
     public List<DataSourceDesc> getPlaylist() {
-        if (mPlaylist == null) {
-            return null;
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                return null;
+            }
+            return new ArrayList(mPlaylist);
         }
-        return new ArrayList(mPlaylist);
     }
 
     /**
@@ -855,19 +869,21 @@
      */
     @Override
     public void setCurrentPlaylistItem(int index) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
-        if (index < 0 || index >= mPlaylist.size()) {
-            throw new IndexOutOfBoundsException("index is out of play list range.");
-        }
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                throw new IllegalArgumentException("play list has not been set yet.");
+            }
+            if (index < 0 || index >= mPlaylist.size()) {
+                throw new IndexOutOfBoundsException("index is out of play list range.");
+            }
 
-        if (index == mPLCurrentIndex) {
-            return;
-        }
+            if (index == mPlCurrentIndex) {
+                return;
+            }
 
-        // TODO: in playing state, stop current source and start to play source of index.
-        mPLCurrentIndex = index;
+            // TODO: in playing state, stop current source and start to play source of index.
+            mPlCurrentIndex = index;
+        }
     }
 
     /**
@@ -879,19 +895,21 @@
      */
     @Override
     public void setNextPlaylistItem(int index) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
-        if (index < 0 || index >= mPlaylist.size()) {
-            throw new IndexOutOfBoundsException("index is out of play list range.");
-        }
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                throw new IllegalArgumentException("play list has not been set yet.");
+            }
+            if (index < 0 || index >= mPlaylist.size()) {
+                throw new IndexOutOfBoundsException("index is out of play list range.");
+            }
 
-        if (index == mPLNextIndex) {
-            return;
-        }
+            if (index == mPlNextIndex) {
+                return;
+            }
 
-        // TODO: prepare the new next-to-be-played DataSourceDesc
-        mPLNextIndex = index;
+            // TODO: prepare the new next-to-be-played DataSourceDesc
+            mPlNextIndex = index;
+        }
     }
 
     /**
@@ -901,7 +919,9 @@
      */
     @Override
     public int getCurrentPlaylistItemIndex() {
-        return mPLCurrentIndex;
+        synchronized (mPlLock) {
+            return mPlCurrentIndex;
+        }
     }
 
     /**
@@ -920,12 +940,15 @@
             && mode != LOOPING_MODE_SHUFFLE) {
             throw new IllegalArgumentException("mode is not supported.");
         }
-        mLoopingMode = mode;
-        if (mPlaylist == null) {
-            return;
-        }
 
-        // TODO: handle the new mode if necessary.
+        synchronized (mPlLock) {
+            mLoopingMode = mode;
+            if (mPlaylist == null) {
+                return;
+            }
+
+            // TODO: handle the new mode if necessary.
+        }
     }
 
     /**
@@ -935,7 +958,9 @@
      */
     @Override
     public int getLoopingMode() {
-        return mPLCurrentIndex;
+        synchronized (mPlLock) {
+            return mPlCurrentIndex;
+        }
     }
 
     /**
@@ -946,10 +971,12 @@
      */
     @Override
     public void movePlaylistItem(int indexFrom, int indexTo) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                throw new IllegalArgumentException("play list has not been set yet.");
+            }
+            // TODO: move the DataSourceDesc from indexFrom to indexTo.
         }
-        // TODO: move the DataSourceDesc from indexFrom to indexTo.
     }
 
     /**
@@ -964,14 +991,16 @@
      */
     @Override
     public DataSourceDesc removePlaylistItem(int index) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                throw new IllegalArgumentException("play list has not been set yet.");
+            }
 
-        DataSourceDesc oldDsd = mPlaylist.remove(index);
-        // TODO: if index == mPLCurrentIndex, stop current source and move to next one.
-        // if index == mPLNextIndex, prepare the new next-to-be-played source.
-        return oldDsd;
+            DataSourceDesc oldDsd = mPlaylist.remove(index);
+            // TODO: if index == mPlCurrentIndex, stop current source and move to next one.
+            // if index == mPlNextIndex, prepare the new next-to-be-played source.
+            return oldDsd;
+        }
     }
 
     /**
@@ -990,26 +1019,28 @@
     public void addPlaylistItem(int index, DataSourceDesc dsd) {
         Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
 
-        if (mPlaylist == null) {
-            if (index == 0) {
-                mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>());
-                mPlaylist.add(dsd);
-                mPLCurrentIndex = 0;
-                return;
+        synchronized (mPlLock) {
+            if (mPlaylist == null) {
+                if (index == 0) {
+                    mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>());
+                    mPlaylist.add(dsd);
+                    mPlCurrentIndex = 0;
+                    return;
+                }
+                throw new IllegalArgumentException("index should be 0 for first DataSourceDesc.");
             }
-            throw new IllegalArgumentException("index should be 0 for first DataSourceDesc.");
-        }
 
-        long id = dsd.getId();
-        for (DataSourceDesc pldsd : mPlaylist) {
-            if (id == pldsd.getId()) {
-                throw new IllegalArgumentException("Id of dsd already exists in the play list.");
+            long id = dsd.getId();
+            for (DataSourceDesc pldsd : mPlaylist) {
+                if (id == pldsd.getId()) {
+                    throw new IllegalArgumentException("Id of dsd already exists in the play list.");
+                }
             }
-        }
 
-        mPlaylist.add(index, dsd);
-        if (index <= mPLCurrentIndex) {
-            ++mPLCurrentIndex;
+            mPlaylist.add(index, dsd);
+            if (index <= mPlCurrentIndex) {
+                ++mPlCurrentIndex;
+            }
         }
     }
 
@@ -1036,42 +1067,62 @@
         Preconditions.checkNotNull(mPlaylist, "the play list cannot be null");
 
         long id = dsd.getId();
-        for (int i = 0; i < mPlaylist.size(); ++i) {
-            if (i == index) {
-                continue;
+        synchronized (mPlLock) {
+            for (int i = 0; i < mPlaylist.size(); ++i) {
+                if (i == index) {
+                    continue;
+                }
+                if (id == mPlaylist.get(i).getId()) {
+                    throw new IllegalArgumentException(
+                            "Id of dsd already exists in the play list.");
+                }
             }
-            if (id == mPlaylist.get(i).getId()) {
-                throw new IllegalArgumentException("Id of dsd already exists in the play list.");
-            }
-        }
 
-        // TODO: if needed, stop playback of current source, and start new dsd.
-        DataSourceDesc oldDsd = mPlaylist.set(index, dsd);
-        return mPlaylist.set(index, dsd);
+            // TODO: if needed, stop playback of current source, and start new dsd.
+            DataSourceDesc oldDsd = mPlaylist.set(index, dsd);
+            return mPlaylist.set(index, dsd);
+        }
     }
 
-    private void setDataSourcePriv(@NonNull DataSourceDesc dsd) throws IOException {
+    // Called with mPlLock acquired.
+    // TODO: support all looping modes
+    private int getNextIndex_l() {
+        if (mPlaylist.size() <= 1) {
+            return -1;
+        }
+        int index = mPlCurrentIndex + 1;
+        if (index >= mPlaylist.size()) {
+            index = 0;
+        }
+        return index;
+    }
+
+    private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd)
+            throws IOException {
         Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
 
         switch (dsd.getType()) {
             case DataSourceDesc.TYPE_CALLBACK:
-                setDataSourcePriv(dsd.getId(),
-                                  dsd.getMedia2DataSource());
+                handleDataSource(isCurrent,
+                                 dsd.getId(),
+                                 dsd.getMedia2DataSource());
                 break;
 
             case DataSourceDesc.TYPE_FD:
-                setDataSourcePriv(dsd.getId(),
-                                  dsd.getFileDescriptor(),
-                                  dsd.getFileDescriptorOffset(),
-                                  dsd.getFileDescriptorLength());
+                handleDataSource(isCurrent,
+                                 dsd.getId(),
+                                 dsd.getFileDescriptor(),
+                                 dsd.getFileDescriptorOffset(),
+                                 dsd.getFileDescriptorLength());
                 break;
 
             case DataSourceDesc.TYPE_URI:
-                setDataSourcePriv(dsd.getId(),
-                                  dsd.getUriContext(),
-                                  dsd.getUri(),
-                                  dsd.getUriHeaders(),
-                                  dsd.getUriCookies());
+                handleDataSource(isCurrent,
+                                 dsd.getId(),
+                                 dsd.getUriContext(),
+                                 dsd.getUri(),
+                                 dsd.getUriHeaders(),
+                                 dsd.getUriCookies());
                 break;
 
             default:
@@ -1098,66 +1149,59 @@
      * @throws NullPointerException     if context or uri is null
      * @throws IOException              if uri has a file scheme and an I/O error occurs
      */
-    private void setDataSourcePriv(long srcId, @NonNull Context context, @NonNull Uri uri,
+    private void handleDataSource(
+            boolean isCurrent, long srcId,
+            @NonNull Context context, @NonNull Uri uri,
             @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
             throws IOException {
-        if (context == null) {
-            throw new NullPointerException("context param can not be null.");
-        }
-
-        if (uri == null) {
-            throw new NullPointerException("uri param can not be null.");
-        }
-
-        if (cookies != null) {
-            CookieHandler cookieHandler = CookieHandler.getDefault();
-            if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
-                throw new IllegalArgumentException("The cookie handler has to be of CookieManager "
-                        + "type when cookies are provided.");
-            }
-        }
-
         // The context and URI usually belong to the calling user. Get a resolver for that user
         // and strip out the userId from the URI if present.
         final ContentResolver resolver = context.getContentResolver();
         final String scheme = uri.getScheme();
         final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
         if (ContentResolver.SCHEME_FILE.equals(scheme)) {
-            setDataSourcePriv(srcId, uri.getPath(), null, null);
+            handleDataSource(isCurrent, srcId, uri.getPath(), null, null);
             return;
-        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
+        }
+
+        if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                 && Settings.AUTHORITY.equals(authority)) {
             // Try cached ringtone first since the actual provider may not be
             // encryption aware, or it may be stored on CE media storage
             final int type = RingtoneManager.getDefaultType(uri);
             final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
             final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
-            if (attemptDataSource(srcId, resolver, cacheUri)) {
+            if (attemptDataSource(isCurrent, srcId, resolver, cacheUri)) {
                 return;
-            } else if (attemptDataSource(srcId, resolver, actualUri)) {
-                return;
-            } else {
-                setDataSourcePriv(srcId, uri.toString(), headers, cookies);
             }
+            if (attemptDataSource(isCurrent, srcId, resolver, actualUri)) {
+                return;
+            }
+            handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
         } else {
             // Try requested Uri locally first, or fallback to media server
-            if (attemptDataSource(srcId, resolver, uri)) {
+            if (attemptDataSource(isCurrent, srcId, resolver, uri)) {
                 return;
-            } else {
-                setDataSourcePriv(srcId, uri.toString(), headers, cookies);
             }
+            handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
         }
     }
 
-    private boolean attemptDataSource(long srcId, ContentResolver resolver, Uri uri) {
+    private boolean attemptDataSource(
+            boolean isCurrent, long srcId, ContentResolver resolver, Uri uri) {
         try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
             if (afd.getDeclaredLength() < 0) {
-                setDataSourcePriv(srcId, afd.getFileDescriptor(), 0, DataSourceDesc.LONG_MAX);
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 afd.getFileDescriptor(),
+                                 0,
+                                 DataSourceDesc.LONG_MAX);
             } else {
-                setDataSourcePriv(srcId,
-                                  afd.getFileDescriptor(),
-                                  afd.getStartOffset(),
-                                  afd.getDeclaredLength());
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 afd.getFileDescriptor(),
+                                 afd.getStartOffset(),
+                                 afd.getDeclaredLength());
             }
             return true;
         } catch (NullPointerException | SecurityException | IOException ex) {
@@ -1166,10 +1210,10 @@
         }
     }
 
-    private void setDataSourcePriv(
-            long srcId, String path, Map<String, String> headers, List<HttpCookie> cookies)
-            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
-    {
+    private void handleDataSource(
+            boolean isCurrent, long srcId,
+            String path, Map<String, String> headers, List<HttpCookie> cookies)
+            throws IOException {
         String[] keys = null;
         String[] values = null;
 
@@ -1184,19 +1228,20 @@
                 ++i;
             }
         }
-        setDataSourcePriv(srcId, path, keys, values, cookies);
+        handleDataSource(isCurrent, srcId, path, keys, values, cookies);
     }
 
-    private void setDataSourcePriv(long srcId, String path, String[] keys, String[] values,
-            List<HttpCookie> cookies)
-            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+    private void handleDataSource(boolean isCurrent, long srcId,
+            String path, String[] keys, String[] values, List<HttpCookie> cookies)
+            throws IOException {
         final Uri uri = Uri.parse(path);
         final String scheme = uri.getScheme();
         if ("file".equals(scheme)) {
             path = uri.getPath();
         } else if (scheme != null) {
             // handle non-file sources
-            nativeSetDataSource(
+            nativeHandleDataSourceUrl(
+                isCurrent,
                 srcId,
                 Media2HTTPService.createHTTPService(path, cookies),
                 path,
@@ -1209,16 +1254,17 @@
         if (file.exists()) {
             FileInputStream is = new FileInputStream(file);
             FileDescriptor fd = is.getFD();
-            setDataSourcePriv(srcId, fd, 0, DataSourceDesc.LONG_MAX);
+            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX);
             is.close();
         } else {
-            throw new IOException("setDataSourcePriv failed.");
+            throw new IOException("handleDataSource failed.");
         }
     }
 
-    private native void nativeSetDataSource(
-        long srcId, Media2HTTPService httpService, String path, String[] keys, String[] values)
-        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+    private native void nativeHandleDataSourceUrl(
+            boolean isCurrent, long srcId,
+            Media2HTTPService httpService, String path, String[] keys, String[] values)
+            throws IOException;
 
     /**
      * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
@@ -1229,53 +1275,92 @@
      * @throws IllegalArgumentException if fd is not a valid FileDescriptor
      * @throws IOException if fd can not be read
      */
-    private void setDataSourcePriv(long srcId, FileDescriptor fd, long offset, long length)
-            throws IOException {
-        _setDataSource(srcId, fd, offset, length);
+    private void handleDataSource(
+            boolean isCurrent, long srcId,
+            FileDescriptor fd, long offset, long length) throws IOException {
+        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length);
     }
 
-    private native void _setDataSource(long srcId, FileDescriptor fd, long offset, long length)
-            throws IOException;
+    private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
+            FileDescriptor fd, long offset, long length) throws IOException;
 
     /**
      * @throws IllegalStateException if it is called in an invalid state
      * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
      */
-    private void setDataSourcePriv(long srcId, Media2DataSource dataSource) {
-        _setDataSource(srcId, dataSource);
+    private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource) {
+        nativeHandleDataSourceCallback(isCurrent, srcId, dataSource);
     }
 
-    private native void _setDataSource(long srcId, Media2DataSource dataSource);
+    private native void nativeHandleDataSourceCallback(
+            boolean isCurrent, long srcId, Media2DataSource dataSource);
 
-    /**
-     * Prepares the player for playback, synchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
-     * which blocks until MediaPlayer2 is ready for playback.
-     *
-     * @throws IOException if source can not be accessed
-     * @throws IllegalStateException if it is called in an invalid state
-     * @hide
-     */
-    @Override
-    public void prepare() throws IOException {
-        _prepare();
-        scanInternalSubtitleTracks();
+    // This function shall be called with |mPlLock| acquired.
+    private void prepareNextDataSource_l() {
+        if (mPlNextIndex < 0 || mPlNextSourceState != NEXT_SOURCE_STATE_INIT) {
+            // There is no next source or it's in preparing or prepared state.
+            return;
+        }
 
-        // DrmInfo, if any, has been resolved by now.
-        synchronized (mDrmLock) {
-            mDrmInfoResolved = true;
+        try {
+            mPlNextSourceState = NEXT_SOURCE_STATE_PREPARING;
+            handleDataSource(false /* isCurrent */, mPlaylist.get(mPlNextIndex));
+        } catch (Exception e) {
+            Message msg2 = mEventHandler.obtainMessage(
+                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+            final long nextSrcId = mPlaylist.get(mPlNextIndex).getId();
+            mEventHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mEventHandler.handleMessage(msg2, nextSrcId);
+                }
+            });
         }
     }
 
-    private native void _prepare() throws IOException, IllegalStateException;
+    // This function shall be called with |mPlLock| acquired.
+    private void playNextDataSource_l() {
+        if (mPlNextIndex < 0) {
+            return;
+        }
+
+        if (mPlNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
+            // Switch to next source only when it's in prepared state.
+            mPlCurrentIndex = mPlNextIndex;
+            mPlNextIndex = getNextIndex_l();
+            mPlNextSourceState = NEXT_SOURCE_STATE_INIT;
+            mPlNextSourcePlayPending = false;
+
+            long srcId = mPlaylist.get(mPlCurrentIndex).getId();
+            try {
+                nativePlayNextDataSource(srcId);
+            } catch (Exception e) {
+                Message msg2 = mEventHandler.obtainMessage(
+                        MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+                mEventHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mEventHandler.handleMessage(msg2, srcId);
+                    }
+                });
+            }
+
+            // Wait for MEDIA2_INFO_STARTED_AS_NEXT to prepare next source.
+        } else {
+            if (mPlNextSourceState == NEXT_SOURCE_STATE_INIT) {
+                prepareNextDataSource_l();
+            }
+            mPlNextSourcePlayPending = true;
+        }
+    }
+
+    private native void nativePlayNextDataSource(long srcId);
 
     /**
      * Prepares the player for playback, asynchronously.
      *
      * After setting the datasource and the display surface, you need to either
-     * call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
+     * call prepareAsync(). For streams, you should call prepareAsync(),
      * which returns immediately, rather than blocking until enough data has been
      * buffered.
      *
@@ -1925,7 +2010,7 @@
     /**
      * Resets the MediaPlayer2 to its uninitialized state. After calling
      * this method, you will have to initialize it again by setting the
-     * data source and calling prepare().
+     * data source and calling prepareAsync().
      */
     @Override
     public void reset() {
@@ -2000,7 +2085,7 @@
     /**
      * Sets the audio attributes for this MediaPlayer2.
      * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order
+     * You must call this method before {@link #prepareAsync()} in order
      * for the audio attributes to become effective thereafter.
      * @param attributes a non-null set of audio attributes
      * @throws IllegalArgumentException if the attributes are null or invalid.
@@ -3089,6 +3174,19 @@
                     sendMessage(msg2);
                 }
 
+                synchronized (mPlLock) {
+                    Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
+                            + ", currentIndex=" + mPlCurrentIndex + ", nextIndex=" + mPlNextIndex);
+                    if (mPlCurrentIndex >= 0 && srcId == mPlaylist.get(mPlCurrentIndex).getId()) {
+                        prepareNextDataSource_l();
+                    } else if (mPlNextIndex >= 0 && srcId == mPlaylist.get(mPlNextIndex).getId()) {
+                        mPlNextSourceState = NEXT_SOURCE_STATE_PREPARED;
+                        if (mPlNextSourcePlayPending) {
+                            playNextDataSource_l();
+                        }
+                    }
+                }
+
                 synchronized (mEventCbLock) {
                     for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                         cb.first.execute(() -> cb.second.onInfo(
@@ -3127,6 +3225,14 @@
                 return;
 
             case MEDIA_PLAYBACK_COMPLETE:
+                synchronized (mPlLock) {
+                    if (mPlCurrentIndex >= 0 && srcId == mPlaylist.get(mPlCurrentIndex).getId()) {
+                        Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
+                                + ", currentIndex=" + mPlCurrentIndex + ", nextIndex=" + mPlNextIndex);
+                        playNextDataSource_l();
+                    }
+                }
+
                 synchronized (mEventCbLock) {
                     for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                         cb.first.execute(() -> cb.second.onInfo(
@@ -3209,6 +3315,12 @@
 
             case MEDIA_INFO:
                 switch (msg.arg1) {
+                    case MEDIA_INFO_STARTED_AS_NEXT:
+                        if (mPlCurrentIndex >= 0 && srcId == mPlaylist.get(mPlCurrentIndex).getId()) {
+                            prepareNextDataSource_l();
+                        }
+                        break;
+
                     case MEDIA_INFO_VIDEO_TRACK_LAGGING:
                         Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
                         break;
@@ -3517,7 +3629,7 @@
     /**
      * Retrieves the DRM Info associated with the current source
      *
-     * @throws IllegalStateException if called before prepare()
+     * @throws IllegalStateException if called before prepareAsync()
      */
     @Override
     public DrmInfo getDrmInfo() {
@@ -3568,7 +3680,7 @@
      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
      * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
      *
-     * @throws IllegalStateException              if called before prepare(), or the DRM was
+     * @throws IllegalStateException              if called before prepareAsync(), or the DRM was
      *                                            prepared already
      * @throws UnsupportedSchemeException         if the crypto scheme is not supported
      * @throws ResourceBusyException              if required DRM resources are in use
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 823410f..9ad5cd9 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.hardware.Camera;
@@ -278,6 +279,7 @@
          * third-party applications.
          * </p>
          */
+        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
         public static final int REMOTE_SUBMIX = 8;
 
         /** Microphone audio source tuned for unprocessed (raw) sound if available, behaves like
@@ -303,6 +305,7 @@
          * @hide
          */
         @SystemApi
+        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
         public static final int HOTWORD = 1999;
     }
 
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 2190635..343bbda 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -400,6 +400,7 @@
                 new AudioAttributes.Builder()
                         .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
                         .addTag(addressForTag(mix))
+                        .addTag(AudioRecord.SUBMIX_FIXED_VOLUME)
                         .build(),
                 mixFormat,
                 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 51bc330..5a0081a 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -273,9 +273,9 @@
 }
 
 static void
-android_media_MediaPlayer2_setDataSourceAndHeaders(
-        JNIEnv *env, jobject thiz, jlong srcId, jobject httpServiceObj, jstring path,
-        jobjectArray keys, jobjectArray values) {
+android_media_MediaPlayer2_handleDataSourceUrl(
+        JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
+        jobject httpServiceObj, jstring path, jobjectArray keys, jobjectArray values) {
 
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL) {
@@ -292,10 +292,10 @@
     if (tmp == NULL) {  // Out of memory
         return;
     }
-    ALOGV("setDataSourceAndHeaders: path %s, srcId %lld", tmp, (long long)srcId);
+    ALOGV("handleDataSourceUrl: path %s, srcId %lld", tmp, (long long)srcId);
 
     if (strncmp(tmp, "content://", 10) == 0) {
-        ALOGE("setDataSourceAndHeaders: content scheme is not supported in native code");
+        ALOGE("handleDataSourceUrl: content scheme is not supported in native code");
         jniThrowException(env, "java/io/IOException",
                           "content scheme is not supported in native code");
         return;
@@ -321,14 +321,20 @@
     }
     dsd->mHttpService = httpService;
 
-    process_media_player_call(
-            env, thiz, mp->setDataSource(dsd), "java/io/IOException",
-            "setDataSourceAndHeaders failed." );
+    status_t err;
+    if (isCurrent) {
+        err = mp->setDataSource(dsd);
+    } else {
+        err = mp->prepareNextDataSource(dsd);
+    }
+    process_media_player_call(env, thiz, err,
+            "java/io/IOException", "handleDataSourceUrl failed." );
 }
 
 static void
-android_media_MediaPlayer2_setDataSourceFD(
-    JNIEnv *env, jobject thiz, jlong srcId, jobject fileDescriptor, jlong offset, jlong length)
+android_media_MediaPlayer2_handleDataSourceFD(
+    JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
+    jobject fileDescriptor, jlong offset, jlong length)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -341,14 +347,14 @@
         return;
     }
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-    ALOGV("setDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld",
+    ALOGV("handleDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld",
           (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
 
     struct stat sb;
     int ret = fstat(fd, &sb);
     if (ret != 0) {
-        ALOGE("setDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
-        jniThrowException(env, "java/io/IOException", "setDataSourceFD failed fstat");
+        ALOGE("handleDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
+        jniThrowException(env, "java/io/IOException", "handleDataSourceFD failed fstat");
         return;
     }
 
@@ -359,14 +365,14 @@
     ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));
 
     if (offset >= sb.st_size) {
-        ALOGE("setDataSourceFD: offset is out of range");
+        ALOGE("handleDataSourceFD: offset is out of range");
         jniThrowException(env, "java/lang/IllegalArgumentException",
-                          "setDataSourceFD failed, offset is out of range.");
+                          "handleDataSourceFD failed, offset is out of range.");
         return;
     }
     if (offset + length > sb.st_size) {
         length = sb.st_size - offset;
-        ALOGV("setDataSourceFD: adjusted length = %lld", (long long)length);
+        ALOGV("handleDataSourceFD: adjusted length = %lld", (long long)length);
     }
 
     sp<DataSourceDesc> dsd = new DataSourceDesc();
@@ -375,13 +381,20 @@
     dsd->mFD = fd;
     dsd->mFDOffset = offset;
     dsd->mFDLength = length;
-    process_media_player_call(env, thiz, mp->setDataSource(dsd),
-                              "java/io/IOException", "setDataSourceFD failed." );
+
+    status_t err;
+    if (isCurrent) {
+        err = mp->setDataSource(dsd);
+    } else {
+        err = mp->prepareNextDataSource(dsd);
+    }
+    process_media_player_call(env, thiz, err,
+            "java/io/IOException", "handleDataSourceFD failed." );
 }
 
 static void
-android_media_MediaPlayer2_setDataSourceCallback(
-    JNIEnv *env, jobject thiz, jlong srcId, jobject dataSource)
+android_media_MediaPlayer2_handleDataSourceCallback(
+    JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId, jobject dataSource)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -398,8 +411,15 @@
     dsd->mId = srcId;
     dsd->mType = DataSourceDesc::TYPE_CALLBACK;
     dsd->mCallbackSource = callbackDataSource;
-    process_media_player_call(env, thiz, mp->setDataSource(dsd),
-                              "java/lang/RuntimeException", "setDataSourceCallback failed." );
+
+    status_t err;
+    if (isCurrent) {
+        err = mp->setDataSource(dsd);
+    } else {
+        err = mp->prepareNextDataSource(dsd);
+    }
+    process_media_player_call(env, thiz, err,
+            "java/lang/RuntimeException", "handleDataSourceCallback failed." );
 }
 
 static sp<ANativeWindowWrapper>
@@ -503,20 +523,16 @@
 }
 
 static void
-android_media_MediaPlayer2_prepare(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_playNextDataSource(JNIEnv *env, jobject thiz, jlong srcId)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
+    if (mp == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
         return;
     }
 
-    // Handle the case where the display surface was set before the mp was
-    // initialized. We try again to make it stick.
-    sp<ANativeWindowWrapper> st = getVideoSurfaceTexture(env, thiz);
-    mp->setVideoSurfaceTexture(st);
-
-    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
+    process_media_player_call(env, thiz, mp->playNextDataSource((int64_t)srcId),
+            "java/io/IOException", "playNextDataSource failed." );
 }
 
 static void
@@ -1449,18 +1465,25 @@
 
 static const JNINativeMethod gMethods[] = {
     {
-        "nativeSetDataSource",
-        "(JLandroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
+        "nativeHandleDataSourceUrl",
+        "(ZJLandroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
         "[Ljava/lang/String;)V",
-        (void *)android_media_MediaPlayer2_setDataSourceAndHeaders
+        (void *)android_media_MediaPlayer2_handleDataSourceUrl
     },
-
-    {"_setDataSource",      "(JLjava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer2_setDataSourceFD},
-    {"_setDataSource",      "(JLandroid/media/Media2DataSource;)V",(void *)android_media_MediaPlayer2_setDataSourceCallback },
+    {
+        "nativeHandleDataSourceFD",
+        "(ZJLjava/io/FileDescriptor;JJ)V",
+        (void *)android_media_MediaPlayer2_handleDataSourceFD
+    },
+    {
+        "nativeHandleDataSourceCallback",
+        "(ZJLandroid/media/Media2DataSource;)V",
+        (void *)android_media_MediaPlayer2_handleDataSourceCallback
+    },
+    {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer2_setVideoSurface},
     {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
     {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
-    {"_prepare",            "()V",                              (void *)android_media_MediaPlayer2_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer2_prepareAsync},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer2_start},
     {"_stop",               "()V",                              (void *)android_media_MediaPlayer2_stop},
diff --git a/telephony/java/android/telephony/OWNERS b/packages/CarrierDefaultApp/OWNERS
similarity index 92%
rename from telephony/java/android/telephony/OWNERS
rename to packages/CarrierDefaultApp/OWNERS
index 68dedce..7057ce6 100644
--- a/telephony/java/android/telephony/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -1,14 +1,12 @@
-set noparent
-
-amitmahajan@google.com
+tgunn@google.com
 breadley@google.com
-fionaxu@google.com
-jackyu@google.com
 hallliu@google.com
 rgreenwalt@google.com
-tgunn@google.com
-jminjie@google.com
 mpq@google.com
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+jminjie@google.com
+satk@google.com
 shuoq@google.com
-refuhoo@google.com
-
+refuhoo@google.com
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 769b7e9..1e8c523 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -870,6 +870,12 @@
         dumpSetting(s, p,
                 Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                 GlobalSettingsProto.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING);
+        dumpSetting(s, p,
+                Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
+                GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT);
+        dumpSetting(s, p,
+                Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
+                GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS);
         // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.LOW_POWER_MODE,
diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk
new file mode 100644
index 0000000..00a2e60
--- /dev/null
+++ b/packages/SimAppDialog/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SimAppDialog
+LOCAL_CERTIFICATE := platform
+
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    android-support-v4
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
new file mode 100644
index 0000000..873f6c5
--- /dev/null
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.simappdialog">
+    <application android:label="@string/app_name">
+        <activity
+            android:name=".InstallCarrierAppActivity"
+            android:exported="true"
+            android:permission="android.permission.NETWORK_SETTINGS"
+            android:theme="@style/SuwThemeGlif.Light">
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
new file mode 100644
index 0000000..85896e8
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
@@ -0,0 +1,51 @@
+<?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="21dp"
+        android:height="22dp"
+        android:viewportWidth="21"
+        android:viewportHeight="22">
+
+    <group
+        android:translateX="-196.000000"
+        android:translateY="-77.000000">
+        <group
+            android:translateX="190.000000"
+            android:translateY="72.000000">
+            <path
+                android:fillType="evenOdd"
+                android:strokeWidth="1"
+                android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z"/>
+            <group
+                android:translateX="6.666667"
+                android:translateY="5.333333">
+                <path
+                    android:fillColor="#4285F4"
+                    android:strokeWidth="1"
+                    android:pathData="M 17 0 L 19 0 Q 20 0 20 1 L 20 20.3333333 Q 20 21.3333333 19 21.3333333 L 17 21.3333333 Q 16 21.3333333 16 20.3333333 L 16 1 Q 16 0 17 0 Z"/>
+                <path
+                    android:fillColor="#4285F4"
+                    android:strokeWidth="1"
+                    android:pathData="M 1 13.3333333 L 3 13.3333333 Q 4 13.3333333 4 14.3333333 L 4 20.3333333 Q 4 21.3333333 3 21.3333333 L 1 21.3333333 Q 0 21.3333333 0 20.3333333 L 0 14.3333333 Q 0 13.3333333 1 13.3333333 Z"/>
+                <path
+                    android:fillColor="#4285F4"
+                    android:strokeWidth="1"
+                    android:pathData="M 9 6.66666667 L 11 6.66666667 Q 12 6.66666667 12 7.66666667 L 12 20.33333337 Q 12 21.33333337 11 21.33333337 L 9 21.33333337 Q 8 21.33333337 8 20.33333337 L 8 7.66666667 Q 8 6.66666667 9 6.66666667 Z"/>
+            </group>
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SimAppDialog/res/drawable/placeholder.xml b/packages/SimAppDialog/res/drawable/placeholder.xml
new file mode 100644
index 0000000..53eee74
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/placeholder.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<!-- TODO(b/72511181): replace when illustration is finished -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="270dp"
+        android:height="270dp"
+        android:viewportHeight="270.0"
+        android:viewportWidth="270.0">
+    <path android:fillColor="#E8EAED"
+          android:pathData="M183.54,265H84.88c-7.63,0 -13.81,-6.18 -13.81,-13.81V18.81C71.07,11.18 77.25,5 84.88,5h98.66c7.63,0 13.81,6.18 13.81,13.81v232.38C197.35,258.82 191.17,265 183.54,265z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M183.54,6.63c6.72,0 12.18,5.46 12.18,12.18v232.38c0,6.72 -5.46,12.18 -12.18,12.18H84.88c-6.72,0 -12.18,-5.46 -12.18,-12.18V18.81c0,-6.72 5.46,-12.18 12.18,-12.18H183.54M183.54,5H84.88c-7.63,0 -13.81,6.18 -13.81,13.81v232.38c0,7.63 6.18,13.81 13.81,13.81h98.66c7.63,0 13.81,-6.18 13.81,-13.81V18.81C197.35,11.18 191.17,5 183.54,5L183.54,5z"/>
+    <path android:fillColor="#FFFFFF"
+          android:pathData="M186.34,243.74H82.08c-2.41,0 -4.36,-1.95 -4.36,-4.36V30.61c0,-2.41 1.95,-4.36 4.36,-4.36h104.26c2.41,0 4.36,1.95 4.36,4.36v208.78C190.7,241.79 188.75,243.74 186.34,243.74z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M156.07,254.78h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18v-0.08c0,-0.65 0.53,-1.18 1.18,-1.18h43.72c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,254.25 156.72,254.78 156.07,254.78z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M156.07,17.67h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18V16.4c0,-0.65 0.53,-1.18 1.18,-1.18l43.72,0c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,17.14 156.72,17.67 156.07,17.67z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M197.85,84.16h-0.5V67.51h0.5c0.6,0 1.08,0.48 1.08,1.08v14.5C198.93,83.68 198.45,84.16 197.85,84.16z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M197.41,136.45h-0.06v-32.87h0.06c0.84,0 1.52,0.68 1.52,1.52v29.84C198.93,135.77 198.25,136.45 197.41,136.45z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M119.3,74.73l2.71,2.71c6.74,-6.74 17.67,-6.74 24.4,0l2.71,-2.71C140.89,66.49 127.54,66.49 119.3,74.73zM130.15,85.57l4.07,4.07l4.07,-4.07C136.04,83.33 132.39,83.33 130.15,85.57zM124.72,80.15l2.71,2.71c3.74,-3.74 9.82,-3.74 13.56,0l2.71,-2.71C138.46,74.91 129.96,74.91 124.72,80.15z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M143.7,179h-1.36v-2.71h-2.71V179h-10.85v-2.71h-2.71V179h-1.36c-1.5,0 -2.7,1.21 -2.7,2.71l-0.01,18.98c0,1.5 1.21,2.71 2.71,2.71h18.98c1.5,0 2.71,-1.21 2.71,-2.71v-18.98C146.41,180.22 145.2,179 143.7,179zM143.7,200.7h-18.98v-14.91h18.98V200.7zM127.43,188.49h6.78v6.78h-6.78V188.49z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M146.41,144.49v-18.98c0,-1.5 -1.21,-2.71 -2.71,-2.71h-18.98c-1.5,0 -2.71,1.21 -2.71,2.71v18.98c0,1.5 1.21,2.71 2.71,2.71h18.98C145.2,147.2 146.41,145.99 146.41,144.49zM129.47,137.03l3.39,4.07l4.75,-6.11l6.1,8.13h-18.98L129.47,137.03z"/>
+</vector>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
new file mode 100644
index 0000000..0462a93
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -0,0 +1,57 @@
+<?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.
+-->
+<com.android.setupwizardlib.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:icon="@drawable/ic_signal_cellular_alt_rounded_24px"
+    app:suwHeaderText="@string/install_carrier_app_title"
+    app:suwFooter="@layout/install_carrier_app_footer">
+
+    <LinearLayout
+        style="@style/SuwContentFrame"
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/install_carrier_app_description"
+            style="@style/SuwDescription.Glif"
+            android:text="@string/install_carrier_app_description_default"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+
+        <com.android.setupwizardlib.view.FillContentLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1">
+
+            <!-- TODO(b/72511181): final illo and content description update -->
+            <ImageView
+                android:src="@drawable/placeholder"
+                style="@style/SuwContentIllustration"
+                android:contentDescription="@null"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+        </com.android.setupwizardlib.view.FillContentLayout>
+    </LinearLayout>
+
+</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
new file mode 100644
index 0000000..10dcb77
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+
+<com.android.setupwizardlib.view.ButtonBarLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/footer"
+    style="@style/SuwGlifButtonBar.Stackable"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Button
+        android:id="@+id/skip_button"
+        style="@style/SuwGlifButton.Secondary"
+        android:text="@string/install_carrier_app_defer_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/download_button"
+        style="@style/SuwGlifButton.Primary"
+        android:text="@string/install_carrier_app_download_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml
new file mode 100644
index 0000000..0c3930d
--- /dev/null
+++ b/packages/SimAppDialog/res/values/strings.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- TODO character limits -->
+    <!-- The name of this application -->
+    <string name="app_name">Sim App Dialog</string>
+    <!-- Install Carrier App Activity -->
+    <!-- Title of screen asking user to download the carrier app to match the inserted SIM card -->
+    <string name="install_carrier_app_title">Activate mobile service</string>
+    <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we know the name of the carrier-->
+    <string name="install_carrier_app_description">To get your new SIM working properly, you\'ll
+        need to install the <xliff:g name="carrier_name" example="Project Fi">%1$s</xliff:g> app
+    </string>
+    <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we don't know the name of the carrier-->
+    <string name="install_carrier_app_description_default">To get your new SIM working properly,
+        you\'ll need to install the carrier app
+    </string>
+    <!-- Name of the button used to defer downloading the carrier app -->
+    <string name="install_carrier_app_defer_action">Not now</string>
+    <!-- Name of the button for downloading the carrier app -->
+    <string name="install_carrier_app_download_action">Download app</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
new file mode 100644
index 0000000..9e9b80d
--- /dev/null
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -0,0 +1,95 @@
+/*
+ * 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.simappdialog;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.util.WizardManagerHelper;
+
+/**
+ * Activity that gives a user the choice to download the SIM app or defer until a later time
+ *
+ * Will finish with result {@link #DEFER_RESULT} on defer button press or {@link #DOWNLOAD_RESULT}
+ * if the download button is pressed
+ *
+ * Can display the carrier app name if its passed into the intent with key
+ * {@link #BUNDLE_KEY_CARRIER_NAME}
+ */
+public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
+    /**
+     * Key for the carrier app name that will be displayed as the app to download.  If unset, a
+     * default description will be used
+     */
+    public static final String BUNDLE_KEY_CARRIER_NAME = "carrier_name";
+    /** Result code when the defer button is pressed */
+    public static final int DEFER_RESULT = 1;
+    /** Result code when the download button is pressed */
+    public static final int DOWNLOAD_RESULT = 2;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        // Setup theme for aosp/pixel
+        setTheme(
+                WizardManagerHelper.getThemeRes(
+                        SystemProperties.get("setupwizard.theme"),
+                        R.style.SuwThemeGlif_Light
+                )
+        );
+
+        super.onCreate(icicle);
+        setContentView(R.layout.install_carrier_app_activity);
+
+        Button notNowButton = findViewById(R.id.skip_button);
+        notNowButton.setOnClickListener(this);
+
+        Button downloadButton = findViewById(R.id.download_button);
+        downloadButton.setOnClickListener(this);
+
+        // Include carrier name in description text if its present in the intent
+        Intent intent = getIntent();
+        if (intent != null) {
+            String carrierName = intent.getStringExtra(BUNDLE_KEY_CARRIER_NAME);
+            if (!TextUtils.isEmpty(carrierName)) {
+                TextView subtitle = findViewById(R.id.install_carrier_app_description);
+                subtitle.setText(getString(R.string.install_carrier_app_description, carrierName));
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.skip_button:
+                finish(DEFER_RESULT);
+                break;
+            case R.id.download_button:
+                finish(DOWNLOAD_RESULT);
+                break;
+        }
+    }
+
+    private void finish(int resultCode) {
+        setResult(resultCode);
+        finish();
+    }
+}
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step.png
new file mode 100644
index 0000000..d7f9449
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step_dark.png
new file mode 100644
index 0000000..7c65703
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..eea819a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..504ceb7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..8e7d8cb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..456a68f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..fb854ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..75d184a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..9e0af28
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..7c00bd5d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..81b4466
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..724aa9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..7ba0d1b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..a175ccb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..45ce1d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..6da0c9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..71e8959
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..bb7ae26
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..32b9ded
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..ed1949c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step.png
new file mode 100644
index 0000000..d888869
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step_dark.png
new file mode 100644
index 0000000..dc3b25c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..d4e5a94
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..0e693f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..0757799
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..4f07ec1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step.png
new file mode 100644
index 0000000..ba5b457
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step_dark.png
new file mode 100644
index 0000000..a55ea1d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..407ef28
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..39cfbf2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..a7fd3a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..f2a1255
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step.png
new file mode 100644
index 0000000..5a7eec6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step_dark.png
new file mode 100644
index 0000000..f7abb54
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..a1f44dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..175a9ae
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..0fb93ca
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..1052940
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/SystemUI/res/drawable/car_ic_hvac.xml
new file mode 100644
index 0000000..bdc44b3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_hvac.xml
@@ -0,0 +1,51 @@
+<!--
+    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="37dp"
+    android:height="31dp"
+    android:viewportWidth="37"
+    android:viewportHeight="31">
+
+    <group
+            android:translateX="-4.000000"
+            android:translateY="-6.000000">
+        <group
+                android:translateX="5.000000"
+                android:translateY="5.000000">
+            <path
+                android:fillType="evenOdd"
+                android:strokeColor="#FAFAFA"
+                android:strokeWidth="3.5"
+                android:pathData="M0.320769938,6.07518051 C6.46754647,1.46509811 12.4222362,1.46509811
+18.1848392,6.07518051 C23.9474422,10.6852629 29.3258717,10.4931761
+34.3201276,5.49892021" />
+            <path
+                android:fillType="evenOdd"
+                android:strokeColor="#FAFAFA"
+                android:strokeWidth="3.5"
+                android:pathData="M0.320769938,17.0751805 C6.46754647,12.4650981 12.4222362,12.4650981
+18.1848392,17.0751805 C23.9474422,21.6852629 29.3258717,21.4931761
+34.3201276,16.4989202" />
+            <path
+                android:fillType="evenOdd"
+                android:strokeColor="#FAFAFA"
+                android:strokeWidth="3.5"
+                android:pathData="M0.320769938,28.0751805 C6.46754647,23.4650981 12.4222362,23.4650981
+18.1848392,28.0751805 C23.9474422,32.6852629 29.3258717,32.4931761
+34.3201276,27.4989202" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..61d937b90
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_notification.xml
@@ -0,0 +1,28 @@
+<!--
+    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="56dp"
+    android:height="56dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
+4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
+1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
+9v12z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..4651dcb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_overview.xml
@@ -0,0 +1,28 @@
+<!--
+    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="56dp"
+    android:height="56dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:pathData="M0 0h48v48H0z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
+0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml
new file mode 100644
index 0000000..f432d36
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_facet_button.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/car_facet_button"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:animateLayoutChanges="true"
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_icon"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:animateLayoutChanges="true"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter">
+        </com.android.keyguard.AlphaOptimizedImageButton>
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_more_icon"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:animateLayoutChanges="true"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter">
+        </com.android.keyguard.AlphaOptimizedImageButton>
+
+    </LinearLayout>
+</merge>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
new file mode 100644
index 0000000..866b5a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:id="@+id/nav_buttons"
+        android:orientation="vertical"
+        android:gravity="top"
+        android:paddingTop="30dp"
+        android:layout_weight="1"
+        android:background="@drawable/system_bar_background"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/notifications"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:src="@drawable/car_ic_notification"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="20dp"
+            android:paddingBottom="20dp"
+            android:alpha="0.7"
+        />
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:paddingStart="@dimen/status_bar_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_clock_end_padding"
+            android:gravity="center_horizontal"
+            android:paddingBottom="20dp"
+        />
+
+        <Space
+            android:layout_height="10dp"
+            android:layout_width="match_parent"/>
+
+    </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
index 999dbac..4666c60 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -19,34 +19,80 @@
 
 <com.android.systemui.statusbar.car.CarNavigationBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
-    android:gravity="center"
     android:background="@drawable/system_bar_background">
 
-    <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
-         rotation so skip this level of the heirarchy.
-    -->
     <LinearLayout
         android:layout_height="match_parent"
-        android:layout_width="@dimen/car_navigation_bar_width"
+        android:layout_width="wrap_content"
         android:orientation="horizontal"
-        android:clipChildren="false"
-        android:clipToPadding="false"
         android:id="@+id/nav_buttons"
+        android:gravity="left"
+        android:paddingLeft="30dp"
+        android:layout_weight="1"
         android:animateLayoutChanges="true">
 
-        <!-- Buttons get populated here from a car_arrays.xml. -->
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="30dp"
+            android:paddingRight="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="30dp"
+            android:paddingRight="30dp"
+        />
     </LinearLayout>
 
-    <!-- lights out layout to match exactly -->
     <LinearLayout
+        android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:orientation="horizontal"
-        android:id="@+id/lights_out"
-        android:visibility="gone">
-        <!-- Must match nav_buttons. -->
+        android:layout_weight="1"
+        android:gravity="right"
+        android:orientation="horizontal">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/notifications"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:src="@drawable/car_ic_notification"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="20dp"
+            android:paddingRight="20dp"
+            android:alpha="0.7"
+        />
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:singleLine="true"
+            android:paddingStart="@dimen/status_bar_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_clock_end_padding"
+            android:gravity="center_vertical"
+            android:paddingRight="20dp"
+        />
+
+        <Space
+            android:layout_width="10dp"
+            android:layout_height="match_parent"/>
+
     </LinearLayout>
 
 </com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml
index 7677646..4062eb8 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/SystemUI/res/layout/car_navigation_button.xml
@@ -17,28 +17,13 @@
 */
 -->
 
-<com.android.systemui.statusbar.car.CarNavigationButton
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:orientation="horizontal"
-        android:background="?android:attr/selectableItemBackground">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
     <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/car_nav_button_icon"
-            android:layout_height="match_parent"
-            android:layout_width="@dimen/car_navigation_button_width"
-            android:layout_centerInParent="true"
-            android:animateLayoutChanges="true"
-            android:scaleType="fitCenter">
+        android:id="@+id/car_nav_button_icon"
+        android:layout_height="wrap_content"
+        android:layout_width="@dimen/car_navigation_button_width"
+        android:layout_centerInParent="true"
+        android:animateLayoutChanges="true"
+        android:scaleType="fitCenter">
     </com.android.keyguard.AlphaOptimizedImageButton>
-
-    <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/car_nav_button_more_icon"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:layout_centerVertical="true"
-            android:layout_toRightOf="@+id/car_nav_button_icon"
-            android:animateLayoutChanges="true"
-            android:scaleType="fitCenter">
-    </com.android.keyguard.AlphaOptimizedImageButton>
-</com.android.systemui.statusbar.car.CarNavigationButton>
+</merge>
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
new file mode 100644
index 0000000..99ab802
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:id="@+id/nav_buttons"
+        android:orientation="vertical"
+        android:gravity="top"
+        android:paddingTop="30dp"
+        android:layout_weight="1"
+        android:background="@drawable/system_bar_background"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/notifications"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:src="@drawable/car_ic_notification"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="20dp"
+            android:paddingBottom="20dp"
+            android:alpha="0.7"
+        />
+
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:paddingStart="@dimen/status_bar_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_clock_end_padding"
+            android:gravity="center_horizontal"
+            android:paddingBottom="20dp"
+        />
+
+        <Space
+            android:layout_height="10dp"
+            android:layout_width="match_parent"/>
+
+    </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/quick_settings_header.xml b/packages/SystemUI/res/layout/quick_settings_header.xml
deleted file mode 100644
index 43197c4..0000000
--- a/packages/SystemUI/res/layout/quick_settings_header.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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
-  -->
-<com.android.systemui.qs.QSTooltipView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/qs_header_tooltip_height"
-    android:alpha="0"
-    android:gravity="center_horizontal|bottom"
-    android:visibility="invisible">
-
-    <TextView
-        android:id="@+id/header_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/quick_settings_header_onboarding_text"
-        android:textAppearance="@style/TextAppearance.QS.TileLabel"
-        android:textColor="?android:attr/colorAccent" />
-
-</com.android.systemui.qs.QSTooltipView>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 13ca114..a5e37d5 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -40,7 +40,7 @@
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
 
-    <dimen name="qs_tile_margin_top">32dp</dimen>
+    <dimen name="qs_tile_margin_top">16dp</dimen>
     <dimen name="qs_brightness_padding_top">6dp</dimen>
     <dimen name="qs_detail_margin_top">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index 7541b0e..9197bb5 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -30,7 +30,7 @@
         <item>com.google.android.apps.mediashell/.settings.CastSettingsActivity</item>
         <item>com.google.android.katniss.setting/.SpeechSettingsActivity</item>
         <item>com.google.android.katniss.setting/.SearchSettingsActivity</item>
-        <item>com.google.android.gsf.notouch/.UsageDiagnosticsSettingActivity</item>
+        <item>com.google.android.tungsten.setupwraith/.settings.usage.UsageDiagnosticsSettingActivity</item>
         <item>com.google.android.tvlauncher/.notifications.NotificationsSidePanelActivity</item>
     </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
new file mode 100644
index 0000000..b1097c3
--- /dev/null
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<resources>
+    <!-- Allow for custom attribs to be added to a facet button -->
+    <declare-styleable name="CarFacetButton">
+        <!-- icon to be rendered (drawable) -->
+        <attr name="icon" format="reference"/>
+        <!-- intent to start when button is click -->
+        <attr name="intent" format="string"/>
+        <!-- intent to start when a long press has happened -->
+        <attr name="longIntent" format="string"/>
+        <!-- categories that will be added as extras to the fired intents -->
+        <attr name="categories" format="string"/>
+        <!-- package names that will be added as extras to the fired intents -->
+        <attr name="packages" format="string" />
+    </declare-styleable>
+
+
+    <!-- Allow for custom attribs to be added to a nav button -->
+    <declare-styleable name="CarNavigationButton">
+        <!-- intent to start when button is click -->
+        <attr name="intent" format="string"/>
+        <!-- intent to start when a long press has happened -->
+        <attr name="longIntent" format="string"/>
+        <!-- start the intent as a broad cast instead of an activity if true-->
+        <attr name="broadcast" format="boolean"/>
+    </declare-styleable>
+</resources>
diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml
index 9c8dcb1..db829f2 100644
--- a/packages/SystemUI/res/values/config_car.xml
+++ b/packages/SystemUI/res/values/config_car.xml
@@ -22,4 +22,9 @@
          uri that will be launched into the docked window. -->
     <bool name="config_enablePersistentDockedActivity">false</bool>
     <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
+
+    <!-- configure which system ui bars should be displayed -->
+    <bool name="config_enableLeftNavigationBar">false</bool>
+    <bool name="config_enableRightNavigationBar">false</bool>
+    <bool name="config_enableBottomNavigationBar">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bc828ff..d17cd21 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -290,7 +290,7 @@
 
     <dimen name="qs_tile_height">106dp</dimen>
     <dimen name="qs_tile_margin">19dp</dimen>
-    <dimen name="qs_tile_margin_top">32dp</dimen>
+    <dimen name="qs_tile_margin_top">16dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
@@ -309,7 +309,6 @@
     <dimen name="qs_tile_padding_bottom">16dp</dimen>
     <dimen name="qs_tile_spacing">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">0dp</dimen>
-    <dimen name="qs_panel_padding_top">32dp</dimen>
     <dimen name="qs_detail_header_height">56dp</dimen>
     <dimen name="qs_detail_header_padding">0dp</dimen>
     <dimen name="qs_detail_image_width">56dp</dimen>
@@ -334,9 +333,6 @@
     <dimen name="qs_detail_item_icon_width">32dp</dimen>
     <dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
     <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
-    <dimen name="qs_header_padding_start">16dp</dimen>
-    <dimen name="qs_header_padding_end">24dp</dimen>
-    <dimen name="qs_header_tooltip_height">32dp</dimen>
     <dimen name="qs_footer_padding_start">16dp</dimen>
     <dimen name="qs_footer_padding_end">24dp</dimen>
     <dimen name="qs_footer_icon_size">16dp</dimen>
@@ -924,15 +920,16 @@
     <integer name="wireless_charging_fade_duration">200</integer>
 
     <!-- Wired charging on AOD, text animation duration -->
-    <integer name="wired_charging_aod_text_animation_duration_down">500</integer>
+    <integer name="wired_charging_keyguard_text_animation_duration_down">500</integer>
     <!-- Wired charging on AOD, text animation duration -->
-    <integer name="wired_charging_aod_text_animation_duration_up">300</integer>
+    <integer name="wired_charging_keyguard_text_animation_duration_up">300</integer>
     <!-- Wired charging on AOD, text animation distance -->
-    <integer name="wired_charging_aod_text_animation_distance">-30</integer>
+    <integer name="wired_charging_keyguard_text_animation_distance">-30</integer>
 
     <!-- Logout button -->
     <dimen name="logout_button_layout_height">32dp</dimen>
     <dimen name="logout_button_padding_horizontal">16dp</dimen>
     <dimen name="logout_button_margin_bottom">12dp</dimen>
     <dimen name="logout_button_corner_radius">2dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 86cab22..8c59e75 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -666,8 +666,6 @@
     <!-- Textual description of Ethernet connections -->
     <string name="ethernet_label">Ethernet</string>
 
-    <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_header_onboarding_text">Press &amp; hold on the icons for more options</string>
     <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
     <string name="quick_settings_dnd_label">Do not disturb</string>
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 8b57740..adb4e33 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -47,7 +47,6 @@
         Key.QS_INVERT_COLORS_ADDED,
         Key.QS_WORK_ADDED,
         Key.QS_NIGHTDISPLAY_ADDED,
-        Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
         Key.SEEN_MULTI_USER,
         Key.NUM_APPS_LAUNCHED,
         Key.HAS_SEEN_RECENTS_ONBOARDING,
@@ -77,11 +76,6 @@
         String QS_WORK_ADDED = "QsWorkAdded";
         @Deprecated
         String QS_NIGHTDISPLAY_ADDED = "QsNightDisplayAdded";
-        /**
-         * Used for tracking how many times the user has seen the long press tooltip in the Quick
-         * Settings panel.
-         */
-        String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
         String SEEN_MULTI_USER = "HasSeenMultiUser";
         String NUM_APPS_LAUNCHED = "NumAppsLaunched";
         String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index ddf0bd0..bb82a54 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -28,6 +28,8 @@
 import java.io.PrintWriter;
 
 import com.android.internal.os.BinderInternal;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManagerImpl;
 
 public class SystemUIService extends Service {
 
@@ -70,6 +72,10 @@
                 pw.println("dumping service: " + ui.getClass().getName());
                 ui.dump(fd, pw, args);
             }
+            if (Build.IS_DEBUGGABLE) {
+                pw.println("dumping plugins:");
+                ((PluginManagerImpl) Dependency.get(PluginManager.class)).dump(fd, pw, args);
+            }
         } else {
             String svc = args[0];
             for (SystemUI ui: services) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 34d3928..aa26419 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -59,6 +59,12 @@
     }
 
     @Override
+    public void onDestroy() {
+        Dependency.get(PluginManager.class).removePluginListener(this);
+        super.onDestroy();
+    }
+
+    @Override
     public void onPluginConnected(DozeServicePlugin plugin, Context pluginContext) {
         mDozePlugin = plugin;
         mDozePlugin.setDozeRequester(this);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
index 82c0128..d5541e9 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -168,6 +168,12 @@
         return false;
     }
 
+    @Override
+    public String toString() {
+        return String.format("%s@%s (action=%s)",
+                getClass().getSimpleName(), hashCode(), mAction);
+    }
+
     private class MainHandler extends Handler {
         private static final int PLUGIN_CONNECTED = 1;
         private static final int PLUGIN_DISCONNECTED = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
index 03747d5..2a17e35 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -50,6 +50,8 @@
 
 import dalvik.system.PathClassLoader;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.lang.Thread.UncaughtExceptionHandler;
 import java.util.Map;
 
@@ -303,6 +305,14 @@
         }
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(String.format("  plugin map (%d):", mPluginMap.size()));
+        for (PluginListener listener: mPluginMap.keySet()) {
+            pw.println(String.format("    %s -> %s",
+                    listener, mPluginMap.get(listener)));
+        }
+    }
+
     @VisibleForTesting
     public static class PluginInstanceManagerFactory {
         public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index fccd9ce..222c6e82 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -44,7 +44,6 @@
 
     public static final float EXPANDED_TILE_DELAY = .86f;
 
-
     private final ArrayList<View> mAllViews = new ArrayList<>();
     /**
      * List of {@link View}s representing Quick Settings that are being animated from the quick QS
@@ -66,11 +65,6 @@
     private TouchAnimator mNonfirstPageDelayedAnimator;
     private TouchAnimator mBrightnessAnimator;
 
-    /**
-     * Whether the animation is stable and not in the middle of animating between the collapsed and
-     * expanded states.
-     */
-    private boolean mIsInStableState;
     private boolean mOnKeyguard;
 
     private boolean mAllowFancy;
@@ -95,10 +89,6 @@
             Log.w(TAG, "QS Not using page layout");
         }
         panel.setPageListener(this);
-
-        // At time of creation, the QS panel is always considered stable as it's not in the middle
-        // of collapse/expanded.
-        mIsInStableState = true;
     }
 
     public void onRtlChanged() {
@@ -253,11 +243,6 @@
             } else {
                 mBrightnessAnimator = null;
             }
-            View headerView = mQsPanel.getHeaderView();
-            if (headerView!= null) {
-                firstPageBuilder.addFloat(headerView, "translationY", heightDiff, 0);
-                mAllViews.add(headerView);
-            }
             mFirstPageAnimator = firstPageBuilder
                     .setListener(this)
                     .build();
@@ -341,21 +326,11 @@
 
     @Override
     public void onAnimationAtStart() {
-        if (!mIsInStableState) {
-            mQsPanel.onCollapse();
-        }
-        mIsInStableState = true;
-
         mQuickQsPanel.setVisibility(View.VISIBLE);
     }
 
     @Override
     public void onAnimationAtEnd() {
-        if (!mIsInStableState) {
-            mQsPanel.onExpanded();
-        }
-        mIsInStableState = true;
-
         mQuickQsPanel.setVisibility(View.INVISIBLE);
         final int N = mQuickQsViews.size();
         for (int i = 0; i < N; i++) {
@@ -365,11 +340,6 @@
 
     @Override
     public void onAnimationStarted() {
-        if (mIsInStableState) {
-            mQsPanel.onAnimating();
-        }
-        mIsInStableState = false;
-
         mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
         if (mOnFirstPage) {
             final int N = mQuickQsViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5640be5..f7c388d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
 
-import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -59,7 +58,6 @@
 public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
 
     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
-    public static final String QS_SHOW_LONG_PRESS_TOOLTIP = "qs_show_long_press";
 
     protected final Context mContext;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -74,7 +72,6 @@
     private BrightnessController mBrightnessController;
     protected QSTileHost mHost;
 
-    protected QSTooltipView mTooltipView;
     protected QSSecurityFooter mFooter;
     private boolean mGridContentVisible = true;
 
@@ -97,9 +94,6 @@
 
         setOrientation(VERTICAL);
 
-        mTooltipView = (QSTooltipView) LayoutInflater.from(mContext)
-                .inflate(R.layout.quick_settings_header, this, false);
-
         mBrightnessView = LayoutInflater.from(mContext).inflate(
             R.layout.quick_settings_brightness_dialog, this, false);
         mTileLayout = new TileLayout(mContext);
@@ -107,12 +101,7 @@
         Space space = new Space(mContext);
         space.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                 mContext.getResources().getDimensionPixelSize(R.dimen.qs_footer_height)));
-        mScrollLayout = new QSScrollLayout(
-                mContext,
-                mTooltipView,
-                mBrightnessView,
-                (View) mTileLayout,
-                space);
+        mScrollLayout = new QSScrollLayout(mContext, mBrightnessView, (View) mTileLayout, space);
         addView(mScrollLayout);
 
         addDivider();
@@ -145,10 +134,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        final TunerService tunerService = Dependency.get(TunerService.class);
-        tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
-        tunerService.addTunable(this, QS_SHOW_LONG_PRESS_TOOLTIP);
-
+        Dependency.get(TunerService.class).addTunable(this, QS_SHOW_BRIGHTNESS);
         if (mHost != null) {
             setTiles(mHost.getTiles());
         }
@@ -180,16 +166,11 @@
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (QS_SHOW_BRIGHTNESS.equals(key)) {
-            updateViewVisibilityForTuningValue(mBrightnessView, newValue);
-        } else if (QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
-            updateViewVisibilityForTuningValue(mTooltipView, newValue);
+            mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
+                    ? VISIBLE : GONE);
         }
     }
 
-    private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) {
-        view.setVisibility(newValue == null || Integer.parseInt(newValue) != 0 ? VISIBLE : GONE);
-    }
-
     public void openDetails(String subPanel) {
         QSTile tile = getTile(subPanel);
         showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
@@ -224,10 +205,6 @@
         return mBrightnessView;
     }
 
-    View getHeaderView() {
-        return mTooltipView;
-    }
-
     public void setCallback(QSDetail.Callback callback) {
         mCallback = callback;
     }
@@ -289,27 +266,11 @@
         if (mCustomizePanel != null && mCustomizePanel.isShown()) {
             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
         }
-
-        // Instantly hide the header here since we don't want it to still be animating.
-        mTooltipView.setVisibility(View.INVISIBLE);
-    }
-
-    /**
-     * Called when the panel is fully animated out/expanded. This is different from the state
-     * tracked by {@link #mExpanded}, which only checks if the panel is even partially pulled out.
-     */
-    public void onExpanded() {
-        mTooltipView.fadeIn();
-    }
-
-    public void onAnimating() {
-        mTooltipView.fadeOut();
     }
 
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
-
         if (!mExpanded) {
             if (mTileLayout instanceof PagedTileLayout) {
                 ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
deleted file mode 100644
index d1f9741..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
+++ /dev/null
@@ -1,122 +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 com.android.systemui.qs;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Prefs;
-
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * Tooltip/header view for the Quick Settings panel.
- */
-public class QSTooltipView extends LinearLayout {
-
-    private static final int FADE_ANIMATION_DURATION_MS = 300;
-    private static final long AUTO_FADE_OUT_DELAY_MS = TimeUnit.SECONDS.toMillis(6);
-    private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
-    public static final int MAX_TOOLTIP_SHOWN_COUNT = 3;
-
-    private final Handler mHandler = new Handler();
-    private final Runnable mAutoFadeOutRunnable = () -> fadeOut();
-
-    private int mShownCount;
-
-    public QSTooltipView(Context context) {
-        this(context, null);
-    }
-
-    public QSTooltipView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mShownCount = getStoredShownCount();
-    }
-
-    /** Returns the latest stored tooltip shown count from SharedPreferences. */
-    private int getStoredShownCount() {
-        return Prefs.getInt(
-                mContext,
-                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
-                TOOLTIP_NOT_YET_SHOWN_COUNT);
-    }
-
-    /**
-     * Fades in the header view if we can show the tooltip - short circuits any running animation.
-     */
-    public void fadeIn() {
-        if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
-            animate().cancel();
-            setVisibility(View.VISIBLE);
-            animate()
-                    .alpha(1f)
-                    .setDuration(FADE_ANIMATION_DURATION_MS)
-                    .setListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mHandler.postDelayed(mAutoFadeOutRunnable, AUTO_FADE_OUT_DELAY_MS);
-                        }
-                    })
-                    .start();
-
-            // Increment and drop the shown count in prefs for the next time we're deciding to
-            // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
-            // in prefs (say, from a long press).
-            if (getStoredShownCount() <= mShownCount) {
-                Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
-            }
-        }
-    }
-
-    /**
-     * Fades out the header view if it's partially visible - short circuits any running animation.
-     */
-    public void fadeOut() {
-        animate().cancel();
-        if (getVisibility() == View.VISIBLE && getAlpha() != 0f) {
-            mHandler.removeCallbacks(mAutoFadeOutRunnable);
-            animate()
-                    .alpha(0f)
-                    .setDuration(FADE_ANIMATION_DURATION_MS)
-                    .setListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            perhapsMakeViewInvisible();
-                        }
-                    })
-                    .start();
-        } else {
-            perhapsMakeViewInvisible();
-        }
-    }
-
-    /**
-     * Only update visibility if the view is currently being shown. Otherwise, it's already been
-     * hidden by some other manner.
-     */
-    private void perhapsMakeViewInvisible() {
-        if (getVisibility() == View.VISIBLE) {
-            setVisibility(View.INVISIBLE);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 1b4b7df..8314855 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -124,8 +124,9 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
-        if (QS_SHOW_BRIGHTNESS.equals(key) || QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
-            // No Brightness or Tooltip for you!
+        // No tunings for you.
+        if (key.equals(QS_SHOW_BRIGHTNESS)) {
+            // No Brightness for you.
             super.onTuningChanged(key, "0");
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 9fa7beb..65135ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -99,11 +99,7 @@
             record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
             previousView = record.tileView.updateAccessibilityOrder(previousView);
         }
-
-        // Only include the top margin in our measurement if we have more than 1 row to show.
-        // Otherwise, don't add the extra margin buffer at top.
-        int height = (mCellHeight + mCellMargin) * rows +
-                rows != 0 ? (mCellMarginTop - mCellMargin) : 0;
+        int height = (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin);
         if (height < 0) height = 0;
         setMeasuredDimension(width, height);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 6263efa..37f2528 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -107,7 +107,7 @@
         void onAnimationAtStart();
 
         /**
-         * Called when the animator moves into a position of "1". Start and end delays are
+         * Called when the animator moves into a position of "0". Start and end delays are
          * taken into account, so this position may cover a range of fractional inputs.
          */
         void onAnimationAtEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 016cbd6..72592829 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -42,7 +42,6 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
-import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
@@ -50,7 +49,6 @@
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTooltipView;
 
 import java.util.ArrayList;
 
@@ -193,11 +191,6 @@
     public void longClick() {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
         mHandler.sendEmptyMessage(H.LONG_CLICK);
-
-        Prefs.putInt(
-                mContext,
-                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
-                QSTooltipView.MAX_TOOLTIP_SHOWN_COUNT);
     }
 
     public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 6205e9a..2d31669 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -240,6 +240,7 @@
     public void handleSetListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
+        if (mController == null) return;
         if (mListening) {
             mController.addCallback(mZenCallback);
             Prefs.registerListener(mContext, mPrefListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index b3ff4e5b..12daff1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -98,6 +98,8 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
         final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
+
+        if (getAdapter() == null) return;
         state.value = getAdapter().isEnabled();
         state.label = mContext.getString(R.string.quick_settings_nfc_label);
         state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index b7a1500..22e8909 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -300,34 +300,10 @@
                 } else if (mPowerPluggedIn) {
                     String indication = computePowerIndication();
                     if (animate) {
-                        int yTranslation = mContext.getResources().getInteger(
-                                R.integer.wired_charging_aod_text_animation_distance);
-                        int animateUpDuration = mContext.getResources().getInteger(
-                                R.integer.wired_charging_aod_text_animation_duration_up);
-                        int animateDownDuration = mContext.getResources().getInteger(
-                                R.integer.wired_charging_aod_text_animation_duration_down);
-                        mTextView.animate()
-                                .translationYBy(yTranslation)
-                                .setInterpolator(Interpolators.LINEAR)
-                                .setDuration(animateUpDuration)
-                                .setListener(new AnimatorListenerAdapter() {
-                                    @Override
-                                    public void onAnimationStart(Animator animation) {
-                                        mTextView.switchIndication(indication);
-                                    }
-                                    @Override
-                                    public void onAnimationEnd(Animator animation) {
-                                        mTextView.animate()
-                                                .setDuration(animateDownDuration)
-                                                .setInterpolator(Interpolators.BOUNCE)
-                                                .translationYBy(-1 * yTranslation)
-                                                .setListener(null);
-                                    }
-                                });
+                        animateText(mTextView, indication);
                     } else {
                         mTextView.switchIndication(indication);
                     }
-
                 } else {
                     String percentage = NumberFormat.getPercentInstance()
                             .format(mBatteryLevel / 100f);
@@ -355,8 +331,12 @@
                 if (DEBUG_CHARGING_SPEED) {
                     indication += ",  " + (mChargingWattage / 1000) + " mW";
                 }
-                mTextView.switchIndication(indication);
                 mTextView.setTextColor(mInitialTextColor);
+                if (animate) {
+                    animateText(mTextView, indication);
+                } else {
+                    mTextView.switchIndication(indication);
+                }
             } else if (!TextUtils.isEmpty(trustManagedIndication)
                     && updateMonitor.getUserTrustIsManaged(userId)
                     && !updateMonitor.getUserHasTrust(userId)) {
@@ -369,6 +349,34 @@
         }
     }
 
+    // animates textView - textView moves up and bounces down
+    private void animateText(KeyguardIndicationTextView textView, String indication) {
+        int yTranslation = mContext.getResources().getInteger(
+                R.integer.wired_charging_keyguard_text_animation_distance);
+        int animateUpDuration = mContext.getResources().getInteger(
+                R.integer.wired_charging_keyguard_text_animation_duration_up);
+        int animateDownDuration = mContext.getResources().getInteger(
+                R.integer.wired_charging_keyguard_text_animation_duration_down);
+        textView.animate()
+                .translationYBy(yTranslation)
+                .setInterpolator(Interpolators.LINEAR)
+                .setDuration(animateUpDuration)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        textView.switchIndication(indication);
+                    }
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        textView.animate()
+                                .setDuration(animateDownDuration)
+                                .setInterpolator(Interpolators.BOUNCE)
+                                .translationYBy(-1 * yTranslation)
+                                .setListener(null);
+                    }
+                });
+    }
+
     private String computePowerIndication() {
         if (mPowerCharged) {
             return mContext.getResources().getString(R.string.keyguard_charged);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
new file mode 100644
index 0000000..53101a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -0,0 +1,161 @@
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.R;
+
+/**
+ * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
+ * category. It can also render a indicator impling that there are more options of apps to launch
+ * using this component. This is done with a "More icon" currently an arrow as defined in the layout
+ * file. The class is to serve as an example.
+ * Usage example: A button that allows a user to select a music app and indicate that there are
+ * other music apps installed.
+ */
+public class CarFacetButton extends LinearLayout {
+    private static final float SELECTED_ALPHA = 1f;
+    private static final float UNSELECTED_ALPHA = 0.7f;
+
+    private static final String FACET_FILTER_DELIMITER = ";";
+    /**
+     * Extra information to be sent to a helper to make the decision of what app to launch when
+     * clicked.
+     */
+    private static final String EXTRA_FACET_CATEGORIES = "categories";
+    private static final String EXTRA_FACET_PACKAGES = "packages";
+    private static final String EXTRA_FACET_ID = "filter_id";
+    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
+
+    private Context mContext;
+    private AlphaOptimizedImageButton mIcon;
+    private AlphaOptimizedImageButton mMoreIcon;
+    private boolean mSelected = false;
+    /** App categories that are to be used with this widget */
+    private String[] mFacetCategories;
+    /** App packages that are allowed to be used with this widget */
+    private String[] mFacetPackages;
+
+
+    public CarFacetButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        View.inflate(context, R.layout.car_facet_button, this);
+
+        // extract custom attributes
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
+        setupIntents(typedArray);
+        setupIcons(typedArray);
+    }
+
+    /**
+     * Reads the custom attributes to setup click handlers for this component.
+     */
+    private void setupIntents(TypedArray typedArray) {
+        String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
+        String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
+        String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
+        String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
+        try {
+            final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
+            intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
+
+            if (packageString != null) {
+                mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
+                intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
+            }
+            if (categoryString != null) {
+                mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
+                intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
+            }
+
+            setOnClickListener(v -> {
+                intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
+                mContext.startActivity(intent);
+            });
+
+            if (longPressIntentString != null) {
+                final Intent longPressIntent = Intent.parseUri(longPressIntentString,
+                        Intent.URI_INTENT_SCHEME);
+                setOnLongClickListener(v -> {
+                    mContext.startActivity(longPressIntent);
+                    return true;
+                });
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to attach intent", e);
+        }
+    }
+
+
+    private void setupIcons(TypedArray styledAttributes) {
+        mIcon = findViewById(R.id.car_nav_button_icon);
+        mIcon.setScaleType(ImageView.ScaleType.CENTER);
+        mIcon.setClickable(false);
+        mIcon.setAlpha(UNSELECTED_ALPHA);
+        int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
+        if (iconResourceId == 0)  {
+            throw new RuntimeException("specified icon resource was not found and is required");
+        }
+        mIcon.setImageResource(iconResourceId);
+
+        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
+        mMoreIcon.setClickable(false);
+        mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
+        mMoreIcon.setAlpha(UNSELECTED_ALPHA);
+        mMoreIcon.setVisibility(GONE);
+    }
+
+    /**
+     * @return The app categories the component represents
+     */
+    public String[] getCategories() {
+        if (mFacetCategories == null) {
+            return new String[0];
+        }
+        return mFacetCategories;
+    }
+
+    /**
+     * @return The valid packages that should be considered.
+     */
+    public String[] getFacetPackages() {
+        if (mFacetPackages == null) {
+            return new String[0];
+        }
+        return mFacetPackages;
+    }
+
+    /**
+     * Updates the alpha of the icons to "selected" and shows the "More icon"
+     * @param selected true if the view must be selected, false otherwise
+     */
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        setSelected(selected, selected);
+    }
+
+    /**
+     * Updates the visual state to let the user know if it's been selected.
+     * @param selected true if should update the alpha of the icon to selected, false otherwise
+     * @param showMoreIcon true if the "more icon" should be shown, false otherwise
+     */
+    public void setSelected(boolean selected, boolean showMoreIcon) {
+        mSelected = selected;
+        if (selected) {
+            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
+            mMoreIcon.setAlpha(SELECTED_ALPHA);
+            mIcon.setAlpha(SELECTED_ALPHA);
+        } else {
+            mMoreIcon.setVisibility(GONE);
+            mIcon.setAlpha(UNSELECTED_ALPHA);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
new file mode 100644
index 0000000..e8c9a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -0,0 +1,114 @@
+package com.android.systemui.statusbar.car;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
+ * application on screen is associated with it. This is basically a similar concept to a radio
+ * button group.
+ */
+public class CarFacetButtonController {
+
+    protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
+    protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
+    protected CarFacetButton mSelectedFacetButton;
+    protected Context mContext;
+
+    public CarFacetButtonController(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons
+     * such that it can select and unselect them based on running task chages
+     * @param bar that may contain CarFacetButtons
+     */
+    public void addCarNavigationBar(CarNavigationBarView bar) {
+        findFacets(bar);
+    }
+
+    private void findFacets(ViewGroup root) {
+        final int childCount = root.getChildCount();
+
+        for (int i = 0; i < childCount; ++i) {
+            final View v = root.getChildAt(i);
+            if (v instanceof CarFacetButton) {
+                CarFacetButton facetButton = (CarFacetButton) v;
+                String[] categories = facetButton.getCategories();
+                for (int j = 0; j < categories.length; j++) {
+                    String category = categories[j];
+                    mButtonsByCategory.put(category, facetButton);
+                }
+
+                String[] facetPackages = facetButton.getFacetPackages();
+                for (int j = 0; j < facetPackages.length; j++) {
+                    String facetPackage = facetPackages[j];
+                    mButtonsByPackage.put(facetPackage, facetButton);
+                }
+            } else if (v instanceof ViewGroup) {
+                findFacets((ViewGroup) v);
+            }
+        }
+    }
+
+
+    /**
+     * This will unselect the currently selected CarFacetButton and determine which one should be
+     * selected next. It does this by reading the properties on the CarFacetButton and seeing if
+     * they are a match with the supplied taskino.
+     * @param taskInfo of the currently running application
+     */
+    public void taskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        if (taskInfo == null || taskInfo.baseActivity == null) {
+            return;
+        }
+        String packageName = taskInfo.baseActivity.getPackageName();
+
+        // If the package name belongs to a filter, then highlight appropriate button in
+        // the navigation bar.
+        if (mSelectedFacetButton != null) {
+            mSelectedFacetButton.setSelected(false);
+        }
+        CarFacetButton facetButton = mButtonsByPackage.get(packageName);
+        if (facetButton != null) {
+            facetButton.setSelected(true);
+            mSelectedFacetButton = facetButton;
+        } else {
+            String category = getPackageCategory(packageName);
+            if (category != null) {
+                facetButton = mButtonsByCategory.get(category);
+                facetButton.setSelected(true);
+                mSelectedFacetButton = facetButton;
+            }
+        }
+    }
+
+    protected String getPackageCategory(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        Set<String> supportedCategories = mButtonsByCategory.keySet();
+        for (String category : supportedCategories) {
+            Intent intent = new Intent();
+            intent.setPackage(packageName);
+            intent.setAction(Intent.ACTION_MAIN);
+            intent.addCategory(category);
+            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+            if (list.size() > 0) {
+                // Cache this package name into facetPackageMap, so we won't have to query
+                // all categories next time this package name shows up.
+                mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
+                return category;
+            }
+        }
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
deleted file mode 100644
index 64c52ed..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.car;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.v4.util.SimpleArrayMap;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.view.View;
-import android.widget.LinearLayout;
-import com.android.systemui.R;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A controller to populate data for CarNavigationBarView and handle user interactions.
- *
- * <p>Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can
- * customize the navigation buttons by updating arrays_car.xml appropriately in an overlay.
- */
-class CarNavigationBarController {
-    private static final String TAG = "CarNavBarController";
-
-    private static final String EXTRA_FACET_CATEGORIES = "categories";
-    private static final String EXTRA_FACET_PACKAGES = "packages";
-    private static final String EXTRA_FACET_ID = "filter_id";
-    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
-
-    /**
-     * Each facet of the navigation bar maps to a set of package names or categories defined in
-     * arrays_car.xml. Package names for a given facet are delimited by ";".
-     */
-    private static final String FACET_FILTER_DELIMITER = ";";
-
-    private final Context mContext;
-    private final CarNavigationBarView mNavBar;
-    private final CarStatusBar mStatusBar;
-
-    /**
-     * Set of categories each facet will filter on.
-     */
-    private final List<String[]> mFacetCategories = new ArrayList<>();
-
-    /**
-     * Set of package names each facet will filter on.
-     */
-    private final List<String[]> mFacetPackages = new ArrayList<>();
-
-    private final SimpleArrayMap<String, Integer> mFacetCategoryMap = new SimpleArrayMap<>();
-    private final SimpleArrayMap<String, Integer> mFacetPackageMap = new SimpleArrayMap<>();
-
-    private final List<CarNavigationButton> mNavButtons = new ArrayList<>();
-
-    private final SparseBooleanArray mFacetHasMultipleAppsCache = new SparseBooleanArray();
-
-    private int mCurrentFacetIndex;
-    private Intent mPersistentTaskIntent;
-
-    public CarNavigationBarController(Context context, CarNavigationBarView navBar,
-            CarStatusBar activityStarter) {
-        mContext = context;
-        mNavBar = navBar;
-        mStatusBar = activityStarter;
-        bind();
-
-        if (context.getResources().getBoolean(R.bool.config_enablePersistentDockedActivity)) {
-            setupPersistentDockedTask();
-        }
-    }
-
-    private void setupPersistentDockedTask() {
-        try {
-            mPersistentTaskIntent = Intent.parseUri(
-                    mContext.getString(R.string.config_persistentDockedActivityIntentUri),
-                    Intent.URI_INTENT_SCHEME);
-        } catch (URISyntaxException e) {
-            Log.e(TAG, "Malformed persistent task intent.");
-        }
-    }
-
-    public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) {
-        // If the package name belongs to a filter, then highlight appropriate button in
-        // the navigation bar.
-        if (mFacetPackageMap.containsKey(packageName)) {
-            setCurrentFacet(mFacetPackageMap.get(packageName));
-        }
-
-        // Check if the package matches any of the categories for the facets
-        String category = getPackageCategory(packageName);
-        if (category != null) {
-            setCurrentFacet(mFacetCategoryMap.get(category));
-        }
-
-        // Set up the persistent docked task if needed.
-        boolean isHomeTask =
-                taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
-        if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) {
-            mStatusBar.startActivityOnStack(mPersistentTaskIntent,
-                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
-        }
-    }
-
-    public void onPackageChange(String packageName) {
-        if (mFacetPackageMap.containsKey(packageName)) {
-            int index = mFacetPackageMap.get(packageName);
-            mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
-            // No need to check categories because we've already refreshed the cache.
-            return;
-        }
-
-        String category = getPackageCategory(packageName);
-        if (mFacetCategoryMap.containsKey(category)) {
-            int index = mFacetCategoryMap.get(category);
-            mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
-        }
-    }
-
-    /**
-     * Iterates through the items in arrays_car.xml and sets up the facet bar buttons to
-     * perform the task in that configuration file when clicked or long-pressed.
-     */
-    private void bind() {
-        Resources res = mContext.getResources();
-
-        TypedArray icons = res.obtainTypedArray(R.array.car_facet_icons);
-        TypedArray intents = res.obtainTypedArray(R.array.car_facet_intent_uris);
-        TypedArray longPressIntents = res.obtainTypedArray(R.array.car_facet_longpress_intent_uris);
-        TypedArray facetPackageNames = res.obtainTypedArray(R.array.car_facet_package_filters);
-        TypedArray facetCategories = res.obtainTypedArray(R.array.car_facet_category_filters);
-
-        try {
-            if (icons.length() != intents.length()
-                    || icons.length() != longPressIntents.length()
-                    || icons.length() != facetPackageNames.length()
-                    || icons.length() != facetCategories.length()) {
-                throw new RuntimeException("car_facet array lengths do not match");
-            }
-
-            for (int i = 0, size = icons.length(); i < size; i++) {
-                Drawable icon = icons.getDrawable(i);
-                CarNavigationButton button = createNavButton(icon);
-                initClickListeners(button, i, intents.getString(i), longPressIntents.getString(i));
-
-                mNavButtons.add(button);
-                mNavBar.addButton(button, createNavButton(icon) /* lightsOutButton */);
-
-                initFacetFilterMaps(i, facetPackageNames.getString(i).split(FACET_FILTER_DELIMITER),
-                        facetCategories.getString(i).split(FACET_FILTER_DELIMITER));
-                mFacetHasMultipleAppsCache.put(i, facetHasMultiplePackages(i));
-            }
-        } finally {
-            // Clean up all the TypedArrays.
-            icons.recycle();
-            intents.recycle();
-            longPressIntents.recycle();
-            facetPackageNames.recycle();
-            facetCategories.recycle();
-        }
-    }
-
-    /**
-     * Recreates each of the buttons on a density or font scale change. This manual process is
-     * necessary since this class is not part of an activity that automatically gets recreated.
-     */
-    public void onDensityOrFontScaleChanged() {
-        TypedArray icons = mContext.getResources().obtainTypedArray(R.array.car_facet_icons);
-
-        try {
-            int length = icons.length();
-            if (length != mNavButtons.size()) {
-                // This should not happen since the mNavButtons list is created from the length
-                // of the icons array in bind().
-                throw new RuntimeException("car_facet array lengths do not match number of "
-                        + "created buttons.");
-            }
-
-            for (int i = 0; i < length; i++) {
-                Drawable icon = icons.getDrawable(i);
-
-                // Setting a new icon will trigger a requestLayout() call if necessary.
-                mNavButtons.get(i).setResources(icon);
-            }
-        } finally {
-            icons.recycle();
-        }
-    }
-
-    private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) {
-        mFacetCategories.add(categories);
-        for (String category : categories) {
-            mFacetCategoryMap.put(category, id);
-        }
-
-        mFacetPackages.add(packageNames);
-        for (String packageName : packageNames) {
-            mFacetPackageMap.put(packageName, id);
-        }
-    }
-
-    private String getPackageCategory(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        int size = mFacetCategories.size();
-        // For each facet, check if the given package name matches one of its categories
-        for (int i = 0; i < size; i++) {
-            String[] categories = mFacetCategories.get(i);
-            for (int j = 0; j < categories.length; j++) {
-                String category = categories[j];
-                Intent intent = new Intent();
-                intent.setPackage(packageName);
-                intent.setAction(Intent.ACTION_MAIN);
-                intent.addCategory(category);
-                List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
-                if (list.size() > 0) {
-                    // Cache this package name into facetPackageMap, so we won't have to query
-                    // all categories next time this package name shows up.
-                    mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category));
-                    return category;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Helper method to check if a given facet has multiple packages associated with it. This can
-     * be resource defined package names or package names filtered by facet category.
-     *
-     * @return {@code true} if the facet at the given index has more than one package.
-     */
-    private boolean facetHasMultiplePackages(int index) {
-        PackageManager pm = mContext.getPackageManager();
-
-        // Check if the packages defined for the filter actually exists on the device
-        String[] packages = mFacetPackages.get(index);
-        if (packages.length > 1) {
-            int count = 0;
-            for (int i = 0; i < packages.length; i++) {
-                count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0;
-                if (count > 1) {
-                    return true;
-                }
-            }
-        }
-
-        // If there weren't multiple packages defined for the facet, check the categories
-        // and see if they resolve to multiple package names
-        String categories[] = mFacetCategories.get(index);
-
-        int count = 0;
-        for (int i = 0; i < categories.length; i++) {
-            String category = categories[i];
-            Intent intent = new Intent();
-            intent.setAction(Intent.ACTION_MAIN);
-            intent.addCategory(category);
-            count += pm.queryIntentActivities(intent, 0).size();
-            if (count > 1) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Sets the facet at the given index to be the facet that is currently active. The button will
-     * be highlighted appropriately.
-     */
-    private void setCurrentFacet(int index) {
-        if (index == mCurrentFacetIndex) {
-            return;
-        }
-
-        if (mNavButtons.get(mCurrentFacetIndex) != null) {
-            mNavButtons.get(mCurrentFacetIndex)
-                    .setSelected(false /* selected */, false /* showMoreIcon */);
-        }
-
-        if (mNavButtons.get(index) != null) {
-            mNavButtons.get(index).setSelected(true /* selected */,
-                    mFacetHasMultipleAppsCache.get(index)  /* showMoreIcon */);
-        }
-
-        mCurrentFacetIndex = index;
-    }
-
-    /**
-     * Creates the View that is used for the buttons along the navigation bar.
-     *
-     * @param icon The icon to be used for the button.
-     */
-    private CarNavigationButton createNavButton(Drawable icon) {
-        CarNavigationButton button = (CarNavigationButton) View.inflate(mContext,
-                R.layout.car_navigation_button, null);
-        button.setResources(icon);
-        LinearLayout.LayoutParams lp =
-                new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
-        button.setLayoutParams(lp);
-
-        return button;
-    }
-
-    /**
-     * Initializes the click and long click listeners that correspond to the given command string.
-     * The click listeners are attached to the given button.
-     */
-    private void initClickListeners(View button, int index, String clickString,
-            String longPressString) {
-        // Each button at least have an action when pressed.
-        if (TextUtils.isEmpty(clickString)) {
-            throw new RuntimeException("Facet at index " + index + " does not have click action.");
-        }
-
-        try {
-            Intent intent = Intent.parseUri(clickString, Intent.URI_INTENT_SCHEME);
-            button.setOnClickListener(v -> onFacetClicked(intent, index));
-        } catch (URISyntaxException e) {
-            throw new RuntimeException("Malformed intent uri", e);
-        }
-
-        if (TextUtils.isEmpty(longPressString)) {
-            button.setLongClickable(false);
-            return;
-        }
-
-        try {
-            Intent intent = Intent.parseUri(longPressString, Intent.URI_INTENT_SCHEME);
-            button.setOnLongClickListener(v -> {
-                onFacetLongClicked(intent, index);
-                return true;
-            });
-        } catch (URISyntaxException e) {
-            throw new RuntimeException("Malformed long-press intent uri", e);
-        }
-    }
-
-    /**
-     * Handles a click on a facet. A click will trigger the given Intent.
-     *
-     * @param index The index of the facet that was clicked.
-     */
-    private void onFacetClicked(Intent intent, int index) {
-        String packageName = intent.getPackage();
-
-        if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) {
-            return;
-        }
-
-        intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
-        intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
-        // The facet is identified by the index in which it was added to the nav bar.
-        // This value can be used to determine which facet was selected
-        intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));
-
-        // If the current facet is clicked, we want to launch the picker by default
-        // rather than the "preferred/last run" app.
-        intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
-
-        int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-        int activityType = ACTIVITY_TYPE_UNDEFINED;
-        if (intent.getCategories().contains(Intent.CATEGORY_HOME)) {
-            windowingMode = WINDOWING_MODE_UNDEFINED;
-            activityType = ACTIVITY_TYPE_HOME;
-        }
-
-        setCurrentFacet(index);
-        mStatusBar.startActivityOnStack(intent, windowingMode, activityType);
-    }
-
-    /**
-     * Handles a long-press on a facet. The long-press will trigger the given Intent.
-     *
-     * @param index The index of the facet that was clicked.
-     */
-    private void onFacetLongClicked(Intent intent, int index) {
-        setCurrentFacet(index);
-        mStatusBar.startActivityOnStack(intent,
-                WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index e5a311d..1d9ef61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -16,17 +16,15 @@
 
 package com.android.systemui.statusbar.car;
 
+import android.app.UiModeManager;
 import android.content.Context;
-import android.graphics.Canvas;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
+import android.util.Log;
 import android.view.View;
 import android.widget.LinearLayout;
 
+import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.phone.NavGesture;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-import com.android.systemui.statusbar.phone.NavigationBarView;
 
 /**
  * A custom navigation bar for the automotive use case.
@@ -34,9 +32,10 @@
  * The navigation bar in the automotive use case is more like a list of shortcuts, rendered
  * in a linear layout.
  */
-class CarNavigationBarView extends NavigationBarView {
+class CarNavigationBarView extends LinearLayout {
     private LinearLayout mNavButtons;
-    private LinearLayout mLightsOutButtons;
+    private AlphaOptimizedImageButton mNotificationsButton;
+    private CarStatusBar mCarStatusBar;
 
     public CarNavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -45,99 +44,16 @@
     @Override
     public void onFinishInflate() {
         mNavButtons = findViewById(R.id.nav_buttons);
-        mLightsOutButtons = findViewById(R.id.lights_out);
+
+        mNotificationsButton = findViewById(R.id.notifications);
+        mNotificationsButton.setOnClickListener(this::onNotificationsClick);
     }
 
-    public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){
-        mNavButtons.addView(button);
-        mLightsOutButtons.addView(lightsOutButton);
+    void setStatusBar(CarStatusBar carStatusBar) {
+        mCarStatusBar = carStatusBar;
     }
 
-    @Override
-    public void setDisabledFlags(int disabledFlags, boolean force) {
-        // TODO: Populate.
-    }
-
-    @Override
-    public void reorient() {
-        // We expect the car head unit to always have a fixed rotation so we ignore this. The super
-        // class implentation expects mRotatedViews to be populated, so if you call into it, there
-        // is a possibility of a NullPointerException.
-    }
-
-    @Override
-    public View getCurrentView() {
-        return this;
-    }
-
-    @Override
-    public void setNavigationIconHints(int hints, boolean force) {
-        // We do not need to set the navigation icon hints for a vehicle
-        // Calling setNavigationIconHints in the base class will result in a NPE as the car
-        // navigation bar does not have a back button.
-    }
-
-    @Override
-    public void onPluginConnected(NavGesture plugin, Context context) {
-        // set to null version of the plugin ignoring incoming arg.
-        super.onPluginConnected(new NullNavGesture(), context);
-    }
-
-    @Override
-    public void onPluginDisconnected(NavGesture plugin) {
-        // reinstall the null nav gesture plugin
-        super.onPluginConnected(new NullNavGesture(), getContext());
-    }
-
-    /**
-     * Null object pattern to work around expectations of the base class.
-     * This is a temporary solution to have the car system ui working.
-     * Already underway is a refactor of they car sys ui as to not use this class
-     * hierarchy.
-     */
-    private static class NullNavGesture implements NavGesture {
-        @Override
-        public GestureHelper getGestureHelper() {
-            return new GestureHelper() {
-                @Override
-                public boolean onTouchEvent(MotionEvent event) {
-                    return false;
-                }
-
-                @Override
-                public boolean onInterceptTouchEvent(MotionEvent event) {
-                    return false;
-                }
-
-                @Override
-                public void setBarState(boolean vertical, boolean isRtl) {
-                }
-
-                @Override
-                public void onDraw(Canvas canvas) {
-                }
-
-                @Override
-                public void onDarkIntensityChange(float intensity) {
-                }
-
-                @Override
-                public void onLayout(boolean changed, int left, int top, int right, int bottom) {
-                }
-            };
-        }
-
-        @Override
-        public int getVersion() {
-            return 0;
-        }
-
-        @Override
-        public void onCreate(Context sysuiContext, Context pluginContext) {
-        }
-
-        @Override
-        public void onDestroy() {
-        }
+    protected void onNotificationsClick(View v) {
+        mCarStatusBar.togglePanel();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 2de358f..0cdaec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,72 +1,87 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 package com.android.systemui.statusbar.car;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
+import android.content.Intent;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.widget.ImageView;
-import android.widget.RelativeLayout;
 
-import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
 
+import java.net.URISyntaxException;
+
 /**
- * A wrapper view for a car navigation facet, which includes a button icon and a drop down icon.
+ * CarNavigationButton is an image button that allows for a bit more configuration at the
+ * xml file level. This allows for more control via overlays instead of having to update
+ * code.
  */
-public class CarNavigationButton extends RelativeLayout {
+public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
+
     private static final float SELECTED_ALPHA = 1;
     private static final float UNSELECTED_ALPHA = 0.7f;
 
-    private AlphaOptimizedImageButton mIcon;
-    private AlphaOptimizedImageButton mMoreIcon;
+    private Context mContext;
+    private String mIntent = null;
+    private String mLongIntent = null;
+    private boolean mBroadcastIntent = false;
+    private boolean mSelected = false;
+
 
     public CarNavigationButton(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarNavigationButton);
+        mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
+        mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
+        mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
     }
 
+
+    /**
+     * After the standard inflate this then adds the xml defined intents to click and long click
+     * actions if defined.
+     */
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mIcon = findViewById(R.id.car_nav_button_icon);
-        mIcon.setScaleType(ImageView.ScaleType.CENTER);
-        mIcon.setClickable(false);
-        mIcon.setBackgroundColor(android.R.color.transparent);
-        mIcon.setAlpha(UNSELECTED_ALPHA);
-
-        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
-        mMoreIcon.setClickable(false);
-        mMoreIcon.setBackgroundColor(android.R.color.transparent);
-        mMoreIcon.setVisibility(INVISIBLE);
-        mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
-        mMoreIcon.setAlpha(UNSELECTED_ALPHA);
-    }
-
-    public void setResources(Drawable icon) {
-        mIcon.setImageDrawable(icon);
-    }
-
-    public void setSelected(boolean selected, boolean showMoreIcon) {
-        if (selected) {
-            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE);
-            mMoreIcon.setAlpha(SELECTED_ALPHA);
-            mIcon.setAlpha(SELECTED_ALPHA);
-        } else {
-            mMoreIcon.setVisibility(INVISIBLE);
-            mIcon.setAlpha(UNSELECTED_ALPHA);
+        setScaleType(ImageView.ScaleType.CENTER);
+        setAlpha(UNSELECTED_ALPHA);
+        try {
+            if (mIntent != null) {
+                final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+                setOnClickListener(v -> {
+                    if (mBroadcastIntent) {
+                        mContext.sendBroadcast(intent);
+                        return;
+                    }
+                    mContext.startActivity(intent);
+                });
+            }
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to attach intent", e);
         }
+
+        try {
+            if (mLongIntent != null) {
+                final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+                setOnLongClickListener(v -> {
+                    mContext.startActivity(intent);
+                    return true;
+                });
+            }
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to attach long press intent", e);
+        }
+    }
+
+    /**
+     * @param selected true if should indicate if this is a selected state, false otherwise
+     */
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        mSelected = selected;
+        setAlpha(mSelected ? SELECTED_ALPHA : UNSELECTED_ALPHA);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3dfb913..c15a013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -18,17 +18,14 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -46,10 +43,7 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -69,7 +63,6 @@
 
     private TaskStackListenerImpl mTaskStackListener;
 
-    private CarNavigationBarController mController;
     private FullscreenUserSwitcher mFullscreenUserSwitcher;
 
     private CarBatteryController mCarBatteryController;
@@ -78,15 +71,23 @@
 
     private ConnectedDeviceSignalController mConnectedDeviceSignalController;
     private ViewGroup mNavigationBarWindow;
+    private ViewGroup mLeftNavigationBarWindow;
+    private ViewGroup mRightNavigationBarWindow;
     private CarNavigationBarView mNavigationBarView;
+    private CarNavigationBarView mLeftNavigationBarView;
+    private CarNavigationBarView mRightNavigationBarView;
 
     private final Object mQueueLock = new Object();
+    private boolean mShowLeft;
+    private boolean mShowRight;
+    private boolean mShowBottom;
+    private CarFacetButtonController mCarFacetButtonController;
+
     @Override
     public void start() {
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-        registerPackageChangeReceivers();
 
         mStackScroller.setScrollingEnabled(true);
 
@@ -104,6 +105,16 @@
             mNavigationBarView = null;
         }
 
+        if (mLeftNavigationBarWindow != null) {
+            mWindowManager.removeViewImmediate(mLeftNavigationBarWindow);
+            mLeftNavigationBarView = null;
+        }
+
+        if (mRightNavigationBarWindow != null) {
+            mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
+            mRightNavigationBarView = null;
+        }
+
         super.destroy();
     }
 
@@ -153,10 +164,36 @@
 
     @Override
     protected void createNavigationBar() {
+        mCarFacetButtonController = new CarFacetButtonController(mContext);
         if (mNavigationBarView != null) {
             return;
         }
 
+        mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
+        if (mShowBottom) {
+            buildBottomBar();
+        }
+
+        int widthForSides = mContext.getResources().getDimensionPixelSize(
+                R.dimen.navigation_bar_height_car_mode);
+
+
+        mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
+
+        if (mShowLeft) {
+            buildLeft(widthForSides);
+        }
+
+        mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+
+        if (mShowRight) {
+            buildRight(widthForSides);
+        }
+
+    }
+
+
+    private void buildBottomBar() {
         // SystemUI requires that the navigation bar view have a parent. Since the regular
         // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
         // CarNavigationBarView.
@@ -171,17 +208,15 @@
         mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
         if (mNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            throw new RuntimeException("Unable to build botom nav bar due to missing layout");
         }
+        mNavigationBarView.setStatusBar(this);
 
 
-        mController = new CarNavigationBarController(mContext, mNavigationBarView,
-                this /* ActivityStarter*/);
-        mNavigationBarView.getBarTransitions().setAlwaysOpaque(true);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
@@ -189,9 +224,74 @@
         lp.setTitle("CarNavigationBar");
         lp.windowAnimations = 0;
 
+
+        mCarFacetButtonController.addCarNavigationBar(mNavigationBarView);
         mWindowManager.addView(mNavigationBarWindow, lp);
     }
 
+    private void buildLeft(int widthForSides) {
+        mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                R.layout.navigation_bar_window, null);
+        if (mLeftNavigationBarWindow == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
+        }
+
+        View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow);
+        mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
+        if (mLeftNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            throw new RuntimeException("Unable to build left nav bar due to missing layout");
+        }
+        mLeftNavigationBarView.setStatusBar(this);
+        mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView);
+
+        WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
+                widthForSides, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        leftlp.setTitle("LeftCarNavigationBar");
+        leftlp.windowAnimations = 0;
+        leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+        leftlp.gravity = Gravity.LEFT;
+        mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
+    }
+
+
+    private void buildRight(int widthForSides) {
+        mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                R.layout.navigation_bar_window, null);
+        if (mRightNavigationBarWindow == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
+        }
+
+        View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow);
+        mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
+        if (mRightNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            throw new RuntimeException("Unable to build right nav bar due to missing layout");
+        }
+        mRightNavigationBarView.setStatusBar(this);
+        mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView);
+
+        WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
+                widthForSides, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        rightlp.setTitle("RightCarNavigationBar");
+        rightlp.windowAnimations = 0;
+        rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+        rightlp.gravity = Gravity.RIGHT;
+        mWindowManager.addView(mRightNavigationBarWindow, rightlp);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         //When executing dump() funciton simultaneously, we need to serialize them
@@ -204,8 +304,8 @@
         }
 
         pw.print("  mTaskStackListener="); pw.println(mTaskStackListener);
-        pw.print("  mController=");
-        pw.println(mController);
+        pw.print("  mCarFacetButtonController=");
+        pw.println(mCarFacetButtonController);
         pw.print("  mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher);
         pw.print("  mCarBatteryController=");
         pw.println(mCarBatteryController);
@@ -229,10 +329,6 @@
         }
     }
 
-    @Override
-    public NavigationBarView getNavigationBarView() {
-        return mNavigationBarView;
-    }
 
     @Override
     public View getNavigationBarWindow() {
@@ -269,24 +365,6 @@
         }
     }
 
-    private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getData() == null || mController == null) {
-                return;
-            }
-            String packageName = intent.getData().getSchemeSpecificPart();
-            mController.onPackageChange(packageName);
-        }
-    };
-
-    private void registerPackageChangeReceivers() {
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(mPackageChangeReceiver, filter);
-    }
 
     public boolean hasDockedTask() {
         return Recents.getSystemServices().hasDockedTask();
@@ -301,10 +379,7 @@
         public void onTaskStackChanged() {
             ActivityManager.RunningTaskInfo runningTaskInfo =
                     ActivityManagerWrapper.getInstance().getRunningTask();
-            if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) {
-                mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(),
-                        runningTaskInfo);
-            }
+            mCarFacetButtonController.taskChanged(runningTaskInfo);
         }
     }
 
@@ -346,33 +421,6 @@
         // Do nothing, we don't want to display media art in the lock screen for a car.
     }
 
-    private int startActivityWithOptions(Intent intent, Bundle options) {
-        int result = ActivityManager.START_CANCELED;
-        try {
-            result = ActivityManager.getService().startActivityAsUser(null /* caller */,
-                    mContext.getBasePackageName(),
-                    intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    null /* resultTo*/,
-                    null /* resultWho*/,
-                    0 /* requestCode*/,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP,
-                    null /* profilerInfo*/,
-                    options,
-                    UserHandle.CURRENT.getIdentifier());
-        } catch (RemoteException e) {
-            Log.w(TAG, "Unable to start activity", e);
-        }
-
-        return result;
-    }
-
-    public int startActivityOnStack(Intent intent, int windowingMode, int activityType) {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(windowingMode);
-        options.setLaunchActivityType(activityType);
-        return startActivityWithOptions(intent, options.toBundle());
-    }
 
     @Override
     public void animateExpandNotificationsPanel() {
@@ -390,8 +438,6 @@
     @Override
     public void onDensityOrFontScaleChanged() {
         super.onDensityOrFontScaleChanged();
-        mController.onDensityOrFontScaleChanged();
-
         // Need to update the background on density changed in case the change was due to night
         // mode.
         mNotificationPanelBackground = getDefaultWallpaper();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cd4eb23..c047670 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -403,14 +403,18 @@
     }
 
     private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
+        final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled();
         if (oldConfig.orientation != newConfig.orientation
                 || oldConfig.densityDpi != newConfig.densityDpi) {
             mDockedIcon = getDrawable(ctx,
                     R.drawable.ic_sysbar_docked, R.drawable.ic_sysbar_docked_dark);
+            mHomeDefaultIcon = quickStepEnabled
+                    ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step,
+                    R.drawable.ic_sysbar_home_quick_step_dark)
+                    : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
         }
         if (oldConfig.densityDpi != newConfig.densityDpi
                 || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
-            final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled();
             mBackIcon = quickStepEnabled
                     ? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step,
                             R.drawable.ic_sysbar_back_quick_step_dark)
@@ -422,11 +426,6 @@
                     : getDrawable(ctx, R.drawable.ic_sysbar_back_ime,
                             R.drawable.ic_sysbar_back_ime_dark);
             mBackAltLandIcon = mBackAltIcon;
-
-            mHomeDefaultIcon = quickStepEnabled
-                    ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step,
-                            R.drawable.ic_sysbar_home_quick_step_dark)
-                    : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
             mRecentIcon = getDrawable(ctx,
                     R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark);
             mMenuIcon = getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark);
@@ -909,6 +908,11 @@
             Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
         }
 
+        // Resolve layout direction if not resolved since components changing layout direction such
+        // as changing languages will recreate this view and the direction will be resolved later
+        if (!isLayoutDirectionResolved()) {
+            resolveLayoutDirection();
+        }
         updateTaskSwitchHelper();
         setNavigationIconHints(mNavigationIconHints, true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 6444cc8..747a551 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -575,7 +575,7 @@
 
         Intent browserIntent = getTaskIntent(taskId, userId);
         Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL);
-        if (browserIntent != null) {
+        if (browserIntent != null && browserIntent.isWebIntent()) {
             // Make sure that this doesn't resolve back to an instant app
             browserIntent.setComponent(null)
                     .setPackage(null)
@@ -597,8 +597,9 @@
                     .addCategory("unique:" + System.currentTimeMillis())
                     .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
                     .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
-                    .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
-                    .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
+                    .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
+                    .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent)
+                    .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
 
             PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
             Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index 0bf01b0..378858a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -224,6 +224,10 @@
             case MotionEvent.ACTION_DOWN: {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
+                // End any existing quickscrub animations before starting the new transition
+                if (mQuickScrubEndAnimator != null) {
+                    mQuickScrubEndAnimator.end();
+                }
                 mHomeButtonView = homeButton.getCurrentView();
                 if (mNavigationBarView.isQuickScrubEnabled()
                         && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 6764634..1c9c794 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,7 +37,6 @@
 import android.content.Intent;
 import android.metrics.LogMaker;
 import android.support.test.filters.SmallTest;
-import android.support.test.InstrumentationRegistry;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -74,7 +73,6 @@
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mHost = mock(QSTileHost.class);
         when(mHost.indexOf(spec)).thenReturn(POSITION);
-        when(mHost.getContext()).thenReturn(mContext.getBaseContext());
 
         mTile = spy(new TileImpl(mHost));
         mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 320c37f..abf1de5 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5257,6 +5257,23 @@
     // OS: P
     FIELD_END_BATTERY_PERCENT = 1308;
 
+    // ACTION: Settings > Display > Night Light
+    // SUBTYPE: com.android.server.display.ColorDisplayService.AutoMode value
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED = 1309;
+
+    // ACTION: Settings > Display > Night Light
+    // CATEGORY: SETTINGS
+    // SUBTYPE: 0 is starting time, 1 is ending time
+    // OS: P
+    ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED = 1310;
+
+    // FIELD: Current mode corresponding to a QS tile
+    // CATEGORY: QUICK SETTINGS
+    // OS: P
+    FIELD_QS_MODE = 1311;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/rs/OWNERS b/rs/OWNERS
new file mode 100644
index 0000000..61853d3
--- /dev/null
+++ b/rs/OWNERS
@@ -0,0 +1,5 @@
+butlermichael@google.com
+dgross@google.com
+jeanluc@google.com
+miaowang@google.com
+yangni@google.com
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 4b3abea..3689536 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -514,7 +514,7 @@
      */
     @GuardedBy("mLock")
     private void updateCachedServiceLocked(int userId, boolean disabled) {
-        AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+        AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
         if (service != null) {
             service.destroySessionsLocked();
             service.updateLocked(disabled);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 8240e4b..c504465 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.service.autofill.Dataset;
@@ -37,6 +38,7 @@
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillId;
@@ -98,22 +100,28 @@
 
     private @Nullable AnnounceFilterResult mAnnounceFilterResult;
 
+    private final boolean mFullScreen;
     private int mContentWidth;
     private int mContentHeight;
 
     private boolean mDestroyed;
 
+    public static boolean isFullScreen(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
     FillUi(@NonNull Context context, @NonNull FillResponse response,
-            @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
-            @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
+           @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
+           @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
         mContext = context;
         mCallback = callback;
+        mFullScreen = isFullScreen(context);
 
         final LayoutInflater inflater = LayoutInflater.from(context);
 
         final ViewGroup decor = (ViewGroup) inflater.inflate(
-                R.layout.autofill_dataset_picker, null);
-
+                mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
+                        : R.layout.autofill_dataset_picker, null);
 
         final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() {
             @Override
@@ -130,31 +138,41 @@
             mListView = null;
             mAdapter = null;
 
+            // insert authentication item under autofill_dataset_container or decor
+            ViewGroup container = decor.findViewById(R.id.autofill_dataset_container);
+            if (container == null) {
+                container = decor;
+            }
             final View content;
             try {
                 content = response.getPresentation().apply(context, decor, interceptionHandler);
-                decor.addView(content);
+                container.addView(content);
             } catch (RuntimeException e) {
                 callback.onCanceled();
                 Slog.e(TAG, "Error inflating remote views", e);
                 mWindow = null;
                 return;
             }
+            decor.setFocusable(true);
+            decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
 
             Point maxSize = mTempPoint;
             resolveMaxWindowSize(context, maxSize);
+            // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+            content.getLayoutParams().width = mFullScreen ? maxSize.x
+                    : ViewGroup.LayoutParams.WRAP_CONTENT;
+            content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
             final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
                     MeasureSpec.AT_MOST);
             final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
                     MeasureSpec.AT_MOST);
 
             decor.measure(widthMeasureSpec, heightMeasureSpec);
-            decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
             mContentWidth = content.getMeasuredWidth();
             mContentHeight = content.getMeasuredHeight();
 
             mWindow = new AnchoredWindow(decor, overlayControl);
-            mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+            requestShowFillUi();
         } else {
             final int datasetCount = response.getDatasets().size();
 
@@ -261,6 +279,15 @@
         }
     }
 
+    void requestShowFillUi() {
+        if (mFullScreen) {
+            mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                    mWindowPresenter);
+        } else {
+            mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+        }
+    }
+
     /**
      * Creates a remoteview interceptor used to block clicks.
      */
@@ -289,7 +316,13 @@
                 mCallback.requestHideFillUi();
             } else {
                 if (updateContentSize()) {
-                    mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+                    if (mFullScreen) {
+                        LayoutParams lp = mListView.getLayoutParams();
+                        lp.width = mContentWidth;
+                        lp.height = mContentHeight;
+                        mListView.setLayoutParams(lp);
+                    }
+                    requestShowFillUi();
                 }
                 if (mAdapter.getCount() > VISIBLE_OPTIONS_MAX_COUNT) {
                     mListView.setVerticalScrollBarEnabled(true);
@@ -310,7 +343,7 @@
             // ViewState doesn't not support filtering - typically when it's for an authenticated
             // FillResponse.
             if (TextUtils.isEmpty(filterText)) {
-                mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+                requestShowFillUi();
             } else {
                 mCallback.requestHideFillUi();
             }
@@ -366,23 +399,39 @@
         final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
                 MeasureSpec.AT_MOST);
         final int itemCount = mAdapter.getCount();
+        if (mFullScreen) {
+            // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+            changed = true;
+            mContentWidth = maxSize.x;
+        }
         for (int i = 0; i < itemCount; i++) {
             final View view = mAdapter.getItem(i).view;
             view.measure(widthMeasureSpec, heightMeasureSpec);
-            final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
-            final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
-            if (newContentWidth != mContentWidth) {
-                mContentWidth = newContentWidth;
-                changed = true;
-            }
-            // Update the width to fit only the first items up to max count
-            if (i < VISIBLE_OPTIONS_MAX_COUNT) {
-                final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
-                final int newContentHeight = mContentHeight + clampedMeasuredHeight;
-                if (newContentHeight != mContentHeight) {
-                    mContentHeight = newContentHeight;
+            if (mFullScreen) {
+                // for fullscreen, add up all children height until hit max height.
+                final int newContentHeight = mContentHeight + view.getMeasuredHeight();
+                final int clampedNewHeight = Math.min(newContentHeight, maxSize.y);
+                if (clampedNewHeight != mContentHeight) {
+                    mContentHeight = clampedNewHeight;
+                } else if (view.getMeasuredHeight() > 0) {
+                    break;
+                }
+            } else {
+                final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
+                final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
+                if (newContentWidth != mContentWidth) {
+                    mContentWidth = newContentWidth;
                     changed = true;
                 }
+                // Update the width to fit only the first items up to max count
+                if (i < VISIBLE_OPTIONS_MAX_COUNT) {
+                    final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
+                    final int newContentHeight = mContentHeight + clampedMeasuredHeight;
+                    if (newContentHeight != mContentHeight) {
+                        mContentHeight = newContentHeight;
+                        changed = true;
+                    }
+                }
             }
         }
         return changed;
@@ -575,6 +624,7 @@
 
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
+        pw.print(prefix); pw.print("mFullScreen: "); pw.println(mFullScreen);
         pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
         pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
         pw.print(prefix); pw.print("mFilterText: ");
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5c30764..f4f60c2 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4516,7 +4516,7 @@
 
     int startActivityFromRecents(int callingPid, int callingUid, int taskId,
             SafeActivityOptions options) {
-        final TaskRecord task;
+        TaskRecord task = null;
         final String callingPackage;
         final Intent intent;
         final int userId;
@@ -4579,13 +4579,6 @@
                             targetActivity);
                 }
 
-                // If we are launching the task in the docked stack, put it into resizing mode so
-                // the window renders full-screen with the background filling the void. Also only
-                // call this at the end to make sure that tasks exists on the window manager side.
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                    setResizingDuringAnimation(task);
-                }
-
                 mService.getActivityStartController().postStartActivityProcessingForLastStarter(
                         task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT,
                         task.getStack());
@@ -4595,15 +4588,28 @@
             intent = task.intent;
             intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
             userId = task.userId;
-            int result = mService.getActivityStartController().startActivityInPackage(
+            return mService.getActivityStartController().startActivityInPackage(
                     task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
-                    null, 0, 0, options, userId, task,
-                    "startActivityFromRecents");
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                setResizingDuringAnimation(task);
-            }
-            return result;
+                    null, 0, 0, options, userId, task, "startActivityFromRecents");
         } finally {
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
+                // If we are launching the task in the docked stack, put it into resizing mode so
+                // the window renders full-screen with the background filling the void. Also only
+                // call this at the end to make sure that tasks exists on the window manager side.
+                setResizingDuringAnimation(task);
+
+                final ActivityDisplay display = task.getStack().getDisplay();
+                final ActivityStack topSecondaryStack =
+                        display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                if (topSecondaryStack.isActivityTypeHome()) {
+                    // If the home activity if the top split-screen secondary stack, then the
+                    // primary split-screen stack is in the minimized mode which means it can't
+                    // receive input keys, so we should move the focused app to the home app so that
+                    // window manager can correctly calculate the focus window that can receive
+                    // input keys.
+                    moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");
+                }
+            }
             mWindowManager.continueSurfaceLayout();
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 0dcefbf..8205265 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -932,6 +932,7 @@
         // Don't modify the client's object!
         intent = new Intent(intent);
         if (componentSpecified
+                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
                 && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
                 && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
                 && mService.getPackageManagerInternalLocked()
diff --git a/services/core/java/com/android/server/audio/OWNERS b/services/core/java/com/android/server/audio/OWNERS
new file mode 100644
index 0000000..b70de29
--- /dev/null
+++ b/services/core/java/com/android/server/audio/OWNERS
@@ -0,0 +1,2 @@
+jmtrivi@google.com
+elaurent@google.com
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index d87a1bb..79450a0 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2078,8 +2078,33 @@
     protected void dumpSyncState(PrintWriter pw) {
         final StringBuilder sb = new StringBuilder();
 
-        pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
-        pw.print("auto sync: ");
+        pw.print("Data connected: "); pw.println(mDataConnectionIsConnected);
+        pw.print("Battery saver: ");
+        pw.println((mPowerManager != null) && mPowerManager.isPowerSaveMode());
+
+        pw.print("Background network restriction: ");
+        {
+            final ConnectivityManager cm = getConnectivityManager();
+            final int status = (cm == null) ? -1 : cm.getRestrictBackgroundStatus();
+            switch (status) {
+                case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED:
+                    pw.println(" disabled");
+                    break;
+                case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+                    pw.println(" whitelisted");
+                    break;
+                case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED:
+                    pw.println(" enabled");
+                    break;
+                default:
+                    pw.print("Unknown(");
+                    pw.print(status);
+                    pw.println(")");
+                    break;
+            }
+        }
+
+        pw.print("Auto sync: ");
         List<UserInfo> users = getAllUsers();
         if (users != null) {
             for (UserInfo user : users) {
@@ -2088,26 +2113,26 @@
             }
             pw.println();
         }
-        pw.print("memory low: "); pw.println(mStorageIsLow);
-        pw.print("device idle: "); pw.println(mDeviceIsIdle);
-        pw.print("reported active: "); pw.println(mReportedSyncActive);
+        pw.print("Memory low: "); pw.println(mStorageIsLow);
+        pw.print("Device idle: "); pw.println(mDeviceIsIdle);
+        pw.print("Reported active: "); pw.println(mReportedSyncActive);
 
         final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
 
-        pw.print("accounts: ");
+        pw.print("Accounts: ");
         if (accounts != INITIAL_ACCOUNTS_ARRAY) {
             pw.println(accounts.length);
         } else {
             pw.println("not known yet");
         }
         final long now = SystemClock.elapsedRealtime();
-        pw.print("now: "); pw.print(now);
+        pw.print("Now: "); pw.print(now);
         pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
 
         sb.setLength(0);
-        pw.print("uptime: "); pw.print(formatDurationHMS(sb, now));
+        pw.print("Uptime: "); pw.print(formatDurationHMS(sb, now));
         pw.println();
-        pw.print("time spent syncing: ");
+        pw.print("Time spent syncing: ");
 
         sb.setLength(0);
         pw.print(formatDurationHMS(sb,
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 3139d54..061e4ca 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -55,7 +55,6 @@
     protected SyncManagerConstants(Context context) {
         super(null);
         mContext = context;
-        refresh();
     }
 
     public void start() {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 3da3551..692535c 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -421,7 +421,7 @@
                 byteToken[i] = token.get(i);
             }
             // Send to Keystore
-            KeyStore.getInstance().addAuthToken(byteToken);
+            KeyStore.getInstance().addAuthToken(byteToken, mCurrentUserId);
         }
         if (client != null && client.onAuthenticated(fingerId, groupId)) {
             removeClient(client);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
old mode 100644
new mode 100755
index e5f4282..0cba76b
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -660,7 +660,8 @@
     @ServiceThreadOnly
     void startQueuedActions() {
         assertRunOnServiceThread();
-        for (HdmiCecFeatureAction action : mActions) {
+        // Use copied action list in that start() may remove itself.
+        for (HdmiCecFeatureAction action : new ArrayList<>(mActions)) {
             if (!action.started()) {
                 Slog.i(TAG, "Starting queued action:" + action);
                 action.start();
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index be48f69..c33d7f4 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
 import android.app.job.IJobScheduler;
@@ -184,6 +185,7 @@
     IBatteryStats mBatteryStats;
     DeviceIdleController.LocalService mLocalDeviceIdleController;
     AppStateTracker mAppStateTracker;
+    final UsageStatsManagerInternal mUsageStats;
 
     /**
      * Set to true once we are allowed to run third party apps.
@@ -225,7 +227,10 @@
      */
     final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
     long mHeartbeat = 0;
-    long mLastHeartbeatTime = 0;
+    long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
+
+    static final String HEARTBEAT_TAG = "*job.heartbeat*";
+    final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
 
     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
 
@@ -495,6 +500,9 @@
                 STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
                         DEFAULT_STANDBY_RARE_BEATS);
             }
+
+            // Reset the heartbeat alarm based on the new heartbeat duration
+            setNextHeartbeatAlarm();
         }
 
         void dump(PrintWriter pw) {
@@ -1090,9 +1098,9 @@
         mJobSchedulerStub = new JobSchedulerStub();
 
         // Set up the app standby bucketing tracker
-        UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class);
-        mStandbyTracker = new StandbyTracker(usageStats);
-        usageStats.addAppIdleStateChangeListener(mStandbyTracker);
+        mStandbyTracker = new StandbyTracker();
+        mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+        mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
 
         // The job store needs to call back
         publishLocalService(JobSchedulerInternal.class, new LocalService());
@@ -1177,6 +1185,7 @@
 
             mAppStateTracker = Preconditions.checkNotNull(
                     LocalServices.getService(AppStateTracker.class));
+            setNextHeartbeatAlarm();
 
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
@@ -1418,6 +1427,23 @@
                 periodicToReschedule.getLastFailedRunTime());
     }
 
+    long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
+        final long heartbeat;
+        final long timeSinceLastJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
+        synchronized (mLock) {
+            heartbeat = mHeartbeat - (timeSinceLastJob / mConstants.STANDBY_HEARTBEAT_TIME);
+        }
+        if (DEBUG_STANDBY) {
+            Slog.v(TAG, "Last job heartbeat " + heartbeat + " for " + packageName + "/" + userId
+                    + " delta=" + timeSinceLastJob);
+        }
+        return heartbeat;
+    }
+
+    long heartbeatWhenJobsLastRun(JobStatus job) {
+        return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
+    }
+
     // JobCompletedListener implementations.
 
     /**
@@ -1560,9 +1586,7 @@
         noteJobsNonpending(mPendingJobs);
         mPendingJobs.clear();
         stopNonReadyActiveJobsLocked();
-        boolean updated = updateStandbyHeartbeatLocked();
         mJobs.forEachJob(mReadyQueueFunctor);
-        if (updated) updateNextStandbyHeartbeatsLocked();
         mReadyQueueFunctor.postProcess();
 
         if (DEBUG) {
@@ -1716,36 +1740,78 @@
         noteJobsNonpending(mPendingJobs);
         mPendingJobs.clear();
         stopNonReadyActiveJobsLocked();
-        boolean updated = updateStandbyHeartbeatLocked();
         mJobs.forEachJob(mMaybeQueueFunctor);
-        if (updated) updateNextStandbyHeartbeatsLocked();
         mMaybeQueueFunctor.postProcess();
     }
 
-    private boolean updateStandbyHeartbeatLocked() {
-        final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
-        final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
-        if (beatsElapsed > 0) {
-            mHeartbeat += beatsElapsed;
-            mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
-            if (DEBUG_STANDBY) {
-                Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed + " to " + mHeartbeat);
+    /**
+     * Heartbeat tracking.  The heartbeat alarm is intentionally non-wakeup.
+     */
+    class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
+
+        @Override
+        public void onAlarm() {
+            synchronized (mLock) {
+                final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
+                final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
+                if (beatsElapsed > 0) {
+                    mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
+                    advanceHeartbeatLocked(beatsElapsed);
+                }
             }
-            return true;
+            setNextHeartbeatAlarm();
         }
-        return false;
     }
 
-    private void updateNextStandbyHeartbeatsLocked() {
-        // don't update ACTIVE or NEVER bucket milestones
+    // Intentionally does not touch the alarm timing
+    void advanceHeartbeatLocked(long beatsElapsed) {
+        mHeartbeat += beatsElapsed;
+        if (DEBUG_STANDBY) {
+            Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
+                    + " to " + mHeartbeat);
+        }
+        // Don't update ACTIVE or NEVER bucket milestones.  Note that mHeartbeat
+        // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
+        // new jobs scheduled by apps in that bucket will be permitted to run
+        // immediately.
+        boolean didAdvanceBucket = false;
         for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
-            while (mHeartbeat >= mNextBucketHeartbeat[i]) {
+            // Did we reach or cross a bucket boundary?
+            if (mHeartbeat >= mNextBucketHeartbeat[i]) {
+                didAdvanceBucket = true;
+            }
+            while (mHeartbeat > mNextBucketHeartbeat[i]) {
                 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
             }
             if (DEBUG_STANDBY) {
-                Slog.v(TAG, "   Bucket " + i + " next heartbeat " + mNextBucketHeartbeat[i]);
+                Slog.v(TAG, "   Bucket " + i + " next heartbeat "
+                        + mNextBucketHeartbeat[i]);
             }
         }
+
+        if (didAdvanceBucket) {
+            if (DEBUG_STANDBY) {
+                Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
+            }
+            mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+        }
+    }
+
+    void setNextHeartbeatAlarm() {
+        final long heartbeatLength;
+        synchronized (mLock) {
+            heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
+        }
+        final long now = sElapsedRealtimeClock.millis();
+        final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
+        final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
+        if (DEBUG_STANDBY) {
+            Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
+                    + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
+        }
+        AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+        am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
+                HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
     }
 
     /**
@@ -1811,17 +1877,20 @@
         if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
             final int bucket = job.getStandbyBucket();
             if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
-                // Only skip this job if it's still waiting for the end of its (initial) nominal
+                // Only skip this job if the app is still waiting for the end of its nominal
                 // bucket interval.  Once it's waited that long, we let it go ahead and clear.
                 // The final (NEVER) bucket is special; we never age those apps' jobs into
                 // runnability.
+                final long appLastRan = heartbeatWhenJobsLastRun(job);
                 if (bucket >= mConstants.STANDBY_BEATS.length
-                        || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) {
+                        || (mHeartbeat > appLastRan
+                                && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
                     // TODO: log/trace that we're deferring the job due to bucketing if we hit this
                     if (job.getWhenStandbyDeferred() == 0) {
                         if (DEBUG_STANDBY) {
                             Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
-                                    + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job);
+                                    + (appLastRan + mConstants.STANDBY_BEATS[bucket])
+                                    + " for " + job);
                         }
                         job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
                     }
@@ -2078,18 +2147,19 @@
                 // ACTIVE => everything can be run right away
                 // NEVER => we won't run them anyway, so let them go in the future
                 // as soon as the app enters normal use
+                if (DEBUG_STANDBY) {
+                    Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
+                            + packageName + "/" + userId);
+                }
                 return 0;
             }
 
-            final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun(
-                    packageName, userId);
-            final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket];
-            final long bucketsAgo = timeSinceLastJob / bucketLength;
-
-            // If we haven't run any jobs for more than the app's current bucket period, just
-            // consider anything new to be immediately runnable.  Otherwise, base it on the
-            // bucket at which we last ran jobs.
-            return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo);
+            final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
+            if (DEBUG_STANDBY) {
+                Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
+                        + packageName + "/" + userId);
+            }
+            return baseHeartbeat;
         }
 
         /**
@@ -2166,15 +2236,6 @@
      * Tracking of app assignments to standby buckets
      */
     final class StandbyTracker extends AppIdleStateChangeListener {
-        final UsageStatsManagerInternal mUsageStats;
-
-        StandbyTracker(UsageStatsManagerInternal usageStats) {
-            mUsageStats = usageStats;
-        }
-
-        public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) {
-            return mUsageStats.getTimeSinceLastJobRun(packageName, userId);
-        }
 
         // AppIdleStateChangeListener interface for live updates
 
@@ -2256,6 +2317,7 @@
         else return 0;
     }
 
+    // Static to support external callers
     public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
         UsageStatsManagerInternal usageStats = LocalServices.getService(
                 UsageStatsManagerInternal.class);
@@ -2682,6 +2744,7 @@
         }
     }
 
+    // Shell command infrastructure
     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
         try {
             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
@@ -2759,6 +2822,21 @@
         return 0;
     }
 
+    // Shell command infrastructure
+    int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
+        if (numBeats < 1) {
+            pw.println(getCurrentHeartbeat());
+            return 0;
+        }
+
+        pw.print("Advancing standby heartbeat by ");
+        pw.println(numBeats);
+        synchronized (mLock) {
+            advanceHeartbeatLocked(numBeats);
+        }
+        return 0;
+    }
+
     private String printContextIdToJobMap(JobStatus[] map, String initial) {
         StringBuilder s = new StringBuilder(initial + ": ");
         for (int i=0; i<map.length; i++) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
index d630aab..63225f3 100644
--- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -64,6 +64,8 @@
                     return getStorageNotLow(pw);
                 case "get-job-state":
                     return getJobState(pw);
+                case "heartbeat":
+                    return doHeartbeat(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -333,6 +335,20 @@
         }
     }
 
+    private int doHeartbeat(PrintWriter pw) throws Exception {
+        checkPermission("manipulate scheduler heartbeat");
+
+        final String arg = getNextArg();
+        final int numBeats = (arg != null) ? Integer.parseInt(arg) : 0;
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mInternal.executeHeartbeatCommand(pw, numBeats);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -359,6 +375,9 @@
         pw.println("    Options:");
         pw.println("      -u or --user: specify which user's job is to be run; the default is");
         pw.println("         the primary or system user");
+        pw.println("  heartbeat [num]");
+        pw.println("    With no argument, prints the current standby heartbeat.  With a positive");
+        pw.println("    argument, advances the standby heartbeat by that number.");
         pw.println("  monitor-battery [on|off]");
         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
         pw.println("    on makes get-battery-seq useful.");
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 4988974..1f8cf76 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -240,11 +240,6 @@
                 }
             }
 
-            UsageStatsManagerInternal usageStats =
-                    LocalServices.getService(UsageStatsManagerInternal.class);
-            usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
-                    mExecutionStartTimeElapsed);
-
             // Once we'e begun executing a job, we by definition no longer care whether
             // it was inflated from disk with not-yet-coherent delay/deadline bounds.
             job.clearPersistedUtcTimes();
@@ -267,12 +262,16 @@
                 removeOpTimeOutLocked();
                 return false;
             }
+            mJobPackageTracker.noteActive(job);
             try {
                 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
             } catch (RemoteException e) {
                 // Whatever.
             }
-            mJobPackageTracker.noteActive(job);
+            UsageStatsManagerInternal usageStats =
+                    LocalServices.getService(UsageStatsManagerInternal.class);
+            usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
+                    mExecutionStartTimeElapsed);
             mAvailable = false;
             mStoppedReason = null;
             mStoppedTime = 0;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 08ff7bd..3867306 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1346,6 +1346,15 @@
         }
         pw.print(prefix); pw.print("Standby bucket: ");
         pw.println(bucketName(standbyBucket));
+        if (standbyBucket > 0) {
+            pw.print(prefix); pw.print("Base heartbeat: ");
+            pw.println(baseHeartbeat);
+        }
+        if (whenStandbyDeferred != 0) {
+            pw.print(prefix); pw.print("  Deferred since: ");
+            TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
+            pw.println();
+        }
         pw.print(prefix); pw.print("Enqueue time: ");
         TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
         pw.println();
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
new file mode 100644
index 0000000..6f8d823
--- /dev/null
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -0,0 +1,2 @@
+lajos@google.com
+elaurent@google.com
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1746dd1..9bba9ed 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -289,14 +289,14 @@
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
             @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
-            @Nullable String profileName, @Nullable String dexMetadataPath)
-            throws InstallerException {
+            @Nullable String profileName, @Nullable String dexMetadataPath,
+            @Nullable String compilationReason) throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
                     dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
-                    targetSdkVersion, profileName, dexMetadataPath);
+                    targetSdkVersion, profileName, dexMetadataPath, compilationReason);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index fc73142..9420a6c 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -262,11 +262,12 @@
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
                     @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
                     int targetSdkVersion, @Nullable String profileName,
-                    @Nullable String dexMetadataPath) throws InstallerException {
+                    @Nullable String dexMetadataPath, @Nullable String dexoptCompilationReason)
+                    throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 6.
-                builder.append("6 ");
+                // The version. Right now it's 7.
+                builder.append("7 ");
 
                 builder.append("dexopt");
 
@@ -285,6 +286,7 @@
                 encodeParameter(builder, targetSdkVersion);
                 encodeParameter(builder, profileName);
                 encodeParameter(builder, dexMetadataPath);
+                encodeParameter(builder, dexoptCompilationReason);
 
                 commands.add(builder.toString());
             }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 458d725..77bf67d 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -34,7 +34,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.DexoptUtils;
 import com.android.server.pm.dex.PackageDexUsage;
@@ -63,7 +62,8 @@
 
 import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
 
-import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
 import static dalvik.system.DexFile.getSafeModeCompilerFilter;
 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
 
@@ -231,7 +231,8 @@
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
-                        packageStats, options.isDowngrade(), profileName, dexMetadataPath);
+                        packageStats, options.isDowngrade(), profileName, dexMetadataPath,
+                        options.getCompilationReason());
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
@@ -256,7 +257,7 @@
     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
             String compilerFilter, boolean profileUpdated, String classLoaderContext,
             int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
-            String profileName, String dexMetadataPath) {
+            String profileName, String dexMetadataPath, int compilationReason) {
         int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
                 profileUpdated, downgrade);
         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -283,7 +284,7 @@
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                     compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
                     false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
-                    profileName, dexMetadataPath);
+                    profileName, dexMetadataPath, getReasonName(compilationReason));
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -394,7 +395,7 @@
         // Note this trades correctness for performance since the resulting slow down is
         // unacceptable in some cases until b/64530081 is fixed.
         String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
-
+        int reason = options.getCompilationReason();
         try {
             for (String isa : dexUseInfo.getLoaderIsas()) {
                 // Reuse the same dexopt path as for the primary apks. We don't need all the
@@ -405,7 +406,7 @@
                         /*oatDir*/ null, dexoptFlags,
                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
                         options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
-                        /*dexMetadataPath*/ null);
+                        /*dexMetadataPath*/ null, getReasonName(reason));
             }
 
             return DEX_OPT_PERFORMED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b9f6d21..884606d2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -598,6 +598,7 @@
     }
 
     // Compilation reasons.
+    public static final int REASON_UNKNOWN = -1;
     public static final int REASON_FIRST_BOOT = 0;
     public static final int REASON_BOOT = 1;
     public static final int REASON_INSTALL = 2;
@@ -8836,7 +8837,7 @@
 
         final long startTime = System.nanoTime();
         final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
-                    getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT),
+                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                     false /* bootComplete */);
 
         final int elapsedTimeSeconds =
@@ -8863,7 +8864,7 @@
      * and {@code numberOfPackagesFailed}.
      */
     private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
-            final String compilerFilter, boolean bootComplete) {
+            final int compilationReason, boolean bootComplete) {
 
         int numberOfPackagesVisited = 0;
         int numberOfPackagesOptimized = 0;
@@ -8963,13 +8964,11 @@
                 }
             }
 
-            String pkgCompilerFilter = compilerFilter;
+            int pkgCompilationReason = compilationReason;
             if (useProfileForDexopt) {
                 // Use background dexopt mode to try and use the profile. Note that this does not
                 // guarantee usage of the profile.
-                pkgCompilerFilter =
-                        PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
-                                PackageManagerService.REASON_BACKGROUND_DEXOPT);
+                pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
             }
 
             // checkProfiles is false to avoid merging profiles during boot which
@@ -8980,7 +8979,7 @@
             int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
             int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
                     pkg.packageName,
-                    pkgCompilerFilter,
+                    pkgCompilationReason,
                     dexoptFlags));
 
             switch (primaryDexOptStaus) {
@@ -9081,8 +9080,8 @@
         int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
                 (force ? DexoptOptions.DEXOPT_FORCE : 0) |
                 (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
-        return performDexOpt(new DexoptOptions(packageName, targetCompilerFilter,
-                splitName, flags));
+        return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN,
+                targetCompilerFilter, splitName, flags));
     }
 
     /**
@@ -9191,7 +9190,8 @@
         final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
         if (!deps.isEmpty()) {
             DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
-                    options.getCompilerFilter(), options.getSplitName(),
+                    options.getCompilationReason(), options.getCompilerFilter(),
+                    options.getSplitName(),
                     options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
             for (PackageParser.Package depPackage : deps) {
                 // TODO: Analyze and investigate if we (should) profile libraries.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 19b0d9b..fce8285 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -123,4 +123,14 @@
 
         return value;
     }
+
+    public static String getReasonName(int reason) {
+        if (reason == PackageManagerService.REASON_UNKNOWN) {
+            return "unknown";
+        }
+        if (reason < 0 || reason >= REASON_STRINGS.length) {
+            throw new IllegalArgumentException("reason " + reason + " invalid");
+        }
+        return REASON_STRINGS[reason];
+    }
 }
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 0e2730c..3e63fb4 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -549,13 +549,12 @@
             mPackageDexUsage.maybeWriteAsync();
         }
 
-        // Try to optimize the package according to the install reason.
-        String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
-                PackageManagerService.REASON_INSTALL);
         DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName)
                 .getDexUseInfoMap().get(dexPath);
 
-        DexoptOptions options = new DexoptOptions(info.packageName, compilerFilter, /*flags*/0);
+        // Try to optimize the package according to the install reason.
+        DexoptOptions options = new DexoptOptions(info.packageName,
+                PackageManagerService.REASON_INSTALL, /*flags*/0);
 
         int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo,
                 options);
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index d4f95cb..a7a7686 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -77,15 +77,21 @@
     // It only applies for primary apk and it's always null if mOnlySecondaryDex is true.
     private final String mSplitName;
 
+    // The reason for invoking dexopt (see PackageManagerService.REASON_* constants).
+    // A -1 value denotes an unknown reason.
+    private final int mCompilationReason;
+
     public DexoptOptions(String packageName, String compilerFilter, int flags) {
-        this(packageName, compilerFilter, /*splitName*/ null, flags);
+        this(packageName, /*compilationReason*/ -1, compilerFilter, /*splitName*/ null, flags);
     }
 
-    public DexoptOptions(String packageName, int compilerReason, int flags) {
-        this(packageName, getCompilerFilterForReason(compilerReason), flags);
+    public DexoptOptions(String packageName, int compilationReason, int flags) {
+        this(packageName, compilationReason, getCompilerFilterForReason(compilationReason),
+                /*splitName*/ null, flags);
     }
 
-    public DexoptOptions(String packageName, String compilerFilter, String splitName, int flags) {
+    public DexoptOptions(String packageName, int compilationReason, String compilerFilter,
+                String splitName, int flags) {
         int validityMask =
                 DEXOPT_CHECK_FOR_PROFILES_UPDATES |
                 DEXOPT_FORCE |
@@ -104,6 +110,7 @@
         mCompilerFilter = compilerFilter;
         mFlags = flags;
         mSplitName = splitName;
+        mCompilationReason = compilationReason;
     }
 
     public String getPackageName() {
@@ -157,4 +164,8 @@
     public int getFlags() {
         return mFlags;
     }
+
+    public int getCompilationReason() {
+        return mCompilationReason;
+    }
 }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 941cd44..efcadad 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.IKeystoreService;
 import android.util.Slog;
 
 import com.android.internal.policy.IKeyguardService;
@@ -51,11 +53,16 @@
     private final LockPatternUtils mLockPatternUtils;
     private final StateCallback mCallback;
 
+    IKeystoreService mKeystoreService;
+
     public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) {
         mLockPatternUtils = new LockPatternUtils(context);
         mCurrentUserId = ActivityManager.getCurrentUser();
         mCallback = callback;
 
+        mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager
+                .getService("android.security.keystore"));
+
         try {
             service.addStateMonitorCallback(this);
         } catch (RemoteException e) {
@@ -86,6 +93,12 @@
     @Override // Binder interface
     public void onShowingStateChanged(boolean showing) {
         mIsShowing = showing;
+
+        if (showing) try {
+            mKeystoreService.lock(mCurrentUserId); // as long as this doesn't recur...
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error locking keystore", e);
+        }
     }
 
     @Override // Binder interface
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 3cd3e8b..db95634 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -53,7 +53,8 @@
 
     AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
         mAppToken = appToken;
-        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService);
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished,
+                appToken.mService.mAnimator::addAfterPrepareSurfacesRunnable, appToken.mService);
         mWidth = thumbnailHeader.getWidth();
         mHeight = thumbnailHeader.getHeight();
 
@@ -144,6 +145,11 @@
     }
 
     @Override
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
+        mAppToken.destroyAfterPendingTransaction(surface);
+    }
+
+    @Override
     public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
         t.setLayer(leash, Integer.MAX_VALUE);
     }
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index a180a3a..4394a99 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -55,6 +55,11 @@
         }
 
         @Override
+        public void destroyAfterPendingTransaction(SurfaceControl surface) {
+            mHost.destroyAfterPendingTransaction(surface);
+        }
+
+        @Override
         public SurfaceControl.Builder makeAnimationLeash() {
             return mHost.makeAnimationLeash();
         }
@@ -114,7 +119,7 @@
                 if (!mDimming) {
                     mDimLayer.destroy();
                 }
-            }, mHost.mService);
+            }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2d32c81..d22828d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -380,6 +380,11 @@
      */
     private int mSurfaceSize;
 
+    /**
+     * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+     */
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
     /** Temporary float array to retrieve 3x3 matrix values. */
     private final float[] mTmpFloats = new float[9];
 
@@ -1930,6 +1935,10 @@
                 }
             }
             mService.mAnimator.removeDisplayLocked(mDisplayId);
+
+            // The pending transaction won't be applied so we should
+            // just clean up any surfaces pending destruction.
+            onPendingTransactionApplied();
         } finally {
             mRemovingDisplay = false;
         }
@@ -3846,6 +3855,22 @@
     }
 
     @Override
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterPendingTransaction}.
+     */
+    void onPendingTransactionApplied() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
+
+    @Override
     void prepareSurfaces() {
         final ScreenRotationAnimation screenRotationAnimation =
                 mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
@@ -3859,7 +3884,6 @@
             mPendingTransaction.setAlpha(mWindowingLayer,
                     screenRotationAnimation.getEnterTransformation().getAlpha());
         }
-
         super.prepareSurfaces();
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e4edeb87..78dd580 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -219,7 +219,7 @@
     private void addAnimation(Task task) {
         if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
         final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
-                mService);
+                mService.mAnimator::addAfterPrepareSurfacesRunnable, mService);
         final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task);
         anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
         task.commitPendingTransaction();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2fe55b9..3d60ee4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -800,6 +800,7 @@
         mService.enableScreenIfNeededLocked();
 
         mService.scheduleAnimationLocked();
+        mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
         if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
                 "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 37be149..83baee1 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -33,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 
 /**
  * A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -59,17 +60,21 @@
     /**
      * @param animatable The object to animate.
      * @param animationFinishedCallback Callback to invoke when an animation has finished running.
+     * @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing
+     *                                surfaces in WM. Can be implemented differently during testing.
      */
     SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
-            WindowManagerService service) {
+            Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) {
         mAnimatable = animatable;
         mService = service;
         mAnimationFinishedCallback = animationFinishedCallback;
-        mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
+        mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback,
+                addAfterPrepareSurfaces);
     }
 
     private OnAnimationFinishedCallback getFinishedCallback(
-            @Nullable Runnable animationFinishedCallback) {
+            @Nullable Runnable animationFinishedCallback,
+            Consumer<Runnable> addAfterPrepareSurfaces) {
         return anim -> {
             synchronized (mService.mWindowMap) {
                 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
@@ -78,13 +83,30 @@
                     return;
                 }
 
-                if (anim != mAnimation) {
-                    return;
-                }
-                reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
-                if (animationFinishedCallback != null) {
-                    animationFinishedCallback.run();
-                }
+                // TODO: This should use pendingTransaction eventually, but right now things
+                // happening on the animation finished callback are happening on the global
+                // transaction.
+                // For now we need to run this after it's guaranteed that the transaction that
+                // reparents the surface onto the leash is executed already. Otherwise this may be
+                // executed first, leading to surface loss, as the reparent operations wouldn't
+                // be in order.
+                addAfterPrepareSurfaces.accept(() -> {
+                    if (anim != mAnimation) {
+                        // Callback was from another animation - ignore.
+                        return;
+                    }
+                    final Transaction t = new Transaction();
+                    SurfaceControl.openTransaction();
+                    try {
+                        reset(t, true /* destroyLeash */);
+                        if (animationFinishedCallback != null) {
+                            animationFinishedCallback.run();
+                        }
+                    } finally {
+                        SurfaceControl.mergeToGlobalTransaction(t);
+                        SurfaceControl.closeTransaction();
+                    }
+                });
             }
         };
     }
@@ -268,7 +290,7 @@
         }
         mService.mAnimationTransferMap.remove(mAnimation);
         if (mLeash != null && destroyLeash) {
-            t.destroy(mLeash);
+            mAnimatable.destroyAfterPendingTransaction(mLeash);
         }
         mLeash = null;
         mAnimation = null;
@@ -357,6 +379,13 @@
         void onAnimationLeashDestroyed(Transaction t);
 
         /**
+         * Destroy a given surface after executing {@link #getPendingTransaction}.
+         *
+         * @see WindowContainer#destroyAfterPendingTransaction
+         */
+        void destroyAfterPendingTransaction(SurfaceControl surface);
+
+        /**
          * @return A new surface to be used for the animation leash, inserted at the correct
          *         position in the hierarchy.
          */
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ab10197..20349b9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -226,6 +226,13 @@
                 if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
             }
 
+            final int numDisplays = mDisplayContentsAnimators.size();
+            for (int i = 0; i < numDisplays; i++) {
+                final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+                dc.onPendingTransactionApplied();
+            }
+
             boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
             boolean doRequest = false;
             if (mBulkUpdateParams != 0) {
@@ -259,6 +266,7 @@
             }
 
             mService.destroyPreservedSurfaceLocked();
+            mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
             executeAfterPrepareSurfacesRunnables();
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 93e9137..1f7caff 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -109,7 +109,8 @@
     WindowContainer(WindowManagerService service) {
         mService = service;
         mPendingTransaction = service.mTransactionFactory.make();
-        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished,
+                service.mAnimator::addAfterPrepareSurfacesRunnable, service);
     }
 
     @Override
@@ -285,9 +286,8 @@
         }
 
         if (mSurfaceControl != null) {
-            getPendingTransaction().destroy(mSurfaceControl);
+            destroyAfterPendingTransaction(mSurfaceControl);
             mSurfaceControl = null;
-            scheduleAnimation();
         }
 
         if (mParent != null) {
@@ -1075,6 +1075,19 @@
         return mSurfaceControl;
     }
 
+    /**
+     * Destroy a given surface after executing mPendingTransaction. This is
+     * largely a workaround for destroy not being part of transactions
+     * rather than an intentional design, so please take care when
+     * expanding use.
+     */
+    @Override
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
+        if (mParent != null) {
+            mParent.destroyAfterPendingTransaction(surface);
+        }
+    }
+
     @Override
     public Transaction getPendingTransaction() {
         return mPendingTransaction;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c1a1452..21b4361 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4067,9 +4067,7 @@
 
         final boolean hasSurface = mWinAnimator.hasSurface();
         if (hasSurface) {
-            // Use pendingTransaction here so hide is done the same transaction as the other
-            // animations when exiting
-            mWinAnimator.hide(getPendingTransaction(), "onExitAnimationDone");
+            mWinAnimator.hide("onExitAnimationDone");
         }
 
         // If we have an app token, we ask it to destroy the surface for us, so that it can take
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a699ba0..9621ee5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -164,8 +164,6 @@
 
     private boolean mAnimationStartDelayed;
 
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-
     /** The pixel format of the underlying SurfaceControl */
     int mSurfaceFormat;
 
@@ -282,21 +280,16 @@
         }
     }
 
-    void hide(SurfaceControl.Transaction transaction, String reason) {
+    void hide(String reason) {
         if (!mLastHidden) {
             //dump();
             mLastHidden = true;
             if (mSurfaceController != null) {
-                mSurfaceController.hide(transaction, reason);
+                mSurfaceController.hideInTransaction(reason);
             }
         }
     }
 
-    void hide(String reason) {
-        hide(mTmpTransaction, reason);
-        SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
-    }
-
     boolean finishDrawingLocked() {
         final boolean startingWindow =
                 mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index d88e59c..554a600 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -86,8 +86,6 @@
     private final int mWindowType;
     private final Session mWindowSession;
 
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-
     public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
             int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
         mAnimator = animator;
@@ -150,23 +148,21 @@
         }
     }
 
-    void hide(SurfaceControl.Transaction transaction, String reason) {
+    void hideInTransaction(String reason) {
         if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
         mHiddenForOtherReasons = true;
 
         mAnimator.destroyPreservedSurfaceLocked();
-        if (mSurfaceShown) {
-            hideSurface(transaction);
-        }
+        updateVisibility();
     }
 
-    private void hideSurface(SurfaceControl.Transaction transaction) {
+    private void hideSurface() {
         if (mSurfaceControl == null) {
             return;
         }
         setShown(false);
         try {
-            transaction.hide(mSurfaceControl);
+            mSurfaceControl.hide();
         } catch (RuntimeException e) {
             Slog.w(TAG, "Exception hiding surface in " + this);
         }
@@ -425,8 +421,7 @@
     private boolean updateVisibility() {
         if (mHiddenForCrop || mHiddenForOtherReasons) {
             if (mSurfaceShown) {
-                hideSurface(mTmpTransaction);
-                SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
+                hideSurface();
             }
             return false;
         } else {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 272f3a5..7364e87 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -102,6 +102,7 @@
     }
     private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
 
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
     private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
 
     private final Runnable mPerformSurfacePlacement;
@@ -696,6 +697,25 @@
         }
     }
 
+    /**
+     * Puts the {@param surface} into a pending list to be destroyed after the current transaction
+     * has been committed.
+     */
+    void destroyAfterTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterTransaction}.
+     */
+    void destroyPendingSurfaces() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled);
         pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 75bb5e4..61c8b79 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -196,7 +196,7 @@
     private static final String THERMAL_OBSERVER_CLASS =
             "com.google.android.clockwork.ThermalObserver";
     private static final String WEAR_CONNECTIVITY_SERVICE_CLASS =
-            "com.google.android.clockwork.connectivity.WearConnectivityService";
+            "com.android.clockwork.connectivity.WearConnectivityService";
     private static final String WEAR_SIDEKICK_SERVICE_CLASS =
             "com.google.android.clockwork.sidekick.SidekickService";
     private static final String WEAR_DISPLAY_SERVICE_CLASS =
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index f559986a..93064bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -118,7 +118,7 @@
     public void testCreateDexoptOptionsSplit() {
         int flags = DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE;
 
-        DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, mSplitName, flags);
+        DexoptOptions opt = new DexoptOptions(mPackageName, -1, mCompilerFilter, mSplitName, flags);
         assertEquals(mPackageName, opt.getPackageName());
         assertEquals(mCompilerFilter, opt.getCompilerFilter());
         assertEquals(mSplitName, opt.getSplitName());
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 79a9610..b55c79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -131,6 +131,7 @@
             assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
 
             controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+            mDisplayContent.onPendingTransactionApplied();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index a120eba..64c3037 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -23,11 +23,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.SurfaceControl;
@@ -39,6 +39,7 @@
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -46,6 +47,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * Test class for {@link SurfaceAnimatorTest}.
@@ -85,7 +87,7 @@
         callbackCaptor.getValue().onAnimationFinished(mSpec);
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
         // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
     }
 
@@ -95,7 +97,7 @@
         final SurfaceControl firstLeash = mAnimatable.mLeash;
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
 
-        verify(mTransaction).destroy(eq(firstLeash));
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(firstLeash));
         assertFalse(mAnimatable.mFinishedCallbackCalled);
 
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -122,7 +124,7 @@
         assertNotAnimating(mAnimatable);
         verify(mSpec).onAnimationCancelled(any());
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
     }
 
     @Test
@@ -143,7 +145,7 @@
         verifyZeroInteractions(mSpec);
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
     }
 
     @Test
@@ -159,11 +161,11 @@
         assertNotAnimating(mAnimatable);
         assertAnimating(mAnimatable2);
         assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
-        verify(mTransaction, never()).destroy(eq(leash));
+        assertFalse(mAnimatable.mPendingDestroySurfaces.contains(leash));
         callbackCaptor.getValue().onAnimationFinished(mSpec);
         assertNotAnimating(mAnimatable2);
         assertTrue(mAnimatable2.mFinishedCallbackCalled);
-        verify(mTransaction).destroy(eq(leash));
+        assertTrue(mAnimatable2.mPendingDestroySurfaces.contains(leash));
     }
 
     private void assertAnimating(MyAnimatable animatable) {
@@ -180,6 +182,7 @@
 
         final SurfaceControl mParent;
         final SurfaceControl mSurface;
+        final ArrayList<SurfaceControl> mPendingDestroySurfaces = new ArrayList<>();
         final SurfaceAnimator mSurfaceAnimator;
         SurfaceControl mLeash;
         boolean mFinishedCallbackCalled;
@@ -195,7 +198,7 @@
                     .build();
             mFinishedCallbackCalled = false;
             mLeash = null;
-            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, Runnable::run, sWm);
         }
 
         @Override
@@ -216,6 +219,11 @@
         }
 
         @Override
+        public void destroyAfterPendingTransaction(SurfaceControl surface) {
+            mPendingDestroySurfaces.add(surface);
+        }
+
+        @Override
         public Builder makeAnimationLeash() {
             return new SurfaceControl.Builder(mSession) {
 
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
new file mode 100644
index 0000000..7897a0c
--- /dev/null
+++ b/services/usb/OWNERS
@@ -0,0 +1,4 @@
+badhri@google.com
+elaurent@google.com
+moltmann@google.com
+zhangjerry@google.com
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
new file mode 100644
index 0000000..a3bcfb2
--- /dev/null
+++ b/telecomm/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+rgreenwalt@google.com
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 8c18518..0c92c20 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -425,8 +425,14 @@
          */
         public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
 
+        /**
+         * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the
+         * {@link RttCall} object that is used to send and receive text.
+         */
+        public static final int PROPERTY_RTT = 0x00000400;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000400
+        // Next PROPERTY value: 0x00000800
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -1189,6 +1195,23 @@
                 return null;
             }
         }
+
+        /**
+         * Closes the underlying file descriptors
+         * @hide
+         */
+        public void close() {
+            try {
+                mReceiveStream.close();
+            } catch (IOException e) {
+                // ignore
+            }
+            try {
+                mTransmitStream.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
     }
 
     /**
@@ -1664,7 +1687,7 @@
      * @return true if there is a connection, false otherwise.
      */
     public boolean isRttActive() {
-        return mRttCall != null;
+        return mRttCall != null && mDetails.hasProperty(Details.PROPERTY_RTT);
     }
 
     /**
@@ -1867,7 +1890,8 @@
 
         boolean isRttChanged = false;
         boolean rttModeChanged = false;
-        if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) {
+        if (parcelableCall.getIsRttCallChanged()
+                && mDetails.hasProperty(Details.PROPERTY_RTT)) {
             ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall();
             InputStreamReader receiveStream = new InputStreamReader(
                     new ParcelFileDescriptor.AutoCloseInputStream(
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3229705..26a2f1c 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -41,6 +41,8 @@
 import android.util.ArraySet;
 import android.view.Surface;
 
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
@@ -860,18 +862,19 @@
             mFdFromInCall = fromInCall;
             mFdToInCall = toInCall;
             mPipeFromInCall = new InputStreamReader(
-                    new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
+                    new FileInputStream(fromInCall.getFileDescriptor()));
             mPipeToInCall = new OutputStreamWriter(
-                    new ParcelFileDescriptor.AutoCloseOutputStream(toInCall));
+                    new FileOutputStream(toInCall.getFileDescriptor()));
         }
 
         /**
          * Writes the string {@param input} into the text stream to the UI for this RTT call. Since
          * RTT transmits text in real-time, this method should be called as often as text snippets
          * are received from the remote user, even if it is only one character.
-         *
+         * <p>
          * This method is not thread-safe -- calling it from multiple threads simultaneously may
          * lead to interleaved text.
+         *
          * @param input The message to send to the in-call app.
          */
         public void write(String input) throws IOException {
@@ -884,9 +887,10 @@
          * Reads a string from the in-call app, blocking if there is no data available. Returns
          * {@code null} if the RTT conversation has been terminated and there is no further data
          * to read.
-         *
+         * <p>
          * This method is not thread-safe -- calling it from multiple threads simultaneously may
          * lead to interleaved text.
+         *
          * @return A string containing text entered by the user, or {@code null} if the
          * conversation has been terminated or if there was an error while reading.
          */
@@ -901,6 +905,7 @@
         /**
          * Non-blocking version of {@link #read()}. Returns {@code null} if there is nothing to
          * be read.
+         *
          * @return A string containing text entered by the user, or {@code null} if the user has
          * not entered any new text yet.
          */
@@ -2635,7 +2640,6 @@
      * {@link #onStartRtt(RttTextStream)} has succeeded.
      */
     public final void sendRttInitiationSuccess() {
-        setRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
     }
 
@@ -2647,7 +2651,6 @@
      *               exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
      */
     public final void sendRttInitiationFailure(int reason) {
-        unsetRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
     }
 
@@ -2656,7 +2659,6 @@
      * side of the coll.
      */
     public final void sendRttSessionRemotelyTerminated() {
-        unsetRttProperty();
         mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
     }
 
@@ -2956,22 +2958,6 @@
      */
     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
 
-    /**
-     * Internal method to set {@link #PROPERTY_IS_RTT}.
-     * @hide
-     */
-    void setRttProperty() {
-        setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT);
-    }
-
-    /**
-     * Internal method to un-set {@link #PROPERTY_IS_RTT}.
-     * @hide
-     */
-    void unsetRttProperty() {
-        setConnectionProperties(getConnectionProperties() & (~PROPERTY_IS_RTT));
-    }
-
     static String toLogSafePhoneNumber(String number) {
         // For unknown number, log empty string.
         if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 658b473..b6e6b0e 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -143,6 +143,8 @@
     private final boolean mShouldShowIncomingCallUi;
     private final ParcelFileDescriptor mRttPipeToInCall;
     private final ParcelFileDescriptor mRttPipeFromInCall;
+    // Cached return value of getRttTextStream -- we don't want to wrap it more than once.
+    private Connection.RttTextStream mRttTextStream;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
@@ -312,7 +314,10 @@
      */
     public Connection.RttTextStream getRttTextStream() {
         if (isRequestingRtt()) {
-            return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+            if (mRttTextStream == null) {
+                mRttTextStream = new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+            }
+            return mRttTextStream;
         } else {
             return null;
         }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 1547857..ffa0c94 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -143,6 +143,7 @@
     private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
     private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
     private static final String SESSION_START_RTT = "CS.+RTT";
+    private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT";
     private static final String SESSION_STOP_RTT = "CS.-RTT";
     private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
     private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
@@ -1864,7 +1865,6 @@
         Log.d(this, "stopRtt(%s)", callId);
         if (mConnectionById.containsKey(callId)) {
             findConnectionForAction(callId, "stopRtt").onStopRtt();
-            findConnectionForAction(callId, "stopRtt").unsetRttProperty();
         } else if (mConferenceById.containsKey(callId)) {
             Log.w(this, "stopRtt called on a conference.");
         }
diff --git a/telephony/java/android/telephony/OWNERS b/telephony/OWNERS
similarity index 92%
copy from telephony/java/android/telephony/OWNERS
copy to telephony/OWNERS
index 68dedce..6f67bc2 100644
--- a/telephony/java/android/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -1,14 +1,14 @@
 set noparent
 
-amitmahajan@google.com
+tgunn@google.com
 breadley@google.com
-fionaxu@google.com
-jackyu@google.com
 hallliu@google.com
 rgreenwalt@google.com
-tgunn@google.com
-jminjie@google.com
 mpq@google.com
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+jminjie@google.com
+satk@google.com
 shuoq@google.com
 refuhoo@google.com
-
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index f009fb1..7e86966 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -172,7 +172,7 @@
     }
 
     /**
-     * Get the timing advance value for LTE, as a value between 0..63.
+     * Get the timing advance value for LTE, as a value in range of 0..1282.
      * Integer.MAX_VALUE is reported when there is no active RRC
      * connection. Refer to 3GPP 36.213 Sec 4.2.3
      * @return the LTE timing advance, if available.
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index cb867ab..ec348df 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1526,7 +1526,9 @@
      */
     @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates() {
-        return mNetworkRegistrationStates;
+        synchronized (mNetworkRegistrationStates) {
+            return new ArrayList<>(mNetworkRegistrationStates);
+        }
     }
 
     /**
@@ -1539,11 +1541,15 @@
     @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
         List<NetworkRegistrationState> list = new ArrayList<>();
-        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
-            if (networkRegistrationState.getTransportType() == transportType) {
-                list.add(networkRegistrationState);
+
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getTransportType() == transportType) {
+                    list.add(networkRegistrationState);
+                }
             }
         }
+
         return list;
     }
 
@@ -1557,12 +1563,36 @@
      */
     @SystemApi
     public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
-        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
-            if (networkRegistrationState.getTransportType() == transportType
-                    && networkRegistrationState.getDomain() == domain) {
-                return networkRegistrationState;
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getTransportType() == transportType
+                        && networkRegistrationState.getDomain() == domain) {
+                    return networkRegistrationState;
+                }
             }
         }
+
         return null;
     }
+
+    /**
+     * @hide
+     */
+    public void addNetworkRegistrationState(NetworkRegistrationState regState) {
+        if (regState == null) return;
+
+        synchronized (mNetworkRegistrationStates) {
+            for (int i = 0; i < mNetworkRegistrationStates.size(); i++) {
+                NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i);
+                if (curRegState.getTransportType() == regState.getTransportType()
+                        && curRegState.getDomain() == regState.getDomain()) {
+                    mNetworkRegistrationStates.remove(i);
+                    break;
+                }
+            }
+
+            mNetworkRegistrationStates.add(regState);
+        }
+    }
+
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7afd28c..fefc03d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -53,6 +53,7 @@
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -6410,84 +6411,106 @@
         return false;
     }
 
-   /**
-    * Returns the IMS Registration Status
-    * @hide
-    */
-   public boolean isImsRegistered() {
-       try {
-           ITelephony telephony = getITelephony();
-           if (telephony == null)
-               return false;
-           return telephony.isImsRegistered();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
     /**
-     * Returns the IMS Registration Status for a particular Subscription ID
+     * Returns the IMS Registration Status for a particular Subscription ID.
      *
      * @param subId Subscription ID
      * @return true if IMS status is registered, false if the IMS status is not registered or a
      * RemoteException occurred.
-     *
      * @hide
      */
     public boolean isImsRegistered(int subId) {
-       try {
-           return getITelephony().isImsRegisteredForSubscriber(subId);
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-    }
-
-    /**
-     * Returns the Status of Volte
-     * @hide
-     */
-    public boolean isVolteAvailable() {
-       try {
-           return getITelephony().isVolteAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
-    /**
-     * Returns the Status of video telephony (VT)
-     * @hide
-     */
-    public boolean isVideoTelephonyAvailable() {
         try {
-            return getITelephony().isVideoTelephonyAvailable();
-        } catch (RemoteException ex) {
-            return false;
-        } catch (NullPointerException ex) {
+            return getITelephony().isImsRegistered(subId);
+        } catch (RemoteException | NullPointerException ex) {
             return false;
         }
     }
 
     /**
-     * Returns the Status of Wi-Fi Calling
+     * Returns the IMS Registration Status for a particular Subscription ID, which is determined
+     * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an
+     * invalid subscription ID is used during creation, will the default subscription ID will be
+     * used.
+     *
+     * @return true if IMS status is registered, false if the IMS status is not registered or a
+     * RemoteException occurred.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    public boolean isImsRegistered() {
+       try {
+           return getITelephony().isImsRegistered(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
+           return false;
+       }
+    }
+
+    /**
+     * The current status of Voice over LTE for the subscription associated with this instance when
+     * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used.
+     * @return true if Voice over LTE is available or false if it is unavailable or unknown.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    public boolean isVolteAvailable() {
+        try {
+            return getITelephony().isVolteAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * The availability of Video Telephony (VT) for the subscription ID specified when this instance
+     * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used. To query the
+     * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
+     * @return true if VT is available, or false if it is unavailable or unknown.
+     * @hide
+     */
+    public boolean isVideoTelephonyAvailable() {
+        try {
+            return getITelephony().isVideoTelephonyAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
+     * @param subId the subscription ID.
+     * @return true if VoWiFi is available, or false if it is unavailable or unknown.
      * @hide
      */
     public boolean isWifiCallingAvailable() {
        try {
-           return getITelephony().isWifiCallingAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
+           return getITelephony().isWifiCallingAvailable(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
            return false;
        }
    }
 
+    /**
+     * The technology that IMS is registered for for the MMTEL feature.
+     * @param subId subscription ID to get IMS registration technology for.
+     * @return The IMS registration technology that IMS is registered to for the MMTEL feature.
+     * Valid return results are:
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
+     *  result is unavailable.
+     *  @hide
+     */
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+        try {
+            return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+        } catch (RemoteException ex) {
+            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+        }
+    }
+
    /**
     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
     *
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index bfdd453..1fdbae9 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -80,7 +80,7 @@
     public static final String EXTRA_PHONE_ID = "android:phone_id";
 
     /**
-     * Invalid feature value\
+     * Invalid feature value
      * @hide
      */
     public static final int FEATURE_INVALID = -1;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2b4c059..02cc82c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1123,33 +1123,33 @@
     boolean isHearingAidCompatibilitySupported();
 
     /**
-     * Get IMS Registration Status
-     */
-    boolean isImsRegistered();
-
-    /**
      * Get IMS Registration Status on a particular subid.
      *
      * @param subId user preferred subId.
      *
      * @return {@code true} if the IMS status is registered.
      */
-    boolean isImsRegisteredForSubscriber(int subId);
+    boolean isImsRegistered(int subId);
 
     /**
-     * Returns the Status of Wi-Fi Calling
+     * Returns the Status of Wi-Fi Calling for the subscription id specified.
      */
-    boolean isWifiCallingAvailable();
+    boolean isWifiCallingAvailable(int subId);
 
     /**
-     * Returns the Status of Volte
+     * Returns the Status of VoLTE for the subscription ID specified.
      */
-    boolean isVolteAvailable();
+    boolean isVolteAvailable(int subId);
 
      /**
-     * Returns the Status of VT (video telephony)
+     * Returns the Status of VT (video telephony) for the subscription ID specified.
      */
-    boolean isVideoTelephonyAvailable();
+    boolean isVideoTelephonyAvailable(int subId);
+
+    /**
+    * Returns the MMTEL IMS registration technology for the subsciption ID specified.
+    */
+    int getImsRegTechnologyForMmTel(int subId);
 
     /**
       * Returns the unique device ID of phone, for example, the IMEI for
diff --git a/tests/ActivityManagerPerfTests/test-app/Android.mk b/tests/ActivityManagerPerfTests/test-app/Android.mk
index b0a5db7..767e899 100644
--- a/tests/ActivityManagerPerfTests/test-app/Android.mk
+++ b/tests/ActivityManagerPerfTests/test-app/Android.mk
@@ -23,6 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ActivityManagerPerfTestsUtils
 
+LOCAL_MIN_SDK_VERSION := 25
+
 LOCAL_PACKAGE_NAME := ActivityManagerPerfTestsTestApp
 
 include $(BUILD_PACKAGE)
diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk
index daf603d..7597e69 100644
--- a/tests/ActivityManagerPerfTests/tests/Android.mk
+++ b/tests/ActivityManagerPerfTests/tests/Android.mk
@@ -27,6 +27,8 @@
 
 LOCAL_PACKAGE_NAME := ActivityManagerPerfTests
 
+LOCAL_MIN_SDK_VERSION := 25
+
 # For android.permission.FORCE_STOP_PACKAGES permission
 LOCAL_CERTIFICATE := platform
 
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 063060f..83354d5 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -65,6 +65,7 @@
 
     private static final int JOIN_TIMEOUT = 10000;
     private static final String TAG = AppLaunch.class.getSimpleName();
+
     // optional parameter: comma separated list of required account types before proceeding
     // with the app launch
     private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
@@ -73,32 +74,36 @@
     private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
     private static final String KEY_LAUNCH_ORDER = "launch_order";
     private static final String KEY_DROP_CACHE = "drop_cache";
-    private static final String KEY_SIMULATE_MAINTANANCE = "simulate_maintanance";
-    private static final String KEY_SIMPLEPPERF_CMD = "simpleperf_cmd";
+    private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd";
+    private static final String KEY_SIMPLEPERF_APP = "simpleperf_app";
     private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
     private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
     private static final String KEY_TRACE_DIRECTORY = "trace_directory";
     private static final String KEY_TRACE_CATEGORY = "trace_categories";
     private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
     private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
+    private static final String KEY_COMPILER_FILTERS = "compiler_filters";
+
+    private static final String SIMPLEPERF_APP_CMD =
+            "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
     private static final String WEARABLE_ACTION_GOOGLE =
             "com.google.android.wearable.action.GOOGLE";
-    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle
-    private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
-    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps
+    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
+    private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
+    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
     private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
     private static final String LAUNCH_FILE = "applaunch.txt";
     private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
-    private static final String DEFAULT_TRACE_CATEGORIES = "sched,freq,gfx,view,dalvik,webview,"
-            + "input,wm,disk,am,wm";
+    private static final String DEFAULT_TRACE_CATEGORIES =
+            "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
     private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
     private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
-    private static final String TRIAL_LAUNCH = "TRAIL_LAUNCH";
+    private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
     private static final String DELIMITER = ",";
     private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
     private static final String APP_LAUNCH_CMD = "am start -W -n";
     private static final String SUCCESS_MESSAGE = "Status: ok";
-    private static final String PROFILE_COMPILE_SUCCESS = "Success";
+    private static final String COMPILE_SUCCESS = "Success";
     private static final String THIS_TIME = "ThisTime:";
     private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
     private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
@@ -106,14 +111,15 @@
     private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
     private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
     private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
-    private static final String SPEED_PROFILE_CMD = "cmd package compile -f -m speed-profile %s";
-
-
+    private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
+    private static final String SPEED_PROFILE_FILTER = "speed-profile";
+    private static final String VERIFY_FILTER = "verify";
+    private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
 
     private Map<String, Intent> mNameToIntent;
     private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
     private Map<String, String> mNameToResultKey;
-    private Map<String, List<Long>> mNameToLaunchTime;
+    private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
     private IActivityManager mAm;
     private String mSimplePerfCmd = null;
     private String mLaunchOrder = null;
@@ -123,12 +129,10 @@
     private String mTraceDirectoryStr = null;
     private Bundle mResult = new Bundle();
     private Set<String> mRequiredAccounts;
-    private boolean mTrailLaunch = true;
-    private File mFile = null;
-    private FileOutputStream mOutputStream = null;
+    private boolean mTrialLaunch = false;
     private BufferedWriter mBufferedWriter = null;
-    private boolean mSimulateMaintanance = false;
-
+    private boolean mSimplePerfAppOnly = false;
+    private String[] mCompilerFilters = null;
 
     @Override
     protected void setUp() throws Exception {
@@ -142,6 +146,16 @@
         super.tearDown();
     }
 
+    private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) {
+        mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result);
+    }
+
+    private boolean hasFailureOnFirstLaunch(LaunchOrder launch) {
+        List<AppLaunchResult> results =
+            mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter());
+        return (results.size() > 0) && (results.get(0).mLaunchTime < 0);
+    }
+
     public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
             IOException, InterruptedException {
         InstrumentationTestRunner instrumentation =
@@ -149,11 +163,6 @@
         Bundle args = instrumentation.getArguments();
         mAm = ActivityManager.getService();
         String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
-        mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
-        mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
-        mSimplePerfCmd = args.getString(KEY_SIMPLEPPERF_CMD);
-        mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
-        mSimulateMaintanance =  Boolean.parseBoolean(args.getString(KEY_SIMULATE_MAINTANANCE));
 
         createMappings();
         parseArgs(args);
@@ -171,13 +180,14 @@
 
         try {
             File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
             if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
                 throw new IOException("Unable to create the lauch file sub directory");
             }
-            mFile = new File(launchSubDir, LAUNCH_FILE);
-            mOutputStream = new FileOutputStream(mFile);
+            File file = new File(launchSubDir, LAUNCH_FILE);
+            FileOutputStream outputStream = new FileOutputStream(file);
             mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
-                    mOutputStream));
+                    outputStream));
 
             // Root directory for trace file during the launches
             File rootTrace = null;
@@ -217,70 +227,67 @@
             setLaunchOrder();
 
             for (LaunchOrder launch : mLaunchOrderList) {
+                dropCache();
 
                 // App launch times for trial launch will not be used for final
                 // launch time calculations.
                 if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
                     // In the "applaunch.txt" file, trail launches is referenced using
                     // "TRIAL_LAUNCH"
-                    long launchTime = startApp(launch.getApp(), true, launch.getLaunchReason());
-                    if (launchTime < 0) {
-                        List<Long> appLaunchList = new ArrayList<Long>();
-                        appLaunchList.add(-1L);
-                        mNameToLaunchTime.put(launch.getApp(), appLaunchList);
+                    String appPkgName = mNameToIntent.get(launch.getApp())
+                        .getComponent().getPackageName();
+                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
+                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+                              compileApp(VERIFY_FILTER, appPkgName));
+                    } else if (launch.getCompilerFilter() != null) {
+                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+                              compileApp(launch.getCompilerFilter(), appPkgName));
+                    }
+                    // We only need to run a trial for the speed-profile filter, but we always
+                    // run one for "applaunch.txt" consistency.
+                    AppLaunchResult launchResult =
+                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                    if (launchResult.mLaunchTime < 0) {
+                        addLaunchResult(launch, new AppLaunchResult());
                         // simply pass the app if launch isn't successful
                         // error should have already been logged by startApp
                         continue;
                     }
                     sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
-                    closeApp(launch.getApp(), true);
-                    dropCache();
-                    if (mSimulateMaintanance) {
-                        String appPkgName = mNameToIntent.get(launch.getApp())
-                                .getComponent().getPackageName();
-                        assertTrue(String.format("Not able to speed profile the app : %s",
-                                appPkgName), profileCompileApp(appPkgName));
+                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
+                        // Send SIGUSR1 to force dumping a profile.
+                        String sendSignalCommand =
+                            String.format("killall -s SIGUSR1 %s", appPkgName);
+                        getInstrumentation().getUiAutomation().executeShellCommand(
+                            sendSignalCommand);
+                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+                              compileApp(launch.getCompilerFilter(), appPkgName));
                     }
-                    sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
                 }
 
                 // App launch times used for final calculation
-                if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
-                    long launchTime = -1;
-                    if (null != mNameToLaunchTime.get(launch.getApp())) {
-                        long firstLaunchTime = mNameToLaunchTime.get(launch.getApp()).get(0);
-                        if (firstLaunchTime < 0) {
-                            // skip if the app has failures while launched first
-                            continue;
-                        }
+                else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
+                    AppLaunchResult launchResults = null;
+                    if (hasFailureOnFirstLaunch(launch)) {
+                        // skip if the app has failures while launched first
+                        continue;
                     }
                     // In the "applaunch.txt" file app launches are referenced using
                     // "LAUNCH_ITERATION - ITERATION NUM"
-                    launchTime = startApp(launch.getApp(), true, launch.getLaunchReason());
-                    if (launchTime < 0) {
+                    launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
+                    if (launchResults.mLaunchTime < 0) {
+                        addLaunchResult(launch, new AppLaunchResult());
                         // if it fails once, skip the rest of the launches
-                        List<Long> appLaunchList = new ArrayList<Long>();
-                        appLaunchList.add(-1L);
-                        mNameToLaunchTime.put(launch.getApp(), appLaunchList);
                         continue;
                     } else {
-                        if (null != mNameToLaunchTime.get(launch.getApp())) {
-                            mNameToLaunchTime.get(launch.getApp()).add(launchTime);
-                        } else {
-                            List<Long> appLaunchList = new ArrayList<Long>();
-                            appLaunchList.add(launchTime);
-                            mNameToLaunchTime.put(launch.getApp(), appLaunchList);
-                        }
+                        addLaunchResult(launch, launchResults);
                     }
                     sleep(POST_LAUNCH_IDLE_TIMEOUT);
-                    closeApp(launch.getApp(), true);
-                    dropCache();
-                    sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
                 }
 
                 // App launch times for trace launch will not be used for final
                 // launch time calculations.
-                if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
+                else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
                     AtraceLogger atraceLogger = AtraceLogger
                             .getAtraceLoggerInstance(getInstrumentation());
                     // Start the trace
@@ -293,11 +300,10 @@
                     } finally {
                         // Stop the trace
                         atraceLogger.atraceStop();
-                        closeApp(launch.getApp(), true);
-                        dropCache();
-                        sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
                     }
                 }
+                closeApp(launch.getApp());
+                sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
             }
         } finally {
             if (null != mBufferedWriter) {
@@ -306,29 +312,45 @@
         }
 
         for (String app : mNameToResultKey.keySet()) {
-            StringBuilder launchTimes = new StringBuilder();
-            for (Long launch : mNameToLaunchTime.get(app)) {
-                launchTimes.append(launch);
-                launchTimes.append(",");
+            for (String compilerFilter : mCompilerFilters) {
+                StringBuilder launchTimes = new StringBuilder();
+                StringBuilder cpuCycles = new StringBuilder();
+                StringBuilder majorFaults = new StringBuilder();
+                for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) {
+                    launchTimes.append(result.mLaunchTime);
+                    launchTimes.append(",");
+                    if (mSimplePerfAppOnly) {
+                        cpuCycles.append(result.mCpuCycles);
+                        cpuCycles.append(",");
+                        majorFaults.append(result.mMajorFaults);
+                        majorFaults.append(",");
+                    }
+                }
+                String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter);
+                mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString());
+                if (mSimplePerfAppOnly) {
+                    mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles",
+                        cpuCycles.toString());
+                    mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults",
+                        majorFaults.toString());
+                }
             }
-            mResult.putString(mNameToResultKey.get(app), launchTimes.toString());
         }
         instrumentation.sendStatus(0, mResult);
     }
 
     /**
-     * Compile the app package using speed compile command and return true or false
+     * Compile the app package using compilerFilter and return true or false
      * based on status of the compilation command.
      */
-    private boolean profileCompileApp(String appPkgName) throws IOException {
-        Log.i(TAG, "Starting to speed profile " + appPkgName);
+    private boolean compileApp(String compilerFilter, String appPkgName) throws IOException {
         try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
-                executeShellCommand(String.format(SPEED_PROFILE_CMD, appPkgName));
+                executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName));
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
                         new FileInputStream(result.getFileDescriptor())))) {
             String line;
             while ((line = bufferedReader.readLine()) != null) {
-                if (line.contains(PROFILE_COMPILE_SUCCESS)) {
+                if (line.contains(COMPILE_SUCCESS)) {
                     return true;
                 }
             }
@@ -344,38 +366,42 @@
      */
     private void setLaunchOrder() {
         if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
-            if (mTrailLaunch) {
-                for (String app : mNameToResultKey.keySet()) {
-                    mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH));
-                }
-            }
-            for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
-                for (String app : mNameToResultKey.keySet()) {
-                    mLaunchOrderList.add(new LaunchOrder(app,
-                            String.format(LAUNCH_ITERATION, launchCount)));
-                }
-            }
-            if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
-                for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+            for (String compilerFilter : mCompilerFilters) {
+                if (mTrialLaunch) {
                     for (String app : mNameToResultKey.keySet()) {
-                        mLaunchOrderList.add(new LaunchOrder(app,
-                                String.format(TRACE_ITERATION, traceCount)));
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+                    }
+                }
+                for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
+                    for (String app : mNameToResultKey.keySet()) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                  String.format(LAUNCH_ITERATION, launchCount)));
+                    }
+                }
+                if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
+                    for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+                        for (String app : mNameToResultKey.keySet()) {
+                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                      String.format(TRACE_ITERATION, traceCount)));
+                        }
                     }
                 }
             }
         } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
-            for (String app : mNameToResultKey.keySet()) {
-                if (mTrailLaunch) {
-                    mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH));
-                }
-                for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
-                    mLaunchOrderList.add(new LaunchOrder(app,
-                            String.format(LAUNCH_ITERATION, launchCount)));
-                }
-                if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
-                    for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
-                        mLaunchOrderList.add(new LaunchOrder(app,
-                                String.format(TRACE_ITERATION, traceCount)));
+            for (String compilerFilter : mCompilerFilters) {
+                for (String app : mNameToResultKey.keySet()) {
+                    if (mTrialLaunch) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+                    }
+                    for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                String.format(LAUNCH_ITERATION, launchCount)));
+                    }
+                    if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
+                        for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                    String.format(TRACE_ITERATION, traceCount)));
+                        }
                     }
                 }
             }
@@ -385,7 +411,7 @@
     }
 
     private void dropCache() {
-        if (true == mDropCache) {
+        if (mDropCache) {
             assertNotNull("Issue in dropping the cache",
                     getInstrumentation().getUiAutomation()
                             .executeShellCommand(DROP_CACHE_SCRIPT));
@@ -394,7 +420,7 @@
 
     private void parseArgs(Bundle args) {
         mNameToResultKey = new LinkedHashMap<String, String>();
-        mNameToLaunchTime = new HashMap<String, List<Long>>();
+        mNameToLaunchTime = new HashMap<>();
         String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
         if (launchIterations != null) {
             mLaunchIterations = Integer.parseInt(launchIterations);
@@ -421,7 +447,38 @@
                 mRequiredAccounts.add(accountType);
             }
         }
-        mTrailLaunch = "true".equals(args.getString(KEY_TRIAL_LAUNCH));
+
+        String compilerFilterList = args.getString(KEY_COMPILER_FILTERS);
+        if (compilerFilterList != null) {
+            // If a compiler filter is passed, we make a trial launch to force compilation
+            // of the apps.
+            mTrialLaunch = true;
+            mCompilerFilters = compilerFilterList.split("\\|");
+        } else {
+            // Just pass a null compiler filter to use the current state of the app.
+            mCompilerFilters = new String[1];
+        }
+
+        // Pre-populate the results map to avoid null checks.
+        for (String app : mNameToLaunchTime.keySet()) {
+            HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
+            mNameToLaunchTime.put(app, map);
+            for (String compilerFilter : mCompilerFilters) {
+                map.put(compilerFilter, new ArrayList<>());
+            }
+        }
+
+        mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
+        mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
+        mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD);
+        mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
+        mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP));
+        mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
+
+        if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
+            Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
+                KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP));
+        }
     }
 
     private boolean hasLeanback(Context context) {
@@ -465,17 +522,17 @@
         }
     }
 
-    private long startApp(String appName, boolean forceStopBeforeLaunch, String launchReason)
-            throws NameNotFoundException, RemoteException {
+    private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
+            String launchReason) throws NameNotFoundException, RemoteException {
         Log.i(TAG, "Starting " + appName);
 
         Intent startIntent = mNameToIntent.get(appName);
         if (startIntent == null) {
             Log.w(TAG, "App does not exist: " + appName);
             mResult.putString(mNameToResultKey.get(appName), "App does not exist");
-            return -1L;
+            return new AppLaunchResult();
         }
-        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch ,
+        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
                 launchReason);
         Thread t = new Thread(runnable);
         t.start();
@@ -518,22 +575,23 @@
         }
     }
 
-    private void closeApp(String appName, boolean forceStopApp) {
+    private void startHomeIntent() {
         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
         homeIntent.addCategory(Intent.CATEGORY_HOME);
         homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         getInstrumentation().getContext().startActivity(homeIntent);
         sleep(POST_LAUNCH_IDLE_TIMEOUT);
-        if (forceStopApp) {
-            Intent startIntent = mNameToIntent.get(appName);
-            if (startIntent != null) {
-                String packageName = startIntent.getComponent().getPackageName();
-                try {
-                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Error closing app", e);
-                }
+    }
+
+    private void closeApp(String appName) {
+        Intent startIntent = mNameToIntent.get(appName);
+        if (startIntent != null) {
+            String packageName = startIntent.getComponent().getPackageName();
+            try {
+                mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error closing app", e);
             }
         }
     }
@@ -569,10 +627,12 @@
 
     private class LaunchOrder {
         private String mApp;
+        private String mCompilerFilter;
         private String mLaunchReason;
 
-        LaunchOrder(String app,String launchReason){
+        LaunchOrder(String app, String compilerFilter, String launchReason){
             mApp = app;
+            mCompilerFilter = compilerFilter;
             mLaunchReason = launchReason;
         }
 
@@ -584,6 +644,10 @@
             mApp = app;
         }
 
+        public String getCompilerFilter() {
+            return mCompilerFilter;
+        }
+
         public String getLaunchReason() {
             return mLaunchReason;
         }
@@ -593,9 +657,31 @@
         }
     }
 
+    private class AppLaunchResult {
+        long mLaunchTime;
+        long mCpuCycles;
+        long mMajorFaults;
+
+        AppLaunchResult() {
+            mLaunchTime = -1L;
+            mCpuCycles = -1L;
+            mMajorFaults = -1L;
+        }
+
+        AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) {
+            try {
+                mLaunchTime = Long.parseLong(launchTime, 10);
+                mCpuCycles = Long.parseLong(cpuCycles, 10);
+                mMajorFaults = Long.parseLong(majorFaults, 10);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "Error parsing result", e);
+            }
+        }
+    }
+
     private class AppLaunchRunnable implements Runnable {
         private Intent mLaunchIntent;
-        private Long mResult;
+        private AppLaunchResult mLaunchResult;
         private boolean mForceStopBeforeLaunch;
         private String mLaunchReason;
 
@@ -604,14 +690,15 @@
             mLaunchIntent = intent;
             mForceStopBeforeLaunch = forceStopBeforeLaunch;
             mLaunchReason = launchReason;
-            mResult = -1L;
+            mLaunchResult = new AppLaunchResult();
         }
 
-        public Long getResult() {
-            return mResult;
+        public AppLaunchResult getResult() {
+            return mLaunchResult;
         }
 
         public void run() {
+            File launchFile = null;
             try {
                 String packageName = mLaunchIntent.getComponent().getPackageName();
                 String componentName = mLaunchIntent.getComponent().flattenToShortString();
@@ -619,17 +706,38 @@
                     mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
                 }
                 String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
-                if (null != mSimplePerfCmd) {
+                if (mSimplePerfAppOnly) {
+                    try {
+                        // executeShellCommand cannot handle shell specific actions, like '&'.
+                        // Therefore, we create a file containing the command and make that
+                        // the command to launch.
+                        launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh");
+                        launchFile.setExecutable(true);
+                        try (FileOutputStream stream = new FileOutputStream(launchFile);
+                             BufferedWriter writer =
+                                new BufferedWriter(new OutputStreamWriter(stream))) {
+                            String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd);
+                            writer.write(cmd);
+                        }
+                        launchCmd = launchFile.getAbsolutePath();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Error writing the launch command", e);
+                        return;
+                    }
+                } else if (null != mSimplePerfCmd) {
                     launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
                 }
                 Log.v(TAG, "Final launch cmd:" + launchCmd);
                 ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
                         .executeShellCommand(launchCmd);
-                mResult = Long.parseLong(parseLaunchTimeAndWrite(parcelDesc, String.format
-                        ("App Launch :%s %s",
-                                componentName, mLaunchReason)), 10);
+                mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format
+                        ("App Launch :%s %s", componentName, mLaunchReason));
             } catch (RemoteException e) {
                 Log.w(TAG, "Error launching app", e);
+            } finally {
+                if (launchFile != null) {
+                    launchFile.delete();
+                }
             }
         }
 
@@ -639,12 +747,14 @@
          * @param parcelDesc
          * @return
          */
-        private String parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc, String headerInfo) {
+        private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc,
+                String headerInfo) {
             String launchTime = "-1";
+            String cpuCycles = "-1";
+            String majorFaults = "-1";
             boolean launchSuccess = false;
             try {
                 InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
-                StringBuilder appLaunchOuput = new StringBuilder();
                 /* SAMPLE OUTPUT :
                 Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
                 Status: ok
@@ -653,6 +763,11 @@
                 TotalTime: 357
                 WaitTime: 377
                 Complete*/
+                /* WITH SIMPLEPERF :
+                Performance counter statistics,
+                6595722690,cpu-cycles,4.511040,GHz,(100%),
+                0,major-faults,0.000,/sec,(100%),
+                Total test time,1.462129,seconds,*/
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
                         inputStream));
                 String line = null;
@@ -669,6 +784,23 @@
                         String launchSplit[] = line.split(":");
                         launchTime = launchSplit[1].trim();
                     }
+
+                    if (mSimplePerfAppOnly) {
+                        // Parse simpleperf output.
+                        if (lineCount == 9) {
+                            if (!line.contains("cpu-cycles")) {
+                                Log.e(TAG, "Error in simpleperf output");
+                            } else {
+                                cpuCycles = line.split(",")[0].trim();
+                            }
+                        } else if (lineCount == 10) {
+                            if (!line.contains("major-faults")) {
+                                Log.e(TAG, "Error in simpleperf output");
+                            } else {
+                                majorFaults = line.split(",")[0].trim();
+                            }
+                        }
+                    }
                     mBufferedWriter.write(line);
                     mBufferedWriter.newLine();
                     lineCount++;
@@ -678,7 +810,7 @@
             } catch (IOException e) {
                 Log.w(TAG, "Error writing the launch file", e);
             }
-            return launchTime;
+            return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
         }
 
     }
diff --git a/tests/AppLaunchWear/Android.mk b/tests/AppLaunchWear/Android.mk
new file mode 100644
index 0000000..ac123e7
--- /dev/null
+++ b/tests/AppLaunchWear/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := AppLaunchWear
+
+LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AppLaunchWear/AndroidManifest.xml b/tests/AppLaunchWear/AndroidManifest.xml
new file mode 100644
index 0000000..7dfd7ba
--- /dev/null
+++ b/tests/AppLaunchWear/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.applaunch"
+    android:sharedUserId="android.uid.system" >
+
+   <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+   <uses-sdk
+        android:minSdkVersion="22"
+        android:targetSdkVersion="24" />
+
+    <instrumentation android:label="Measure app start up time"
+                     android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.tests.applaunch" />
+
+    <application android:label="App Launch Test">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
new file mode 100644
index 0000000..f32464b
--- /dev/null
+++ b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright (C) 2013 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.tests.applaunch;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
+import android.app.IActivityManager;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.support.test.rule.logging.AtraceLogger;
+import android.test.InstrumentationTestCase;
+import android.test.InstrumentationTestRunner;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+/**
+ * This test is intended to measure the time it takes for the apps to start.
+ * Names of the applications are passed in command line, and the
+ * test starts each application, and reports the start up time in milliseconds.
+ * The instrumentation expects the following key to be passed on the command line:
+ * apps - A list of applications to start and their corresponding result keys
+ * in the following format:
+ * -e apps <app name>^<result key>|<app name>^<result key>
+ */
+public class AppLaunch extends InstrumentationTestCase {
+
+    private static final int JOIN_TIMEOUT = 10000;
+    private static final String TAG = AppLaunch.class.getSimpleName();
+
+    // optional parameter: comma separated list of required account types before proceeding
+    // with the app launch
+    private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
+    private static final String KEY_APPS = "apps";
+    private static final String KEY_TRIAL_LAUNCH = "trial_launch";
+    private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
+    private static final String KEY_LAUNCH_ORDER = "launch_order";
+    private static final String KEY_DROP_CACHE = "drop_cache";
+    private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd";
+    private static final String KEY_SIMPLEPERF_APP = "simpleperf_app";
+    private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
+    private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
+    private static final String KEY_TRACE_DIRECTORY = "trace_directory";
+    private static final String KEY_TRACE_CATEGORY = "trace_categories";
+    private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
+    private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
+    private static final String KEY_COMPILER_FILTERS = "compiler_filters";
+
+    private static final String SIMPLEPERF_APP_CMD =
+            "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
+    private static final String WEARABLE_ACTION_GOOGLE =
+            "com.google.android.wearable.action.GOOGLE";
+    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
+    private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
+    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
+    private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
+    private static final String LAUNCH_FILE = "applaunch.txt";
+    private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
+    private static final String DEFAULT_TRACE_CATEGORIES =
+            "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
+    private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
+    private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
+    private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
+    private static final String DELIMITER = ",";
+    private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
+    private static final String APP_LAUNCH_CMD = "am start -W -n";
+    private static final String SUCCESS_MESSAGE = "Status: ok";
+    private static final String COMPILE_SUCCESS = "Success";
+    private static final String THIS_TIME = "ThisTime:";
+    private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
+    private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
+    private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION";
+    private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
+    private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
+    private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
+    private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
+    private static final String SPEED_PROFILE_FILTER = "speed-profile";
+    private static final String VERIFY_FILTER = "verify";
+    private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
+    private static final String WEARABLE_HOME_PACKAGE = "com.google.android.wearable.app";
+
+    private Map<String, Intent> mNameToIntent;
+    private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+    private Map<String, String> mNameToResultKey;
+    private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
+    private IActivityManager mAm;
+    private String mSimplePerfCmd = null;
+    private String mLaunchOrder = null;
+    private boolean mDropCache = false;
+    private int mLaunchIterations = 10;
+    private int mTraceLaunchCount = 0;
+    private String mTraceDirectoryStr = null;
+    private Bundle mResult = new Bundle();
+    private Set<String> mRequiredAccounts;
+    private boolean mTrialLaunch = false;
+    private BufferedWriter mBufferedWriter = null;
+    private boolean mSimplePerfAppOnly = false;
+    private String[] mCompilerFilters = null;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
+        super.tearDown();
+    }
+
+    private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) {
+        mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result);
+    }
+
+    private boolean hasFailureOnFirstLaunch(LaunchOrder launch) {
+        List<AppLaunchResult> results =
+            mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter());
+        return (results.size() > 0) && (results.get(0).mLaunchTime < 0);
+    }
+
+    public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
+            IOException, InterruptedException {
+        InstrumentationTestRunner instrumentation =
+                (InstrumentationTestRunner)getInstrumentation();
+        Bundle args = instrumentation.getArguments();
+        mAm = ActivityManager.getService();
+        String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
+
+        createMappings();
+        parseArgs(args);
+        checkAccountSignIn();
+
+        // Root directory for applaunch file to log the app launch output
+        // Will be useful in case of simpleperf command is used
+        File launchRootDir = null;
+        if (null != launchDirectory && !launchDirectory.isEmpty()) {
+            launchRootDir = new File(launchDirectory);
+            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+                throw new IOException("Unable to create the destination directory");
+            }
+        }
+
+        try {
+            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
+            if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+                throw new IOException("Unable to create the lauch file sub directory");
+            }
+            File file = new File(launchSubDir, LAUNCH_FILE);
+            FileOutputStream outputStream = new FileOutputStream(file);
+            mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
+                    outputStream));
+
+            // Root directory for trace file during the launches
+            File rootTrace = null;
+            File rootTraceSubDir = null;
+            int traceBufferSize = 0;
+            int traceDumpInterval = 0;
+            Set<String> traceCategoriesSet = null;
+            if (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()) {
+                rootTrace = new File(mTraceDirectoryStr);
+                if (!rootTrace.exists() && !rootTrace.mkdirs()) {
+                    throw new IOException("Unable to create the trace directory");
+                }
+                rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY);
+                if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) {
+                    throw new IOException("Unable to create the trace sub directory");
+                }
+                assertNotNull("Trace iteration parameter is mandatory",
+                        args.getString(KEY_TRACE_ITERATIONS));
+                mTraceLaunchCount = Integer.parseInt(args.getString(KEY_TRACE_ITERATIONS));
+                String traceCategoriesStr = args
+                        .getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES);
+                traceBufferSize = Integer.parseInt(args.getString(KEY_TRACE_BUFFERSIZE,
+                        DEFAULT_TRACE_BUFFER_SIZE));
+                traceDumpInterval = Integer.parseInt(args.getString(KEY_TRACE_DUMPINTERVAL,
+                        DEFAULT_TRACE_DUMP_INTERVAL));
+                traceCategoriesSet = new HashSet<String>();
+                if (!traceCategoriesStr.isEmpty()) {
+                    String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER);
+                    for (int i = 0; i < traceCategoriesSplit.length; i++) {
+                        traceCategoriesSet.add(traceCategoriesSplit[i]);
+                    }
+                }
+            }
+
+            // Get the app launch order based on launch order, trial launch,
+            // launch iterations and trace iterations
+            setLaunchOrder();
+
+            for (LaunchOrder launch : mLaunchOrderList) {
+                dropCache();
+                String appPkgName = mNameToIntent.get(launch.getApp())
+                        .getComponent().getPackageName();
+
+                // App launch times for trial launch will not be used for final
+                // launch time calculations.
+                if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
+                    // In the "applaunch.txt" file, trail launches is referenced using
+                    // "TRIAL_LAUNCH"
+                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
+                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+                              compileApp(VERIFY_FILTER, appPkgName));
+                    } else if (launch.getCompilerFilter() != null) {
+                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+                              compileApp(launch.getCompilerFilter(), appPkgName));
+                    }
+                    // We only need to run a trial for the speed-profile filter, but we always
+                    // run one for "applaunch.txt" consistency.
+                    AppLaunchResult launchResult =
+                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                    if (launchResult.mLaunchTime < 0) {
+                        addLaunchResult(launch, new AppLaunchResult());
+                        // simply pass the app if launch isn't successful
+                        // error should have already been logged by startApp
+                        continue;
+                    }
+                    sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
+                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
+                        // Send SIGUSR1 to force dumping a profile.
+                        String sendSignalCommand =
+                            String.format("killall -s SIGUSR1 %s", appPkgName);
+                        getInstrumentation().getUiAutomation().executeShellCommand(
+                            sendSignalCommand);
+                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+                              compileApp(launch.getCompilerFilter(), appPkgName));
+                    }
+                }
+
+                // App launch times used for final calculation
+                else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
+                    AppLaunchResult launchResults = null;
+                    if (hasFailureOnFirstLaunch(launch)) {
+                        // skip if the app has failures while launched first
+                        continue;
+                    }
+                    // In the "applaunch.txt" file app launches are referenced using
+                    // "LAUNCH_ITERATION - ITERATION NUM"
+                    if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
+                        launchResults = startApp(launch.getApp(), false, launch.getLaunchReason());
+                    } else {
+                        launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
+                    }
+                    if (launchResults.mLaunchTime < 0) {
+                        addLaunchResult(launch, new AppLaunchResult());
+                        // if it fails once, skip the rest of the launches
+                        continue;
+                    } else {
+                        addLaunchResult(launch, launchResults);
+                    }
+                    sleep(POST_LAUNCH_IDLE_TIMEOUT);
+                }
+
+                // App launch times for trace launch will not be used for final
+                // launch time calculations.
+                else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
+                    AtraceLogger atraceLogger = AtraceLogger
+                            .getAtraceLoggerInstance(getInstrumentation());
+                    // Start the trace
+                    try {
+                        atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
+                                traceDumpInterval, rootTraceSubDir,
+                                String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
+                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                        sleep(POST_LAUNCH_IDLE_TIMEOUT);
+                    } finally {
+                        // Stop the trace
+                        atraceLogger.atraceStop();
+                    }
+                }
+                closeApp(launch.getApp(), true);
+                sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
+            }
+        } finally {
+            if (null != mBufferedWriter) {
+                mBufferedWriter.close();
+            }
+        }
+
+        for (String app : mNameToResultKey.keySet()) {
+            for (String compilerFilter : mCompilerFilters) {
+                StringBuilder launchTimes = new StringBuilder();
+                StringBuilder cpuCycles = new StringBuilder();
+                StringBuilder majorFaults = new StringBuilder();
+                for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) {
+                    launchTimes.append(result.mLaunchTime);
+                    launchTimes.append(",");
+                    if (mSimplePerfAppOnly) {
+                        cpuCycles.append(result.mCpuCycles);
+                        cpuCycles.append(",");
+                        majorFaults.append(result.mMajorFaults);
+                        majorFaults.append(",");
+                    }
+                }
+                String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter);
+                mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString());
+                if (mSimplePerfAppOnly) {
+                    mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles",
+                        cpuCycles.toString());
+                    mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults",
+                        majorFaults.toString());
+                }
+            }
+        }
+        instrumentation.sendStatus(0, mResult);
+    }
+
+    /**
+     * Compile the app package using compilerFilter and return true or false
+     * based on status of the compilation command.
+     */
+    private boolean compileApp(String compilerFilter, String appPkgName) throws IOException {
+        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName));
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                if (line.contains(COMPILE_SUCCESS)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * If launch order is "cyclic" then apps will be launched one after the
+     * other for each iteration count.
+     * If launch order is "sequential" then each app will be launched for given number
+     * iterations at once before launching the other apps.
+     */
+    private void setLaunchOrder() {
+        if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
+            for (String compilerFilter : mCompilerFilters) {
+                if (mTrialLaunch) {
+                    for (String app : mNameToResultKey.keySet()) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+                    }
+                }
+                for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
+                    for (String app : mNameToResultKey.keySet()) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                  String.format(LAUNCH_ITERATION, launchCount)));
+                    }
+                }
+                if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
+                    for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+                        for (String app : mNameToResultKey.keySet()) {
+                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                      String.format(TRACE_ITERATION, traceCount)));
+                        }
+                    }
+                }
+            }
+        } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
+            for (String compilerFilter : mCompilerFilters) {
+                for (String app : mNameToResultKey.keySet()) {
+                    if (mTrialLaunch) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+                    }
+                    for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                String.format(LAUNCH_ITERATION, launchCount)));
+                    }
+                    if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
+                        for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+                                    String.format(TRACE_ITERATION, traceCount)));
+                        }
+                    }
+                }
+            }
+        } else {
+            assertTrue("Launch order is not valid parameter", false);
+        }
+    }
+
+    private void dropCache() {
+        if (mDropCache) {
+            assertNotNull("Issue in dropping the cache",
+                    getInstrumentation().getUiAutomation()
+                            .executeShellCommand(DROP_CACHE_SCRIPT));
+        }
+    }
+
+    private void parseArgs(Bundle args) {
+        mNameToResultKey = new LinkedHashMap<String, String>();
+        mNameToLaunchTime = new HashMap<>();
+        String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
+        if (launchIterations != null) {
+            mLaunchIterations = Integer.parseInt(launchIterations);
+        }
+        String appList = args.getString(KEY_APPS);
+        if (appList == null)
+            return;
+
+        String appNames[] = appList.split("\\|");
+        for (String pair : appNames) {
+            String[] parts = pair.split("\\^");
+            if (parts.length != 2) {
+                Log.e(TAG, "The apps key is incorrectly formatted");
+                fail();
+            }
+
+            mNameToResultKey.put(parts[0], parts[1]);
+            mNameToLaunchTime.put(parts[0], null);
+        }
+        String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
+        if (requiredAccounts != null) {
+            mRequiredAccounts = new HashSet<String>();
+            for (String accountType : requiredAccounts.split(",")) {
+                mRequiredAccounts.add(accountType);
+            }
+        }
+
+        String compilerFilterList = args.getString(KEY_COMPILER_FILTERS);
+        if (compilerFilterList != null) {
+            // If a compiler filter is passed, we make a trial launch to force compilation
+            // of the apps.
+            mTrialLaunch = true;
+            mCompilerFilters = compilerFilterList.split("\\|");
+        } else {
+            // Just pass a null compiler filter to use the current state of the app.
+            mCompilerFilters = new String[1];
+        }
+
+        // Pre-populate the results map to avoid null checks.
+        for (String app : mNameToLaunchTime.keySet()) {
+            HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
+            mNameToLaunchTime.put(app, map);
+            for (String compilerFilter : mCompilerFilters) {
+                map.put(compilerFilter, new ArrayList<>());
+            }
+        }
+
+        mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
+        mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
+        mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD);
+        mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
+        mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP));
+        mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
+
+        if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
+            Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
+                KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP));
+        }
+    }
+
+    private boolean hasLeanback(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    private void createMappings() {
+        mNameToIntent = new LinkedHashMap<String, Intent>();
+
+        PackageManager pm = getInstrumentation().getContext()
+                .getPackageManager();
+        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+        intentToResolve.addCategory(hasLeanback(getInstrumentation().getContext()) ?
+                Intent.CATEGORY_LEANBACK_LAUNCHER :
+                Intent.CATEGORY_LAUNCHER);
+        List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
+        resolveLoop(ris, intentToResolve, pm);
+        // For Wear
+        intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
+        ris = pm.queryIntentActivities(intentToResolve, 0);
+        resolveLoop(ris, intentToResolve, pm);
+    }
+
+    private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) {
+        if (ris == null || ris.isEmpty()) {
+            Log.i(TAG, "Could not find any apps");
+        } else {
+            for (ResolveInfo ri : ris) {
+                Intent startIntent = new Intent(intentToResolve);
+                startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                startIntent.setClassName(ri.activityInfo.packageName,
+                        ri.activityInfo.name);
+                String appName = ri.loadLabel(pm).toString();
+                if (appName != null) {
+                    // Support launching intent using package name or app name
+                    mNameToIntent.put(ri.activityInfo.packageName, startIntent);
+                    mNameToIntent.put(appName, startIntent);
+                }
+            }
+        }
+    }
+
+    private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
+            String launchReason) throws NameNotFoundException, RemoteException {
+        Log.i(TAG, "Starting " + appName);
+
+        Intent startIntent = mNameToIntent.get(appName);
+        if (startIntent == null) {
+            Log.w(TAG, "App does not exist: " + appName);
+            mResult.putString(mNameToResultKey.get(appName), "App does not exist");
+            return new AppLaunchResult();
+        }
+        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
+                launchReason);
+        Thread t = new Thread(runnable);
+        t.start();
+        try {
+            t.join(JOIN_TIMEOUT);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+        return runnable.getResult();
+    }
+
+    private void checkAccountSignIn() {
+        // ensure that the device has the required account types before starting test
+        // e.g. device must have a valid Google account sign in to measure a meaningful launch time
+        // for Gmail
+        if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
+            return;
+        }
+        final AccountManager am =
+                (AccountManager) getInstrumentation().getTargetContext().getSystemService(
+                        Context.ACCOUNT_SERVICE);
+        Account[] accounts = am.getAccounts();
+        // use set here in case device has multiple accounts of the same type
+        Set<String> foundAccounts = new HashSet<String>();
+        for (Account account : accounts) {
+            if (mRequiredAccounts.contains(account.type)) {
+                foundAccounts.add(account.type);
+            }
+        }
+        // check if account type matches, if not, fail test with message on what account types
+        // are missing
+        if (mRequiredAccounts.size() != foundAccounts.size()) {
+            mRequiredAccounts.removeAll(foundAccounts);
+            StringBuilder sb = new StringBuilder("Device missing these accounts:");
+            for (String account : mRequiredAccounts) {
+                sb.append(' ');
+                sb.append(account);
+            }
+            fail(sb.toString());
+        }
+    }
+
+    private void closeApp(String appName, boolean forceStopApp) {
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        getInstrumentation().getContext().startActivity(homeIntent);
+        sleep(POST_LAUNCH_IDLE_TIMEOUT);
+        if (forceStopApp) {
+            Intent startIntent = mNameToIntent.get(appName);
+            if (startIntent != null) {
+                String packageName = startIntent.getComponent().getPackageName();
+                try {
+                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error closing app", e);
+                }
+            }
+        }
+    }
+
+    private void sleep(int time) {
+        try {
+            Thread.sleep(time);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+    }
+
+    private void reportError(String appName, String processName) {
+        ActivityManager am = (ActivityManager) getInstrumentation()
+                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
+        if (crashes != null) {
+            for (ProcessErrorStateInfo crash : crashes) {
+                if (!crash.processName.equals(processName))
+                    continue;
+
+                Log.w(TAG, appName + " crashed: " + crash.shortMsg);
+                mResult.putString(mNameToResultKey.get(appName), crash.shortMsg);
+                return;
+            }
+        }
+
+        mResult.putString(mNameToResultKey.get(appName),
+                "Crashed for unknown reason");
+        Log.w(TAG, appName
+                + " not found in process list, most likely it is crashed");
+    }
+
+    private class LaunchOrder {
+        private String mApp;
+        private String mCompilerFilter;
+        private String mLaunchReason;
+
+        LaunchOrder(String app, String compilerFilter, String launchReason){
+            mApp = app;
+            mCompilerFilter = compilerFilter;
+            mLaunchReason = launchReason;
+        }
+
+        public String getApp() {
+            return mApp;
+        }
+
+        public void setApp(String app) {
+            mApp = app;
+        }
+
+        public String getCompilerFilter() {
+            return mCompilerFilter;
+        }
+
+        public String getLaunchReason() {
+            return mLaunchReason;
+        }
+
+        public void setLaunchReason(String launchReason) {
+            mLaunchReason = launchReason;
+        }
+    }
+
+    private class AppLaunchResult {
+        long mLaunchTime;
+        long mCpuCycles;
+        long mMajorFaults;
+
+        AppLaunchResult() {
+            mLaunchTime = -1L;
+            mCpuCycles = -1L;
+            mMajorFaults = -1L;
+        }
+
+        AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) {
+            try {
+                mLaunchTime = Long.parseLong(launchTime, 10);
+                mCpuCycles = Long.parseLong(cpuCycles, 10);
+                mMajorFaults = Long.parseLong(majorFaults, 10);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "Error parsing result", e);
+            }
+        }
+    }
+
+    private class AppLaunchRunnable implements Runnable {
+        private Intent mLaunchIntent;
+        private AppLaunchResult mLaunchResult;
+        private boolean mForceStopBeforeLaunch;
+        private String mLaunchReason;
+
+        public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
+                String launchReason) {
+            mLaunchIntent = intent;
+            mForceStopBeforeLaunch = forceStopBeforeLaunch;
+            mLaunchReason = launchReason;
+            mLaunchResult = new AppLaunchResult();
+        }
+
+        public AppLaunchResult getResult() {
+            return mLaunchResult;
+        }
+
+        public void run() {
+            File launchFile = null;
+            try {
+                String packageName = mLaunchIntent.getComponent().getPackageName();
+                String componentName = mLaunchIntent.getComponent().flattenToShortString();
+                if (mForceStopBeforeLaunch) {
+                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
+                }
+                String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
+                if (mSimplePerfAppOnly) {
+                    try {
+                        // executeShellCommand cannot handle shell specific actions, like '&'.
+                        // Therefore, we create a file containing the command and make that
+                        // the command to launch.
+                        launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh");
+                        launchFile.setExecutable(true);
+                        try (FileOutputStream stream = new FileOutputStream(launchFile);
+                             BufferedWriter writer =
+                                new BufferedWriter(new OutputStreamWriter(stream))) {
+                            String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd);
+                            writer.write(cmd);
+                        }
+                        launchCmd = launchFile.getAbsolutePath();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Error writing the launch command", e);
+                        return;
+                    }
+                } else if (null != mSimplePerfCmd) {
+                    launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
+                }
+                Log.v(TAG, "Final launch cmd:" + launchCmd);
+                ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
+                        .executeShellCommand(launchCmd);
+                mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format
+                        ("App Launch :%s %s", componentName, mLaunchReason));
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error launching app", e);
+            } finally {
+                if (launchFile != null) {
+                    launchFile.delete();
+                }
+            }
+        }
+
+        /**
+         * Method to parse the launch time info and write the result to file
+         *
+         * @param parcelDesc
+         * @return
+         */
+        private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc,
+                String headerInfo) {
+            String launchTime = "-1";
+            String cpuCycles = "-1";
+            String majorFaults = "-1";
+            boolean launchSuccess = false;
+            try {
+                InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
+                /* SAMPLE OUTPUT :
+                Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
+                Status: ok
+                Activity: com.google.android.calculator/com.android.calculator2.Calculator
+                ThisTime: 357
+                TotalTime: 357
+                WaitTime: 377
+                Complete*/
+                /* WITH SIMPLEPERF :
+                Performance counter statistics,
+                6595722690,cpu-cycles,4.511040,GHz,(100%),
+                0,major-faults,0.000,/sec,(100%),
+                Total test time,1.462129,seconds,*/
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        inputStream));
+                String line = null;
+                int lineCount = 1;
+                mBufferedWriter.newLine();
+                mBufferedWriter.write(headerInfo);
+                mBufferedWriter.newLine();
+                while ((line = bufferedReader.readLine()) != null) {
+                    if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) {
+                        launchSuccess = true;
+                    }
+                    // Parse TotalTime which is the launch time
+                    if (launchSuccess && lineCount == 5) {
+                        String launchSplit[] = line.split(":");
+                        launchTime = launchSplit[1].trim();
+                    }
+
+                    if (mSimplePerfAppOnly) {
+                        // Parse simpleperf output.
+                        if (lineCount == 9) {
+                            if (!line.contains("cpu-cycles")) {
+                                Log.e(TAG, "Error in simpleperf output");
+                            } else {
+                                cpuCycles = line.split(",")[0].trim();
+                            }
+                        } else if (lineCount == 10) {
+                            if (!line.contains("major-faults")) {
+                                Log.e(TAG, "Error in simpleperf output");
+                            } else {
+                                majorFaults = line.split(",")[0].trim();
+                            }
+                        }
+                    }
+                    mBufferedWriter.write(line);
+                    mBufferedWriter.newLine();
+                    lineCount++;
+                }
+                mBufferedWriter.flush();
+                inputStream.close();
+            } catch (IOException e) {
+                Log.w(TAG, "Error writing the launch file", e);
+            }
+            return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
+        }
+
+    }
+}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index bdbc149..a61ac54 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -15,6 +15,7 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -986,11 +987,16 @@
                         legacyResults[i] = new RttResult();
                         legacyResults[i].status = result.getStatus();
                         legacyResults[i].bssid = result.getMacAddress().toString();
-                        legacyResults[i].distance = result.getDistanceMm() / 10;
-                        legacyResults[i].distanceStandardDeviation =
-                                result.getDistanceStdDevMm() / 10;
-                        legacyResults[i].rssi = result.getRssi();
-                        legacyResults[i].ts = result.getRangingTimestampUs();
+                        if (result.getStatus() == RangingResult.STATUS_SUCCESS) {
+                            legacyResults[i].distance = result.getDistanceMm() / 10;
+                            legacyResults[i].distanceStandardDeviation =
+                                    result.getDistanceStdDevMm() / 10;
+                            legacyResults[i].rssi = result.getRssi();
+                            legacyResults[i].ts = result.getRangingTimestampUs();
+                        } else {
+                            // just in case legacy API needed some relatively real timestamp
+                            legacyResults[i].ts = SystemClock.elapsedRealtime() * 1000;
+                        }
                     }
                     listener.onSuccess(legacyResults);
                 }