Otapreopt: Adapt for actual A/B

Add postinstall script for update_engine hook.

Add otapreopt_chroot as a gateway between installd and otapreopt.
Installd will fork and run otapreopt_chroot, which has the permission
to set up a chroot in /postinstall and run otapreopt from the B
partition.

Bug: 25612095
Change-Id: I4264598da00053ced87c849c738ddc0bc5437304
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 65bcf39..d35099a 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -80,6 +80,36 @@
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
+# OTA chroot tool
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := otapreopt_chroot
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_cflags)
+
+LOCAL_SRC_FILES := otapreopt_chroot.cpp
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblog \
+
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+include $(BUILD_EXECUTABLE)
+
+# OTA postinstall script
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= otapreopt_script
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := otapreopt_script.sh
+
+# Let this depend on otapreopt and the chroot tool, so we just have to mention one in a
+# configuration.
+LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot
+
+include $(BUILD_PREBUILT)
+
 # Tests.
 
 include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 38290cc..2bf27a2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -216,29 +216,40 @@
     return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
 }
 
-static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-  // Time to fork and run otapreopt.
-  pid_t pid = fork();
-  if (pid == 0) {
-    const char* argv[1 + 9 + 1];
-    argv[0] = "/system/bin/otapreopt";
-    for (size_t i = 1; i <= 9; ++i) {
-      argv[i] = arg[i - 1];
-    }
-    argv[10] = nullptr;
+// We use otapreopt_chroot to get into the chroot.
+static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
 
-    execv(argv[0], (char * const *)argv);
-    ALOGE("execv(OTAPREOPT) failed: %s\n", strerror(errno));
-    exit(99);
-  } else {
-    int res = wait_child(pid);
-    if (res == 0) {
-      ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
-    } else {
-      ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+    // Time to fork and run otapreopt.
+
+    // Check that the tool exists.
+    struct stat s;
+    if (stat(kOtaPreopt, &s) != 0) {
+        LOG(ERROR) << "Otapreopt chroot tool not found.";
+        return -1;
     }
-    return res;
-  }
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        const char* argv[1 + 9 + 1];
+        argv[0] = kOtaPreopt;
+        for (size_t i = 1; i <= 9; ++i) {
+            argv[i] = arg[i - 1];
+        }
+        argv[10] = nullptr;
+
+        execv(argv[0], (char * const *)argv);
+        PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
+        exit(99);
+    } else {
+        int res = wait_child(pid);
+        if (res == 0) {
+            ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
+        } else {
+            ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+        }
+        return res;
+    }
 }
 
 static int do_dexopt(char **arg, char reply[REPLY_MAX])
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
new file mode 100644
index 0000000..f7f69a9
--- /dev/null
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -0,0 +1,99 @@
+/*
+ ** 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.
+ */
+
+#include <linux/unistd.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "otapreopt"
+#endif
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+static int otapreopt_chroot(const int argc, char **arg) {
+    // We need to run the otapreopt tool from the postinstall partition. As such, set up a
+    // mount namespace and change root.
+
+    // Create our own mount namespace.
+    if (unshare(CLONE_NEWNS) != 0) {
+        PLOG(ERROR) << "Failed to unshare() for otapreopt.";
+        exit(200);
+    }
+
+    // Make postinstall private, so that our changes don't propagate.
+    if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) {
+        PLOG(ERROR) << "Failed to mount private.";
+        exit(201);
+    }
+
+    // Bind mount necessary directories.
+    constexpr const char* kBindMounts[] = {
+            "/data", "/dev", "/proc", "/sys"
+    };
+    for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
+        std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
+        if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) {
+            PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i];
+            exit(202);
+        }
+    }
+
+    // Chdir into /postinstall.
+    if (chdir("/postinstall") != 0) {
+        PLOG(ERROR) << "Unable to chdir into /postinstall.";
+        exit(203);
+    }
+
+    // Make /postinstall the root in our mount namespace.
+    if (chroot(".")  != 0) {
+        PLOG(ERROR) << "Failed to chroot";
+        exit(204);
+    }
+
+    if (chdir("/") != 0) {
+        PLOG(ERROR) << "Unable to chdir into /.";
+        exit(205);
+    }
+
+    // Now go on and run otapreopt.
+
+    const char* argv[1 + 9 + 1];
+    CHECK_EQ(argc, 10);
+    argv[0] = "/system/bin/otapreopt";
+    for (size_t i = 1; i <= 9; ++i) {
+        argv[i] = arg[i];
+    }
+    argv[10] = nullptr;
+
+    execv(argv[0], (char * const *)argv);
+    PLOG(ERROR) << "execv(OTAPREOPT) failed.";
+    exit(99);
+}
+
+}  // namespace installd
+}  // namespace android
+
+int main(const int argc, char *argv[]) {
+    return android::installd::otapreopt_chroot(argc, argv);
+}
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
new file mode 100644
index 0000000..a31734a
--- /dev/null
+++ b/cmds/installd/otapreopt_script.sh
@@ -0,0 +1,50 @@
+#!/system/bin/sh
+
+#
+# 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.
+#
+
+# This script will run as a postinstall step to drive otapreopt.
+
+# Maximum number of packages/steps.
+MAXIMUM_PACKAGES=1000
+
+PREPARE=$(cmd otadexopt prepare)
+if [ "$PREPARE" != "Success" ] ; then
+  echo "Failed to prepare."
+  exit 1
+fi
+
+i=0
+while ((i<MAXIMUM_PACKAGES)) ; do
+  cmd otadexopt step
+  DONE=$(cmd otadexopt done)
+  if [ "$DONE" = "OTA complete." ] ; then
+    break
+  fi
+  sleep 1
+  i=$((i+1))
+done
+
+DONE=$(cmd otadexopt done)
+if [ "$DONE" = "OTA incomplete." ] ; then
+  echo "Incomplete."
+else
+  echo "Complete or error."
+fi
+
+cmd otadexopt cleanup
+
+exit 0