Merge changes from topic "add-file-size"

* changes:
  microdroid: remove redundant mk_microdroid_signature
  microdroid: add "apk" to payload
  microdroid: each partition in payload has size in it
diff --git a/apex/Android.bp b/apex/Android.bp
index 91df00c..459545c 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -49,7 +49,6 @@
 
         // tools to create composite images
         "mk_cdisk",
-        "mk_microdroid_signature",
         "mk_payload",
     ],
     prebuilts: [
diff --git a/microdroid/signature/Android.bp b/microdroid/signature/Android.bp
index b993e4e..35c4e9e 100644
--- a/microdroid/signature/Android.bp
+++ b/microdroid/signature/Android.bp
@@ -39,25 +39,6 @@
 }
 
 cc_binary {
-    name: "mk_microdroid_signature",
-    srcs: [
-        "mk_microdroid_signature.cc",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    static_libs: [
-        "lib_microdroid_signature_proto_lite",
-        "libjsoncpp",
-        "libprotobuf-cpp-lite",
-    ],
-    apex_available: [
-        "com.android.virt",
-    ],
-}
-
-cc_binary {
     name: "mk_payload",
     srcs: [
         "mk_payload.cc",
diff --git a/microdroid/signature/README.md b/microdroid/signature/README.md
index 0a39c31..b47c303 100644
--- a/microdroid/signature/README.md
+++ b/microdroid/signature/README.md
@@ -1,12 +1,28 @@
-# Microdroid Signature
+# Microdroid Payload
+
+Payload disk is a composite disk referencing host APEXes and an APK so that microdroid
+reads activates APEXes and executes a binary within the APK.
+
+## Format
+
+Payload disk has 1 + N(number of APEX/APK payloads) partitions.
+
+The first partition is a Microdroid Signature partition which describes other partitions.
+And APEXes and an APK are following as separate partitions.
+
+For now, the order of partitions are important.
+
+* partition 1: Microdroid Signature
+* partition 2 ~ n: APEX payloads
+* partition n + 1: APK payload
+
+It's subject to change in the future, though.
+
+### Microdroid Signature
 
 Microdroid Signature contains the signatures of the payloads so that the payloads are
 verified inside the Guest OS.
 
-* APEX packages that are passed to microdroid should be listed in the Microroid Signature.
-
-## Format
-
 Microdroid Signature is composed of header and body.
 
 | offset | size | description                                                    |
@@ -14,52 +30,25 @@
 | 0      | 4    | Header. unsigned int32: body length(L) in big endian           |
 | 4      | L    | Body. A protobuf message. [schema](microdroid_signature.proto) |
 
+### Payload Partitions
+
+At the end of each payload partition the size of the original payload file (APEX or APK) is stored
+in 4-byte big endian.
+
+For example, the following code shows how to get the original size of host apex file
+when the apex is read in microdroid as /dev/block/vdc2,
+
+    int fd = open("/dev/block/vdc2", O_RDONLY | O_BINARY | O_CLOEXEC);
+    uint32_t size;
+    lseek(fd, -sizeof(size), SEEK_END);
+    read(fd, &size, sizeof(size));
+    size = betoh32(size);
+
 ## How to Create
 
-### `mk_microdroid_signature` and `mk_cdisk`
-
-For testing purpose, use `mk_microdroid_signature` to create a Microdroid Signature.
-
-```
-$ cat signature_config.json
-{
-  "apexes": [
-    {
-      "name": "com.my.hello",
-      "path": "hello.apex"
-    }
-  ]
-}
-$ adb push signature_config.json hello.apex /data/local/tmp/
-$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/mk_microdroid_signature signature_config.json signature
-```
-
-Then, pass the signature as the first partition of the payload disk image.
-
-```
-$ cat payload_cdisk.json
-{
-  "partitions": [
-    {
-      "label": "signature",
-      "path": "signature"
-    },
-    {
-      "label": "com.my.hello",
-      "path": "hello.apex"
-    }
-  ]
-}
-$ adb push payload_cdisk.json /data/local/tmp/
-$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/mk_cdisk payload_cdisk.json payload.img
-$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/crosvm .... --disk=payload.img'
-```
-
 ### `mk_payload`
 
-`mk_payload` combines these two steps into a single step. Additionally, `mk_payload` can search system APEXes as well.
-This will generate the output composite image as well as three more component images. (See below)
-
+`mk_payload` creates a payload image.
 ```
 $ cat payload_config.json
 {
@@ -71,15 +60,22 @@
       "name": "com.my.hello",
       "path": "hello.apex"
     }
-  ]
+  ],
+  "apk": {
+    "name": "com.my.world",
+    "path": "/path/to/world.apk"
+  }
 }
 $ adb push payload_config.json hello.apex /data/local/tmp/
 $ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/mk_payload payload_config.json payload.img
 $ adb shell ls /data/local/tmp/*.img
+payload.img
 payload-footer.img
 payload-header.img
 payload-signature.img
-payload.img
+payload.img.0          # fillers
+payload.img.1
+...
 ```
 
-In the future, [VirtManager](../../virtmanager) will handle these stuffs.
\ No newline at end of file
+In the future, [VirtManager](../../virtmanager) will handle this.
\ No newline at end of file
diff --git a/microdroid/signature/microdroid_signature.proto b/microdroid/signature/microdroid_signature.proto
index 8335ff5..8816aa8 100644
--- a/microdroid/signature/microdroid_signature.proto
+++ b/microdroid/signature/microdroid_signature.proto
@@ -25,6 +25,8 @@
   // Lists the signature information of the payload apexes.
   // The payload apexes are mapped to the partitions following the signature partition.
   repeated ApexSignature apexes = 2;
+
+  ApkSignature apk = 3;
 }
 
 message ApexSignature {
@@ -44,3 +46,12 @@
   // When specified, the root digest of the apex should match with it.
   string rootDigest = 4;
 }
+
+message ApkSignature {
+  // Required.
+  // The name of APK.
+  string name = 1;
+
+  string payload_partition_name = 2;
+  string idsig_partition_name = 3;
+}
\ No newline at end of file
diff --git a/microdroid/signature/mk_microdroid_signature.cc b/microdroid/signature/mk_microdroid_signature.cc
deleted file mode 100644
index d5cb1a5..0000000
--- a/microdroid/signature/mk_microdroid_signature.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2021 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 <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <fstream>
-#include <iostream>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/result.h>
-#include <json/json.h>
-
-#include "microdroid/signature.h"
-
-using android::base::Dirname;
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::microdroid::MicrodroidSignature;
-using android::microdroid::WriteMicrodroidSignature;
-
-Result<uint32_t> GetFileSize(const std::string& path) {
-    struct stat st;
-    if (lstat(path.c_str(), &st) == -1) {
-        return ErrnoError() << "Can't lstat " << path;
-    }
-    return static_cast<uint32_t>(st.st_size);
-}
-
-// config JSON schema:
-// {
-//   "apexes": [
-//     {
-//       "name": string,       // the apex name
-//       "path": string,       // the path to the apex file
-//                             // absolute or relative to the config file
-//       "publicKey": string,  // optional
-//       "rootDigest": string, // optional
-//     }
-//   ]
-// }
-
-Result<MicrodroidSignature> LoadConfig(const std::string& config_file) {
-    MicrodroidSignature signature;
-    signature.set_version(1);
-
-    const std::string dirname = Dirname(config_file);
-    std::ifstream in(config_file);
-    Json::CharReaderBuilder builder;
-    Json::Value root;
-    Json::String errs;
-    if (!parseFromStream(builder, in, &root, &errs)) {
-        return Error() << "bad config: " << errs;
-    }
-
-    for (const Json::Value& apex : root["apexes"]) {
-        auto apex_signature = signature.add_apexes();
-
-        Json::Value name = apex["name"];
-        Json::Value path = apex["path"];
-        Json::Value publicKey = apex["publicKey"];
-        Json::Value rootDigest = apex["rootDigest"];
-
-        if (name.isString()) {
-            apex_signature->set_name(name.asString());
-        } else {
-            return Error() << "bad config: apexes.name should be a string: " << path;
-        }
-
-        if (path.isString()) {
-            std::string apex_path = path.asString();
-
-            // resolve path with the config_file's dirname if not absolute
-            bool is_absolute = !apex_path.empty() && apex_path[0] == '/';
-            if (!is_absolute) {
-                apex_path = dirname + "/" + apex_path;
-            }
-
-            auto file_size = GetFileSize(apex_path);
-            if (!file_size.ok()) {
-                return Error() << "I/O error: " << file_size.error();
-            }
-            apex_signature->set_size(file_size.value());
-        } else {
-            return Error() << "bad config: apexes.path should be a string: " << path;
-        }
-
-        if (publicKey.isString()) {
-            apex_signature->set_publickey(publicKey.asString());
-        } else if (!publicKey.isNull()) {
-            return Error() << "bad config: apexes.publicKey should be a string or null: "
-                           << publicKey;
-        }
-
-        if (rootDigest.isString()) {
-            apex_signature->set_rootdigest(rootDigest.asString());
-        } else if (!rootDigest.isNull()) {
-            return Error() << "bad config: apexes.rootDigest should be a string or null: "
-                           << rootDigest;
-        }
-    }
-
-    return signature;
-}
-
-int main(int argc, char** argv) {
-    if (argc != 3) {
-        std::cerr << "Usage: " << argv[0] << " <config> <output>\n";
-        return 1;
-    }
-
-    auto config = LoadConfig(argv[1]);
-    if (!config.ok()) {
-        std::cerr << config.error() << '\n';
-        return 1;
-    }
-
-    std::ofstream out(argv[2]);
-    auto result = WriteMicrodroidSignature(*config, out);
-    if (!result.ok()) {
-        std::cerr << result.error() << '\n';
-        return 1;
-    }
-    return 0;
-}
\ No newline at end of file
diff --git a/microdroid/signature/mk_payload.cc b/microdroid/signature/mk_payload.cc
index 8046b5e..a3501d4 100644
--- a/microdroid/signature/mk_payload.cc
+++ b/microdroid/signature/mk_payload.cc
@@ -36,16 +36,19 @@
 using android::base::ErrnoError;
 using android::base::Error;
 using android::base::Result;
+using android::base::unique_fd;
 using android::microdroid::ApexSignature;
+using android::microdroid::ApkSignature;
 using android::microdroid::MicrodroidSignature;
 using android::microdroid::WriteMicrodroidSignature;
 
 using com::android::apex::ApexInfoList;
 using com::android::apex::readApexInfoList;
 
+using cuttlefish::AlignToPartitionSize;
 using cuttlefish::CreateCompositeDisk;
-using cuttlefish::ImagePartition;
 using cuttlefish::kLinuxFilesystem;
+using cuttlefish::MultipleImagePartition;
 
 Result<uint32_t> GetFileSize(const std::string& path) {
     struct stat st;
@@ -82,11 +85,18 @@
     std::optional<std::string> root_digest;
 };
 
+struct ApkConfig {
+    std::string name;
+    // TODO(jooyung): find path/idsig with name
+    std::string path;
+};
+
 struct Config {
     std::string dirname; // config file's direname to resolve relative paths in the config
 
     std::vector<std::string> system_apexes;
     std::vector<ApexConfig> apexes;
+    std::optional<ApkConfig> apk;
 };
 
 #define DO(expr) \
@@ -100,7 +110,8 @@
     return {};
 }
 
-Result<void> ParseJson(const Json::Value& value, std::optional<std::string>& s) {
+template <typename T>
+Result<void> ParseJson(const Json::Value& value, std::optional<T>& s) {
     if (value.isNull()) {
         s.reset();
         return {};
@@ -117,6 +128,12 @@
     return {};
 }
 
+Result<void> ParseJson(const Json::Value& value, ApkConfig& apk_config) {
+    DO(ParseJson(value["name"], apk_config.name));
+    DO(ParseJson(value["path"], apk_config.path));
+    return {};
+}
+
 template <typename T>
 Result<void> ParseJson(const Json::Value& values, std::vector<T>& parsed) {
     for (const Json::Value& value : values) {
@@ -130,6 +147,7 @@
 Result<void> ParseJson(const Json::Value& value, Config& config) {
     DO(ParseJson(value["system_apexes"], config.system_apexes));
     DO(ParseJson(value["apexes"], config.apexes));
+    DO(ParseJson(value["apk"], config.apk));
     return {};
 }
 
@@ -207,31 +225,84 @@
         }
     }
 
+    if (config.apk.has_value()) {
+        ApkSignature* apk_signature = signature.mutable_apk();
+        apk_signature->set_name(config.apk->name);
+        apk_signature->set_payload_partition_name("microdroid-apk");
+        // TODO(jooyung): set idsig partition as well
+    }
+
     std::ofstream out(filename);
     return WriteMicrodroidSignature(signature, out);
 }
 
+Result<void> GenerateFiller(const std::string& file_path, const std::string& filler_path) {
+    auto file_size = GetFileSize(file_path);
+    if (!file_size.ok()) {
+        return file_size.error();
+    }
+    auto disk_size = AlignToPartitionSize(*file_size + sizeof(uint32_t));
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(filler_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0600)));
+    if (fd.get() == -1) {
+        return ErrnoError() << "open(" << filler_path << ") failed.";
+    }
+    uint32_t size = htobe32(static_cast<uint32_t>(*file_size));
+    if (ftruncate(fd.get(), disk_size - *file_size) == -1) {
+        return ErrnoError() << "ftruncate(" << filler_path << ") failed.";
+    }
+    if (lseek(fd.get(), -sizeof(size), SEEK_END) == -1) {
+        return ErrnoError() << "lseek(" << filler_path << ") failed.";
+    }
+    if (write(fd.get(), &size, sizeof(size)) <= 0) {
+        return ErrnoError() << "write(" << filler_path << ") failed.";
+    }
+    return {};
+}
+
 Result<void> MakePayload(const Config& config, const std::string& signature_file,
                          const std::string& output_file) {
-    std::vector<ImagePartition> partitions;
+    std::vector<MultipleImagePartition> partitions;
 
     // put signature at the first partition
-    partitions.push_back(ImagePartition{
+    partitions.push_back(MultipleImagePartition{
             .label = "signature",
-            .image_file_path = signature_file,
+            .image_file_paths = {signature_file},
             .type = kLinuxFilesystem,
             .read_only = true,
     });
 
-    // put apexes at the subsequent partitions
-    for (size_t i = 0; i < config.apexes.size(); i++) {
-        const auto& apex_config = config.apexes[i];
-        partitions.push_back(ImagePartition{
-                .label = "payload_apex_" + std::to_string(i),
-                .image_file_path = apex_config.path,
+    int filler_count = 0;
+    auto add_partition = [&](auto partition_name, auto file_path) -> Result<void> {
+        std::string filler_path = output_file + "." + std::to_string(filler_count++);
+        if (auto ret = GenerateFiller(file_path, filler_path); !ret.ok()) {
+            return ret.error();
+        }
+        partitions.push_back(MultipleImagePartition{
+                .label = partition_name,
+                .image_file_paths = {file_path, filler_path},
                 .type = kLinuxFilesystem,
                 .read_only = true,
         });
+        return {};
+    };
+
+    // put apexes at the subsequent partitions with "size" filler
+    for (size_t i = 0; i < config.apexes.size(); i++) {
+        const auto& apex_config = config.apexes[i];
+        std::string apex_path = ToAbsolute(apex_config.path, config.dirname);
+        if (auto ret = add_partition("microdroid-apex-" + std::to_string(i), apex_path);
+            !ret.ok()) {
+            return ret.error();
+        }
+    }
+    // put apk with "size" filler if necessary.
+    // TODO(jooyung): partition name("microdroid-apk") is TBD
+    if (config.apk.has_value()) {
+        std::string apk_path = ToAbsolute(config.apk->path, config.dirname);
+        if (auto ret = add_partition("microdroid-apk", apk_path); !ret.ok()) {
+            return ret.error();
+        }
     }
 
     const std::string gpt_header = AppendFileName(output_file, "-header");