| @@ -847,6 +847,10 @@ public: | |||
| 847 | 847 | ||
| 848 | void DupAllFd(); | 848 | void DupAllFd(); |
| 849 | 849 | ||
| 850 | + bool AssignDup(const Want &src); | ||
| 851 | + | ||
| 852 | + static bool DupCopy(const Want &src, Want &out); | ||
| 853 | + | ||
| 850 | void SetEntities(const std::vector<std::string> &entities); | 854 | void SetEntities(const std::vector<std::string> &entities); |
| 851 | static int32_t Flags_ConvertEts2Native(const int32_t index); | 855 | static int32_t Flags_ConvertEts2Native(const int32_t index); |
| 852 | static int32_t Flags_ConvertNative2Ets(const int32_t nativeValue); | 856 | static int32_t Flags_ConvertNative2Ets(const int32_t nativeValue); |
| @@ -0,0 +1,55 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace OHOS { | ||
| 21 | +namespace AAFwk { | ||
| 22 | + | ||
| 23 | +class WantFdScope final { | ||
| 24 | +public: | ||
| 25 | + explicit WantFdScope(Want &want) : want_(&want) {} | ||
| 26 | + ~WantFdScope() | ||
| 27 | + { | ||
| 28 | + if (want_ != nullptr) { | ||
| 29 | + want_->CloseAllFd(); | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + WantFdScope(const WantFdScope &) = delete; | ||
| 33 | + WantFdScope &operator=(const WantFdScope &) = delete; | ||
| 34 | + WantFdScope(WantFdScope &&) = delete; | ||
| 35 | + WantFdScope &operator=(WantFdScope &&) = delete; | ||
| 36 | + | ||
| 37 | + void Release() noexcept | ||
| 38 | + { | ||
| 39 | + want_ = nullptr; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + void Strip() | ||
| 43 | + { | ||
| 44 | + if (want_ != nullptr) { | ||
| 45 | + want_->RemoveAllFd(); | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | +private: | ||
| 50 | + Want *want_; | ||
| 51 | +}; | ||
| 52 | +} // namespace AAFwk | ||
| 53 | +} // namespace OHOS | ||
| 54 | + | ||
| 55 | + | ||
| @@ -114,6 +114,10 @@ public: | |||
| 114 | 114 | ||
| 115 | void DupAllFd(); | 115 | void DupAllFd(); |
| 116 | 116 | ||
| 117 | + bool AssignDup(const WantParams &src); | ||
| 118 | + | ||
| 119 | + static bool DupCopy(const WantParams &src, WantParams &out); | ||
| 120 | + | ||
| 117 | void GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsuppertedData) const; | 121 | void GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsuppertedData) const; |
| 118 | 122 | ||
| 119 | void SetCachedUnsupportedData(const std::vector<UnsupportedData> &cachedUnsuppertedData); | 123 | void SetCachedUnsupportedData(const std::vector<UnsupportedData> &cachedUnsuppertedData); |
| @@ -221,6 +225,7 @@ private: | |||
| 221 | bool NewArrayData(IArray *source, sptr<IArray> &dest); | 225 | bool NewArrayData(IArray *source, sptr<IArray> &dest); |
| 222 | bool NewParams(const WantParams &source, WantParams &dest); | 226 | bool NewParams(const WantParams &source, WantParams &dest); |
| 223 | bool NewFds(const WantParams &source, WantParams &dest); | 227 | bool NewFds(const WantParams &source, WantParams &dest); |
| 228 | + bool TryDupAllFd(); | ||
| 224 | bool AddWantParamToInterfaceVector(const sptr<WantParams> &value, | 229 | bool AddWantParamToInterfaceVector(const sptr<WantParams> &value, |
| 225 | std::vector<sptr<IInterface>> &array) const; | 230 | std::vector<sptr<IInterface>> &array) const; |
| 226 | 231 | ||
| @@ -2316,6 +2316,16 @@ void Want::DupAllFd() | |||
| 2316 | parameters_.DupAllFd(); | 2316 | parameters_.DupAllFd(); |
| 2317 | } | 2317 | } |
| 2318 | 2318 | ||
| 2319 | +bool Want::AssignDup(const Want &src) | ||
bool Want::AssignDup(const Want &src) { operation_ = src.operation_; bool ret = parameters_.AssignDup(src.GetParams()); ABILITYBASE_LOGI("Want::AssignDup result:%{public}d", ret); return ret; } bool Want::DupCopy(const Want &src, Want &out) { return WantParams::DupCopy(src.GetParams(), out.parameters_); } ![]() ![]() | |||
| 2320 | +{ | ||
| 2321 | + return parameters_.AssignDup(src.GetParams()); | ||
| 2322 | +} | ||
| 2323 | + | ||
| 2324 | +bool Want::DupCopy(const Want &src, Want &out) | ||
| 2325 | +{ | ||
| 2326 | + return WantParams::DupCopy(src.GetParams(), out.parameters_); | ||
| 2327 | +} | ||
| 2328 | + | ||
| 2319 | void Want::SetEntities(const std::vector<std::string> &entities) | 2329 | void Want::SetEntities(const std::vector<std::string> &entities) |
| 2320 | { | 2330 | { |
| 2321 | operation_.SetEntities(entities); | 2331 | operation_.SetEntities(entities); |
| @@ -1885,22 +1885,63 @@ void WantParams::RemoveAllFd() | |||
| 1885 | fds_.clear(); | 1885 | fds_.clear(); |
| 1886 | } | 1886 | } |
| 1887 | 1887 | ||
| 1888 | +bool WantParams::TryDupAllFd() | ||
bool WantParams::TryDupAllFd() { ABILITYBASE_LOGI("TryDupAllFd called, fd count: %{public}zu", fds_.size()); std::vector<std::pair<std::string, int32_t>> staged; for (const auto &it : fds_) { if (it.second < 0) { continue; } int32_t dupFd = dup(it.second); if (dupFd < 0) { ABILITYBASE_LOGE("dup fd failed, key: %{public}s oldFd:%{public}d", it.first.c_str(), it.second); for (const auto &s : staged) { close(s.second); } return false; } ABILITYBASE_LOGI("dup fd key:%{public}s oldFd:%{public}d newFd:%{public}d", it.first.c_str(), it.second, dupFd); staged.emplace_back(it.first, dupFd); } for (const auto &s : staged) { WantParams wp; wp.SetParam(TYPE_PROPERTY, String::Box(FD)); wp.SetParam(VALUE_PROPERTY, Integer::Box(s.second)); sptrAAFwk::IWantParams pWantParams = AAFwk::WantParamWrapper::Box(wp); params_[s.first] = pWantParams; fds_[s.first] = s.second; } return true; } void WantParams::DupAllFd() { (void)TryDupAllFd(); } bool WantParams::AssignDup(const WantParams &src) { if (this == &src) { return TryDupAllFd(); } *this = src; if (!TryDupAllFd()) { RemoveAllFd(); return false; } return true; } bool WantParams::DupCopy(const WantParams &src, WantParams &out) { if (&out == &src) { return out.TryDupAllFd(); } out = src; if (!out.TryDupAllFd()) { out.RemoveAllFd(); return false; } return true; } ![]() ![]() | |||
| 1889 | +{ | ||
| 1890 | + std::vector<std::pair<std::string, int32_t>> staged; | ||
| 1891 | + for (const auto &it : fds_) { | ||
| 1892 | + if (it.second < 0) { | ||
| 1893 | + continue; | ||
| 1894 | + } | ||
| 1895 | + int32_t dupFd = dup(it.second); | ||
| 1896 | + if (dupFd < 0) { | ||
| 1897 | + ABILITYBASE_LOGE("dup fd failed, key: %{public}s", it.first.c_str()); | ||
| 1898 | + for (const auto &s : staged) { | ||
| 1899 | + close(s.second); | ||
| 1900 | + } | ||
| 1901 | + return false; | ||
| 1902 | + } | ||
| 1903 | + staged.emplace_back(it.first, dupFd); | ||
| 1904 | + } | ||
| 1905 | + for (const auto &s : staged) { | ||
| 1906 | + WantParams wp; | ||
| 1907 | + wp.SetParam(TYPE_PROPERTY, String::Box(FD)); | ||
| 1908 | + wp.SetParam(VALUE_PROPERTY, Integer::Box(s.second)); | ||
| 1909 | + sptr<AAFwk::IWantParams> pWantParams = AAFwk::WantParamWrapper::Box(wp); | ||
| 1910 | + params_[s.first] = pWantParams; | ||
| 1911 | + fds_[s.first] = s.second; | ||
| 1912 | + } | ||
| 1913 | + return true; | ||
| 1914 | +} | ||
| 1915 | + | ||
| 1888 | void WantParams::DupAllFd() | 1916 | void WantParams::DupAllFd() |
| 1889 | { | 1917 | { |
| 1890 | - for (auto it : fds_) { | 1918 | + (void)TryDupAllFd(); |
| 1891 | - if (it.second > 0) { | 1919 | +} |
| 1892 | - int dupFd = dup(it.second); | 1920 | + |
| 1893 | - if (dupFd > 0) { | 1921 | +bool WantParams::AssignDup(const WantParams &src) |
| 1894 | - params_.erase(it.first); | 1922 | +{ |
| 1895 | - WantParams wp; | 1923 | + if (this == &src) { |
| 1896 | - wp.SetParam(TYPE_PROPERTY, String::Box(FD)); | 1924 | + return TryDupAllFd(); |
| 1897 | - wp.SetParam(VALUE_PROPERTY, Integer::Box(dupFd)); | ||
| 1898 | - sptr<AAFwk::IWantParams> pWantParams = AAFwk::WantParamWrapper::Box(wp); | ||
| 1899 | - SetParam(it.first, pWantParams); | ||
| 1900 | - fds_[it.first] = dupFd; | ||
| 1901 | - } | ||
| 1902 | - } | ||
| 1903 | } | 1925 | } |
| 1926 | + *this = src; | ||
| 1927 | + if (!TryDupAllFd()) { | ||
| 1928 | + RemoveAllFd(); | ||
| 1929 | + return false; | ||
| 1930 | + } | ||
| 1931 | + return true; | ||
| 1932 | +} | ||
| 1933 | + | ||
| 1934 | +bool WantParams::DupCopy(const WantParams &src, WantParams &out) | ||
| 1935 | +{ | ||
| 1936 | + if (&out == &src) { | ||
| 1937 | + return out.TryDupAllFd(); | ||
| 1938 | + } | ||
| 1939 | + out = src; | ||
| 1940 | + if (!out.TryDupAllFd()) { | ||
| 1941 | + out.RemoveAllFd(); | ||
| 1942 | + return false; | ||
| 1943 | + } | ||
| 1944 | + return true; | ||
| 1904 | } | 1945 | } |
| 1905 | 1946 | ||
| 1906 | void WantParams::GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsupportedData) const | 1947 | void WantParams::GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsupportedData) const |
| @@ -244,6 +244,28 @@ ohos_unittest("want_params_test") { | |||
| 244 | ] | 244 | ] |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | +ohos_unittest("want_fd_scope_test") { | ||
| 248 | + module_out_path = want_output_path | ||
| 249 | + sources = [ "want/want_fd_scope_test.cpp" ] | ||
| 250 | + | ||
| 251 | + configs = [ | ||
| 252 | + ":want_private_config", | ||
| 253 | + "${ability_base_path}:want_public_config", | ||
| 254 | + ] | ||
| 255 | + | ||
| 256 | + deps = [ | ||
| 257 | + "${ability_base_path}:base", | ||
| 258 | + "${ability_base_path}:want", | ||
| 259 | + ] | ||
| 260 | + | ||
| 261 | + external_deps = [ | ||
| 262 | + "c_utils:utils", | ||
| 263 | + "googletest:gtest_main", | ||
| 264 | + "hilog:libhilog", | ||
| 265 | + "ipc:ipc_core", | ||
| 266 | + ] | ||
| 267 | +} | ||
| 268 | + | ||
| 247 | ohos_unittest("want_params_wrapper_test") { | 269 | ohos_unittest("want_params_wrapper_test") { |
| 248 | module_out_path = want_output_path | 270 | module_out_path = want_output_path |
| 249 | sources = [ "want/want_params_wrapper_test.cpp" ] | 271 | sources = [ "want/want_params_wrapper_test.cpp" ] |
| @@ -831,6 +853,7 @@ group("unittest") { | |||
| 831 | ":user_object_wrapper_test", | 853 | ":user_object_wrapper_test", |
| 832 | ":view_data_test", | 854 | ":view_data_test", |
| 833 | ":want_params_test", | 855 | ":want_params_test", |
| 856 | + ":want_fd_scope_test", | ||
| 834 | ":want_params_wrapper_test", | 857 | ":want_params_wrapper_test", |
| 835 | ":want_params_wrapper_json_test", | 858 | ":want_params_wrapper_json_test", |
| 836 | ":want_test", | 859 | ":want_test", |
| @@ -0,0 +1,183 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +using namespace OHOS; | ||
| 32 | +using namespace OHOS::AAFwk; | ||
| 33 | +using testing::ext::TestSize; | ||
| 34 | + | ||
| 35 | +namespace OHOS { | ||
| 36 | +namespace AAFwk { | ||
| 37 | +namespace { | ||
| 38 | +void SetFdMarker(WantParams &p, const std::string &key, int fd) | ||
| 39 | +{ | ||
| 40 | + WantParams wp; | ||
| 41 | + wp.SetParam(TYPE_PROPERTY, String::Box("FD")); | ||
| 42 | + wp.SetParam(VALUE_PROPERTY, Integer::Box(fd)); | ||
| 43 | + sptr<IWantParams> marker = WantParamWrapper::Box(wp); | ||
| 44 | + p.SetParam(key, marker); | ||
| 45 | + p.fds_[key] = fd; | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +int MakePipeReadEnd() | ||
| 49 | +{ | ||
| 50 | + int pipefd[2] = {-1, -1}; | ||
| 51 | + if (pipe(pipefd) != 0) { | ||
| 52 | + return -1; | ||
| 53 | + } | ||
| 54 | + close(pipefd[1]); | ||
| 55 | + return pipefd[0]; | ||
| 56 | +} | ||
| 57 | +} // namespace | ||
| 58 | + | ||
| 59 | +class WantFdScopeTest : public testing::Test { | ||
| 60 | +public: | ||
| 61 | + static void SetUpTestCase() {}; | ||
| 62 | + static void TearDownTestCase() {}; | ||
| 63 | + void SetUp() {}; | ||
| 64 | + void TearDown() {}; | ||
| 65 | +}; | ||
| 66 | + | ||
| 67 | +HWTEST_F(WantFdScopeTest, TryDupAllFd_ProducesIndependentFd, TestSize.Level1) | ||
| 68 | +{ | ||
| 69 | + int fdS = MakePipeReadEnd(); | ||
| 70 | + ASSERT_GE(fdS, 0); | ||
| 71 | + WantParams p; | ||
| 72 | + SetFdMarker(p, "k", fdS); | ||
| 73 | + EXPECT_TRUE(p.TryDupAllFd()); | ||
| 74 | + int fdD = p.fds_["k"]; | ||
| 75 | + EXPECT_GE(fdD, 0); | ||
| 76 | + EXPECT_NE(fdD, fdS); | ||
| 77 | + close(fdD); | ||
| 78 | + EXPECT_NE(-1, fcntl(fdS, F_GETFD)); | ||
| 79 | + close(fdS); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +HWTEST_F(WantFdScopeTest, DupAllFd_VoidWrapper, TestSize.Level1) | ||
| 83 | +{ | ||
| 84 | + int fdS = MakePipeReadEnd(); | ||
| 85 | + ASSERT_GE(fdS, 0); | ||
| 86 | + WantParams p; | ||
| 87 | + SetFdMarker(p, "k", fdS); | ||
| 88 | + p.DupAllFd(); | ||
| 89 | + int fdD = p.fds_["k"]; | ||
| 90 | + EXPECT_GE(fdD, 0); | ||
| 91 | + EXPECT_NE(fdD, fdS); | ||
| 92 | + close(fdD); | ||
| 93 | + close(fdS); | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +HWTEST_F(WantFdScopeTest, AssignDup_SourceUntouchedAfterDstClose, TestSize.Level1) | ||
| 97 | +{ | ||
| 98 | + int fdS = MakePipeReadEnd(); | ||
| 99 | + ASSERT_GE(fdS, 0); | ||
| 100 | + WantParams src; | ||
| 101 | + SetFdMarker(src, "k", fdS); | ||
| 102 | + WantParams dst; | ||
| 103 | + EXPECT_TRUE(dst.AssignDup(src)); | ||
| 104 | + int fdD = dst.fds_["k"]; | ||
| 105 | + EXPECT_GE(fdD, 0); | ||
| 106 | + EXPECT_NE(fdD, fdS); | ||
| 107 | + EXPECT_EQ(fdS, src.fds_["k"]); | ||
| 108 | + dst.CloseAllFd(); | ||
| 109 | + EXPECT_NE(-1, fcntl(fdS, F_GETFD)); | ||
| 110 | + close(fdS); | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +HWTEST_F(WantFdScopeTest, AssignDup_Self, TestSize.Level1) | ||
| 114 | +{ | ||
| 115 | + int fdS = MakePipeReadEnd(); | ||
| 116 | + ASSERT_GE(fdS, 0); | ||
| 117 | + WantParams p; | ||
| 118 | + SetFdMarker(p, "k", fdS); | ||
| 119 | + EXPECT_TRUE(p.AssignDup(p)); | ||
| 120 | + int fdD = p.fds_["k"]; | ||
| 121 | + EXPECT_GE(fdD, 0); | ||
| 122 | + EXPECT_NE(fdD, fdS); | ||
| 123 | + close(fdD); | ||
| 124 | + close(fdS); | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +HWTEST_F(WantFdScopeTest, DupCopy_SourceUntouchedAfterOutClose, TestSize.Level1) | ||
| 128 | +{ | ||
| 129 | + int fdS = MakePipeReadEnd(); | ||
| 130 | + ASSERT_GE(fdS, 0); | ||
| 131 | + WantParams src; | ||
| 132 | + SetFdMarker(src, "k", fdS); | ||
| 133 | + WantParams out; | ||
| 134 | + EXPECT_TRUE(WantParams::DupCopy(src, out)); | ||
| 135 | + int fdO = out.fds_["k"]; | ||
| 136 | + EXPECT_GE(fdO, 0); | ||
| 137 | + EXPECT_NE(fdO, fdS); | ||
| 138 | + out.CloseAllFd(); | ||
| 139 | + EXPECT_NE(-1, fcntl(fdS, F_GETFD)); | ||
| 140 | + close(fdS); | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +HWTEST_F(WantFdScopeTest, WantFdScope_ClosesOnDtor, TestSize.Level1) | ||
| 144 | +{ | ||
| 145 | + int fd = MakePipeReadEnd(); | ||
| 146 | + ASSERT_GE(fd, 0); | ||
| 147 | + Want w; | ||
| 148 | + SetFdMarker(w.parameters_, "k", fd); | ||
| 149 | + { | ||
| 150 | + WantFdScope scope(w); | ||
| 151 | + } | ||
| 152 | + EXPECT_EQ(-1, fcntl(fd, F_GETFD)); | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +HWTEST_F(WantFdScopeTest, WantFdScope_ReleaseDisarms, TestSize.Level1) | ||
| 156 | +{ | ||
| 157 | + int fd = MakePipeReadEnd(); | ||
| 158 | + ASSERT_GE(fd, 0); | ||
| 159 | + Want w; | ||
| 160 | + SetFdMarker(w.parameters_, "k", fd); | ||
| 161 | + { | ||
| 162 | + WantFdScope scope(w); | ||
| 163 | + scope.Release(); | ||
| 164 | + } | ||
| 165 | + EXPECT_NE(-1, fcntl(fd, F_GETFD)); | ||
| 166 | + close(fd); | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +HWTEST_F(WantFdScopeTest, WantFdScope_StripDoesNotClose, TestSize.Level1) | ||
| 170 | +{ | ||
| 171 | + int fd = MakePipeReadEnd(); | ||
| 172 | + ASSERT_GE(fd, 0); | ||
| 173 | + Want w; | ||
| 174 | + SetFdMarker(w.parameters_, "k", fd); | ||
| 175 | + { | ||
| 176 | + WantFdScope scope(w); | ||
| 177 | + scope.Strip(); | ||
| 178 | + } | ||
| 179 | + EXPECT_NE(-1, fcntl(fd, F_GETFD)); | ||
| 180 | + close(fd); | ||
| 181 | +} | ||
| 182 | +} // namespace AAFwk | ||
| 183 | +} // namespace OHOS | ||
| @@ -0,0 +1,174 @@ | |||
| 1 | +# AMS Want FD 调用点 Manifest(步骤 b 产出) | ||
| 2 | + | ||
| 3 | +> 日期:2026-08-29 | ||
| 4 | +> 范围:`services/abilitymgr/`(AMS,SA 180,foundation 进程) | ||
| 5 | +> 扫描口径:`CloseAllFd` / `RemoveAllFd` / `DupAllFd` 全量 | ||
| 6 | +> 实测:16 `CloseAllFd` + 7 `RemoveAllFd` + 0 `DupAllFd` = 23 点(V11 的 20/18 是全 runtime 口径,AMS 子集为 23) | ||
| 7 | +> 用途:为 Scope 方案 P2 试点迁移提供逐点决策依据。**每点迁移须配对应 UT/长跑证据,不做无证据机械删除。** | ||
| 8 | + | ||
| 9 | +--- | ||
| 10 | + | ||
| 11 | +## 1. 汇总与分类 | ||
| 12 | + | ||
| 13 | +| 类别 | 点数 | 迁移决定 | | ||
| 14 | +|---|---|---| | ||
| 15 | +| A. stub 同步局部 Want 末尾兜底 Close | 9 | → `WantFdScope` 替代(删手工 Close) | | ||
| 16 | +| B. 赋值(关旧 + assign 新) | 1 | → `AssignDup` 替换(删 CloseAllFd+operator=) | | ||
| 17 | +| C. 析构中 CloseAllFd | 1 | → 候选删(want_ 迁独立 ownership 后自关) | | ||
| 18 | +| D. 业务剥离(路由/持久化/拦截器只取元数据) | 8 | → 保留 `RemoveAllFd`/`strip()`(业务语义,非兜底) | | ||
| 19 | +| E. 长期对象业务关闭(session/pending 用完关) | 3 | → 保留显式关;入库改 `AssignDup` | | ||
| 20 | +| F. 待确认(同步/生命周期需核实) | 1 | → 分析后归入 A 或 E | | ||
| 21 | +| **合计** | **23** | | | ||
| 22 | + | ||
| 23 | +--- | ||
| 24 | + | ||
| 25 | +## 2. A 类:stub 同步末尾兜底 Close → `WantFdScope`(删) | ||
| 26 | + | ||
| 27 | +通用模式:`Want/SessionInfo *x = data.ReadParcelable<…>(); …处理…; x->CloseAllFd(); return;` | ||
| 28 | + | ||
| 29 | +| # | file:line | 对象 | 处理后调用 | 决策 | | ||
| 30 | +|---|---|---|---|---| | ||
| 31 | +| A1 | ability_manager_stub.cpp:1340 | `extensionSessionInfo->want` | TerminateUIExtensionAbility 后 | `WantFdScope` 覆盖返回路径,删 Close | | ||
| 32 | +| A2 | ability_manager_stub.cpp:1397 | `extensionSessionInfo->want` | MinimizeUIExtensionAbility 后 | 同上 | | ||
| 33 | +| A3 | ability_manager_stub.cpp:2217 | `senderInfo->want` | SendWantSender 后 | 同上 | | ||
| 34 | +| A4 | ability_manager_stub.cpp:2234 | `senderInfo->want` | SendLocalWantSender 后 | 同上 | | ||
| 35 | +| A5 | ability_manager_stub.cpp:5232 | `want` (sptr 局部) | StartSelfUIAbility 后 | 同上 | | ||
| 36 | +| A6 | ability_manager_stub.cpp:5255 | `want` | StartSelfUIAbilityWithStartOptions 后 | 同上 | | ||
| 37 | +| A7 | ability_manager_stub.cpp:5276 | `want` | StartSelfUIAbilityWithToken 后 | 同上 | | ||
| 38 | +| A8 | ability_manager_stub.cpp:5303 | `want` | StartSelfUIAbilityWithStartOptionsAndToken 后 | 同上 | | ||
| 39 | +| A9 | ability_manager_stub.cpp:5327 | `want` | StartSelfUIAbilityWithPidResult 后 | 同上 | | ||
| 40 | + | ||
| 41 | +**注意**:这 9 点的 Want 均为 stub 局部 `sptr`/`unique_ptr`(来自 `ReadParcelable`),IPC 接收侧 Adopt 的 OWNED marker。scope 析构 = stub 局部 Want 析构时关。迁移前须确认:处理函数(如 StartSelfUIAbility)未把 Want 异步逃逸(按值传入则安全;若存成员则该处升级 B/E)。 | ||
| 42 | + | ||
| 43 | +--- | ||
| 44 | + | ||
| 45 | +## 3. B 类:赋值 → `AssignDup`(替换) | ||
| 46 | + | ||
| 47 | +| # | file:line | 现状代码 | 决策 | | ||
| 48 | +|---|---|---|---| | ||
| 49 | +| B1 | ability_record.cpp:2115 | `want_.CloseAllFd(); want_ = want;`(SetWant 中关旧再赋新) | → `want_.AssignDup(want)`:清旧 + share-copy + dup 成独立 ownership,一步原子;删手工 CloseAllFd + legacy operator= | | ||
| 50 | + | ||
| 51 | +**理由**:`want_` 是 AbilityRecord 成员(长期对象),生命周期独立于入参 `want` → 赋值必然 dup。`AssignDup` 失败时 `want_` 清空(无 FD),调用方显式处理 false。 | ||
| 52 | + | ||
| 53 | +--- | ||
| 54 | + | ||
| 55 | +## 4. C 类:析构中 CloseAllFd → 候选删 | ||
| 56 | + | ||
| 57 | +| # | file:line | 上下文 | 决策 | | ||
| 58 | +|---|---|---|---| | ||
| 59 | +| C1 | ability_record.cpp:179 | `~AbilityRecord` 析构清理(伴 RemoveDeathRecipient 等) | **候选删**:B1 迁移后 `want_` 持独立 dup'd marker,析构自关;该显式 CloseAllFd 冗余。须先完成 B1 并验证 `want_` 无共享引用后再删。 | | ||
| 60 | + | ||
| 61 | +--- | ||
| 62 | + | ||
| 63 | +## 5. D 类:业务剥离 → 保留 `RemoveAllFd`/`strip()`(非兜底) | ||
| 64 | + | ||
| 65 | +通用语义:副本只用于路由元数据/持久化/拦截器查询,FD 不应随副本扩散。剥离 ≠ 释放兜底。 | ||
| 66 | + | ||
| 67 | +| # | file:line | 对象 | 上下文 | 决策 | | ||
| 68 | +|---|---|---|---|---| | ||
| 69 | +| D1 | ui_ability_lifecycle_manager.cpp:314 | `sessionInfo->want` | newWant 分支:已 SetWant 给 abilityRecord,session 副本剥离 | 保留 `RemoveAllFd`(→ `strip()`) | | ||
| 70 | +| D2 | ui_ability_lifecycle_manager.cpp:812 | `hookSessionInfo->want` | NotifySCBPendingActivation 后剥离 hook 副本 | 保留 | | ||
| 71 | +| D3 | ui_ability_lifecycle_manager.cpp:896 | `sessionInfo->want` | NotifySCBPendingActivation 后剥离 | 保留 | | ||
| 72 | +| D4 | ui_ability_lifecycle_manager.cpp:975 | `sessionInfo->want` | BatchNotify 后循环剥离各 session | 保留 | | ||
| 73 | +| D5 | ui_ability_lifecycle_manager.cpp:4622 | `sessionInfo->want` | StartSpecifiedRequest cold 通知后剥离 | 保留 | | ||
| 74 | +| D6 | ui_ability_lifecycle_manager.cpp:4892 | `sessionInfo->want` | NotifySCB 后剥离 | 保留 | | ||
| 75 | +| D7 | ecological_rule_interceptor.cpp:43 | `newWant`(局部副本) | ERMS 查询只需元数据,剥离 FD | 保留 | | ||
| 76 | +| D8 | mission_info.cpp:34 | `want`(反序列化后) | MissionInfo::ReadFromParcel:mission 不持久化 FD,剥离 | **保留语义,建议改 `RemoveAllFd`/`strip()`**(现为 CloseAllFd,意图是剥离) | | ||
| 77 | + | ||
| 78 | +> D8 注意:当前用 `CloseAllFd` 实现剥离语义。在 share 模型下 `want = *parcelWant` 后 CloseAllFd 掉 want 引用,parcelWant 析构关 fd——结果正确但语义混用。建议显式改 `RemoveAllFd`(剥离不关,由 parcelWant 持有者负责关),语义更清晰。 | ||
| 79 | + | ||
| 80 | +--- | ||
| 81 | + | ||
| 82 | +## 6. E 类:长期对象业务关闭 → 保留显式关 + 入库 `AssignDup` | ||
| 83 | + | ||
| 84 | +| # | file:line | 对象 | 上下文 | 决策 | | ||
| 85 | +|---|---|---|---|---| | ||
| 86 | +| E1 | pending_want_key.cpp:158 | `wantInfo.want`(循环) | `ClearAllWantsInfosFd`:清空所有 pending want 的 FD | 保留(业务批量回收);`allWantsInfos_` 入库时改 `AssignDup` 使各 pending want 持独立 fd | | ||
| 87 | +| E2 | ui_ability_lifecycle_manager.cpp:316 | `sessionInfo->want` | else 分支(非 newWant):ability 已用,session want FD 不再需要 | 保留显式关(session 长期对象用完关);session 入库改 `AssignDup` | | ||
| 88 | +| E3 | ui_ability_lifecycle_manager.cpp:1145 | `sessionInfo->want` | NotifySCB 后 session want 用完关 | 保留 | | ||
| 89 | + | ||
| 90 | +> E 类不删:这些是「长期对象在业务节点显式回收 FD」,不是「局部 Want 析构兜底」。入库升级 `AssignDup` 后,关闭仍由这些显式点负责(业务决定何时回收,不受 scope 栈期限制)。 | ||
| 91 | + | ||
| 92 | +--- | ||
| 93 | + | ||
| 94 | +## 7. F 类:待确认 | ||
| 95 | + | ||
| 96 | +| # | file:line | 上下文 | 待确认 | 倾向 | | ||
| 97 | +|---|---|---|---|---| | ||
| 98 | +| F1 | ui_ability_lifecycle_manager.cpp:445 | `sessionInfo.want.CloseAllFd()`(IN_PROCESS_CALL LaunchAbility 之后) | sessionInfo 是否纯局部值?NotifySCB 是否同步?Want 是否异步逃逸? | 若局部+同步 → A 类(scope 删);若长期/异步 → E 类(保留) | | ||
| 99 | + | ||
| 100 | +--- | ||
| 101 | + | ||
| 102 | +## 8. 每点 manifest 必填字段(迁移执行时补齐) | ||
| 103 | + | ||
| 104 | +每个迁移点须记录: | ||
| 105 | + | ||
| 106 | +1. 点编号(A1…F1) | ||
| 107 | +2. FD 来源(IPC 接收 / 同进程传入 / dup 生成) | ||
| 108 | +3. 当前 ownership(OWNED marker / 共享 marker / 裸 int) | ||
| 109 | +4. 是否复制 Want(share-copy / 无) | ||
| 110 | +5. 是否进入同进程调用(IN_PROCESS_CALL) | ||
| 111 | +6. 是否进入 IPC(WriteParcelable) | ||
| 112 | +7. 是否跨线程/回调/Promise/任务队列 | ||
| 113 | +8. 最后使用点 | ||
| 114 | +9. 当前 Close/Remove 是资源释放还是业务剥离 | ||
| 115 | +10. 迁移决定(scope / AssignDup / 保留 strip / 候选删) | ||
| 116 | +11. 对应测试编号 | ||
| 117 | + | ||
| 118 | +--- | ||
| 119 | + | ||
| 120 | +## 9. 迁移顺序建议(P2 执行) | ||
| 121 | + | ||
| 122 | +1. **A 类(9 点)**:风险最低、收益最直接(stub 兜底全删)。先做 A1–A4(SessionInfo/SenderInfo),再做 A5–A9(StartSelfUIAbility×5)。每点删 CloseAllFd + 加 `WantFdScope`,配 stub 返回路径 UT。 | ||
| 123 | +2. **B1**:`ability_record.cpp:2115` SetWant → `AssignDup`。配 AssignDup 失败回滚 UT。 | ||
| 124 | +3. **C1**:B1 完成且 `want_` 独立 ownership 验证后,删 `ability_record.cpp:179` 析构 CloseAllFd。 | ||
| 125 | +4. **F1**:核实 sessionInfo 生命周期后归入 A 或 E。 | ||
| 126 | +5. **E1–E3**:入库路径改 `AssignDup`;显式关保留。 | ||
| 127 | +6. **D1–D8**:保留;D8 语义澄清(CloseAllFd→RemoveAllFd)。 | ||
| 128 | +7. 全程:FD 长跑 `/proc/<pid>/fd` 回基线 + fdsan 无 double-close。 | ||
| 129 | + | ||
| 130 | +--- | ||
| 131 | + | ||
| 132 | +## 10. 不迁点(明确保留,不在本轮删) | ||
| 133 | + | ||
| 134 | +- D1–D8:业务剥离,非兜底。 | ||
| 135 | +- E1–E3:长期对象业务回收。 | ||
| 136 | +- 待 P3 评估是否推广到 AppMS/UPMS 等其它服务(不在 AMS 试点范围)。 | ||
| 137 | + | ||
| 138 | +--- | ||
| 139 | + | ||
| 140 | +## 11. P2 执行状态(本轮) | ||
| 141 | + | ||
| 142 | +> 本轮执行环境无 OH sysroot,无法编译/跑单测。仅落地**行为保持**类迁移(A 子集);**语义变更**类(B1/E入库)需 build + `ability_manager_service_*_test` + FD 长跑验证后合入。 | ||
| 143 | + | ||
| 144 | +### 已完成(ability_base + ability_runtime) | ||
| 145 | + | ||
| 146 | +ability_base(API 基座,已落): | ||
| 147 | +- `WantParams::TryDupAllFd`(bool, all-or-nothing, fd-0 修复) + `DupAllFd`(void 包装) | ||
| 148 | +- `WantParams::AssignDup` / `WantParams::DupCopy`(static) | ||
| 149 | +- `Want::AssignDup` / `Want::DupCopy`(委派,镜像 CloseAllFd 模式) | ||
| 150 | +- `WantFdScope`(RAII,包 `Want&`,析构 CloseAllFd / `Release()` 解武装 / `Strip()`=RemoveAllFd) | ||
| 151 | +- `want_fd_scope_test`(8 用例) | ||
| 152 | + | ||
| 153 | +ability_runtime AMS(A5–A9,5 点,行为保持): | ||
| 154 | +- `ability_manager_stub.cpp` 加 `#include "want_fd_scope.h"` | ||
| 155 | +- A5 StartSelfUIAbilityInner / A6 WithStartOptions / A7 WithToken / A8 WithStartOptionsAndToken / A9 WithPidResult:每个 `ReadParcelable<Want>`+null-check 后加 `WantFdScope fdScope(*want);`,删 `want->CloseAllFd();` | ||
| 156 | +- 效果:成功路径关 fd 点不变(行为保持);早返回路径(reply write fail / options null)原先漏关 → 现由 scope 析构覆盖(**顺带修泄漏**) | ||
| 157 | + | ||
| 158 | +### 待续(需 build + 单测验证) | ||
| 159 | + | ||
| 160 | +| 点 | 现状 | 待办 | | ||
| 161 | +|---|---|---| | ||
| 162 | +| A1–A4 | 未迁 | 双 Want(SessionInfo/SenderInfo 的 want + 独立 resultWant,后者 `delete` 前未关 = 既有泄漏)需逐点分析结构再迁 | | ||
| 163 | +| B1 | 未迁 | `ability_record.cpp:2115` SetWant:`want_.CloseAllFd(); want_=want;` → `want_.CloseAllFd(); want_.AssignDup(want);`(★保留 CloseAllFd 关旧值;baseline 无 RAII,旧 fd 不显式关即泄漏)。语义变更(want_ 由共享→独立 dup),需 AbilityRecord 单测 | | ||
| 164 | +| C1 | 不删(Route 1 修正) | `ability_record.cpp:179` 析构 CloseAllFd **保留**:baseline WantParams 析构不关 fd,C1 是关 want_ 的独立 fd 的唯一点(V11「候选删」基于 WantFdValue 自关,Route 1 不适用) | | ||
| 165 | +| D1–D8 | 保留 | 业务剥离(RemoveAllFd/strip),非兜底。D8 mission_info CloseAllFd→RemoveAllFd 语义澄清可选 | | ||
| 166 | +| E1–E3 | 未迁入库 | 入库路径改 `Want::AssignDup` 修共享-close 隐患(baseline 下 E 类 CloseAllFd 当前可能正关共享 fd);显式关保留 | | ||
| 167 | +| F1 | 待确认 | `ui_ability_lifecycle_manager.cpp:445` 核实 sessionInfo 生命周期后归入 A 或 E | | ||
| 168 | + | ||
| 169 | +### 放行门禁(待续项合入前) | ||
| 170 | + | ||
| 171 | +1. `./build.sh --product-name <product> --build-target ability_runtime` 编译通过 | ||
| 172 | +2. `ability_manager_service_first_test` 及后续编号单测通过(AGENTS.md 要求) | ||
| 173 | +3. FD 长跑 `/proc/<pid>/fd` 回基线 + fdsan 无 double-close | ||
| 174 | +4. 每点迁移配对应 UT/manifest 证据(不做无证据机械删除) | ||
| @@ -0,0 +1,245 @@ | |||
| 1 | +# Want FD 生命周期治理 Scope 方案(v1 · Route 1 / baseline 接地) | ||
| 2 | + | ||
| 3 | +> 日期:2026-08-29 | ||
| 4 | +> 路线:**Route 1** — 不复活 WantFdValue;在 **baseline 模型**(FD 为 `Integer::Box(fd)` marker + `fds_` 整数镜像)上叠加 scope+dup 纪律与三个薄 API。 | ||
| 5 | +> 试点:`services/abilitymgr/`(AMS);其它存量保持兼容。 | ||
| 6 | +> 原则:拷贝/赋值取舍**以逐点分析为准**。 | ||
| 7 | + | ||
| 8 | +--- | ||
| 9 | + | ||
| 10 | +## 1. baseline 模型与核心隐患 | ||
| 11 | + | ||
| 12 | +baseline(当前仓库状态)的 FD 表示: | ||
| 13 | + | ||
| 14 | +- marker:`params_[key]` = `WantParamWrapper`,内含 `{TYPE:"FD", VALUE: Integer::Box(fd)}`。 | ||
| 15 | +- 镜像:`fds_[key] = fd`(裸 int)。 | ||
| 16 | +- 复制(copy ctor / `operator=`):**深拷贝**——每个副本得到自己的 `Integer` marker + 自己的 `fds_` 镜像,但 **fd 整数值相同** → 副本**共享同一个内核 fd**。 | ||
| 17 | +- 无引用计数、无 ownership 类型。fd 就是个 int。 | ||
| 18 | + | ||
| 19 | +两大隐患(本方案要解): | ||
| 20 | + | ||
| 21 | +1. **手工释放遗漏**:AMS 约 16 `CloseAllFd` + 7 `RemoveAllFd`,漏一处即泄漏/早关。 | ||
| 22 | +2. **共享整数 double-close**:baseline 深拷贝使副本共享内核 fd。任一副本 `CloseAllFd` 关掉的是**共享的内核 fd** → 其它副本的 fd 整数变 stale → 后续 close/use 即 double-close / use-after-close。 | ||
| 23 | + | ||
| 24 | +> 与 V11 的区别:V11 用 WantFdValue+refcount 让副本共享 owner、靠最后引用 close。Route 1 **不引入 refcount**,改用 **dup 产生独立内核 fd** 作为唯一独立性手段。 | ||
| 25 | + | ||
| 26 | +--- | ||
| 27 | + | ||
| 28 | +## 2. 核心思路 | ||
| 29 | + | ||
| 30 | +``` | ||
| 31 | + IPC 接收 Want(OwnFd=ReadFileDescriptor 给的独立内核 fd) ──┐ | ||
| 32 | + ├─→ WantFdScope(栈) 用完即关(安全:独占 fd) | ||
| 33 | + AssignDup/DupCopy ─→ dup 出独立内核 fd ─→ 持有者自关 ─────┘ | ||
| 34 | + (各持有者不同内核 fd → close 各自合法 → 无 double-close) | ||
| 35 | +``` | ||
| 36 | + | ||
| 37 | +- **dup 是独立性的唯一来源**:baseline 无 refcount,只有 `dup()` 给每个持有者一个**不同的内核 fd**,close 互不影响。 | ||
| 38 | +- **scope 只守独占 fd 的持有者**:IPC 接收件(内核已给独立 fd)或 assign-dup 后的副本。**共享整数副本禁止 CloseAllFd**(会关到源的内核 fd)——这类副本只能 `RemoveAllFd`(剥离不关)或按引用传。 | ||
| 39 | + | ||
| 40 | +--- | ||
| 41 | + | ||
| 42 | +## 3. 前置修复:baseline `DupAllFd` | ||
| 43 | + | ||
| 44 | +baseline `DupAllFd` 有缺陷,须先修(本方案依赖一个可用的 all-or-nothing dup): | ||
| 45 | + | ||
| 46 | +- **fd-0 bug**:`it.second > 0` 漏掉 fd 0 → 改 `>= 0`(fd 0 合法)。 | ||
| 47 | +- **非 all-or-nothing**:逐个 dup+就地替换,中途失败只 skip → 改 staged:全 dup 成功才一次性替换,任一失败则关掉已 staged 的 dup fd、`*this` 不变。 | ||
| 48 | +- **无状态返回**:新增私有 `bool TryDupAllFd()`;`DupAllFd()` 保留 `void` 公开签名(兼容),内部调 `TryDupAllFd()` 忽略返回。 | ||
| 49 | + | ||
| 50 | +```cpp | ||
| 51 | +bool WantParams::TryDupAllFd() { | ||
| 52 | + std::vector<std::pair<std::string, int32_t>> staged; // key, dupFd | ||
| 53 | + for (const auto &it : fds_) { | ||
| 54 | + if (it.second < 0) continue; // fd>=0 合法(修 fd-0 bug) | ||
| 55 | + int32_t dupFd = dup(it.second); | ||
| 56 | + if (dupFd < 0) { | ||
| 57 | + for (const auto &s : staged) close(s.second); // 回收已 dup | ||
| 58 | + return false; // *this 不变 | ||
| 59 | + } | ||
| 60 | + staged.emplace_back(it.first, dupFd); | ||
| 61 | + } | ||
| 62 | + for (const auto &s : staged) { // 全成功才替换 | ||
| 63 | + // 重建 marker:Integer::Box(s.second) + TYPE:"FD" | ||
| 64 | + params_[s.first] = marker; | ||
| 65 | + fds_[s.first] = s.second; | ||
| 66 | + } | ||
| 67 | + return true; | ||
| 68 | +} | ||
| 69 | +void WantParams::DupAllFd() { (void)TryDupAllFd(); } | ||
| 70 | +``` | ||
| 71 | + | ||
| 72 | +> baseline `Integer` marker 析构不关 fd,故 staged dup fd 失败时须**显式 close**(不能靠 RAII)。 | ||
| 73 | + | ||
| 74 | +--- | ||
| 75 | + | ||
| 76 | +## 4. 机制一:`WantFdScope`(同步 RAII,仅守独占 fd) | ||
| 77 | + | ||
| 78 | +```cpp | ||
| 79 | +class WantFdScope final { | ||
| 80 | +public: | ||
| 81 | + explicit WantFdScope(WantParams &p) : params_(&p) {} | ||
| 82 | + ~WantFdScope() { if (params_) params_->CloseAllFd(); } | ||
| 83 | + WantFdScope(const WantFdScope&) = delete; | ||
| 84 | + WantFdScope& operator=(const WantFdScope&) = delete; | ||
| 85 | + void release() noexcept { params_ = nullptr; } // 所有权已 dup 转交则解除武装 | ||
| 86 | + void strip() { if (params_) params_->RemoveAllFd(); } // 共享副本剥离(不关) | ||
| 87 | +private: | ||
| 88 | + WantParams* params_; | ||
| 89 | +}; | ||
| 90 | +``` | ||
| 91 | + | ||
| 92 | +**安全前提**:scope 的析构 `CloseAllFd` 只能用在**独占内核 fd 的持有者**: | ||
| 93 | + | ||
| 94 | +- IPC 接收的 Want(`ReadFileDescriptor` 给的独立 fd)✅ | ||
| 95 | +- `AssignDup`/`DupCopy` 之后的副本(dup 出的独立 fd)✅ | ||
| 96 | + | ||
| 97 | +**禁止**对**共享整数副本**(baseline 深拷贝来的)用 scope/CloseAllFd——会关到源的内核 fd。这类副本用 `strip()`(`RemoveAllFd`,剥离不关)或按引用传。 | ||
| 98 | + | ||
| 99 | +--- | ||
| 100 | + | ||
| 101 | +## 5. 机制二:`AssignDup`(赋值必然 dup) | ||
| 102 | + | ||
| 103 | +```cpp | ||
| 104 | +bool WantParams::AssignDup(const WantParams &src) { | ||
| 105 | + if (this == &src) return TryDupAllFd(); | ||
| 106 | + *this = src; // baseline 深拷贝:*this 持共享整数 marker(=src 的 fd int) | ||
| 107 | + if (!TryDupAllFd()) { | ||
| 108 | + RemoveAllFd(); // ★失败回滚用 RemoveAllFd(不关!*this 持的是 src 的共享内核 fd) | ||
| 109 | + return false; | ||
| 110 | + } | ||
| 111 | + return true; // *this 持独立 dup'd fd → scope/析构关安全 | ||
| 112 | +} | ||
| 113 | +``` | ||
| 114 | + | ||
| 115 | +- 「必然 dup」:AMS 凡赋值 FD-bearing WantParams,必须走 `AssignDup`(review+manifest 强制),不用 legacy `operator=`(仍深拷贝共享,仅供存量兼容)。 | ||
| 116 | +- **失败回滚关键差异**:baseline 下 `*this` 在 `TryDupAllFd` 失败时持的是**与 src 共享的内核 fd 整数**;若 `CloseAllFd` 会关掉 src 的 fd → **必须 `RemoveAllFd`(剥离不关)**,src 的内核 fd 不受影响。`*this` 变无 FD。 | ||
| 117 | +- 错误通道:`TryDupAllFd` all-or-nothing 返回 bool——dup 走带返回值 API,不走 ctor(化解「copy 无错误返回」)。 | ||
| 118 | + | ||
| 119 | +为何赋值「必然」dup:赋值目标是已存在对象(成员/存储值),生命周期独立于源 → 必须独立内核 fd 才能安全自关。 | ||
| 120 | + | ||
| 121 | +--- | ||
| 122 | + | ||
| 123 | +## 6. 机制三:`DupCopy`(拷贝选择性 dup) | ||
| 124 | + | ||
| 125 | +```cpp | ||
| 126 | +bool WantParams::DupCopy(const WantParams &src, WantParams &out) { | ||
| 127 | + if (&out == &src) return out.TryDupAllFd(); | ||
| 128 | + out = src; // 深拷贝共享整数 | ||
| 129 | + if (!out.TryDupAllFd()) { | ||
| 130 | + out.RemoveAllFd(); // 剥离不关 | ||
| 131 | + return false; | ||
| 132 | + } | ||
| 133 | + return true; | ||
| 134 | +} | ||
| 135 | +``` | ||
| 136 | + | ||
| 137 | +拷贝(copy ctor)默认 baseline 深拷贝(共享整数);是否升级 dup **逐点分析**: | ||
| 138 | + | ||
| 139 | +| 场景 | 副本与源关系 | 选择 | 关键 | | ||
| 140 | +|---|---|---|---| | ||
| 141 | +| 同步栈内只读视图 | 源覆盖副本 | **深拷贝 + 不关**(或按引用传) | 共享副本**禁止 CloseAllFd** | | ||
| 142 | +| 路由/持久化/拦截器只取元数据 | 立即剥离 | **深拷贝 + `strip()`(RemoveAllFd)** | 剥离不关,源保留 | | ||
| 143 | +| 存入长期容器(Mission/AbilityRecord/PendingWant/Session) | 副本寿命≥源 | **`DupCopy`** | 独立 fd,源可先关 | | ||
| 144 | +| 异步任务/回调/Promise/跨线程 | 跨栈帧时序 | **`DupCopy`** | scope 管不到,须独立 fd | | ||
| 145 | +| IPC 发送(WriteParcelable) | 内核为接收端 dup | **深拷贝(不必 dup)** | 发送端共享整数即可,内核隔离 | | ||
| 146 | +| 交给会自行 close 的组件 | 所有权移交 | **`DupCopy`** | 避免对方关到源的 fd | | ||
| 147 | + | ||
| 148 | +> 判据:**生命周期/所有权是否发散**。同步同域可共享(但不可关);跨域/跨时序/要自关 → dup。 | ||
| 149 | + | ||
| 150 | +--- | ||
| 151 | + | ||
| 152 | +## 7. 异步:dup-to-independent,杜绝 double-close | ||
| 153 | + | ||
| 154 | +```cpp | ||
| 155 | +void AmsStub::Handle(Want &want) { // IPC 接收,独占 fd | ||
| 156 | + WantFdScope scope(want.GetParams()); // sync 兜底(安全:独占) | ||
| 157 | + WantParams asyncOwned; | ||
| 158 | + if (!WantParams::DupCopy(want.GetParams(), asyncOwned)) { return; } // 独立 dup'd fd | ||
| 159 | + PostTask([asyncOwned = std::move(asyncOwned)]() mutable { | ||
| 160 | + Use(asyncOwned); // 独立内核 fd | ||
| 161 | + }); // 任务结束 → asyncOwned 析构 → CloseAllFd 关自己的 | ||
| 162 | + // scope 关 stub 的;asyncOwned 关自己的;不同内核 fd → 无 double-close | ||
| 163 | +} | ||
| 164 | +``` | ||
| 165 | + | ||
| 166 | +baseline 下这是**唯一**能安全跨异步的途径——共享整数副本跨异步必然 double-close(stub 先关 → 异步 stale)。 | ||
| 167 | + | ||
| 168 | +--- | ||
| 169 | + | ||
| 170 | +## 8. 与 baseline 的关系(不改 WantFdValue,因不复活) | ||
| 171 | + | ||
| 172 | +- **不改** FD 表示(`Integer::Box(fd)` marker + `fds_` 镜像)。 | ||
| 173 | +- **不改** legacy `operator=`/copy ctor(深拷贝共享,存量兼容)。 | ||
| 174 | +- **不改** `ReadFromParcelFD`(仍 `Integer::Box(fd)`,IPC 接收独占 fd)。 | ||
| 175 | +- **修** `DupAllFd`(fd-0 bug + all-or-nothing + `TryDupAllFd`)——属 baseline bug 修复,非新模型。 | ||
| 176 | +- **新增**(薄层):`WantFdScope`、`AssignDup`、`DupCopy`(基于 baseline `operator=`/`DupAllFd`/`CloseAllFd`/`RemoveAllFd` 组合)。 | ||
| 177 | + | ||
| 178 | +即:Route 1 = baseline bug 修复 + 使用纪律 + 三个薄 API,不引入 WantFdValue/refcount。 | ||
| 179 | + | ||
| 180 | +--- | ||
| 181 | + | ||
| 182 | +## 9. AMS 试点 + 兼容性 | ||
| 183 | + | ||
| 184 | +- **试点域**:`services/abilitymgr/`。新 API 作为 WantParams 公开方法(加性、ABI 兼容)+ `WantFdScope` 新 header(经 `want_public_config` include 路径对 AMS 可见,pilot 期暂不登记 inner_kits)。 | ||
| 185 | +- **兼容**:legacy `operator=`/copy(深拷贝)不动;非 AMS、ability_runtime 其它服务、ability_base 既有调用点不动;AMS 逐点迁移,未迁点保留原手工 Close。 | ||
| 186 | +- **强制面**:AMS 内 FD-bearing 赋值禁用 legacy `operator=`、改用 `AssignDup`(manifest+review 守门)。 | ||
| 187 | + | ||
| 188 | +--- | ||
| 189 | + | ||
| 190 | +## 10. 与 manifest 衔接(23 点,见 want_fd_ams_manifest.md) | ||
| 191 | + | ||
| 192 | +manifest 枚举对 baseline 有效。Route 1 下的迁移决定: | ||
| 193 | + | ||
| 194 | +| 类别 | Route 1 决定 | 备注 | | ||
| 195 | +|---|---|---| | ||
| 196 | +| A. stub 末尾兜底 Close(9) | → `WantFdScope`(IPC 接收件独占 fd,安全) | 删手工 Close | | ||
| 197 | +| B. 赋值(关旧+assign)(1) | → `AssignDup` | 删 CloseAllFd+operator= | | ||
| 198 | +| C. 析构中 CloseAllFd(1) | → 候选删(B 迁移后 want_ 独占 fd,析构自关) | 须先 B | | ||
| 199 | +| D. 业务剥离(8) | → 保留 `RemoveAllFd`/`strip()` | baseline RemoveAllFd 不关,安全 | | ||
| 200 | +| E. 长期对象业务关闭(3) | → 入库改 `AssignDup` 修共享整数隐患,显式关保留 | **baseline 下 E 类 CloseAllFd 当前可能正在关共享 fd(bug),AssignDup 修复** | | ||
| 201 | +| F. 待确认(1) | 核实是否独占 fd 后归入 A 或 D | | | ||
| 202 | + | ||
| 203 | +**前置**:先修 `DupAllFd`(fd-0 + all-or-nothing + `TryDupAllFd`),否则 AssignDup/DupCopy 无可靠基础。 | ||
| 204 | + | ||
| 205 | +--- | ||
| 206 | + | ||
| 207 | +## 11. 权衡与风险 | ||
| 208 | + | ||
| 209 | +| 项 | Route 1 取舍 | | ||
| 210 | +|---|---| | ||
| 211 | +| 不引入 refcount | 简单,但失去「最后引用 close」兜底;独立性完全靠 dup 纪律 | | ||
| 212 | +| dup 放大 fd 数 | selective dup(仅发散点)+ scope 即时回收;赋值 dup 有界(AMS 有限点) | | ||
| 213 | +| 共享整数副本禁忌 CloseAllFd | 纪律强约束:共享副本只能 strip 或按引用;scope 只守独占持有者 | | ||
| 214 | +| baseline fd-0 bug | 本方案前置修复 | | ||
| 215 | +| E 类当前共享-close bug | AssignDup 入库修复 | | ||
| 216 | +| dup 失败处理 | all-or-nothing + 失败 RemoveAllFd(不关),调用方显式 false | | ||
| 217 | +| NAPI 裸 int | 不变(已知限制) | | ||
| 218 | + | ||
| 219 | +残留风险: | ||
| 220 | + | ||
| 221 | +- 共享整数副本误用 CloseAllFd 仍会 double-close——靠 review+manifest 守门,非类型级强制。 | ||
| 222 | +- dup 放大瞬时 fd(高并发启 ability)——需长跑 `/proc/<pid>/fd` 监控。 | ||
| 223 | +- AMS 内强制 `AssignDup` 靠 review,非类型级。 | ||
| 224 | + | ||
| 225 | +--- | ||
| 226 | + | ||
| 227 | +## 12. 落地阶段 | ||
| 228 | + | ||
| 229 | +| 阶段 | 内容 | 放行 | | ||
| 230 | +|---|---|---| | ||
| 231 | +| P0 | 本设计冻结 + manifest(23 点,Route 1 分类) | 文档+清单 | | ||
| 232 | +| P1 | 前置修 `DupAllFd`(fd-0+all-or-nothing+`TryDupAllFd`);落 `AssignDup`/`DupCopy`/`WantFdScope`(WantParams 公开方法 + 新 header) | 编译 + UT(DupAllFd fd-0、all-or-nothing 失败回滚、AssignDup 失败 RemoveAllFd、DupCopy、scope 各返回路径、async 无 double-close) | | ||
| 233 | +| P2 | AMS 试点迁移:A→scope、B→AssignDup、E→入库 AssignDup,逐点删手工 Close | AMS UT + FD 长跑回基线 + fdsan 无 double-close | | ||
| 234 | +| P3 | 评估推广 AppMS/其它 | 不在本轮 | | ||
| 235 | + | ||
| 236 | +--- | ||
| 237 | + | ||
| 238 | +## 13. 决策记录 | ||
| 239 | + | ||
| 240 | +- **Route 1 选定**:不复活 WantFdValue;baseline 模型上叠 scope+dup。理由:仓库已回退 baseline,且 dup-as-独立性 + scope-on-独占 纪律足以解遗漏与 double-close,无需 refcount 模型。 | ||
| 241 | +- **dup 是唯一独立性来源**:baseline 无 refcount,`dup()` 给不同内核 fd 是避免共享 double-close 的唯一手段。 | ||
| 242 | +- **scope 只守独占 fd**:IPC 接收件 / AssignDup 后副本。共享整数副本禁止 CloseAllFd(用 strip/RemoveAllFd)。 | ||
| 243 | +- **赋值必然 dup / 拷贝可选 dup**:赋值目标独立生命周期 → dup;拷贝按发散性逐点定。 | ||
| 244 | +- **失败回滚用 RemoveAllFd 不用 CloseAllFd**:baseline 共享整数下,CloseAllFd 会关到源的内核 fd。 | ||
| 245 | +- **前置修 DupAllFd**:fd-0 bug + all-or-nothing + TryDupAllFd 是 AssignDup/DupCopy 的可靠基础。 | ||


/*
#include "want.h"
namespace OHOS { namespace AAFwk {
class WantFdScope final { public: explicit WantFdScope(Want &want) : want_(&want) {} explicit WantFdScope(Want *want) : want_(want) {} ~WantFdScope() { if (want_ != nullptr) { want_->CloseAllFd(); } } WantFdScope(const WantFdScope &) = delete; WantFdScope &operator=(const WantFdScope &) = delete; WantFdScope(WantFdScope &&other) noexcept : want_(other.want_) { other.want_ = nullptr; } WantFdScope &operator=(WantFdScope &&) = delete;
private: Want *want_; }; } // namespace AAFwk } // namespace OHOS
#endif // OHOS_ABILITY_BASE_WANT_FD_SCOPE_H