Merge "Add OWNERS file for the annotations package"
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/Android.mk b/cmds/incidentd/Android.mk
index 2b00d9e..23cd2af 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -56,6 +56,7 @@
         libcutils \
         libincident \
         liblog \
+        libprotobuf-cpp-lite \
         libprotoutil \
         libselinux \
         libservices \
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/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 3f0e331..c5078f0 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -73,11 +73,6 @@
         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..ce1b8e9 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -75,7 +75,6 @@
 
     // 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) {}
 };
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index b9f479b..06baeba 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -18,6 +18,7 @@
 
 #include "Reporter.h"
 
+#include "Privacy.h"
 #include "report_directory.h"
 #include "section_list.h"
 
@@ -65,7 +66,9 @@
     :mRequests(),
      mSections(),
      mMainFd(-1),
-     mMainDest(-1)
+     mMainDest(-1),
+     mMetadata(),
+     mSectionStats()
 {
 }
 
@@ -79,18 +82,32 @@
 {
     mRequests.push_back(request);
     mSections.merge(request->args);
+    mMetadata.set_request_size(mMetadata.request_size() + 1);
 }
 
 void
 ReportRequestSet::setMainFd(int fd)
 {
     mMainFd = fd;
+    mMetadata.set_use_dropbox(fd > 0);
 }
 
 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
@@ -98,6 +115,16 @@
     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; };
 
@@ -128,12 +155,12 @@
 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++) {
@@ -182,7 +209,7 @@
         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
+            // Notify listener of starting.
             for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
                 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
                     (*it)->listener->onReportSectionStatus(id,
@@ -191,14 +218,20 @@
             }
 
             // 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));
                 goto DONE;
             }
 
-            // Notify listener of starting
+            // Notify listener of ending.
             for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
                 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
                     (*it)->listener->onReportSectionStatus(id,
@@ -210,6 +243,9 @@
     }
 
 DONE:
+    // Reports the metdadata when taking the incident report.
+    if (!isTest) metadataSection.Execute(&batch);
+
     // Close the file.
     if (mainFd >= 0) {
         close(mainFd);
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index f30ecf0..6058068 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -17,9 +17,12 @@
 #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>
 
@@ -61,13 +64,20 @@
     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;
 };
 
 // ================================================================================
@@ -81,8 +91,8 @@
 
     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.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index faeab87..3c76298 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -45,6 +45,7 @@
 
 // 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;
@@ -149,6 +150,12 @@
     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;
@@ -182,9 +189,7 @@
 
     // 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 (privacyBuffer.size() == 0) goto DONE;
@@ -196,6 +201,7 @@
         writeable++;
         ALOGD("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:
@@ -236,7 +242,7 @@
 
             // 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_section_header(fd, id, buf->size());
             write_all(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.
@@ -244,7 +250,35 @@
     }
     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());
+        write_all(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size());
+    }
+    if (requests->mainFd() >= 0 && !metadataBuf.empty()) {
+        write_section_header(requests->mainFd(), id, metadataBuf.size());
+        write_all(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size());
+    }
+    return NO_ERROR;
+}
 // ================================================================================
 FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
     :Section(id, timeoutMs),
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index d440ee9..80cc033 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -59,6 +59,18 @@
 };
 
 /**
+ * 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
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index c494bd6..a1e3c34 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -180,3 +180,18 @@
     ASSERT_EQ((int)results.size(), 1);
     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..5752a67 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -18,6 +18,7 @@
 
 #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>
@@ -89,6 +90,18 @@
     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));
@@ -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));
@@ -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/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/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/core/java/android/view/View.java b/core/java/android/view/View.java
index 2af2467..2d85a58 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);
                 }
             }
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/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/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/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/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-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/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 f691044..837113d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2171,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>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 348f0b9..9995642 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2990,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" />
@@ -3018,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/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/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/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/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..0dea6b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -909,6 +909,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/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 8240e4b..c023efb 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;
             }
+            content.setFocusable(true);
+            content.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/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/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/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 063060f..228f9bb 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(), true);
+                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();
@@ -569,10 +626,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 +643,10 @@
             mApp = app;
         }
 
+        public String getCompilerFilter() {
+            return mCompilerFilter;
+        }
+
         public String getLaunchReason() {
             return mLaunchReason;
         }
@@ -593,9 +656,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 +689,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 +705,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 +746,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 +762,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 +783,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 +809,7 @@
             } catch (IOException e) {
                 Log.w(TAG, "Error writing the launch file", e);
             }
-            return launchTime;
+            return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
         }
 
     }