Merge "Call sync() to guarantee blocks were released by unlink" into main
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 79c0b6d..f91d232 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -171,11 +171,12 @@
         }
         if (show_help) {
             show_help = false;
-            std::cerr << "WARNING: Userdata checkpoint is in progress. To force end checkpointing, "
-                         "call 'vdc checkpoint commitChanges'. This can lead to data corruption if "
-                         "rolled back."
+            std::cerr << "WARNING: Userdata checkpoint is in progress. "
+                         "To forcibly end checkpointing, "
+                         "call 'vdc checkpoint commitChanges'. "
+                         "This can lead to data corruption if rolled back."
                       << std::endl;
-            LOG(INFO) << "Waiting for checkpoint to complete and then continue remount.";
+            LOG(INFO) << "Waiting for checkpoint to complete before remounting...";
         }
         std::this_thread::sleep_for(4s);
     }
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 39b5b76..cc6db35 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -316,6 +316,12 @@
     test_options: {
         // Legacy VAB launched in Android R.
         min_shipping_api_level: 30,
+        test_runner_options: [
+            {
+                name: "force-no-test-error",
+                value: "false",
+            },
+        ],
     },
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 649309d..d83524a 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -168,7 +168,7 @@
     vendor_ramdisk_available: true,
 }
 
-// This target will install to /system/bin/snapuserd_ramdisk 
+// This target will install to /system/bin/snapuserd_ramdisk
 // It will also create a symblink on /system/bin/snapuserd that point to
 // /system/bin/snapuserd_ramdisk .
 // This way, init can check if generic ramdisk copy exists.
@@ -249,6 +249,14 @@
     test_suites: [
         "device-tests",
     ],
+    test_options: {
+        test_runner_options: [
+            {
+                name: "force-no-test-error",
+                value: "false",
+            },
+        ],
+    },
 }
 
 // vts tests cannot be host_supported.
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index d40be9f..33e00bc 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -84,6 +84,7 @@
     header_libs: [
         "libcutils_headers",
         "libprocessgroup_headers",
+        "libprocessgroup_util",
     ],
     export_include_dirs: ["include"],
     export_header_lib_headers: [
diff --git a/libprocessgroup/TEST_MAPPING b/libprocessgroup/TEST_MAPPING
index 29a9ff0..0f670ef 100644
--- a/libprocessgroup/TEST_MAPPING
+++ b/libprocessgroup/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit-large": [
     {
       "name": "StagedRollbackTest"
     }
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index ebc0599..52b5afe 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -28,6 +28,7 @@
 #include <android-base/strings.h>
 #include <cgroup_map.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/util.h>
 
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -216,7 +217,13 @@
         for (uint32_t i = 0; i < controller_count; ++i) {
             const ACgroupController* controller = ACgroupFile_getController(i);
             const uint32_t flags = ACgroupController_getFlags(controller);
-            if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+            uint32_t max_activation_depth = UINT32_MAX;
+            if (__builtin_available(android 36, *)) {
+                max_activation_depth = ACgroupController_getMaxActivationDepth(controller);
+            }
+            const int depth = util::GetCgroupDepth(ACgroupController_getPath(controller), path);
+
+            if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {
                 std::string str("+");
                 str.append(ACgroupController_getName(controller));
                 if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
index 5a326e5..889b3be 100644
--- a/libprocessgroup/cgrouprc/cgroup_controller.cpp
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -32,6 +32,11 @@
     return controller->flags();
 }
 
+uint32_t ACgroupController_getMaxActivationDepth(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->max_activation_depth();
+}
+
 const char* ACgroupController_getName(const ACgroupController* controller) {
     CHECK(controller != nullptr);
     return controller->name();
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index e704a36..3a57df5 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -79,6 +79,14 @@
         const ACgroupController*) __INTRODUCED_IN(30);
 
 /**
+ * Returns the maximum activation depth of the given controller.
+ * Only applicable to cgroup v2 controllers.
+ * Returns UINT32_MAX if no maximum activation depth is set.
+ */
+__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getMaxActivationDepth(
+        const ACgroupController* controller) __INTRODUCED_IN(36);
+
+/**
  * Returns the name of the given controller.
  * If the given controller is null, return nullptr.
  */
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
index b62b10f..30bd25f 100644
--- a/libprocessgroup/cgrouprc/libcgrouprc.map.txt
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -16,3 +16,10 @@
   local:
     *;
 };
+
+LIBCGROUPRC_36 { # introduced=36
+  global:
+    ACgroupController_getMaxActivationDepth; # llndk=202504 systemapi
+  local:
+    *;
+};
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
index 56e67df..0dd909a 100644
--- a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -21,13 +21,11 @@
 namespace format {
 
 CgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name,
-                                   const std::string& path)
-{
+                                   const std::string& path, uint32_t max_activation_depth)
+    : version_(version), flags_(flags), max_activation_depth_(max_activation_depth) {
     // strlcpy isn't available on host. Although there is an implementation
     // in licutils, libcutils itself depends on libcgrouprc_format, causing
     // a circular dependency.
-    version_ = version;
-    flags_ = flags;
     strncpy(name_, name.c_str(), sizeof(name_) - 1);
     name_[sizeof(name_) - 1] = '\0';
     strncpy(path_, path.c_str(), sizeof(path_) - 1);
@@ -42,6 +40,10 @@
     return flags_;
 }
 
+uint32_t CgroupController::max_activation_depth() const {
+    return max_activation_depth_;
+}
+
 const char* CgroupController::name() const {
     return name_;
 }
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
index 9427a1c..c0c1f60 100644
--- a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -29,10 +29,11 @@
   public:
     CgroupController() = default;
     CgroupController(uint32_t version, uint32_t flags, const std::string& name,
-                     const std::string& path);
+                     const std::string& path, uint32_t max_activation_depth);
 
     uint32_t version() const;
     uint32_t flags() const;
+    uint32_t max_activation_depth() const;
     const char* name() const;
     const char* path() const;
 
@@ -44,6 +45,7 @@
 
     uint32_t version_ = 0;
     uint32_t flags_ = 0;
+    uint32_t max_activation_depth_ = UINT32_MAX;
     char name_[CGROUP_NAME_BUF_SZ] = {};
     char path_[CGROUP_PATH_BUF_SZ] = {};
 };
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index f2de345..d2fd472 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,7 +24,7 @@
     Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
 }
 
-// Next: 8
+// Next: 9
 message Cgroup {
     string controller = 1 [json_name = "Controller"];
     string path = 2 [json_name = "Path"];
@@ -36,6 +36,7 @@
 // https://developers.google.com/protocol-buffers/docs/proto3#default
     bool needs_activation = 6 [json_name = "NeedsActivation"];
     bool is_optional = 7 [json_name = "Optional"];
+    uint32 max_activation_depth = 8 [json_name = "MaxActivationDepth"];
 }
 
 // Next: 6
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
index 1e0783a..76f0a11 100644
--- a/libprocessgroup/setup/Android.bp
+++ b/libprocessgroup/setup/Android.bp
@@ -37,6 +37,7 @@
     ],
     header_libs: [
         "libprocessgroup_headers",
+        "libprocessgroup_util",
     ],
     export_header_lib_headers: [
         "libprocessgroup_headers",
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
index 9982bfc..06ce186 100644
--- a/libprocessgroup/setup/cgroup_descriptor.h
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -30,7 +30,8 @@
 class CgroupDescriptor {
   public:
     CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
-                     mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags);
+                     mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags,
+                     uint32_t max_activation_depth);
 
     const format::CgroupController* controller() const { return &controller_; }
     mode_t mode() const { return mode_; }
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 1b26fbc..bd41874 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -42,6 +42,7 @@
 #include <processgroup/format/cgroup_file.h>
 #include <processgroup/processgroup.h>
 #include <processgroup/setup.h>
+#include <processgroup/util.h>
 
 #include "../build_flags.h"
 #include "cgroup_descriptor.h"
@@ -173,9 +174,15 @@
         controller_flags |= CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
     }
 
+    uint32_t max_activation_depth = UINT32_MAX;
+    if (cgroup.isMember("MaxActivationDepth")) {
+        max_activation_depth = cgroup["MaxActivationDepth"].asUInt();
+    }
+
     CgroupDescriptor descriptor(
             cgroups_version, name, path, std::strtoul(cgroup["Mode"].asString().c_str(), 0, 8),
-            cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags);
+            cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags,
+            max_activation_depth);
 
     auto iter = descriptors->find(name);
     if (iter == descriptors->end()) {
@@ -324,7 +331,8 @@
         return false;
     }
 
-    if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+    if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION &&
+        controller->max_activation_depth() > 0) {
         std::string str = "+";
         str += controller->name();
         std::string path = controller->path();
@@ -433,8 +441,12 @@
 
 CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
                                    const std::string& path, mode_t mode, const std::string& uid,
-                                   const std::string& gid, uint32_t flags = 0)
-    : controller_(version, flags, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+                                   const std::string& gid, uint32_t flags,
+                                   uint32_t max_activation_depth)
+    : controller_(version, flags, name, path, max_activation_depth),
+      mode_(mode),
+      uid_(uid),
+      gid_(gid) {}
 
 void CgroupDescriptor::set_mounted(bool mounted) {
     uint32_t flags = controller_.flags();
@@ -502,8 +514,11 @@
     for (const auto& [name, descriptor] : descriptors) {
         const format::CgroupController* controller = descriptor.controller();
         std::uint32_t flags = controller->flags();
+        std::uint32_t max_activation_depth = controller->max_activation_depth();
+        const int depth = util::GetCgroupDepth(controller->path(), path);
+
         if (controller->version() == 2 && name != CGROUPV2_HIERARCHY_NAME &&
-            flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+            flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {
             std::string str("+");
             str += controller->name();
             if (!android::base::WriteStringToFile(str, path + "/cgroup.subtree_control")) {
diff --git a/libprocessgroup/util/Android.bp b/libprocessgroup/util/Android.bp
new file mode 100644
index 0000000..4a940b7
--- /dev/null
+++ b/libprocessgroup/util/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+package {
+    default_team: "trendy_team_android_kernel",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_headers {
+    name: "libprocessgroup_util",
+    vendor_available: true,
+    product_available: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+    host_supported: true,
+    native_bridge_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "30",
+    export_include_dirs: [
+        "include",
+    ],
+    defaults: ["libprocessgroup_build_flags_cc"],
+}
+
+cc_test {
+    name: "libprocessgroup_util_test",
+    header_libs: ["libprocessgroup_util"],
+    srcs: ["tests/util.cpp"],
+    test_suites: ["general-tests"],
+}
diff --git a/libprocessgroup/util/OWNERS b/libprocessgroup/util/OWNERS
new file mode 100644
index 0000000..54ea400
--- /dev/null
+++ b/libprocessgroup/util/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1293033
+surenb@google.com
+tjmercier@google.com
diff --git a/libprocessgroup/util/TEST_MAPPING b/libprocessgroup/util/TEST_MAPPING
new file mode 100644
index 0000000..6ae2658
--- /dev/null
+++ b/libprocessgroup/util/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "libprocessgroup_util_test"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libprocessgroup/util/include/processgroup/util.h b/libprocessgroup/util/include/processgroup/util.h
new file mode 100644
index 0000000..5240744
--- /dev/null
+++ b/libprocessgroup/util/include/processgroup/util.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+
+namespace util {
+
+namespace internal {
+
+const char SEP = '/';
+
+std::string DeduplicateAndTrimSeparators(const std::string& path) {
+    bool lastWasSep = false;
+    std::string ret;
+
+    std::copy_if(path.begin(), path.end(), std::back_inserter(ret), [&lastWasSep](char c) {
+        if (lastWasSep) {
+            if (c == SEP) return false;
+            lastWasSep = false;
+        } else if (c == SEP) {
+            lastWasSep = true;
+        }
+        return true;
+    });
+
+    if (ret.length() > 1 && ret.back() == SEP) ret.pop_back();
+
+    return ret;
+}
+
+}  // namespace internal
+
+unsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path) {
+    const std::string deduped_root = internal::DeduplicateAndTrimSeparators(controller_root);
+    const std::string deduped_path = internal::DeduplicateAndTrimSeparators(cgroup_path);
+
+    if (deduped_root.empty() || deduped_path.empty() || !deduped_path.starts_with(deduped_root))
+        return 0;
+
+    return std::count(deduped_path.begin() + deduped_root.size(), deduped_path.end(),
+                      internal::SEP);
+}
+
+}  // namespace util
diff --git a/libprocessgroup/util/tests/util.cpp b/libprocessgroup/util/tests/util.cpp
new file mode 100644
index 0000000..1de7d6f
--- /dev/null
+++ b/libprocessgroup/util/tests/util.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 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 <processgroup/util.h>
+
+#include "gtest/gtest.h"
+
+using util::GetCgroupDepth;
+
+TEST(EmptyInputs, bothEmpty) {
+    EXPECT_EQ(GetCgroupDepth({}, {}), 0);
+}
+
+TEST(EmptyInputs, rootEmpty) {
+    EXPECT_EQ(GetCgroupDepth({}, "foo"), 0);
+}
+
+TEST(EmptyInputs, pathEmpty) {
+    EXPECT_EQ(GetCgroupDepth("foo", {}), 0);
+}
+
+TEST(InvalidInputs, pathNotInRoot) {
+    EXPECT_EQ(GetCgroupDepth("foo", "bar"), 0);
+}
+
+TEST(InvalidInputs, rootLargerThanPath) {
+    EXPECT_EQ(GetCgroupDepth("/a/long/path", "/short"), 0);
+}
+
+TEST(InvalidInputs, pathLargerThanRoot) {
+    EXPECT_EQ(GetCgroupDepth("/short", "/a/long/path"), 0);
+}
+
+TEST(InvalidInputs, missingSeparator) {
+    EXPECT_EQ(GetCgroupDepth("/controller/root", "/controller/rootcgroup"), 0);
+}
+
+TEST(ExtraSeparators, root) {
+    EXPECT_EQ(GetCgroupDepth("///sys/fs/cgroup", "/sys/fs/cgroup/a/b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys///fs/cgroup", "/sys/fs/cgroup/a/b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs///cgroup", "/sys/fs/cgroup/a/b/c"), 3);
+
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "///sys/fs/cgroup/a/b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys///fs/cgroup/a/b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs///cgroup/a/b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup///a/b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a///b/c"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b///c"), 3);
+}
+
+TEST(SeparatorEndings, rootEndsInSeparator) {
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b"), 2);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup///", "/sys/fs/cgroup/a/b"), 2);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b/"), 2);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup///", "/sys/fs/cgroup/a/b/"), 2);
+}
+
+TEST(SeparatorEndings, pathEndsInSeparator) {
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b/"), 2);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b///"), 2);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b/"), 2);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/a/b///"), 2);
+}
+
+TEST(ValidInputs, rootHasZeroDepth) {
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup"), 0);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup"), 0);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/"), 0);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup/", "/sys/fs/cgroup/"), 0);
+}
+
+TEST(ValidInputs, atLeastDepth10) {
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/a/b/c/d/e/f/g/h/i/j"), 10);
+}
+
+TEST(ValidInputs, androidCgroupNames) {
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/system/uid_0/pid_1000"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/uid_0/pid_1000"), 2);
+
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/apps/uid_100000/pid_1000"), 3);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/uid_100000/pid_1000"), 2);
+
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/apps"), 1);
+    EXPECT_EQ(GetCgroupDepth("/sys/fs/cgroup", "/sys/fs/cgroup/system"), 1);
+}
+
+TEST(ValidInputs, androidCgroupNames_nonDefaultRoot) {
+    EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/system/uid_0/pid_1000"), 3);
+    EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/uid_0/pid_1000"), 2);
+
+    EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/apps/uid_100000/pid_1000"), 3);
+    EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/uid_100000/pid_1000"), 2);
+
+    EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/apps"), 1);
+    EXPECT_EQ(GetCgroupDepth("/custom/root", "/custom/root/system"), 1);
+}
diff --git a/libutils/CallStack_test.cpp b/libutils/CallStack_test.cpp
index 7afc2c3..bfe6b87 100644
--- a/libutils/CallStack_test.cpp
+++ b/libutils/CallStack_test.cpp
@@ -72,7 +72,8 @@
 TEST(CallStackTest, log_stack) {
     android::CallStack::logStack("callstack_test");
     auto logger_list = android_logger_list_open(android_name_to_log_id("main"),
-                                                ANDROID_LOG_NONBLOCK, 1000, getpid());
+                                                ANDROID_LOG_NONBLOCK,
+                                                10000 /* tail */, getpid());
     ASSERT_NE(nullptr, logger_list);
     std::string log;
     while (true) {