已合并
fix bug #20216
已合并
xhz-sz创建于 19 天前
15 个文件变更+679-96
@@ -90,6 +90,10 @@ void JsPreloadUIExtensionCallbackClient::ProcessOnDestroyDone(int32_t extensionA
90 90 
91void JsPreloadUIExtensionCallbackClient::CallJsPreloadedUIExtensionAbility(int32_t preloadId)91void JsPreloadUIExtensionCallbackClient::CallJsPreloadedUIExtensionAbility(int32_t preloadId)
92{92{
93+ if (env_ == nullptr) {
xhz-sz
xhz-szxhz-sz18 天前

补充判空

likedislike
94+ TAG_LOGE(AAFwkTag::ABILITYMGR, "null env_");
95+ return;
96+ }
93 HandleScope handleScope(env_);97 HandleScope handleScope(env_);
94 if (callbackRef_ == nullptr) {98 if (callbackRef_ == nullptr) {
95 TAG_LOGE(AAFwkTag::ABILITYMGR, "null callbackRef_");99 TAG_LOGE(AAFwkTag::ABILITYMGR, "null callbackRef_");
@@ -1133,6 +1133,26 @@ bool JsUIExtensionContext::CheckConnectAlreadyExist(napi_env env, AAFwk::Want& w
1133 return true;1133 return true;
1134}1134}
1135 1135 
1136+static sptr<JSUIServiceUIExtConnection> CreateUIServiceExtConnection(napi_env env, AAFwk::Want& want)
1137+{
1138+ sptr<JSUIServiceUIExtConnection> connection = sptr<JSUIServiceUIExtConnection>::MakeSptr(env);
1139+ if (connection == nullptr) {
1140+ TAG_LOGE(AAFwkTag::UISERVC_EXT, "null connection");
1141+ ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER),
1142+ GetInnerErrorMsg(AbilityInnerErrorMsg::MEMORY_ALLOC_FAILED));
1143+ return nullptr;
1144+ }
1145+ sptr<UIExtensionServiceHostStubImpl> stub = connection->GetServiceHostStub();
1146+ if (stub == nullptr) {
1147+ TAG_LOGE(AAFwkTag::UISERVC_EXT, "null service host stub");
1148+ ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER),
1149+ GetInnerErrorMsg(AbilityInnerErrorMsg::MEMORY_ALLOC_FAILED));
1150+ return nullptr;
1151+ }
1152+ want.SetParam(UISERVICEHOSTPROXY_KEY, stub->AsObject());
1153+ return connection;
1154+}
1155+ 
1136napi_value JsUIExtensionContext::OnConnectUIServiceExtension(napi_env env, NapiCallbackInfo& info)1156napi_value JsUIExtensionContext::OnConnectUIServiceExtension(napi_env env, NapiCallbackInfo& info)
1137{1157{
1138 TAG_LOGI(AAFwkTag::UISERVC_EXT, "called");1158 TAG_LOGI(AAFwkTag::UISERVC_EXT, "called");
@@ -1153,9 +1173,10 @@ napi_value JsUIExtensionContext::OnConnectUIServiceExtension(napi_env env, NapiC
1153 return result;1173 return result;
1154 }1174 }
1155 1175 
1156- sptr<JSUIServiceUIExtConnection> connection = sptr<JSUIServiceUIExtConnection>::MakeSptr(env);1176+ sptr<JSUIServiceUIExtConnection> connection = CreateUIServiceExtConnection(env, want);
1157- sptr<UIExtensionServiceHostStubImpl> stub = connection->GetServiceHostStub();1177+ if (connection == nullptr) {
1158- want.SetParam(UISERVICEHOSTPROXY_KEY, stub->AsObject());1178+ return CreateJsUndefined(env);
1179+ }
1159 1180 
1160 result = nullptr;1181 result = nullptr;
1161 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);1182 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
@@ -1203,6 +1224,21 @@ void JsUIExtensionContext::DoConnectUIServiceExtension(napi_env env,
1203 }1224 }
1204}1225}
1205 1226 
1227+static void DoDisconnectUIServiceExtensionComplete(napi_env env, NapiAsyncTask& task,
1228+ int64_t connectId, std::shared_ptr<ErrCode> innerErrCode)
1229+{
1230+ if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)) {
1231+ task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
1232+ UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
1233+ } else if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER)) {
1234+ task.Reject(env, CreateJsError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER),
1235+ GetInnerErrorMsg(AbilityInnerErrorMsg::CONNECTION_NOT_FOUND)));
1236+ UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
1237+ } else {
1238+ task.ResolveWithNoError(env, CreateJsUndefined(env));
1239+ }
1240+}
1241+ 
1206napi_value JsUIExtensionContext::OnDisconnectUIServiceExtension(napi_env env, NapiCallbackInfo& info)1242napi_value JsUIExtensionContext::OnDisconnectUIServiceExtension(napi_env env, NapiCallbackInfo& info)
1207{1243{
1208 if (info.argc < ARGC_ONE) {1244 if (info.argc < ARGC_ONE) {
@@ -1235,21 +1271,13 @@ napi_value JsUIExtensionContext::OnDisconnectUIServiceExtension(napi_env env, Na
1235 if (!connection) {1271 if (!connection) {
1236 TAG_LOGW(AAFwkTag::UISERVC_EXT, "null connection");1272 TAG_LOGW(AAFwkTag::UISERVC_EXT, "null connection");
1237 *innerErrCode = static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER);1273 *innerErrCode = static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER);
1274+ return;
1238 }1275 }
1239 context->DisconnectAbility(want, connection);1276 context->DisconnectAbility(want, connection);
1240 };1277 };
1241 NapiAsyncTask::CompleteCallback complete =1278 NapiAsyncTask::CompleteCallback complete =
1242 [connectId, innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {1279 [connectId, innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
1243- if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)) {1280+ DoDisconnectUIServiceExtensionComplete(env, task, connectId, innerErrCode);
1244- task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
1245- UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
1246- } else if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER)) {
1247- task.Reject(env, CreateJsError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER),
1248- GetInnerErrorMsg(AbilityInnerErrorMsg::CONNECTION_NOT_FOUND)));
1249- UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
1250- } else {
1251- task.ResolveWithNoError(env, CreateJsUndefined(env));
1252- }
1253 };1281 };
1254 napi_value result = nullptr;1282 napi_value result = nullptr;
1255 NapiAsyncTask::Schedule("JsUIExtensionContext::OnDisconnectUIServiceExtension",1283 NapiAsyncTask::Schedule("JsUIExtensionContext::OnDisconnectUIServiceExtension",
@@ -1259,6 +1287,11 @@ napi_value JsUIExtensionContext::OnDisconnectUIServiceExtension(napi_env env, Na
1259 1287 
1260napi_value JsUIExtensionContext::OnReportDrawnCompleted(napi_env env, NapiCallbackInfo& info)1288napi_value JsUIExtensionContext::OnReportDrawnCompleted(napi_env env, NapiCallbackInfo& info)
1261{1289{
1290+ if (info.argc < ARGC_ONE) {
1291+ TAG_LOGE(AAFwkTag::UI_EXT, "invalid argc");
1292+ ThrowTooFewParametersError(env);
1293+ return CreateJsUndefined(env);
1294+ }
1262 TAG_LOGD(AAFwkTag::UI_EXT, "called");1295 TAG_LOGD(AAFwkTag::UI_EXT, "called");
1263 auto innerErrorCode = std::make_shared<int32_t>(ERR_OK);1296 auto innerErrorCode = std::make_shared<int32_t>(ERR_OK);
1264 NapiAsyncTask::ExecuteCallback execute = [weak = context_, innerErrorCode]() {1297 NapiAsyncTask::ExecuteCallback execute = [weak = context_, innerErrorCode]() {
@@ -667,6 +667,9 @@ void JSUIServiceExtensionConnection::HandleOnAbilityDisconnectDone(const AppExec
667 });667 });
668 if (item != g_connects.end()) {668 if (item != g_connects.end()) {
669 // match bundlename && abilityname669 // match bundlename && abilityname
670+ if (item->second) {
671+ item->second->RemoveConnectionObject();
672+ }
670 g_connects.erase(item);673 g_connects.erase(item);
671 TAG_LOGD(674 TAG_LOGD(
672 AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone erase g_connects.size:%{public}zu", g_connects.size());675 AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone erase g_connects.size:%{public}zu", g_connects.size());
@@ -580,15 +580,28 @@ int32_t UIExtensionAbilityManager::RegisterPreloadUIExtensionHostClient(const sp
580 580 
581 {581 {
582 std::lock_guard lock(preloadUIExtRecipientMapMutex_);582 std::lock_guard lock(preloadUIExtRecipientMapMutex_);
583+ auto it = preloadUIExtensionHostClientDeathRecipients_.find(callerPid);
584+ if (it != preloadUIExtensionHostClientDeathRecipients_.end()) {
585+ TAG_LOGW(AAFwkTag::UI_EXT, "recipient added before, callerPid: %{public}d", callerPid);
586+ return ERR_OK;
587+ }
588+ if (!callerToken->AddDeathRecipient(deathRecipient)) {
589+ TAG_LOGE(AAFwkTag::UI_EXT, "AddDeathRecipient fail");
590+ return INNER_ERR;
591+ }
583 preloadUIExtensionHostClientDeathRecipients_[callerPid] = deathRecipient;592 preloadUIExtensionHostClientDeathRecipients_[callerPid] = deathRecipient;
584 }593 }
585 594 
586- callerToken->AddDeathRecipient(deathRecipient);
587 try {595 try {
588 uiExtensionAbilityRecordMgr_->RegisterPreloadUIExtensionHostClient(callerToken);596 uiExtensionAbilityRecordMgr_->RegisterPreloadUIExtensionHostClient(callerToken);
589 } catch (std::exception &e) {597 } catch (std::exception &e) {
590 TAG_LOGE(AAFwkTag::UI_EXT, "RegisterPreloadUIExtensionHostClient failed, exception = %{public}s", e.what());598 TAG_LOGE(AAFwkTag::UI_EXT, "RegisterPreloadUIExtensionHostClient failed, exception = %{public}s", e.what());
591 callerToken->RemoveDeathRecipient(deathRecipient);599 callerToken->RemoveDeathRecipient(deathRecipient);
600+ {
601+ std::lock_guard lock(preloadUIExtRecipientMapMutex_);
602+ preloadUIExtensionHostClientDeathRecipients_.erase(callerPid);
603+ }
604+ return INNER_ERR;
592 }605 }
593 return ERR_OK;606 return ERR_OK;
594}607}
@@ -1550,7 +1563,6 @@ void UIExtensionAbilityManager::CompleteBackground(const std::shared_ptr<BaseExt
1550 return;1563 return;
1551 }1564 }
1552 abilityRecord->SetAbilityState(AbilityState::BACKGROUND);1565 abilityRecord->SetAbilityState(AbilityState::BACKGROUND);
1553- CHECK_POINTER(abilityRecord);
1554 auto sessionInfo = abilityRecord->GetSessionInfo();1566 auto sessionInfo = abilityRecord->GetSessionInfo();
1555 CHECK_POINTER(sessionInfo);1567 CHECK_POINTER(sessionInfo);
1556 TAG_LOGI(AAFwkTag::UI_EXT,1568 TAG_LOGI(AAFwkTag::UI_EXT,
@@ -52,7 +52,7 @@ int WantReceiverStub::SendInner(MessageParcel &data, MessageParcel &reply)
52 52 
53int WantReceiverStub::PerformReceiveInner(MessageParcel &data, MessageParcel &reply)53int WantReceiverStub::PerformReceiveInner(MessageParcel &data, MessageParcel &reply)
54{54{
55- Want *want = data.ReadParcelable<Want>();55+ std::unique_ptr<Want> want(data.ReadParcelable<Want>());
56 if (want == nullptr) {56 if (want == nullptr) {
57 TAG_LOGE(AAFwkTag::WANTAGENT, "null want");57 TAG_LOGE(AAFwkTag::WANTAGENT, "null want");
58 return ERR_INVALID_VALUE;58 return ERR_INVALID_VALUE;
@@ -61,10 +61,9 @@ int WantReceiverStub::PerformReceiveInner(MessageParcel &data, MessageParcel &re
61 int resultCode = data.ReadInt32();61 int resultCode = data.ReadInt32();
62 std::string bundleName = Str16ToStr8(data.ReadString16());62 std::string bundleName = Str16ToStr8(data.ReadString16());
63 63 
64- WantParams *wantParams = data.ReadParcelable<WantParams>();64+ std::unique_ptr<WantParams> wantParams(data.ReadParcelable<WantParams>());
65 if (wantParams == nullptr) {65 if (wantParams == nullptr) {
66 TAG_LOGE(AAFwkTag::WANTAGENT, "null wantParams");66 TAG_LOGE(AAFwkTag::WANTAGENT, "null wantParams");
67- delete want;
68 return ERR_INVALID_VALUE;67 return ERR_INVALID_VALUE;
69 }68 }
70 69 
@@ -72,8 +71,6 @@ int WantReceiverStub::PerformReceiveInner(MessageParcel &data, MessageParcel &re
72 bool sticky = data.ReadBool();71 bool sticky = data.ReadBool();
73 int sendingUser = data.ReadInt32();72 int sendingUser = data.ReadInt32();
74 PerformReceive(*want, resultCode, bundleName, *wantParams, serialized, sticky, sendingUser);73 PerformReceive(*want, resultCode, bundleName, *wantParams, serialized, sticky, sendingUser);
75- delete want;
76- delete wantParams;
77 return NO_ERROR;74 return NO_ERROR;
78}75}
79} // namespace AAFwk76} // namespace AAFwk
@@ -15,6 +15,8 @@
15 15 
16#include "wants_info.h"16#include "wants_info.h"
17 17 
18+#include "hilog_tag_wrapper.h"
19+ 
18namespace OHOS {20namespace OHOS {
19namespace AAFwk {21namespace AAFwk {
20bool WantsInfo::ReadFromParcel(Parcel &parcel)22bool WantsInfo::ReadFromParcel(Parcel &parcel)
@@ -45,8 +47,14 @@ WantsInfo *WantsInfo::Unmarshalling(Parcel &parcel)
45 47 
46bool WantsInfo::Marshalling(Parcel &parcel) const48bool WantsInfo::Marshalling(Parcel &parcel) const
47{49{
48- parcel.WriteParcelable(&want);50+ if (!parcel.WriteParcelable(&want)) {
49- parcel.WriteString16(Str8ToStr16(resolvedTypes));51+ TAG_LOGE(AAFwkTag::ABILITYMGR, "write want failed");
52+ return false;
53+ }
54+ if (!parcel.WriteString16(Str8ToStr16(resolvedTypes))) {
55+ TAG_LOGE(AAFwkTag::ABILITYMGR, "write resolvedTypes failed");
56+ return false;
57+ }
50 return true;58 return true;
51}59}
52} // namespace AAFwk60} // namespace AAFwk
@@ -555,6 +555,7 @@ group("unittest") {
555 "ui_extension_utils_test:unittest",555 "ui_extension_utils_test:unittest",
556 "ui_extension_ability_manager_test:unittest",556 "ui_extension_ability_manager_test:unittest",
557 "ui_extension_ability_manager_second_test:unittest",557 "ui_extension_ability_manager_second_test:unittest",
558+ "ui_extension_ability_manager_third_test:unittest",
558 "update_caller_info_util_test:unittest",559 "update_caller_info_util_test:unittest",
559 "uri_utils_second_test",560 "uri_utils_second_test",
560 "uri_utils_test:unittest",561 "uri_utils_test:unittest",
@@ -28,13 +28,18 @@ ohos_unittest("js_ui_extension_context_test") {
28 debug = false28 debug = false
29 }29 }
30 include_dirs = [30 include_dirs = [
31+ "${ability_runtime_napi_path}/ability_manager",
31 "${ability_runtime_path}/interfaces/kits/native/ability/native",32 "${ability_runtime_path}/interfaces/kits/native/ability/native",
32 "${ability_runtime_path}/interfaces/kits/native/ability/native/ability_runtime",33 "${ability_runtime_path}/interfaces/kits/native/ability/native/ability_runtime",
33 "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability",34 "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability",
34 "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base",35 "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base",
36+ "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection",
35 ]37 ]
36 38 
37- sources = [ "js_ui_extension_context_test.cpp" ]39+ sources = [
40+ "${ability_runtime_napi_path}/ability_manager/js_preload_ui_extension_callback_client.cpp",
41+ "js_ui_extension_context_test.cpp",
42+ ]
38 43 
39 configs = []44 configs = []
40 45 
@@ -46,6 +51,7 @@ ohos_unittest("js_ui_extension_context_test") {
46 "${ability_runtime_native_path}/ability/native:abilitykit_native",51 "${ability_runtime_native_path}/ability/native:abilitykit_native",
47 "${ability_runtime_native_path}/ability/native:extensionkit_native",52 "${ability_runtime_native_path}/ability/native:extensionkit_native",
48 "${ability_runtime_native_path}/ability/native:ui_extension",53 "${ability_runtime_native_path}/ability/native:ui_extension",
54+ "${ability_runtime_native_path}/ability/native:ui_service_extension_connection",
49 "${ability_runtime_native_path}/ability:ability_context_native",55 "${ability_runtime_native_path}/ability:ability_context_native",
50 "${ability_runtime_native_path}/appkit:app_context",56 "${ability_runtime_native_path}/appkit:app_context",
51 "${ability_runtime_path}/js_environment/frameworks/js_environment:js_environment",57 "${ability_runtime_path}/js_environment/frameworks/js_environment:js_environment",
@@ -13,7 +13,9 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16+#include <atomic>
16#include <gtest/gtest.h>17#include <gtest/gtest.h>
18+#include <limits>
17#include <singleton.h>19#include <singleton.h>
18#include <uv.h>20#include <uv.h>
19#include "ability_context.h"21#include "ability_context.h"
@@ -33,6 +35,10 @@
33#include "native_engine/native_engine.h"35#include "native_engine/native_engine.h"
34#include "js_runtime_lite.h"36#include "js_runtime_lite.h"
35#include "napi_common_want.h"37#include "napi_common_want.h"
38+#include "js_preload_ui_extension_callback_client.h"
39+#include "js_ui_service_proxy.h"
40+#include "ui_extension_servicehost_stub_impl.h"
41+#include "js_uiservice_uiext_connection.h"
36 42 
37using namespace testing;43using namespace testing;
38using namespace testing::ext;44using namespace testing::ext;
@@ -66,6 +72,7 @@ class MockDeferred : public NativeDeferred {
66public:72public:
67 void Resolve(napi_value data) override73 void Resolve(napi_value data) override
68 {74 {
75+ settled_ = true;
69 resolved_ = true;76 resolved_ = true;
70 if (nref_ != nullptr) {77 if (nref_ != nullptr) {
71 napi_delete_reference(env_, nref_);78 napi_delete_reference(env_, nref_);
@@ -78,10 +85,12 @@ public:
78 85 
79 void Reject(napi_value reason) override86 void Reject(napi_value reason) override
80 {87 {
88+ settled_ = true;
81 resolved_ = false;89 resolved_ = false;
82 }90 }
83 91 
84public:92public:
93+ static bool IsSettled() { return settled_; }
85 static bool GetLastResolveStatus() { return resolved_; }94 static bool GetLastResolveStatus() { return resolved_; }
86 static napi_ref GetLastResolveValue() { return nref_; }95 static napi_ref GetLastResolveValue() { return nref_; }
87 static void Clear()96 static void Clear()
@@ -90,10 +99,14 @@ public:
90 delete (reinterpret_cast<NativeReference*>(nref_));99 delete (reinterpret_cast<NativeReference*>(nref_));
91 nref_ = nullptr;100 nref_ = nullptr;
92 }101 }
102+ settled_ = false;
103+ resolved_ = false;
93 }104 }
105+ static bool settled_;
94 static bool resolved_;106 static bool resolved_;
95 static napi_ref nref_;107 static napi_ref nref_;
96};108};
109+bool MockDeferred::settled_ = false;
97bool MockDeferred::resolved_ = false;110bool MockDeferred::resolved_ = false;
98napi_ref MockDeferred::nref_ = nullptr;111napi_ref MockDeferred::nref_ = nullptr;
99 112 
@@ -122,8 +135,15 @@ public:
122 virtual ErrCode DisconnectAbility(const AAFwk::Want &want,135 virtual ErrCode DisconnectAbility(const AAFwk::Want &want,
123 const sptr<AbilityConnectCallback> &connectCallback) const override136 const sptr<AbilityConnectCallback> &connectCallback) const override
124 {137 {
138+ disconnectAbilityCount_++;
125 return ERR_OK;139 return ERR_OK;
126 }140 }
141+ 
142+ ErrCode ReportDrawnCompleted() override
143+ {
144+ reportDrawnCompletedCount_++;
145+ return reportDrawnCompletedResult_;
146+ }
127public:147public:
128 static void DoneConnect(int status)148 static void DoneConnect(int status)
129 {149 {
@@ -139,9 +159,14 @@ public:
139 callback_->OnAbilityDisconnectDone(element, 0);159 callback_->OnAbilityDisconnectDone(element, 0);
140 }160 }
141 void SetConnectResult(ErrCode code) { connectRet_ = code; }161 void SetConnectResult(ErrCode code) { connectRet_ = code; }
162+ int32_t GetDisconnectAbilityCount() const { return disconnectAbilityCount_.load(); }
163+ int32_t GetReportDrawnCompletedCount() const { return reportDrawnCompletedCount_.load(); }
142protected:164protected:
143 static sptr<AbilityConnectCallback> callback_;165 static sptr<AbilityConnectCallback> callback_;
144 ErrCode connectRet_ = ERR_OK;166 ErrCode connectRet_ = ERR_OK;
167+ ErrCode reportDrawnCompletedResult_ = ERR_OK;
168+ mutable std::atomic<int32_t> disconnectAbilityCount_ = 0;
169+ std::atomic<int32_t> reportDrawnCompletedCount_ = 0;
145};170};
146 171 
147sptr<AbilityConnectCallback> MockAbilityContextImpl::callback_;172sptr<AbilityConnectCallback> MockAbilityContextImpl::callback_;
@@ -160,6 +185,7 @@ public:
160 }185 }
161 void Connect(napi_value* argv, int32_t argc);186 void Connect(napi_value* argv, int32_t argc);
162 void Disconnect(napi_value* argv, int32_t argc);187 void Disconnect(napi_value* argv, int32_t argc);
188+ void ReportDrawnCompleted(napi_value* argv, int32_t argc);
163public:189public:
164 std::shared_ptr<JsUIExtensionContext> jsUIExtensionContext_;190 std::shared_ptr<JsUIExtensionContext> jsUIExtensionContext_;
165 std::shared_ptr<MockAbilityContextImpl> abilityContextImpl_;191 std::shared_ptr<MockAbilityContextImpl> abilityContextImpl_;
@@ -264,6 +290,27 @@ void UIExtensionContextTest::Disconnect(napi_value* argv, int32_t argc)
264 }290 }
265}291}
266 292 
293+void UIExtensionContextTest::ReportDrawnCompleted(napi_value* argv, int32_t argc)
294+{
295+ napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value {
296+ return JsUIExtensionContext::ReportDrawnCompleted(env, info);
297+ };
298+ HandleScope handleScope(env_);
299+ napi_value recv = nullptr;
300+ napi_create_object(env_, &recv);
301+ napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(),
302+ [](napi_env env, void* data, void* hint) {}, nullptr, nullptr);
303+ EXPECT_EQ(wrapret, napi_ok);
304+ 
305+ napi_value funcValue = nullptr;
306+ napi_create_function(env_, "reportDrawnCompleted", NAPI_AUTO_LENGTH, func, nullptr, &funcValue);
307+ napi_value funcResultValue = nullptr;
308+ napi_status status = napi_call_function(env_, recv, funcValue, argc, argv, &funcResultValue);
309+ if (status != napi_ok) {
310+ TAG_LOGE(AAFwkTag::UI_EXT, "call reportDrawnCompleted failed %{public}d", status);
311+ }
312+}
313+ 
267HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_0100, TestSize.Level1)314HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_0100, TestSize.Level1)
268{315{
269 GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_0100 start";316 GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_0100 start";
@@ -391,6 +438,95 @@ HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_0105, TestSiz
391 GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_0105 end";438 GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_0105 end";
392}439}
393 440 
441+/**
442+ * @tc.name: AbilityRuntime_UIExtensionContext_DisconnectMissingConnection_0100
443+ * @tc.desc: A missing connection is rejected without calling the native disconnect API.
444+ * @tc.type: FUNC
445+ */
446+HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_DisconnectMissingConnection_0100, TestSize.Level1)
447+{
448+ HandleScope handleScope(env_);
449+ TryCatch tryCatch(env_);
450+ constexpr int64_t missingConnectionId = std::numeric_limits<int64_t>::max();
451+ UIServiceConnection::RemoveUIServiceExtensionConnection(missingConnectionId);
452+ sptr<IRemoteObject> remoteObject = nullptr;
453+ napi_value proxy = AAFwk::JsUIServiceProxy::CreateJsUIServiceProxy(
454+ env_, remoteObject, missingConnectionId, remoteObject);
455+ ASSERT_NE(proxy, nullptr);
456+ napi_value argv[] = { proxy };
457+ 
458+ Disconnect(argv, ARGC_ONE);
459+ ArkNativeEngine* engine = reinterpret_cast<ArkNativeEngine*>(env_);
460+ uv_loop_t* loop = engine->GetUVLoop();
461+ RunNowait(loop);
462+ RunNowait(loop);
463+ 
464+ EXPECT_FALSE(tryCatch.HasCaught());
465+ EXPECT_EQ(abilityContextImpl_->GetDisconnectAbilityCount(), 0);
466+ EXPECT_TRUE(MockDeferred::IsSettled());
467+ EXPECT_FALSE(MockDeferred::GetLastResolveStatus());
468+}
469+ 
470+/**
471+ * @tc.name: AbilityRuntime_UIExtensionContext_ReportDrawnCompleted_0100
472+ * @tc.desc: Calling reportDrawnCompleted without a callback reports too few parameters.
473+ * @tc.type: FUNC
474+ */
475+HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_ReportDrawnCompleted_0100, TestSize.Level1)
476+{
477+ HandleScope handleScope(env_);
478+ TryCatch tryCatch(env_);
479+ 
480+ ReportDrawnCompleted(nullptr, ARGC_ZERO);
481+ 
482+ EXPECT_TRUE(tryCatch.HasCaught());
483+ EXPECT_EQ(abilityContextImpl_->GetReportDrawnCompletedCount(), 0);
484+ tryCatch.ClearException();
485+ ArkNativeEngine* engine = reinterpret_cast<ArkNativeEngine*>(env_);
486+ if (!engine->lastException_.IsEmpty()) {
487+ engine->lastException_.Empty();
488+ }
489+}
490+ 
491+/**
492+ * @tc.name: AbilityRuntime_UIExtensionContext_ReportDrawnCompleted_0200
493+ * @tc.desc: A valid callback invokes the native reportDrawnCompleted API and completes asynchronously.
494+ * @tc.type: FUNC
495+ */
496+HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_ReportDrawnCompleted_0200, TestSize.Level1)
497+{
498+ HandleScope handleScope(env_);
499+ bool callbackInvoked = false;
500+ napi_value callback = nullptr;
501+ ASSERT_EQ(napi_create_function(env_, "reportDrawnCallback", NAPI_AUTO_LENGTH,
502+ MarkCallbackInvoked, &callbackInvoked, &callback), napi_ok);
503+ napi_value argv[] = { callback };
504+ 
505+ ReportDrawnCompleted(argv, ARGC_ONE);
506+ ArkNativeEngine* engine = reinterpret_cast<ArkNativeEngine*>(env_);
507+ uv_loop_t* loop = engine->GetUVLoop();
508+ RunNowait(loop);
509+ RunNowait(loop);
510+ 
511+ EXPECT_EQ(abilityContextImpl_->GetReportDrawnCompletedCount(), 1);
512+ EXPECT_TRUE(callbackInvoked);
513+}
514+ 
515+/**
516+ * @tc.name: AbilityRuntime_PreloadUIExtensionCallback_NullEnv_0100
517+ * @tc.desc: Calling the preload callback client with a null NAPI environment does not crash.
518+ * @tc.type: FUNC
519+ */
520+HWTEST_F(UIExtensionContextTest, AbilityRuntime_PreloadUIExtensionCallback_NullEnv_0100, TestSize.Level1)
521+{
522+ auto callbackClient = std::make_shared<JsPreloadUIExtensionCallbackClient>(nullptr, nullptr);
523+ ASSERT_NE(callbackClient, nullptr);
524+ 
525+ callbackClient->CallJsPreloadedUIExtensionAbility(1);
526+ 
527+ SUCCEED();
528+}
529+ 
394HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_0106, TestSize.Level1)530HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_0106, TestSize.Level1)
395{531{
396 GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_0106 start";532 GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_0106 start";
@@ -1513,4 +1649,4 @@ HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultEmbeddable_0400, TestSiz
1513 GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0400 end";1649 GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0400 end";
1514}1650}
1515} // namespace AAFwk1651} // namespace AAFwk
1516-} // namespace OHOS1652+} // namespace OHOS
@@ -37,6 +37,17 @@ namespace OHOS {
37namespace AbilityRuntime {37namespace AbilityRuntime {
38namespace {38namespace {
39const int64_t COMMECTION_ID = 100;39const int64_t COMMECTION_ID = 100;
40+ 
41+napi_value MarkDisconnectCallbackInvoked(napi_env env, napi_callback_info info)
42+{
43+ size_t argc = 0;
44+ void* data = nullptr;
45+ napi_get_cb_info(env, info, &argc, nullptr, nullptr, &data);
46+ if (data != nullptr) {
47+ *static_cast<bool*>(data) = true;
48+ }
49+ return CreateJsUndefined(env);
50+}
40} // namespace51} // namespace
41 52 
42class JsUiServiceExtensionContextSecondTest : public testing::Test {53class JsUiServiceExtensionContextSecondTest : public testing::Test {
@@ -71,6 +82,16 @@ void JsUiServiceExtensionContextSecondTest::SetUp()
71 82 
72void JsUiServiceExtensionContextSecondTest::TearDown()83void JsUiServiceExtensionContextSecondTest::TearDown()
73{84{
85+ {
86+ std::lock_guard guard(g_connectsMutex);
87+ for (auto &item : g_connects) {
88+ if (item.second != nullptr) {
89+ item.second->RemoveConnectionObject();
90+ }
91+ }
92+ g_connects.clear();
93+ g_serialNumber = 0;
94+ }
74 if (env_ != nullptr) {95 if (env_ != nullptr) {
75 delete reinterpret_cast<NativeEngine*>(env_);96 delete reinterpret_cast<NativeEngine*>(env_);
76 env_ = nullptr;97 env_ = nullptr;
@@ -167,6 +188,44 @@ HWTEST_F(JsUiServiceExtensionContextSecondTest, FindConnection_0100, TestSize.Le
167 TAG_LOGI(AAFwkTag::TEST, "FindConnection_0100 end");188 TAG_LOGI(AAFwkTag::TEST, "FindConnection_0100 end");
168}189}
169 190 
191+/**
192+ * @tc.name: HandleOnAbilityDisconnectDone_0400
193+ * @tc.desc: A matched connection releases its JS reference and is removed after disconnect.
194+ * @tc.type: FUNC
195+ */
196+HWTEST_F(JsUiServiceExtensionContextSecondTest, HandleOnAbilityDisconnectDone_0400, TestSize.Level1)
197+{
198+ bool callbackInvoked = false;
199+ napi_value connectionObject = nullptr;
200+ ASSERT_EQ(napi_create_object(env_, &connectionObject), napi_ok);
201+ napi_value onDisconnect = nullptr;
202+ ASSERT_EQ(napi_create_function(env_, "onDisconnect", NAPI_AUTO_LENGTH,
203+ MarkDisconnectCallbackInvoked, &callbackInvoked, &onDisconnect), napi_ok);
204+ ASSERT_EQ(napi_set_named_property(env_, connectionObject, "onDisconnect", onDisconnect), napi_ok);
205+ 
206+ sptr<JSUIServiceExtensionConnection> connection = new JSUIServiceExtensionConnection(env_);
207+ connection->SetJsConnectionObject(connectionObject);
208+ connection->SetConnectionId(COMMECTION_ID);
209+ AppExecFwk::ElementName element("device", "com.example.uiservice", "UIServiceExtensionAbility");
210+ Want want;
211+ want.SetElement(element);
212+ ConnectionKey key;
213+ key.want = want;
214+ key.id = COMMECTION_ID;
215+ key.accountId = -1;
216+ {
217+ std::lock_guard guard(g_connectsMutex);
218+ g_connects.emplace(key, connection);
219+ }
220+ 
221+ connection->HandleOnAbilityDisconnectDone(element, ERR_OK);
222+ 
223+ EXPECT_TRUE(callbackInvoked);
224+ EXPECT_EQ(connection->jsConnectionObject_, nullptr);
225+ std::lock_guard guard(g_connectsMutex);
226+ EXPECT_TRUE(g_connects.empty());
227+}
228+ 
170/**229/**
171 * @tc.name: OnConnectServiceExtensionAbility_0100230 * @tc.name: OnConnectServiceExtensionAbility_0100
172 * @tc.desc: basic function test.231 * @tc.desc: basic function test.
@@ -247,4 +306,4 @@ HWTEST_F(JsUiServiceExtensionContextSecondTest, OnDisConnectServiceExtensionAbil
247 TAG_LOGI(AAFwkTag::TEST, "OnDisConnectServiceExtensionAbility_0200 end");306 TAG_LOGI(AAFwkTag::TEST, "OnDisConnectServiceExtensionAbility_0200 end");
248}307}
249} // namespace AbilityRuntime308} // namespace AbilityRuntime
250-} // namespace OHOS309+} // namespace OHOS
@@ -1018,21 +1018,16 @@ HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_RegisterPreloadUIExtensi
1018/*1018/*
1019 * Feature: UIExtensionAbilityManager1019 * Feature: UIExtensionAbilityManager
1020 * Function: RegisterPreloadUIExtensionHostClient1020 * Function: RegisterPreloadUIExtensionHostClient
1021- * SubFunction: NA1021+ * CaseDescription: Verify registration fails when a local token cannot register a death recipient
1022- * FunctionPoints: NA
1023- * EnvConditions: NA
1024- * CaseDescription: Verify RegisterPreloadUIExtensionHostClient with valid parameters
1025 */1022 */
1026HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_RegisterPreloadUIExtensionHostClient_003, TestSize.Level1)1023HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_RegisterPreloadUIExtensionHostClient_003, TestSize.Level1)
1027{1024{
1028 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);1025 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
1029- std::shared_ptr<AbilityRecord> abilityRecord = serviceRecord_;1026+ sptr<IRemoteObject> callerToken = serviceRecord_->GetToken();
1030- ASSERT_NE(abilityRecord, nullptr);1027+ 
1031- sptr<IRemoteObject> callerToken = abilityRecord->GetToken();1028+ EXPECT_EQ(connectManager->RegisterPreloadUIExtensionHostClient(callerToken), INNER_ERR);
1032- ASSERT_NE(callerToken, nullptr);1029+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
1033- 1030+ EXPECT_TRUE(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.empty());
1034- int32_t res = connectManager->RegisterPreloadUIExtensionHostClient(callerToken);
1035- EXPECT_EQ(res, ERR_OK);
1036}1031}
1037 1032 
1038/*1033/*
@@ -1073,43 +1068,30 @@ HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_UnRegisterPreloadUIExten
1073/*1068/*
1074 * Feature: UIExtensionAbilityManager1069 * Feature: UIExtensionAbilityManager
1075 * Function: UnRegisterPreloadUIExtensionHostClient1070 * Function: UnRegisterPreloadUIExtensionHostClient
1076- * SubFunction: NA1071+ * CaseDescription: Verify unregistering an unknown process is idempotent
1077- * FunctionPoints: NA
1078- * EnvConditions: NA
1079- * CaseDescription: Verify UnRegisterPreloadUIExtensionHostClient with valid callerPid
1080 */1072 */
1081HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_UnRegisterPreloadUIExtensionHostClient_003, TestSize.Level1)1073HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_UnRegisterPreloadUIExtensionHostClient_003, TestSize.Level1)
1082{1074{
1083 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);1075 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
1084- int32_t callerPid = 5678;1076+ 
1085- 1077+ EXPECT_EQ(connectManager->UnRegisterPreloadUIExtensionHostClient(5678), ERR_OK);
1086- int32_t res = connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid);1078+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
1087- EXPECT_EQ(res, ERR_OK);
1088}1079}
1089 1080 
1090/*1081/*
1091 * Feature: UIExtensionAbilityManager1082 * Feature: UIExtensionAbilityManager
1092 * Function: UnRegisterPreloadUIExtensionHostClient1083 * Function: UnRegisterPreloadUIExtensionHostClient
1093- * SubFunction: NA1084+ * CaseDescription: Verify failed registration leaves no state for unregistering
1094- * FunctionPoints: NA
1095- * EnvConditions: NA
1096- * CaseDescription: Verify UnRegisterPreloadUIExtensionHostClient
1097 */1085 */
1098HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_UnRegisterPreloadUIExtensionHostClient_004, TestSize.Level1)1086HWTEST_F(UIExtensionAbilityManagerTest, AAFwk_AbilityMS_UnRegisterPreloadUIExtensionHostClient_004, TestSize.Level1)
1099{1087{
1100 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);1088 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
1101- std::shared_ptr<AbilityRecord> abilityRecord = serviceRecord_;1089+ sptr<IRemoteObject> callerToken = serviceRecord_->GetToken();
1102- ASSERT_NE(abilityRecord, nullptr);
1103- sptr<IRemoteObject> callerToken = abilityRecord->GetToken();
1104- int32_t callerPid = IPCSkeleton::GetCallingPid();
1105- connectManager->RegisterPreloadUIExtensionHostClient(callerToken);
1106- EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.size(), 1);
1107-
1108- connectManager->UnRegisterPreloadUIExtensionHostClient(1);
1109- EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.size(), 1);
1110 1090 
1111- connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid);1091+ EXPECT_EQ(connectManager->RegisterPreloadUIExtensionHostClient(callerToken), INNER_ERR);
1112- EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.size(), 0);1092+ EXPECT_EQ(connectManager->UnRegisterPreloadUIExtensionHostClient(IPCSkeleton::GetCallingPid()), ERR_OK);
1093+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
1094+ EXPECT_TRUE(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.empty());
1113}1095}
1114 1096 
1115/*1097/*
@@ -1340,21 +1322,16 @@ HWTEST_F(UIExtensionAbilityManagerTest, RegisterPreloadUIExtensionHostClient_002
1340/*1322/*
1341 * Feature: UIExtensionAbilityManager1323 * Feature: UIExtensionAbilityManager
1342 * Function: RegisterPreloadUIExtensionHostClient1324 * Function: RegisterPreloadUIExtensionHostClient
1343- * SubFunction: NA1325+ * CaseDescription: Verify registration fails when a local token cannot register a death recipient
1344- * FunctionPoints: NA
1345- * EnvConditions: NA
1346- * CaseDescription: Verify RegisterPreloadUIExtensionHostClient with valid parameters
1347 */1326 */
1348HWTEST_F(UIExtensionAbilityManagerTest, RegisterPreloadUIExtensionHostClient_003, TestSize.Level1)1327HWTEST_F(UIExtensionAbilityManagerTest, RegisterPreloadUIExtensionHostClient_003, TestSize.Level1)
1349{1328{
1350 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);1329 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
1351- std::shared_ptr<AbilityRecord> abilityRecord = serviceRecord_;1330+ sptr<IRemoteObject> callerToken = serviceRecord_->GetToken();
1352- ASSERT_NE(abilityRecord, nullptr);1331+ 
1353- sptr<IRemoteObject> callerToken = abilityRecord->GetToken();1332+ EXPECT_EQ(connectManager->RegisterPreloadUIExtensionHostClient(callerToken), INNER_ERR);
1354- ASSERT_NE(callerToken, nullptr);1333+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
1355- 1334+ EXPECT_TRUE(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.empty());
1356- int32_t res = connectManager->RegisterPreloadUIExtensionHostClient(callerToken);
1357- EXPECT_EQ(res, ERR_OK);
1358}1335}
1359 1336 
1360/*1337/*
@@ -1395,43 +1372,30 @@ HWTEST_F(UIExtensionAbilityManagerTest, UnRegisterPreloadUIExtensionHostClient_0
1395/*1372/*
1396 * Feature: UIExtensionAbilityManager1373 * Feature: UIExtensionAbilityManager
1397 * Function: UnRegisterPreloadUIExtensionHostClient1374 * Function: UnRegisterPreloadUIExtensionHostClient
1398- * SubFunction: NA1375+ * CaseDescription: Verify unregistering an unknown process is idempotent
1399- * FunctionPoints: NA
1400- * EnvConditions: NA
1401- * CaseDescription: Verify UnRegisterPreloadUIExtensionHostClient with valid callerPid
1402 */1376 */
1403HWTEST_F(UIExtensionAbilityManagerTest, UnRegisterPreloadUIExtensionHostClient_003, TestSize.Level1)1377HWTEST_F(UIExtensionAbilityManagerTest, UnRegisterPreloadUIExtensionHostClient_003, TestSize.Level1)
1404{1378{
1405 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);1379 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
1406- int32_t callerPid = 5678;1380+ 
1407- 1381+ EXPECT_EQ(connectManager->UnRegisterPreloadUIExtensionHostClient(5678), ERR_OK);
1408- int32_t res = connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid);1382+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
1409- EXPECT_EQ(res, ERR_OK);
1410}1383}
1411 1384 
1412/*1385/*
1413 * Feature: UIExtensionAbilityManager1386 * Feature: UIExtensionAbilityManager
1414 * Function: UnRegisterPreloadUIExtensionHostClient1387 * Function: UnRegisterPreloadUIExtensionHostClient
1415- * SubFunction: NA1388+ * CaseDescription: Verify failed registration leaves no state for unregistering
1416- * FunctionPoints: NA
1417- * EnvConditions: NA
1418- * CaseDescription: Verify UnRegisterPreloadUIExtensionHostClient
1419 */1389 */
1420HWTEST_F(UIExtensionAbilityManagerTest, UnRegisterPreloadUIExtensionHostClient_004, TestSize.Level1)1390HWTEST_F(UIExtensionAbilityManagerTest, UnRegisterPreloadUIExtensionHostClient_004, TestSize.Level1)
1421{1391{
1422 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);1392 std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
1423- std::shared_ptr<AbilityRecord> abilityRecord = serviceRecord_;1393+ sptr<IRemoteObject> callerToken = serviceRecord_->GetToken();
1424- ASSERT_NE(abilityRecord, nullptr);
1425- sptr<IRemoteObject> callerToken = abilityRecord->GetToken();
1426- int32_t callerPid = IPCSkeleton::GetCallingPid();
1427- connectManager->RegisterPreloadUIExtensionHostClient(callerToken);
1428- EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.size(), 1);
1429-
1430- connectManager->UnRegisterPreloadUIExtensionHostClient(1);
1431- EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.size(), 1);
1432 1394 
1433- connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid);1395+ EXPECT_EQ(connectManager->RegisterPreloadUIExtensionHostClient(callerToken), INNER_ERR);
1434- EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.size(), 0);1396+ EXPECT_EQ(connectManager->UnRegisterPreloadUIExtensionHostClient(IPCSkeleton::GetCallingPid()), ERR_OK);
1397+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
1398+ EXPECT_TRUE(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.empty());
1435}1399}
1436 1400 
1437/*1401/*
@@ -0,0 +1,101 @@
1+# Copyright (c) 2026 Huawei Device Co., Ltd.
2+# Licensed under the Apache License, Version 2.0 (the "License");
3+# you may not use this file except in compliance with the License.
4+# You may obtain a copy of the License at
5+#
6+# http://www.apache.org/licenses/LICENSE-2.0
7+#
8+# Unless required by applicable law or agreed to in writing, software
9+# distributed under the License is distributed on an "AS IS" BASIS,
10+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+# See the License for the specific language governing permissions and
12+# limitations under the License.
13+ 
14+import("//build/test.gni")
15+import("//foundation/ability/ability_runtime/ability_runtime.gni")
16+ 
17+module_output_path = "ability_runtime/ability_runtime/ui_extension_ability_manager_third"
18+ 
19+ohos_unittest("ui_extension_ability_manager_third_test") {
20+ module_out_path = module_output_path
21+ sanitize = {
22+ cfi = true
23+ cfi_cross_dso = true
24+ debug = false
25+ blocklist = "../../cfi_blocklist.txt"
26+ }
27+ branch_protector_ret = "pac_ret"
28+ 
29+ include_dirs = [
30+ "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/system_ability_mock",
31+ "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include",
32+ "${ability_runtime_test_path}/mock/mock_sa_call",
33+ "${ability_runtime_test_path}/mock/task_handler_wrap_mock/include",
34+ ]
35+ 
36+ sources = [
37+ # add mock file
38+ "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core/src/appmgr/mock_app_scheduler.cpp",
39+ "${ability_runtime_test_path}/mock/task_handler_wrap_mock/src/mock_task_handler_wrap.cpp",
40+ "ui_extension_ability_manager_third_test.cpp",
41+ ]
42+ 
43+ configs = [
44+ "${ability_runtime_services_path}/abilitymgr:abilityms_config",
45+ "${ability_runtime_test_path}/mock/services_abilitymgr_test:aafwk_mock_config",
46+ ]
47+ cflags = []
48+ if (target_cpu == "arm") {
49+ cflags += [ "-DBINDER_IPC_32BIT" ]
50+ }
51+ deps = [
52+ "${ability_runtime_innerkits_path}/ability_manager:ability_connect_callback_stub",
53+ "${ability_runtime_innerkits_path}/ability_manager:ability_manager",
54+ "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper",
55+ "${ability_runtime_native_path}/ability/native:abilitykit_native",
56+ "${ability_runtime_services_path}/abilitymgr:abilityms",
57+ "${ability_runtime_services_path}/common:perm_verification",
58+ "${ability_runtime_services_path}/common:task_handler_wrap",
59+ "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/aakit:aakit_mock",
60+ "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_bundlemgr_mock",
61+ ]
62+ 
63+ external_deps = [
64+ "ability_base:want",
65+ "ability_base:zuri",
66+ "access_token:libaccesstoken_sdk",
67+ "access_token:libnativetoken",
68+ "access_token:libtoken_setproc",
69+ "c_utils:utils",
70+ "common_event_service:cesfwk_innerkits",
71+ "eventhandler:libeventhandler",
72+ "ffrt:libffrt",
73+ "googletest:gmock_main",
74+ "googletest:gtest_main",
75+ "hilog:libhilog",
76+ "hisysevent:libhisysevent",
77+ "init:libbeget_proxy",
78+ "ipc:ipc_core",
79+ "napi:ace_napi",
80+ "safwk:system_ability_fwk",
81+ "samgr:samgr_proxy",
82+ "selinux_adapter:librestorecon",
83+ ]
84+ 
85+ if (ability_runtime_graphics) {
86+ external_deps += [
87+ "image_framework:image_native",
88+ "window_manager:libwsutils",
89+ "window_manager:scene_session",
90+ ]
91+ }
92+ 
93+ if (background_task_mgr_continuous_task_enable) {
94+ external_deps += [ "background_task_mgr:bgtaskmgr_innerkits" ]
95+ }
96+}
97+ 
98+group("unittest") {
99+ testonly = true
100+ deps = [ ":ui_extension_ability_manager_third_test" ]
101+}
@@ -0,0 +1,189 @@
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+#include <gtest/gtest.h>
17+#include <memory>
18+#include <string>
19+#include <vector>
20+ 
21+#define private public
22+#define protected public
23+#include "ui_extension_ability_manager.h"
24+#undef private
25+#undef protected
26+ 
27+#include "ability_manager_errors.h"
28+#include "errors.h"
29+#include "ipc_skeleton.h"
30+#include "iremote_object.h"
31+#include "message_option.h"
32+#include "message_parcel.h"
33+ 
34+using namespace testing::ext;
35+ 
36+namespace OHOS {
37+namespace AAFwk {
38+class MockPreloadHostClient final : public IRemoteObject {
39+public:
40+ explicit MockPreloadHostClient(bool addDeathRecipientResult)
41+ : IRemoteObject(u"mock_preload_host_client"), addDeathRecipientResult_(addDeathRecipientResult)
42+ {}
43+ 
44+ ~MockPreloadHostClient() override = default;
45+ 
46+ int32_t GetObjectRefCount() override
47+ {
48+ return 0;
49+ }
50+ 
51+ int SendRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override
52+ {
53+ (void)code;
54+ (void)data;
55+ (void)reply;
56+ (void)option;
57+ return ERR_OK;
58+ }
59+ 
60+ bool IsProxyObject() const override
61+ {
62+ return true;
63+ }
64+ 
65+ bool CheckObjectLegality() const override
66+ {
67+ return true;
68+ }
69+ 
70+ bool AddDeathRecipient(const sptr<DeathRecipient> &recipient) override
71+ {
72+ addDeathRecipientCount_++;
73+ deathRecipient_ = recipient;
74+ return addDeathRecipientResult_;
75+ }
76+ 
77+ bool RemoveDeathRecipient(const sptr<DeathRecipient> &recipient) override
78+ {
79+ removeDeathRecipientCount_++;
80+ return recipient == deathRecipient_;
81+ }
82+ 
83+ bool Marshalling(Parcel &parcel) const override
84+ {
85+ (void)parcel;
86+ return true;
87+ }
88+ 
89+ sptr<IRemoteBroker> AsInterface() override
90+ {
91+ return nullptr;
92+ }
93+ 
94+ int Dump(int fd, const std::vector<std::u16string> &args) override
95+ {
96+ (void)fd;
97+ (void)args;
98+ return ERR_OK;
99+ }
100+ 
101+ bool addDeathRecipientResult_ = true;
102+ int32_t addDeathRecipientCount_ = 0;
103+ int32_t removeDeathRecipientCount_ = 0;
104+ sptr<DeathRecipient> deathRecipient_ = nullptr;
105+};
106+ 
107+class UIExtensionAbilityManagerThirdTest : public testing::Test {};
108+ 
109+/*
110+ * Feature: UIExtensionAbilityManager
111+ * Function: RegisterPreloadUIExtensionHostClient
112+ * CaseDescription: Verify successful registration and unregister cleanup
113+ */
114+HWTEST_F(UIExtensionAbilityManagerThirdTest, RegisterPreloadUIExtensionHostClient_006, TestSize.Level1)
115+{
116+ std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
117+ sptr<MockPreloadHostClient> callerToken = new MockPreloadHostClient(true);
118+ const int32_t callerPid = IPCSkeleton::GetCallingPid();
119+ 
120+ int32_t res = connectManager->RegisterPreloadUIExtensionHostClient(callerToken);
121+ 
122+ EXPECT_EQ(res, ERR_OK);
123+ EXPECT_EQ(callerToken->addDeathRecipientCount_, 1);
124+ EXPECT_EQ(connectManager->preloadUIExtensionHostClientDeathRecipients_.count(callerPid), 1);
125+ EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.count(
126+ callerPid), 1);
127+ 
128+ res = connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid + 1);
129+ EXPECT_EQ(res, ERR_OK);
130+ EXPECT_EQ(callerToken->removeDeathRecipientCount_, 0);
131+ EXPECT_EQ(connectManager->preloadUIExtensionHostClientDeathRecipients_.count(callerPid), 1);
132+ EXPECT_EQ(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.count(
133+ callerPid), 1);
134+ 
135+ res = connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid);
136+ EXPECT_EQ(res, ERR_OK);
137+ EXPECT_EQ(callerToken->removeDeathRecipientCount_, 1);
138+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
139+ EXPECT_TRUE(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.empty());
140+}
141+ 
142+/*
143+ * Feature: UIExtensionAbilityManager
144+ * Function: RegisterPreloadUIExtensionHostClient
145+ * CaseDescription: Verify registration rollback when adding a death recipient fails
146+ */
147+HWTEST_F(UIExtensionAbilityManagerThirdTest, RegisterPreloadUIExtensionHostClient_007, TestSize.Level1)
148+{
149+ std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
150+ sptr<MockPreloadHostClient> callerToken = new MockPreloadHostClient(false);
151+ 
152+ int32_t res = connectManager->RegisterPreloadUIExtensionHostClient(callerToken);
153+ 
154+ EXPECT_EQ(res, INNER_ERR);
155+ EXPECT_EQ(callerToken->addDeathRecipientCount_, 1);
156+ EXPECT_EQ(callerToken->removeDeathRecipientCount_, 0);
157+ EXPECT_TRUE(connectManager->preloadUIExtensionHostClientDeathRecipients_.empty());
158+ EXPECT_TRUE(connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.empty());
159+}
160+ 
161+/*
162+ * Feature: UIExtensionAbilityManager
163+ * Function: RegisterPreloadUIExtensionHostClient
164+ * CaseDescription: Verify duplicate registration for the same process is idempotent
165+ */
166+HWTEST_F(UIExtensionAbilityManagerThirdTest, RegisterPreloadUIExtensionHostClient_008, TestSize.Level1)
167+{
168+ std::shared_ptr<UIExtensionAbilityManager> connectManager = std::make_shared<UIExtensionAbilityManager>(0);
169+ sptr<MockPreloadHostClient> firstCallerToken = new MockPreloadHostClient(true);
170+ sptr<MockPreloadHostClient> secondCallerToken = new MockPreloadHostClient(true);
171+ const int32_t callerPid = IPCSkeleton::GetCallingPid();
172+ 
173+ EXPECT_EQ(connectManager->RegisterPreloadUIExtensionHostClient(firstCallerToken), ERR_OK);
174+ EXPECT_EQ(connectManager->RegisterPreloadUIExtensionHostClient(secondCallerToken), ERR_OK);
175+ 
176+ EXPECT_EQ(firstCallerToken->addDeathRecipientCount_, 1);
177+ EXPECT_EQ(secondCallerToken->addDeathRecipientCount_, 0);
178+ EXPECT_EQ(connectManager->preloadUIExtensionHostClientDeathRecipients_.size(), 1);
179+ auto tokenIter = connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.find(
180+ callerPid);
181+ ASSERT_NE(tokenIter,
182+ connectManager->uiExtensionAbilityRecordMgr_->preloadUIExtensionHostClientCallerTokens_.end());
183+ sptr<IRemoteObject> expectedCallerToken = firstCallerToken;
184+ EXPECT_EQ(tokenIter->second, expectedCallerToken);
185+ 
186+ EXPECT_EQ(connectManager->UnRegisterPreloadUIExtensionHostClient(callerPid), ERR_OK);
187+}
188+} // namespace AAFwk
189+} // namespace OHOS
@@ -33,6 +33,7 @@ ohos_unittest("wants_info_test") {
33 external_deps = [33 external_deps = [
34 "ability_base:want",34 "ability_base:want",
35 "c_utils:utils",35 "c_utils:utils",
36+ "hilog:libhilog",
36 "ipc:ipc_core",37 "ipc:ipc_core",
37 ]38 ]
38}39}
@@ -13,7 +13,9 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16+#include <cstdlib>
16#include <gtest/gtest.h>17#include <gtest/gtest.h>
18+ 
17#include "parcel.h"19#include "parcel.h"
18#define private public20#define private public
19#define protected public21#define protected public
@@ -28,8 +30,38 @@ using OHOS::AppExecFwk::ElementName;
28 30 
29namespace OHOS {31namespace OHOS {
30namespace AAFwk {32namespace AAFwk {
31-#define SLEEP(milli) std::this_thread::sleep_for(std::chrono::seconds(milli))33+namespace {
32-namespace {} // namespace34+class LimitedAllocator final : public Allocator {
35+public:
36+ explicit LimitedAllocator(size_t maxAllocationSize) : maxAllocationSize_(maxAllocationSize) {}
37+ 
38+ ~LimitedAllocator() override = default;
39+ 
40+ void *Realloc(void *data, size_t newSize) override
41+ {
42+ if (newSize > maxAllocationSize_) {
43+ return nullptr;
44+ }
45+ return std::realloc(data, newSize);
46+ }
47+ 
48+ void *Alloc(size_t size) override
49+ {
50+ if (size > maxAllocationSize_) {
51+ return nullptr;
52+ }
53+ return std::malloc(size);
54+ }
55+ 
56+ void Dealloc(void *data) override
57+ {
58+ std::free(data);
59+ }
60+ 
61+private:
62+ size_t maxAllocationSize_;
63+};
64+} // namespace
33class WantsInfoTest : public testing::Test {65class WantsInfoTest : public testing::Test {
34public:66public:
35 static void SetUpTestCase();67 static void SetUpTestCase();
@@ -66,12 +98,49 @@ HWTEST_F(WantsInfoTest, WantsInfoTest_0100, TestSize.Level1)
66 info.want = want;98 info.want = want;
67 info.resolvedTypes = "nihao";99 info.resolvedTypes = "nihao";
68 Parcel parcel;100 Parcel parcel;
69- info.Marshalling(parcel);101+ ASSERT_TRUE(info.Marshalling(parcel));
70 auto unInfo = WantsInfo::Unmarshalling(parcel);102 auto unInfo = WantsInfo::Unmarshalling(parcel);
103+ ASSERT_NE(unInfo, nullptr);
71 EXPECT_EQ(unInfo->want.GetElement().GetBundleName(), "com.ix.hiMusic");104 EXPECT_EQ(unInfo->want.GetElement().GetBundleName(), "com.ix.hiMusic");
72 EXPECT_EQ(unInfo->want.GetElement().GetAbilityName(), "MusicSAbility");105 EXPECT_EQ(unInfo->want.GetElement().GetAbilityName(), "MusicSAbility");
73 EXPECT_EQ(unInfo->resolvedTypes, "nihao");106 EXPECT_EQ(unInfo->resolvedTypes, "nihao");
74 delete unInfo;107 delete unInfo;
75}108}
109+ 
110+/*
111+ * @tc.number : WantsInfoTest_0200
112+ * @tc.name : Marshalling want failure
113+ * @tc.desc : Marshalling returns false when the Want cannot be written.
114+ */
115+HWTEST_F(WantsInfoTest, WantsInfoTest_0200, TestSize.Level1)
116+{
117+ WantsInfo info;
118+ Parcel parcel(new LimitedAllocator(0));
119+ 
120+ EXPECT_FALSE(info.Marshalling(parcel));
121+}
122+ 
123+/*
124+ * @tc.number : WantsInfoTest_0300
125+ * @tc.name : Marshalling resolvedTypes failure
126+ * @tc.desc : Marshalling returns false when resolvedTypes cannot be written after the Want.
127+ */
128+HWTEST_F(WantsInfoTest, WantsInfoTest_0300, TestSize.Level1)
129+{
130+ WantsInfo info;
131+ Want want;
132+ ElementName element("device", "com.ix.hiMusic", "MusicSAbility");
133+ want.SetElement(element);
134+ info.want = want;
135+ 
136+ Parcel wantParcel;
137+ ASSERT_TRUE(wantParcel.WriteParcelable(&info.want));
138+ const size_t wantParcelCapacity = wantParcel.GetDataCapacity();
139+ ASSERT_GT(wantParcelCapacity, 0);
140+ info.resolvedTypes.assign(wantParcelCapacity, 'a');
141+ Parcel parcel(new LimitedAllocator(wantParcelCapacity));
142+ 
143+ EXPECT_FALSE(info.Marshalling(parcel));
144+}
76} // namespace AAFwk145} // namespace AAFwk
77} // namespace OHOS146} // namespace OHOS