Merge "Add jwcai and tianyuj to the owner's file of libs/ui"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 9aa1075..e938b10 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -99,8 +99,9 @@
         "utils.cpp",
     ],
     static_libs: [
+        "libincidentcompanion",
         "libdumpsys",
-        "libserviceutils"
+        "libserviceutils",
     ],
 }
 
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ba9a967..b13478c 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -31,6 +31,13 @@
 
 namespace {
 
+struct DumpstateInfo {
+  public:
+    Dumpstate* ds = nullptr;
+    int32_t calling_uid = -1;
+    std::string calling_package;
+};
+
 static binder::Status exception(uint32_t code, const std::string& msg) {
     MYLOGE("%s (%d) ", msg.c_str(), code);
     return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
@@ -42,14 +49,15 @@
 }
 
 static void* callAndNotify(void* data) {
-    Dumpstate& ds = *static_cast<Dumpstate*>(data);
-    ds.Run();
-    MYLOGE("Finished Run()\n");
+    DumpstateInfo& ds_info = *static_cast<DumpstateInfo*>(data);
+    ds_info.ds->Run(ds_info.calling_uid, ds_info.calling_package);
+    MYLOGD("Finished Run()\n");
     return nullptr;
 }
 
 class DumpstateToken : public BnDumpstateToken {};
-}
+
+}  // namespace
 
 DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
 }
@@ -98,15 +106,12 @@
 }
 
 // TODO(b/111441001): Hook up to consent service & copy final br only if user approves.
-binder::Status DumpstateService::startBugreport(int32_t /* calling_uid */,
-                                                const std::string& /* calling_package */,
+binder::Status DumpstateService::startBugreport(int32_t calling_uid,
+                                                const std::string& calling_package,
                                                 const android::base::unique_fd& bugreport_fd,
                                                 const android::base::unique_fd& screenshot_fd,
                                                 int bugreport_mode,
                                                 const sp<IDumpstateListener>& listener) {
-    // TODO(b/111441001):
-    // 1. check DUMP permission (again)?
-    // 2. check if primary user? If non primary user the consent service will reject anyway.
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
 
     if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL &&
@@ -135,6 +140,11 @@
         ds_.listener_ = listener;
     }
 
+    DumpstateInfo ds_info;
+    ds_info.ds = &ds_;
+    ds_info.calling_uid = calling_uid;
+    ds_info.calling_package = calling_package;
+
     pthread_t thread;
     status_t err = pthread_create(&thread, nullptr, callAndNotify, &ds_);
     if (err != 0) {
@@ -143,6 +153,14 @@
     return binder::Status::ok();
 }
 
+binder::Status DumpstateService::cancelBugreport() {
+    // This is a no-op since the cancellation is done from java side via setting sys properties.
+    // See BugreportManagerServiceImpl.
+    // TODO(b/111441001): maybe make native and java sides use different binder interface
+    // to avoid these annoyances.
+    return binder::Status::ok();
+}
+
 status_t DumpstateService::dump(int fd, const Vector<String16>&) {
     std::string destination = ds_.options_->bugreport_fd.get() != -1
                                   ? StringPrintf("[fd:%d]", ds_.options_->bugreport_fd.get())
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index b6ba32d..faeea53 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -47,6 +47,9 @@
                                   const android::base::unique_fd& screenshot_fd, int bugreport_mode,
                                   const sp<IDumpstateListener>& listener) override;
 
+    // No-op
+    binder::Status cancelBugreport();
+
   private:
     Dumpstate& ds_;
     std::mutex lock_;
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 2635157..f58535e 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -79,4 +79,9 @@
     void startBugreport(int callingUid, @utf8InCpp String callingPackage,
                         FileDescriptor bugreportFd, FileDescriptor screenshotFd,
                         int bugreportMode, IDumpstateListener listener);
+
+    /*
+     * Cancels the bugreport currently in progress.
+     */
+    void cancelBugreport();
 }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index bcd0cb7..907a67c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -38,20 +38,18 @@
     /* User denied consent to share the bugreport with the specified app */
     const int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3;
 
+    /* The request to get user consent timed out */
+    const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4;
+
     /**
      * Called on an error condition with one of the error codes listed above.
      */
     oneway void onError(int errorCode);
 
     /**
-     * Called when taking bugreport finishes successfully
-     *
-     * @param durationMs time capturing bugreport took in milliseconds
-     * @param title title for the bugreport; helpful in reminding the user why they took it
-     * @param description detailed description for the bugreport
+     * Called when taking bugreport finishes successfully.
      */
-     oneway void onFinished(long durationMs, @utf8InCpp String title,
-                            @utf8InCpp String description);
+     oneway void onFinished();
 
     // TODO(b/111441001): Remove old methods when not used anymore.
     void onProgressUpdated(int progress);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b226a7d..dfc5b49 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -51,6 +51,7 @@
 #include <android-base/unique_fd.h>
 #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/os/IIncidentCompanion.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
 #include <dumpsys.h>
@@ -83,15 +84,19 @@
 using android::UNKNOWN_ERROR;
 using android::Vector;
 using android::base::StringPrintf;
+using android::os::IDumpstateListener;
 using android::os::dumpstate::CommandOptions;
 using android::os::dumpstate::DumpFileToFd;
 using android::os::dumpstate::DumpstateSectionReporter;
 using android::os::dumpstate::GetPidByName;
 using android::os::dumpstate::PropertiesHelper;
 
+typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult;
+
 /* read before root is shed */
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = nullptr;
+static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
 
 // TODO: variables and functions below should be part of dumpstate object
 
@@ -165,6 +170,13 @@
     return false;
 }
 
+static bool UnlinkAndLogOnError(const std::string& file) {
+    if (unlink(file.c_str()) != -1) {
+        MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno));
+        return false;
+    }
+    return true;
+}
 
 }  // namespace
 }  // namespace os
@@ -657,6 +669,32 @@
     return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
 }
 
+Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportApproved() {
+    std::lock_guard<std::mutex> lock(lock_);
+    result_ = APPROVED;
+    MYLOGD("User approved consent to share bugreport\n");
+    return android::binder::Status::ok();
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportDenied() {
+    std::lock_guard<std::mutex> lock(lock_);
+    result_ = DENIED;
+    MYLOGW("User denied consent to share bugreport\n");
+    return android::binder::Status::ok();
+}
+
+UserConsentResult Dumpstate::ConsentCallback::getResult() {
+    std::lock_guard<std::mutex> lock(lock_);
+    return result_;
+}
+
+uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
+    return Nanotime() - start_time_;
+}
+
 void Dumpstate::PrintHeader() const {
     std::string build, fingerprint, radio, bootloader, network;
     char date[80];
@@ -1654,6 +1692,7 @@
             "progress (requires -o and -B)\n"
             "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
             "shouldn't be used with -P)\n"
+            "  -w: start binder service and make it wait for a call to startBugreport\n"
             "  -v: prints the dumpstate header and exit\n");
 }
 
@@ -1885,14 +1924,6 @@
                     ds.path_ = new_path;
                 }
             }
-            // The zip file lives in an internal directory. Copy it over to output.
-            if (ds.options_->bugreport_fd.get() != -1) {
-                bool copy_succeeded =
-                    android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get());
-                if (!copy_succeeded && remove(ds.path_.c_str())) {
-                    MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
-                }
-            }  // else - the file just remains in the internal directory.
         }
     }
     if (do_text_file) {
@@ -2112,7 +2143,7 @@
 Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
     RunStatus status = RunStatus::OK;
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) {
         switch (c) {
             // clang-format off
             case 'd': do_add_date = true;            break;
@@ -2129,6 +2160,9 @@
             case 'R': is_remote_mode = true;         break;
             case 'B': do_broadcast = true;           break;
             case 'V':                                break;  // compatibility no-op
+            case 'w':
+                // This was already processed
+                break;
             case 'h':
                 status = RunStatus::HELP;
                 break;
@@ -2182,22 +2216,26 @@
     options_ = std::move(options);
 }
 
-Dumpstate::RunStatus Dumpstate::Run() {
-    Dumpstate::RunStatus status = RunInternal();
+Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
+    Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
     if (listener_ != nullptr) {
         switch (status) {
             case Dumpstate::RunStatus::OK:
-                // TODO(b/111441001): duration argument does not make sense. Remove.
-                listener_->onFinished(0 /* duration */, options_->notification_title,
-                                      options_->notification_description);
+                listener_->onFinished();
                 break;
             case Dumpstate::RunStatus::HELP:
                 break;
             case Dumpstate::RunStatus::INVALID_INPUT:
-                listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
                 break;
             case Dumpstate::RunStatus::ERROR:
-                listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+                break;
+            case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT);
+                break;
+            case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
                 break;
         }
     }
@@ -2225,7 +2263,8 @@
  * Bugreports are first generated in a local directory and later copied to the caller's fd if
  * supplied.
  */
-Dumpstate::RunStatus Dumpstate::RunInternal() {
+Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
+                                            const std::string& calling_package) {
     LogDumpOptions(*options_);
     if (!options_->ValidateOptions()) {
         MYLOGE("Invalid options specified\n");
@@ -2263,6 +2302,12 @@
         return RunStatus::OK;
     }
 
+    if (options_->bugreport_fd.get() != -1) {
+        // If the output needs to be copied over to the caller's fd, get user consent.
+        android::String16 package(calling_package.c_str());
+        CheckUserConsent(calling_uid, package);
+    }
+
     // Redirect output if needed
     bool is_redirecting = options_->OutputToFile();
 
@@ -2414,11 +2459,24 @@
         TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
     }
 
-    /* rename or zip the (now complete) .tmp file to its final location */
+    // Rename, and/or zip the (now complete) .tmp file within the internal directory.
     if (options_->OutputToFile()) {
         FinalizeFile();
     }
 
+    // Share the final file with the caller if the user has consented.
+    Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
+    if (options_->bugreport_fd.get() != -1) {
+        status = CopyBugreportIfUserConsented();
+        if (status != Dumpstate::RunStatus::OK &&
+            status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
+            // Do an early return if there were errors. We make an exception for consent
+            // timing out because it's possible the user got distracted. In this case the
+            // bugreport is not shared but made available for manual retrieval.
+            return status;
+        }
+    }
+
     /* vibrate a few but shortly times to let user know it's finished */
     if (options_->do_vibrate) {
         for (int i = 0; i < 3; i++) {
@@ -2450,7 +2508,73 @@
     tombstone_data_.clear();
     anr_data_.clear();
 
-    return RunStatus::OK;
+    return (consent_callback_ != nullptr &&
+            consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
+               ? USER_CONSENT_TIMED_OUT
+               : RunStatus::OK;
+}
+
+void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
+    consent_callback_ = new ConsentCallback();
+    const String16 incidentcompanion("incidentcompanion");
+    sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+    if (ics != nullptr) {
+        MYLOGD("Checking user consent via incidentcompanion service\n");
+        android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
+            calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
+            consent_callback_.get());
+    } else {
+        MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
+    }
+}
+
+void Dumpstate::CleanupFiles() {
+    android::os::UnlinkAndLogOnError(tmp_path_);
+    android::os::UnlinkAndLogOnError(screenshot_path_);
+    android::os::UnlinkAndLogOnError(path_);
+}
+
+Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
+    MYLOGD("User denied consent; deleting files and returning\n");
+    CleanupFiles();
+    return USER_CONSENT_DENIED;
+}
+
+Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() {
+    // If the caller has asked to copy the bugreport over to their directory, we need explicit
+    // user consent.
+    UserConsentResult consent_result = consent_callback_->getResult();
+    if (consent_result == UserConsentResult::UNAVAILABLE) {
+        // User has not responded yet.
+        uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
+        if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
+            uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+            MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
+            sleep(delay_seconds);
+        }
+        consent_result = consent_callback_->getResult();
+    }
+    if (consent_result == UserConsentResult::DENIED) {
+        // User has explicitly denied sharing with the app. To be safe delete the
+        // internal bugreport & tmp files.
+        return HandleUserConsentDenied();
+    }
+    if (consent_result == UserConsentResult::APPROVED) {
+        bool copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get());
+        if (copy_succeeded && remove(ds.path_.c_str())) {
+            MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
+        }
+        return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR;
+    } else if (consent_result == UserConsentResult::UNAVAILABLE) {
+        // consent_result is still UNAVAILABLE. The user has likely not responded yet.
+        // Since we do not have user consent to share the bugreport it does not get
+        // copied over to the calling app but remains in the internal directory from
+        // where the user can manually pull it.
+        return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
+    }
+    // Unknown result; must be a programming error.
+    MYLOGE("Unknown user consent result:%d\n", consent_result);
+    return Dumpstate::RunStatus::ERROR;
 }
 
 /* Main entry point for dumpstate binary. */
@@ -2459,7 +2583,14 @@
     Dumpstate::RunStatus status = options->Initialize(argc, argv);
     if (status == Dumpstate::RunStatus::OK) {
         ds.SetOptions(std::move(options));
-        status = ds.Run();
+        // When directly running dumpstate binary, the output is not expected to be written
+        // to any external file descriptor.
+        assert(ds.options_->bugreport_fd.get() == -1);
+
+        // calling_uid and calling_package are for user consent to share the bugreport with
+        // an app; they are irrelvant here because bugreport is only written to a local
+        // directory, and not shared.
+        status = ds.Run(-1 /* calling_uid */, "" /* calling_package */);
     }
 
     switch (status) {
@@ -2473,9 +2604,10 @@
             ShowUsage();
             exit(1);
         case Dumpstate::RunStatus::ERROR:
-            exit(2);
-        default:
-            fprintf(stderr, "Unknown status: %d\n", status);
+            FALLTHROUGH_INTENDED;
+        case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+            FALLTHROUGH_INTENDED;
+        case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
             exit(2);
     }
 }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3dfe4e9..4766d82 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,7 @@
 
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
+#include <android/os/BnIncidentAuthListener.h>
 #include <android/os/IDumpstate.h>
 #include <android/os/IDumpstateListener.h>
 #include <utils/StrongPointer.h>
@@ -192,7 +193,7 @@
     friend class DumpstateTest;
 
   public:
-    enum RunStatus { OK, HELP, INVALID_INPUT, ERROR };
+    enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
 
     // The mode under which the bugreport should be run. Each mode encapsulates a few options.
     enum BugreportMode {
@@ -319,7 +320,7 @@
     struct DumpOptions;
 
     /* Main entry point for running a complete bugreport. */
-    RunStatus Run();
+    RunStatus Run(int32_t calling_uid, const std::string& calling_package);
 
     /* Sets runtime options. */
     void SetOptions(std::unique_ptr<DumpOptions> options);
@@ -447,12 +448,47 @@
     // List of open ANR dump files.
     std::vector<DumpData> anr_data_;
 
+    // A callback to IncidentCompanion service, which checks user consent for sharing the
+    // bugreport with the calling app. If the user has not responded yet to the dialog it will
+    // be neither confirmed nor denied.
+    class ConsentCallback : public android::os::BnIncidentAuthListener {
+      public:
+        ConsentCallback();
+        android::binder::Status onReportApproved() override;
+        android::binder::Status onReportDenied() override;
+
+        enum ConsentResult { APPROVED, DENIED, UNAVAILABLE };
+
+        ConsentResult getResult();
+
+        // Returns the time since creating this listener
+        uint64_t getElapsedTimeMs() const;
+
+      private:
+        ConsentResult result_;
+        uint64_t start_time_;
+        std::mutex lock_;
+    };
+
   private:
-    RunStatus RunInternal();
+    RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
+
+    void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+
+    // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
+    // but leaves the log file alone.
+    void CleanupFiles();
+
+    RunStatus HandleUserConsentDenied();
+
+    // Copies bugreport artifacts over to the caller's directories provided there is user consent.
+    RunStatus CopyBugreportIfUserConsented();
 
     // Used by GetInstance() only.
     explicit Dumpstate(const std::string& version = VERSION_CURRENT);
 
+    android::sp<ConsentCallback> consent_callback_;
+
     DISALLOW_COPY_AND_ASSIGN(Dumpstate);
 };
 
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 2e72574..14937b8 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,3 +17,9 @@
     class main
     disabled
     oneshot
+
+# bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
+service bugreportd /system/bin/dumpstate -w
+    class main
+    disabled
+    oneshot
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index 78aad11..68d3733 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -14,8 +14,55 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "dumpstate"
+
+#include <binder/IPCThreadState.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
 #include "dumpstate.h"
 
+namespace {
+
+// Returns true if we should start the service and wait for a listener
+// to bind with bugreport options.
+bool ShouldStartServiceAndWait(int argc, char* argv[]) {
+    bool do_wait = false;
+    int c;
+    // Keep flags in sync with Dumpstate::DumpOptions::Initialize.
+    while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) {
+        switch (c) {
+            case 'w':
+                do_wait = true;
+                break;
+            default:
+                // Ignore all other options
+                break;
+        }
+    }
+
+    // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to
+    // parse bugreport options.
+    optind = 1;
+    return do_wait;
+}
+
+}  // namespace
+
 int main(int argc, char* argv[]) {
-    return run_main(argc, argv);
+    if (ShouldStartServiceAndWait(argc, argv)) {
+        int ret;
+        if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+            MYLOGE("Unable to start 'dumpstate' service: %d", ret);
+            exit(1);
+        }
+        MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");
+
+        // Waits forever for an incoming connection.
+        // TODO(b/111441001): should this time out?
+        android::IPCThreadState::self()->joinThreadPool();
+        return 0;
+    } else {
+        return run_main(argc, argv);
+    }
 }
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c57775f..570c6c9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -61,9 +61,8 @@
         dprintf(outFd_, "\rError %d", error_code);
         return binder::Status::ok();
     }
-    binder::Status onFinished(int64_t duration_ms, const ::std::string&,
-                              const ::std::string&) override {
-        dprintf(outFd_, "\rFinished in %lld", (long long) duration_ms);
+    binder::Status onFinished() override {
+        dprintf(outFd_, "\rFinished");
         return binder::Status::ok();
     }
     binder::Status onProgressUpdated(int32_t progress) override {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 0302c46..eb73d41 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -61,8 +61,7 @@
   public:
     MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
     MOCK_METHOD1(onError, binder::Status(int32_t error_code));
-    MOCK_METHOD3(onFinished, binder::Status(int64_t duration_ms, const ::std::string& title,
-                                            const ::std::string& description));
+    MOCK_METHOD0(onFinished, binder::Status());
     MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
     MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
     MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status,
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 6f4e699..9a8a4c3 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,7 @@
         "dexopt.cpp",
         "globals.cpp",
         "utils.cpp",
+        "view_compiler.cpp",
         ":installd_aidl",
     ],
     header_libs: [
@@ -137,10 +138,23 @@
     ],
     clang: true,
 
-    srcs: ["otapreopt_chroot.cpp"],
+    srcs: [
+        "otapreopt_chroot.cpp",
+        "otapreopt_utils.cpp",
+    ],
     shared_libs: [
         "libbase",
         "liblog",
+        "libprotobuf-cpp-full",
+        "libselinux",
+        "libziparchive",
+    ],
+    static_libs: [
+        "libapex",
+        "libapexd",
+        "lib_apex_manifest_proto",
+        "libavb",
+        "libdm",
     ],
 }
 
@@ -189,7 +203,9 @@
         "dexopt.cpp",
         "globals.cpp",
         "otapreopt.cpp",
+        "otapreopt_utils.cpp",
         "utils.cpp",
+        "view_compiler.cpp",
     ],
 
     header_libs: ["dex2oat_headers"],
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 2439dff..9e252ee 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -41,6 +41,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -60,6 +61,7 @@
 #include "installd_deps.h"
 #include "otapreopt_utils.h"
 #include "utils.h"
+#include "view_compiler.h"
 
 #include "CacheTracker.h"
 #include "MatchExtensionGen.h"
@@ -745,6 +747,162 @@
     return ok();
 }
 
+static int32_t copy_directory_recursive(const char* from, const char* to) {
+    char *argv[] = {
+        (char*) kCpPath,
+        (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+        (char*) "-p", /* preserve timestamps, ownership, and permissions */
+        (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+        (char*) "-P", /* Do not follow symlinks [default] */
+        (char*) "-d", /* don't dereference symlinks */
+        (char*) from,
+        (char*) to
+    };
+
+    LOG(DEBUG) << "Copying " << from << " to " << to;
+    return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+}
+
+// TODO(narayan): We should pass through the ceDataInode so that we can call
+// clearAppData(FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE before we commence
+// the copy.
+//
+// TODO(narayan): For snapshotAppData as well as restoreAppDataSnapshot, we
+// should validate that volumeUuid is either nullptr or TEST, we won't support
+// anything else.
+//
+// TODO(narayan): We need to be clearer about the expected behaviour for the
+// case where a snapshot already exists. We either need to clear the contents
+// of the snapshot directory before we make a copy, or we need to ensure that
+// the caller always clears it before requesting a snapshot.
+binder::Status InstalldNativeService::snapshotAppData(
+        const std::unique_ptr<std::string>& volumeUuid,
+        const std::string& packageName, int32_t user, int32_t storageFlags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(volumeUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+
+    binder::Status res = ok();
+    bool clear_ce_on_exit = false;
+    bool clear_de_on_exit = false;
+
+    auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] {
+        if (clear_de_on_exit) {
+            auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
+                LOG(WARNING) << "Failed to delete app data snapshot: " << to;
+            }
+        }
+
+        if (clear_ce_on_exit) {
+            auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
+                LOG(WARNING) << "Failed to delete app data snapshot: " << to;
+            }
+        }
+    };
+
+    auto scope_guard = android::base::make_scope_guard(deleter);
+
+    // The app may not have any data at all, in which case it's OK to skip here.
+    auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
+    if (access(from_ce.c_str(), F_OK) != 0) {
+        LOG(INFO) << "Missing source " << from_ce;
+        return ok();
+    }
+
+    if (storageFlags & FLAG_STORAGE_DE) {
+        auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
+        auto to = create_data_misc_de_rollback_path(volume_uuid, user);
+
+        int rc = copy_directory_recursive(from.c_str(), to.c_str());
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from + " to " + to);
+            clear_de_on_exit = true;
+            return res;
+        }
+    }
+
+    if (storageFlags & FLAG_STORAGE_CE) {
+        auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
+        auto to = create_data_misc_ce_rollback_path(volume_uuid, user);
+        int rc = copy_directory_recursive(from.c_str(), to.c_str());
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from + " to " + to);
+            clear_ce_on_exit = true;
+            return res;
+        }
+    }
+
+    return res;
+}
+
+binder::Status InstalldNativeService::restoreAppDataSnapshot(
+        const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName,
+        const int32_t appId, const int64_t ceDataInode, const std::string& seInfo,
+        const int32_t user, int32_t storageFlags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(volumeUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+
+    auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
+            user, package_name);
+    auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
+            user, package_name);
+
+    const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) &&
+        (access(from_ce.c_str(), F_OK) == 0);
+    const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) &&
+        (access(from_de.c_str(), F_OK) == 0);
+
+    if (!needs_ce_rollback && !needs_de_rollback) {
+        return ok();
+    }
+
+    // We know we're going to rollback one of the CE or DE data, so we clear
+    // application data first. Note that it's possible that we're asked to
+    // restore both CE & DE data but that one of the restores fail. Leaving the
+    // app with no data in those cases is arguably better than leaving the app
+    // with mismatched / stale data.
+    LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot.";
+    binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode);
+    if (!res.isOk()) {
+        return res;
+    }
+
+    if (needs_ce_rollback) {
+        auto to_ce = create_data_user_ce_path(volume_uuid, user);
+        int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str());
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from_ce + " to " + to_ce);
+            return res;
+        }
+    }
+
+    if (needs_de_rollback) {
+        auto to_de = create_data_user_de_path(volume_uuid, user);
+        int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str());
+        if (rc != 0) {
+            // TODO(narayan): Should we clear clear the rolled back CE data if
+            // something goes wrong here ? We're choosing between leaving the
+            // app devoid of all its data or with just its ce data installed.
+            res = error(rc, "Failed copying " + from_de + " to " + to_de);
+            return res;
+        }
+    }
+
+    // Finally, restore the SELinux label on the app data.
+    return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
+}
+
 binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
         const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
         const std::string& dataAppName, int32_t appId, const std::string& seInfo,
@@ -769,19 +927,7 @@
         auto to = create_data_app_package_path(to_uuid, data_app_name);
         auto to_parent = create_data_app_path(to_uuid);
 
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            (char*) from.c_str(),
-            (char*) to_parent.c_str()
-        };
-
-        LOG(DEBUG) << "Copying " << from << " to " << to;
-        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+        int rc = copy_directory_recursive(from.c_str(), to_parent.c_str());
         if (rc != 0) {
             res = error(rc, "Failed copying " + from + " to " + to);
             goto fail;
@@ -809,25 +955,11 @@
             goto fail;
         }
 
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            nullptr,
-            nullptr
-        };
-
         {
             auto from = create_data_user_de_package_path(from_uuid, user, package_name);
             auto to = create_data_user_de_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
 
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+            int rc = copy_directory_recursive(from.c_str(), to.c_str());
             if (rc != 0) {
                 res = error(rc, "Failed copying " + from + " to " + to);
                 goto fail;
@@ -836,11 +968,8 @@
         {
             auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
             auto to = create_data_user_ce_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
 
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+            int rc = copy_directory_recursive(from.c_str(), to.c_str());
             if (rc != 0) {
                 res = error(rc, "Failed copying " + from + " to " + to);
                 goto fail;
@@ -1831,6 +1960,17 @@
     return res ? error(res, error_msg) : ok();
 }
 
+binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath,
+                                                     const std::string& packageName,
+                                                     const std ::string& outDexFile, int uid,
+                                                     bool* _aidl_return) {
+    const char* apk_path = apkPath.c_str();
+    const char* package_name = packageName.c_str();
+    const char* out_dex_file = outDexFile.c_str();
+    *_aidl_return = android::installd::view_compiler(apk_path, package_name, out_dex_file, uid);
+    return *_aidl_return ? ok() : error("viewcompiler failed");
+}
+
 binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
     ENFORCE_UID(AID_SYSTEM);
     std::lock_guard<std::recursive_mutex> lock(mLock);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 367f2c1..098a0c2 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -60,6 +60,12 @@
 
     binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
 
+    binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
+            const std::string& packageName, const int32_t user, int32_t storageFlags);
+    binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
+            const std::string& packageName, const int32_t appId, const int64_t ceDataInode,
+            const std::string& seInfo, const int32_t user, int32_t storageFlags);
+
     binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
             const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
             int32_t appId, const std::vector<int64_t>& ceDataInodes,
@@ -89,6 +95,9 @@
             const std::unique_ptr<std::string>& dexMetadataPath,
             const std::unique_ptr<std::string>& compilationReason);
 
+    binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
+                                  const std::string& outDexFile, int uid, bool* _aidl_return);
+
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
     binder::Status mergeProfiles(int32_t uid, const std::string& packageName,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 91e20b7..a70e9ff 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -55,6 +55,8 @@
             @nullable @utf8InCpp String profileName,
             @nullable @utf8InCpp String dexMetadataPath,
             @nullable @utf8InCpp String compilationReason);
+    boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName,
+            @utf8InCpp String outDexFile, int uid);
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
@@ -102,4 +104,16 @@
     boolean prepareAppProfile(@utf8InCpp String packageName,
         int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath,
         @nullable @utf8InCpp String dexMetadata);
+
+    void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+            int userId, int storageFlags);
+    void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+            int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags);
+
+    // TODO(narayan) we need an API to delete the app data snapshot as well.
+    // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid,
+    //        in @utf8InCpp String packageName, int userId, int storageFlags);
+
+    const int FLAG_STORAGE_DE = 0x1;
+    const int FLAG_STORAGE_CE = 0x2;
 }
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 07ea77b..940ba79 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -333,8 +333,7 @@
             MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
 
         // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
-        const char* dex2oat_bin = "/system/bin/dex2oat";
-        constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
+        const char* dex2oat_bin = kDex2oatPath;
         // Do not use dex2oatd for release candidates (give dex2oat more soak time).
         bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
         if (is_debug_runtime() ||
@@ -628,27 +627,6 @@
     }
 }
 
-static void drop_capabilities(uid_t uid) {
-    if (setgid(uid) != 0) {
-        PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt";
-        exit(DexoptReturnCodes::kSetGid);
-    }
-    if (setuid(uid) != 0) {
-        PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt";
-        exit(DexoptReturnCodes::kSetUid);
-    }
-    // drop capabilities
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    if (capset(&capheader, &capdata[0]) < 0) {
-        PLOG(ERROR) << "capset failed";
-        exit(DexoptReturnCodes::kCapSet);
-    }
-}
-
 static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
 static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
 static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
@@ -663,8 +641,7 @@
                   const std::vector<std::string>& dex_locations,
                   bool copy_and_update,
                   bool store_aggregation_counters) {
-        const char* profman_bin =
-                is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
+        const char* profman_bin = is_debug_runtime() ? kProfmanDebugPath: kProfmanPath;
 
         if (copy_and_update) {
             CHECK_EQ(1u, profile_fds.size());
@@ -1480,9 +1457,7 @@
                     const char* class_loader_context) {
         CHECK_GE(zip_fd, 0);
         const char* dexoptanalyzer_bin =
-                is_debug_runtime()
-                        ? "/system/bin/dexoptanalyzerd"
-                        : "/system/bin/dexoptanalyzer";
+            is_debug_runtime() ? kDexoptanalyzerDebugPath : kDexoptanalyzerPath;
 
         std::string dex_file_arg = "--dex-file=" + dex_file;
         std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 0db11e1..5902659 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -32,6 +32,16 @@
 static constexpr int DEX2OAT_FOR_BOOT_IMAGE      = 2;
 static constexpr int DEX2OAT_FOR_FILTER          = 3;
 
+#define ANDROID_RUNTIME_APEX_BIN "/apex/com.android.runtime/bin"
+// Location of binaries in the Android Runtime APEX.
+static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat";
+static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd";
+static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer";
+static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd";
+#undef ANDROID_RUNTIME_APEX_BIN
+
 // Clear the reference profile identified by the given profile name.
 bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name);
 // Clear the current profile identified by the given profile name (for single user).
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index b2e7047..de7b249 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,7 +26,6 @@
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
-#include <sys/wait.h>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -58,7 +57,6 @@
 #define REPLY_MAX     256   /* largest reply allowed */
 
 using android::base::EndsWith;
-using android::base::Join;
 using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -440,7 +438,7 @@
                           const char* isa) const {
         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
         std::vector<std::string> cmd;
-        cmd.push_back("/system/bin/dex2oat");
+        cmd.push_back(kDex2oatPath);
         cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
         for (const std::string& boot_part : Split(boot_cp, ":")) {
             cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
@@ -619,61 +617,6 @@
     // Helpers, mostly taken from ART //
     ////////////////////////////////////
 
-    // Wrapper on fork/execv to run a command in a subprocess.
-    static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
-        const std::string command_line = Join(arg_vector, ' ');
-
-        CHECK_GE(arg_vector.size(), 1U) << command_line;
-
-        // Convert the args to char pointers.
-        const char* program = arg_vector[0].c_str();
-        std::vector<char*> args;
-        for (size_t i = 0; i < arg_vector.size(); ++i) {
-            const std::string& arg = arg_vector[i];
-            char* arg_str = const_cast<char*>(arg.c_str());
-            CHECK(arg_str != nullptr) << i;
-            args.push_back(arg_str);
-        }
-        args.push_back(nullptr);
-
-        // Fork and exec.
-        pid_t pid = fork();
-        if (pid == 0) {
-            // No allocation allowed between fork and exec.
-
-            // Change process groups, so we don't get reaped by ProcessManager.
-            setpgid(0, 0);
-
-            execv(program, &args[0]);
-
-            PLOG(ERROR) << "Failed to execv(" << command_line << ")";
-            // _exit to avoid atexit handlers in child.
-            _exit(1);
-        } else {
-            if (pid == -1) {
-                *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
-                        command_line.c_str(), strerror(errno));
-                return false;
-            }
-
-            // wait for subprocess to finish
-            int status;
-            pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-            if (got_pid != pid) {
-                *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
-                        "wanted %d, got %d: %s",
-                        command_line.c_str(), pid, got_pid, strerror(errno));
-                return false;
-            }
-            if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-                *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
-                        command_line.c_str());
-                return false;
-            }
-        }
-        return true;
-    }
-
     // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
     static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
         constexpr size_t kPageSize = PAGE_SIZE;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index e90cf3b..9965d58 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -17,6 +17,7 @@
 #include <fcntl.h>
 #include <linux/unistd.h>
 #include <sys/mount.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 
 #include <sstream>
@@ -24,6 +25,9 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <selinux/android.h>
+
+#include <apexd.h>
 
 #include "installd_constants.h"
 #include "otapreopt_utils.h"
@@ -138,6 +142,33 @@
       UNUSED(product_result);
     }
 
+    // Setup APEX mount point and its security context.
+    static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
+    // The following logic is similar to the one in system/core/rootdir/init.rc:
+    //
+    //   mount tmpfs tmpfs /apex nodev noexec nosuid
+    //   chmod 0755 /apex
+    //   chown root root /apex
+    //   restorecon /apex
+    //
+    if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
+        != 0) {
+        PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir;
+        exit(209);
+    }
+    if (chmod(kPostinstallApexDir, 0755) != 0) {
+        PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755";
+        exit(210);
+    }
+    if (chown(kPostinstallApexDir, 0, 0) != 0) {
+        PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root";
+        exit(211);
+    }
+    if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
+        PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
+        exit(212);
+    }
+
     // Chdir into /postinstall.
     if (chdir("/postinstall") != 0) {
         PLOG(ERROR) << "Unable to chdir into /postinstall.";
@@ -155,22 +186,52 @@
         exit(205);
     }
 
+    // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
+    // the Android Runtime APEX, as it is required by otapreopt to run dex2oat.
+    // The logic here is (partially) copied and adapted from
+    // system/apex/apexd/apexd_main.cpp.
+    //
+    // Only scan the APEX directory under /system (within the chroot dir).
+    // Note that this leaves around the loop devices created and used by
+    // libapexd's code, but this is fine, as we expect to reboot soon after.
+    apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir);
+    // Collect activated packages.
+    std::vector<apex::ApexFile> active_packages = apex::getActivePackages();
+
     // Now go on and run otapreopt.
 
-    // Incoming:  cmd + status-fd + target-slot + cmd... + null      | Incoming | = argc + 1
-    // Outgoing:  cmd             + target-slot + cmd... + null      | Outgoing | = argc
-    const char** argv = new const char*[argc];
-
-    argv[0] = "/system/bin/otapreopt";
+    // Incoming:  cmd + status-fd + target-slot + cmd...      | Incoming | = argc
+    // Outgoing:  cmd             + target-slot + cmd...      | Outgoing | = argc - 1
+    std::vector<std::string> cmd;
+    cmd.reserve(argc);
+    cmd.push_back("/system/bin/otapreopt");
 
     // The first parameter is the status file descriptor, skip.
-    for (size_t i = 2; i <= static_cast<size_t>(argc); ++i) {
-        argv[i - 1] = arg[i];
+    for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
+        cmd.push_back(arg[i]);
     }
 
-    execv(argv[0], static_cast<char * const *>(const_cast<char**>(argv)));
-    PLOG(ERROR) << "execv(OTAPREOPT) failed.";
-    exit(99);
+    // Fork and execute otapreopt in its own process.
+    std::string error_msg;
+    bool exec_result = Exec(cmd, &error_msg);
+    if (!exec_result) {
+        LOG(ERROR) << "Running otapreopt failed: " << error_msg;
+    }
+
+    // Tear down the work down by the apexd logic above (i.e. deactivate packages).
+    for (const apex::ApexFile& apex_file : active_packages) {
+        const std::string& package_path = apex_file.GetPath();
+        apex::Status status = apex::deactivatePackage(package_path);
+        if (!status.Ok()) {
+            LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage();
+        }
+    }
+
+    if (!exec_result) {
+        exit(213);
+    }
+
+    return 0;
 }
 
 }  // namespace installd
diff --git a/cmds/installd/otapreopt_utils.cpp b/cmds/installd/otapreopt_utils.cpp
new file mode 100644
index 0000000..124f726
--- /dev/null
+++ b/cmds/installd/otapreopt_utils.cpp
@@ -0,0 +1,88 @@
+/*
+ ** Copyright 2019, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "otapreopt_utils.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
+    const std::string command_line = Join(arg_vector, ' ');
+
+    CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+    // Convert the args to char pointers.
+    const char* program = arg_vector[0].c_str();
+    std::vector<char*> args;
+    for (size_t i = 0; i < arg_vector.size(); ++i) {
+        const std::string& arg = arg_vector[i];
+        char* arg_str = const_cast<char*>(arg.c_str());
+        CHECK(arg_str != nullptr) << i;
+        args.push_back(arg_str);
+    }
+    args.push_back(nullptr);
+
+    // Fork and exec.
+    pid_t pid = fork();
+    if (pid == 0) {
+        // No allocation allowed between fork and exec.
+
+        // Change process groups, so we don't get reaped by ProcessManager.
+        setpgid(0, 0);
+
+        execv(program, &args[0]);
+
+        PLOG(ERROR) << "Failed to execv(" << command_line << ")";
+        // _exit to avoid atexit handlers in child.
+        _exit(1);
+    } else {
+        if (pid == -1) {
+            *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+                    command_line.c_str(), strerror(errno));
+            return false;
+        }
+
+        // wait for subprocess to finish
+        int status;
+        pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+        if (got_pid != pid) {
+            *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+                    "wanted %d, got %d: %s",
+                    command_line.c_str(), pid, got_pid, strerror(errno));
+            return false;
+        }
+        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+            *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+                    command_line.c_str());
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/otapreopt_utils.h b/cmds/installd/otapreopt_utils.h
index 436e554..03a6d87 100644
--- a/cmds/installd/otapreopt_utils.h
+++ b/cmds/installd/otapreopt_utils.h
@@ -18,6 +18,8 @@
 #define OTAPREOPT_UTILS_H_
 
 #include <regex>
+#include <string>
+#include <vector>
 
 namespace android {
 namespace installd {
@@ -28,6 +30,9 @@
     return std::regex_match(input, slot_suffix_match, slot_suffix_regex);
 }
 
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index a5af5d7..0d46341 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -21,6 +21,8 @@
 #include <sys/xattr.h>
 
 #include <android-base/logging.h>
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
@@ -240,5 +242,180 @@
     EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf));
 }
 
+static bool mkdirs(const std::string& path, mode_t mode) {
+    struct stat sb;
+    if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
+        return true;
+    }
+
+    if (!mkdirs(android::base::Dirname(path), mode)) {
+        return false;
+    }
+
+    return (::mkdir(path.c_str(), mode) != -1);
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot) {
+  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+          &fake_package_ce_path, &fake_package_de_path]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  // Request a snapshot of the CE content but not the DE content.
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE).isOk());
+
+  std::string ce_content, de_content;
+  // At this point, we should have the CE content but not the DE content.
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_FALSE(android::base::ReadFileToString(
+      rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("TEST_CONTENT_CE", ce_content);
+
+  // Modify the CE content, so we can assert later that it's reflected
+  // in the snapshot.
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  // Request a snapshot of the DE content but not the CE content.
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+
+  // At this point, both the CE as well as the DE content should be fully
+  // populated.
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("TEST_CONTENT_CE", ce_content);
+  ASSERT_EQ("TEST_CONTENT_DE", de_content);
+
+  // Modify the DE content, so we can assert later that it's reflected
+  // in our final snapshot.
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  // Request a snapshot of both the CE as well as the DE content.
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content);
+  ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content);
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) {
+  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+  auto deleter = [&rollback_ce_dir, &rollback_de_dir]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE).isOk());
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+
+  // The snapshot calls must succeed but there should be no snapshot
+  // created.
+  struct stat sb;
+  ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+  ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+}
+
+TEST_F(ServiceTest, RestoreAppDataSnapshot) {
+  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+          &fake_package_ce_path, &fake_package_de_path]() {
+      delete_dir_contents(rollback_ce_dir, true);
+      delete_dir_contents(rollback_de_dir, true);
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      rmdir(rollback_ce_dir.c_str());
+      rmdir(rollback_de_dir.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  // Write contents to the rollback location. We'll write the same files to the
+  // app data location and make sure the restore has overwritten them.
+  ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700));
+  ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+
+  ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+          "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+
+  std::string ce_content, de_content;
+  ASSERT_TRUE(android::base::ReadFileToString(
+      fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::ReadFileToString(
+      fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("CE_RESTORE_CONTENT", ce_content);
+  ASSERT_EQ("DE_RESTORE_CONTENT", de_content);
+}
+
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ad7a5ae..ce99fff 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -544,5 +544,35 @@
     EXPECT_EQ(0, MatchExtension("docx"));
 }
 
+TEST_F(UtilsTest, TestRollbackPaths) {
+    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_de/0/rollback/com.foo",
+            create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/rollback/com.foo",
+            create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_ce/0/rollback",
+            create_data_misc_ce_rollback_path(nullptr, 0));
+    EXPECT_EQ("/data/misc_ce/10/rollback",
+            create_data_misc_ce_rollback_path(nullptr, 10));
+
+    EXPECT_EQ("/data/misc_de/0/rollback",
+            create_data_misc_de_rollback_path(nullptr, 0));
+    EXPECT_EQ("/data/misc_de/10/rollback",
+            create_data_misc_de_rollback_path(nullptr, 10));
+
+    // These last couple of cases are never exercised in production because we
+    // only snapshot apps in the primary data partition. Exercise them here for
+    // the sake of completeness.
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example",
+            create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example",
+            create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 74ad184..24f5eab 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 #include <fts.h>
 #include <stdlib.h>
+#include <sys/capability.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
@@ -34,6 +35,7 @@
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
+#include "dexopt_return_codes.h"
 #include "globals.h"  // extern variables.
 
 #ifndef LOG_TAG
@@ -192,6 +194,27 @@
     return StringPrintf("%s/user_de/%u", data.c_str(), userid);
 }
 
+
+std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user) {
+    return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
+}
+
+std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user) {
+    return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user);
+}
+
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name) {
+    return StringPrintf("%s/%s",
+           create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name);
+}
+
+std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name) {
+    return StringPrintf("%s/%s",
+           create_data_misc_de_rollback_path(volume_uuid, user).c_str(), package_name);
+}
+
 /**
  * Create the path name for media for a certain userid.
  */
@@ -1063,5 +1086,26 @@
     }
 }
 
+void drop_capabilities(uid_t uid) {
+    if (setgid(uid) != 0) {
+        PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt";
+        exit(DexoptReturnCodes::kSetGid);
+    }
+    if (setuid(uid) != 0) {
+        PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt";
+        exit(DexoptReturnCodes::kSetUid);
+    }
+    // drop capabilities
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    if (capset(&capheader, &capdata[0]) < 0) {
+        PLOG(ERROR) << "capset failed";
+        exit(DexoptReturnCodes::kCapSet);
+    }
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index d05724a..430f515 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -61,6 +61,13 @@
 std::string create_data_user_ce_package_path_as_user_link(
         const char* volume_uuid, userid_t userid, const char* package_name);
 
+std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user);
+std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user);
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name);
+std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name);
+
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
 std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
 std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
@@ -140,6 +147,8 @@
 // It returns true if there were no errors at all, and false otherwise.
 bool collect_profiles(std::vector<std::string>* profiles_paths);
 
+void drop_capabilities(uid_t uid);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
new file mode 100644
index 0000000..f1ac717
--- /dev/null
+++ b/cmds/installd/view_compiler.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "view_compiler.h"
+
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace installd {
+
+using base::unique_fd;
+
+bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
+                   int uid) {
+    CHECK(apk_path != nullptr);
+    CHECK(package_name != nullptr);
+    CHECK(out_dex_file != nullptr);
+
+    // viewcompiler won't have permission to open anything, so we have to open the files first
+    // and pass file descriptors.
+
+    // Open input file
+    unique_fd infd{open(apk_path, 0)};
+    if (infd.get() < 0) {
+        PLOG(ERROR) << "Could not open input file: " << apk_path;
+        return false;
+    }
+
+    // Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so
+    // we close stdout and open it towards the right output.
+    unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)};
+    if (outfd.get() < 0) {
+        PLOG(ERROR) << "Could not open output file: " << out_dex_file;
+        return false;
+    }
+    if (fchmod(outfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) {
+        PLOG(ERROR) << "Could not change output file permissions";
+        return false;
+    }
+    if (close(STDOUT_FILENO) != 0) {
+        PLOG(ERROR) << "Could not close stdout";
+        return false;
+    }
+    if (dup2(outfd, STDOUT_FILENO) < 0) {
+        PLOG(ERROR) << "Could not duplicate output file descriptor";
+        return false;
+    }
+
+    // Prepare command line arguments for viewcompiler
+    std::string args[] = {"/system/bin/viewcompiler",
+                          "--apk",
+                          "--infd",
+                          android::base::StringPrintf("%d", infd.get()),
+                          "--dex",
+                          "--package",
+                          package_name};
+    char* const argv[] = {const_cast<char*>(args[0].c_str()), const_cast<char*>(args[1].c_str()),
+                          const_cast<char*>(args[2].c_str()), const_cast<char*>(args[3].c_str()),
+                          const_cast<char*>(args[4].c_str()), const_cast<char*>(args[5].c_str()),
+                          const_cast<char*>(args[6].c_str()), nullptr};
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        // Now that we've opened the files we need, drop privileges.
+        drop_capabilities(uid);
+        execv("/system/bin/viewcompiler", argv);
+        _exit(1);
+    }
+
+    return wait_child(pid) == 0;
+}
+
+} // namespace installd
+} // namespace android
\ No newline at end of file
diff --git a/cmds/installd/view_compiler.h b/cmds/installd/view_compiler.h
new file mode 100644
index 0000000..f7c6e57
--- /dev/null
+++ b/cmds/installd/view_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIEW_COMPILER_H_
+#define VIEW_COMPILER_H_
+
+namespace android {
+namespace installd {
+
+bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
+                   int uid);
+
+} // namespace installd
+} // namespace android
+
+#endif // VIEW_COMPILER_H_
\ No newline at end of file
diff --git a/data/etc/android.hardware.telephony.ims.xml b/data/etc/android.hardware.telephony.ims.xml
new file mode 100644
index 0000000..eeb7b00
--- /dev/null
+++ b/data/etc/android.hardware.telephony.ims.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- Feature for devices that support IMS via ImsService APIs. -->
+<permissions>
+    <feature name="android.hardware.telephony.ims" />
+</permissions>
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
new file mode 100644
index 0000000..45eab00
--- /dev/null
+++ b/libs/incidentcompanion/Android.bp
@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+filegroup {
+    name: "incidentcompanion_aidl",
+    srcs: [
+        "binder/android/os/IIncidentAuthListener.aidl",
+        "binder/android/os/IIncidentCompanion.aidl",
+    ],
+    path: "binder",
+}
+
+cc_library_static {
+    name: "libincidentcompanion",
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    aidl: {
+        local_include_dirs: ["binder"],
+        export_aidl_headers: true,
+    },
+    srcs: [
+        ":incidentcompanion_aidl",
+    ],
+    export_include_dirs: ["binder"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+}
+
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
new file mode 100644
index 0000000..5484be8
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Callback for IIncidentCompanion.
+ *
+ * @hide
+ */
+oneway interface IIncidentAuthListener {
+    /**
+     * The user approved the incident or bug report to be sent.
+     */
+    void onReportApproved();
+
+    /**
+     * The user did not approve the incident or bug report to be sent.
+     */
+    void onReportDenied();
+}
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
new file mode 100644
index 0000000..6bf98d2
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IIncidentAuthListener;
+
+/**
+ * Helper service for incidentd and dumpstated to provide user feedback
+ * and authorization for bug and inicdent reports to be taken.
+ *
+ * @hide
+ */
+interface IIncidentCompanion {
+    /**
+     * Request an authorization for an incident or bug report.
+     * // TODO(b/111441001): Add the permission
+     * <p>
+     * This function requires the ___ permission.
+     *
+     * @param callingUid The original application that requested the report.  This function
+     *      returns via the callback whether the application should be trusted.  It is up
+     *      to the caller to actually implement the restriction to take or not take
+     *      the incident or bug report.
+     * @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog.  Otherwise
+     *      a dialog will be shown as a notification.
+     * @param callback Interface to receive results.  The results may not come back for
+     *      a long (user's choice) time, or ever (if they never respond to the notification).
+     *      Authorization requests are not persisted across reboot.  It is up to the calling
+     *      service to request another authorization after reboot if they still would like
+     *      to send their report.
+     */
+    oneway void authorizeReport(int callingUid, String callingPackage,
+            int flags, IIncidentAuthListener callback);
+
+    /**
+     * Cancel an authorization.
+     */
+    oneway void cancelAuthorization(IIncidentAuthListener callback);
+
+    /**
+     * Return the list of pending approvals.
+     */
+    List<String> getPendingReports();
+
+    /**
+     * The user has authorized the report to be shared.
+     *
+     * @param uri the report.
+     */
+    void approveReport(String uri);
+
+    /**
+     * The user has denied the report from being shared.
+     *
+     * @param uri the report.
+     */
+    void denyReport(String uri);
+}