Merge "init: Shutdown services and umount partitions" into main
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3f8a415..1c52da2 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -588,12 +588,6 @@
             "                            Delete a logical partition with the given name.\n"
             " resize-logical-partition NAME SIZE\n"
             "                            Change the size of the named logical partition.\n"
-            " update-super NAME\n"
-            "                            Merges changes to the super partition metadata.\n"
-            "                            If a merge isn't possible (for example, the format\n"
-            "                            on the device is an unsupported version), then this\n"
-            "                            command fails. An optional wipe parameter overwrites\n"
-            "                            the device's metadata, rather than performing a merge.\n"
             " snapshot-update cancel     On devices that support snapshot-based updates, cancel\n"
             "                            an in-progress update. This may make the device\n"
             "                            unbootable until it is reflashed.\n"
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 991e17c..66f9a83 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -329,6 +329,16 @@
     uint8_t read_ahead_state;
 } __attribute__((packed));
 
+constexpr size_t GetCowOpSize(size_t version) {
+    if (version == 3) {
+        return sizeof(CowOperationV3);
+    } else if (version == 2 || version == 1) {
+        return sizeof(CowOperationV2);
+    } else {
+        return 0;
+    }
+}
+
 // 2MB Scratch space used for read-ahead
 static constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);
 
diff --git a/init/README.md b/init/README.md
index 251fe98..89e20a4 100644
--- a/init/README.md
+++ b/init/README.md
@@ -454,44 +454,66 @@
 
 Triggers
 --------
-Triggers are strings which can be used to match certain kinds of
-events and used to cause an action to occur.
+Triggers of an action specifies one or more conditions when satisfied
+execute the commands in the action. A trigger encodes a single atomic
+condition, and multiple triggers can be combined using the `&&`
+operator to form a bigger AND condition.
 
-Triggers are subdivided into event triggers and property triggers.
-
-Event triggers are strings triggered by the 'trigger' command or by
-the QueueEventTrigger() function within the init executable.  These
-take the form of a simple string such as 'boot' or 'late-init'.
-
-Property triggers are strings triggered when a named property changes
-value to a given new value or when a named property changes value to
-any new value.  These take the form of 'property:<name>=<value>' and
-'property:<name>=\*' respectively.  Property triggers are additionally
-evaluated and triggered accordingly during the initial boot phase of
-init.
-
-An Action can have multiple property triggers but may only have one
+There are two types of triggers: event triggers and action triggers.
+An action can have multiple property triggers but may have only one
 event trigger.
 
-For example:
-`on boot && property:a=b` defines an action that is only executed when
-the 'boot' event trigger happens and the property a equals b at the moment. This
-will NOT be executed when the property a transitions to value b after the `boot`
-event was triggered.
+An event trigger takes the simple form of `<event>` where `<event>` is
+the name of a boot stage like `early-init` or `boot`. This trigger
+is satisfied when init reaches the stage via the `trigger` command or
+by the `QueueEventTrigger()` function in the init executable.
 
-`on property:a=b && property:c=d` defines an action that is executed
-at three times:
+A property trigger takes the form of `property:<name>=<value>`. This
+trigger is satisfied when the property of name `<name>` is found to
+have the value of `<value>` when the check is made. The `<value>` part
+can be `\*` to match with any value.
 
-   1. During initial boot if property a=b and property c=d.
-   2. Any time that property a transitions to value b, while property c already equals d.
-   3. Any time that property c transitions to value d, while property a already equals b.
+The check for property trigger is made in the following cases:
 
-Note that, for bootloader-provided properties (ro.boot.*), their action cannot be
-auto-triggered until `boot` stage. If they need to be triggered earlier, like at `early-boot`
-stage, they should be tied to the `event`. For example:
+* All property triggers get checked at least once when the `boot`
+  event is finished (i.e. when the last command under `on boot ...` is
+finished).
 
-`on early-boot && property:a=b`.
+* After the one-time check, `property:a=b` is checked when property `a`
+  is newly created, or when the property is set to a new value.
 
+* Property triggers are also checked when other triggers in the same
+  action are checked. For example, `property:a=b && property:c=d` is
+checked not only when property `a` gets a new value, but also when
+property `c` gets a new value (and of course when the one-time check
+is made).
+
+* Before the one-time check, `property:a=b` without an event trigger
+  is NOT checked, even if property `a` gets a new value. Care must be
+taken since this is a non-intuitive behavior, which unfortunately
+can't be changed due to compatibility concerns.
+
+Some examples:
+
+`on property:a=b` is executed in two cases:
+
+1. during the one-time check if property `a` is `b` at the moment.
+2. if property `a` is set to or changed to `b` after the one-time
+   check, but not before then.
+
+`on property:a=b && property:c=d` is executed in three cases:
+
+1. during the one-time check if property `a` is `b` and property `c`
+   is `d` at the moment.
+2. (after the one-time check) property `a` becomes `b` while property
+   `c` already equals to `d`.
+3. (after the one-time check) property `c` becomes `d` while property
+   `a` already equals to `b`.
+
+`on property:a=b && post-fs` is executed in one case only:
+
+1. `post-fs` is triggered while property `a` already equals to `b`.
+   This is NOT executed when property `a` becomes `b` AFTER `post-fs`.
 
 Trigger Sequence
 ----------------
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 8bdf5b6..2a27c1d 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -749,7 +749,7 @@
         PLOG(FATAL) << "Failed to unlink " << or_dest;
     }
     const char* args[] = {or_dest.c_str(), nullptr};
-    fexecve(dest.get(), const_cast<char**>(args), nullptr);
+    fexecve(dest.get(), const_cast<char**>(args), environ);
 
     // execv() only returns if an error happened, in which case we
     // panic and never return from this function.
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 1915f22..269f300 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -35,6 +35,8 @@
 namespace {
 
 constexpr useconds_t kRetrySleepForWriting = 1000;  // 1 ms
+// This makes the total wait time to allocate a buffer 5 seconds
+const int kNumberOfRetriesForWriting = 5000;
 
 template <typename T>
 bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
@@ -92,6 +94,7 @@
 
     const char* const buf = reinterpret_cast<const char*>(self);
     const auto& header = static_cast<const T*>(self)->header;
+    int retry = kNumberOfRetriesForWriting;
 
     while (true) {
         int result;
@@ -110,8 +113,14 @@
                 case ENOBUFS:
                     // When returning ENOBUFS, epoll still reports the FD is writable. Just usleep
                     // and retry again.
-                    usleep(kRetrySleepForWriting);
-                    continue;
+                    if (retry > 0) {
+                        usleep(kRetrySleepForWriting);
+                        retry--;
+                        continue;
+                    } else {
+                        LOG(ERROR) << "Failed to write a FUSE message: ENOBUFS retries are failed";
+                        return ResultOrAgain::kFailure;
+                    }
                 case EAGAIN:
                     return ResultOrAgain::kAgain;
                 default:
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
index 3244fe8..a3e4c70 100644
--- a/libsysutils/src/OWNERS
+++ b/libsysutils/src/OWNERS
@@ -1,2 +1 @@
-per-file OWNERS,Netlink* = jchalard@google.com, lorenzo@google.com, maze@google.com, satk@google.com
-
+per-file OWNERS,Netlink* = file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/llkd/OWNERS b/llkd/OWNERS
index b6af537..b15bb48 100644
--- a/llkd/OWNERS
+++ b/llkd/OWNERS
@@ -1,2 +1 @@
-salyzyn@google.com
 surenb@google.com
diff --git a/storaged/OWNERS b/storaged/OWNERS
index d033f00..9e70e7d 100644
--- a/storaged/OWNERS
+++ b/storaged/OWNERS
@@ -1,2 +1 @@
-salyzyn@google.com
 dvander@google.com
diff --git a/trusty/fuzz/tipc_fuzzer.cpp b/trusty/fuzz/tipc_fuzzer.cpp
index d5e23e0..f9f6c8c 100644
--- a/trusty/fuzz/tipc_fuzzer.cpp
+++ b/trusty/fuzz/tipc_fuzzer.cpp
@@ -97,48 +97,47 @@
         static_assert(MAX_CONNECTIONS >= 1);
 
         // Either
-        // 1. Add a new TA and connect.
-        // 2. Remove a TA.
-        // 3. Send a random message to a random TA.
+        // 1. (20%) Add a new TA and connect.
+        // 2. (20%) Remove a TA.
+        // 3. (60%) Send a random message to a random TA.
+        auto add_ta = [&]() {
+            if (trustyApps.size() >= MAX_CONNECTIONS) {
+                return;
+            }
+            auto& ta = trustyApps.emplace_back(TIPC_DEV, TRUSTY_APP_PORT);
+            abortResult(ta.Connect());
+        };
+        auto remove_ta = [&]() {
+            if (trustyApps.empty()) {
+                return;
+            }
+            trustyApps.pop_back();
+        };
+        auto send_message = [&]() {
+            if (trustyApps.empty()) {
+                return;
+            }
+
+            // Choose a random TA.
+            const auto i = provider.ConsumeIntegralInRange<size_t>(0, trustyApps.size() - 1);
+            std::swap(trustyApps[i], trustyApps.back());
+            auto& ta = trustyApps.back();
+
+            // Send a random message.
+            const auto data = provider.ConsumeRandomLengthString();
+            abortResult(ta.Write(data.data(), data.size()));
+
+            std::array<uint8_t, TIPC_MAX_MSG_SIZE> buf;
+            abortResult(ta.Read(buf.data(), buf.size()));
+
+            // Reconnect to ensure that the service is still up.
+            ta.Disconnect();
+            abortResult(ta.Connect());
+        };
         const std::function<void()> options[] = {
-                // Add a new TA and connect.
-                [&]() {
-                    if (trustyApps.size() >= MAX_CONNECTIONS) {
-                        return;
-                    }
-                    auto& ta = trustyApps.emplace_back(TIPC_DEV, TRUSTY_APP_PORT);
-                    abortResult(ta.Connect());
-                },
-                // Remove a TA.
-                [&]() {
-                    if (trustyApps.empty()) {
-                        return;
-                    }
-                    trustyApps.pop_back();
-                },
-                // Send a random message to a random TA.
-                [&]() {
-                    if (trustyApps.empty()) {
-                        return;
-                    }
-
-                    // Choose a random TA.
-                    const auto i =
-                            provider.ConsumeIntegralInRange<size_t>(0, trustyApps.size() - 1);
-                    std::swap(trustyApps[i], trustyApps.back());
-                    auto& ta = trustyApps.back();
-
-                    // Send a random message.
-                    const auto data = provider.ConsumeRandomLengthString();
-                    abortResult(ta.Write(data.data(), data.size()));
-
-                    std::array<uint8_t, TIPC_MAX_MSG_SIZE> buf;
-                    abortResult(ta.Read(buf.data(), buf.size()));
-
-                    // Reconnect to ensure that the service is still up.
-                    ta.Disconnect();
-                    abortResult(ta.Connect());
-                },
+                add_ta,                                    // 1x: 20%
+                remove_ta,                                 // 1x: 20%
+                send_message, send_message, send_message,  // 3x: 60%
         };
 
         provider.PickValueInArray(options)();
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
index 04e84c6..4a19692 100644
--- a/trusty/libtrusty/include/trusty/ipc.h
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -23,19 +23,21 @@
 
 /**
  * enum transfer_kind - How to send an fd to Trusty
- * @TRUSTY_SHARE:       Memory will be accessible by Linux and Trusty. On ARM it
- *                      will be mapped as nonsecure. Suitable for shared memory.
- *                      The paired fd must be a "dma_buf".
- * @TRUSTY_LEND:        Memory will be accessible only to Trusty. On ARM it will
- *                      be transitioned to "Secure" memory if Trusty is in
- *                      TrustZone. This transfer kind is suitable for donating
- *                      video buffers or other similar resources. The paired fd
- *                      may need to come from a platform-specific allocator for
- *                      memory that may be transitioned to "Secure".
- * @TRUSTY_SEND_SECURE: Send memory that is already "Secure". Memory will be
- *                      accessible only to Trusty. The paired fd may need to
- *                      come from a platform-specific allocator that returns
- *                      "Secure" buffers.
+ * @TRUSTY_SHARE:                Memory will be accessible by Linux and Trusty. On ARM it
+ *                               will be mapped as nonsecure. Suitable for shared memory.
+ *                               The paired fd must be a "dma_buf".
+ * @TRUSTY_LEND:                 Memory will be accessible only to Trusty. On ARM it will
+ *                               be transitioned to "Secure" memory if Trusty is in
+ *                               TrustZone. This transfer kind is suitable for donating
+ *                               video buffers or other similar resources. The paired fd
+ *                               may need to come from a platform-specific allocator for
+ *                               memory that may be transitioned to "Secure".
+ * @TRUSTY_SEND_SECURE:          Send memory that is already "Secure". Memory will be
+ *                               accessible only to Trusty. The paired fd may need to
+ *                               come from a platform-specific allocator that returns
+ *                               "Secure" buffers.
+ * @TRUSTY_SEND_SECURE_OR_SHARE: Acts as TRUSTY_SEND_SECURE if the memory is already
+ *                               "Secure" and as TRUSTY_SHARE otherwise.
  *
  * Describes how the user would like the resource in question to be sent to
  * Trusty. Options may be valid only for certain kinds of fds.
@@ -44,6 +46,7 @@
     TRUSTY_SHARE = 0,
     TRUSTY_LEND = 1,
     TRUSTY_SEND_SECURE = 2,
+    TRUSTY_SEND_SECURE_OR_SHARE = 3,
 };
 
 /**
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 121837d..9910aee 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -55,6 +55,8 @@
 "}"
 /* clang-format on */
 
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
 static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
 static const char *echo_name = "com.android.ipc-unittest.srv.echo";
 static const char *ta_only_name = "com.android.ipc-unittest.srv.ta_only";
@@ -904,12 +906,14 @@
 
 static int send_fd_test(const struct tipc_test_params* params) {
     int ret;
-    int dma_buf = -1;
+    int dma_buf[] = {-1, -1, -1};
     int fd = -1;
-    volatile char* buf = MAP_FAILED;
+    volatile char* buf[countof(dma_buf)] = {MAP_FAILED, MAP_FAILED, MAP_FAILED};
     BufferAllocator* allocator = NULL;
+    uint i;
 
     const size_t num_chunks = 10;
+    const size_t buf_size = memref_chunk_size * num_chunks;
 
     fd = tipc_connect(params->dev_name, receiver_name);
     if (fd < 0) {
@@ -925,56 +929,86 @@
         goto cleanup;
     }
 
-    size_t buf_size = memref_chunk_size * num_chunks;
-    dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
-    if (dma_buf < 0) {
-        ret = dma_buf;
-        fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
-        goto cleanup;
+    for (i = 0; i < countof(dma_buf); i++) {
+        ret = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
+        if (ret < 0) {
+            fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
+            goto cleanup;
+        }
+        dma_buf[i] = ret;
     }
 
-    buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
-    if (buf == MAP_FAILED) {
-        fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
-        ret = -1;
-        goto cleanup;
+    for (i = 0; i < countof(dma_buf); i++) {
+        buf[i] = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf[i], 0);
+        if (buf[i] == MAP_FAILED) {
+            fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
+            ret = -1;
+            goto cleanup;
+        }
+
+        strcpy((char*)buf[i], "From NS");
     }
 
-    strcpy((char*)buf, "From NS");
-
-    struct trusty_shm shm = {
-            .fd = dma_buf,
-            .transfer = TRUSTY_SHARE,
+    struct trusty_shm shm[] = {
+            {
+                    .fd = dma_buf[0],
+                    .transfer = TRUSTY_SHARE,
+            },
+            {
+                    .fd = dma_buf[0],
+                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
+            },
+            {
+                    .fd = dma_buf[1],
+                    .transfer = TRUSTY_LEND,
+            },
+            {
+                    .fd = dma_buf[1],
+                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
+            },
+            {
+                    .fd = dma_buf[2],
+                    .transfer = TRUSTY_SEND_SECURE_OR_SHARE,
+            },
     };
 
-    ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
-    if (rc < 0) {
-        fprintf(stderr, "tipc_send failed: %zd\n", rc);
-        ret = rc;
-        goto cleanup;
+    for (i = 0; i < countof(shm); i++) {
+        ssize_t rc = tipc_send(fd, NULL, 0, &shm[i], 1);
+        if (rc < 0) {
+            fprintf(stderr, "tipc_send failed: %zd\n", rc);
+            ret = rc;
+            goto cleanup;
+        }
+        char c;
+        read(fd, &c, 1);
     }
-    char c;
-    read(fd, &c, 1);
-    tipc_close(fd);
 
     ret = 0;
-    for (size_t skip = 0; skip < num_chunks; skip++) {
-        int cmp = strcmp("Hello from Trusty!",
-                         (const char*)&buf[skip * memref_chunk_size]) ? (-1) : 0;
-        if (cmp)
-            fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
-        ret |= cmp;
+    for (i = 0; i < countof(buf); i++) {
+        for (size_t skip = 0; skip < num_chunks; skip++) {
+            int cmp = strcmp("Hello from Trusty!", (const char*)&buf[i][skip * memref_chunk_size])
+                              ? (-1)
+                              : 0;
+            if (cmp) fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
+            ret |= cmp;
+        }
     }
 
 cleanup:
-    if (buf != MAP_FAILED) {
-        munmap((char*)buf, buf_size);
+    for (i = 0; i < countof(dma_buf); i++) {
+        if (buf[i] != MAP_FAILED) {
+            munmap((char*)buf[i], buf_size);
+        }
+        if (dma_buf[i] >= 0) {
+            close(dma_buf[i]);
+        }
     }
-    close(dma_buf);
     if (allocator) {
         FreeDmabufHeapBufferAllocator(allocator);
     }
-    tipc_close(fd);
+    if (fd >= 0) {
+        tipc_close(fd);
+    }
     return ret;
 }
 
diff --git a/trusty/trusty-storage.mk b/trusty/trusty-storage.mk
index 5bfbf1e..d2bc0b1 100644
--- a/trusty/trusty-storage.mk
+++ b/trusty/trusty-storage.mk
@@ -38,6 +38,6 @@
 	storageproxyd.system \
 	rpmb_dev.test.system \
 	rpmb_dev.system \
-	# rpmb_dev.wv.system \
+	rpmb_dev.wv.system \
 
 endif
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc b/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc
index 4e42d46..ac18f81 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc
+++ b/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc
@@ -4,7 +4,6 @@
         -p /data/secure_storage_wv_system \
         -t sock
     disabled
-    class hal
     user system
     group system
 
@@ -47,3 +46,9 @@
     chown root system /data/secure_storage_wv_system/persist
     exec_start rpmb_mock_init_wv_system
     start rpmb_mock_wv_system
+
+on post-fs-data && \
+    property:trusty.widevine_vm.nonsecure_vm_ready=1 && \
+    property:trusty.widevine_vm.vm_cid=*
+    start storageproxyd_wv_system
+