1 /* 2 * Copyright (c) 2025 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 <gtest/hwext/gtest-multithread.h> 18 19 #define private public 20 #include "input_method_ability.h" 21 #include "input_method_ability_interface.h" 22 #include "task_manager.h" 23 #undef private 24 #include "input_method_controller.h" 25 #include "input_method_engine_listener_impl.h" 26 #include "keyboard_listener_test_impl.h" 27 #include "scope_utils.h" 28 #include "sys_cfg_parser.h" 29 #include "text_listener.h" 30 #include "ime_setting_listener_test_impl.h" 31 #include "ime_event_monitor_manager_impl.h" 32 33 using namespace testing::ext; 34 using namespace testing::mt; 35 namespace OHOS { 36 namespace MiscServices { 37 constexpr int32_t INVALID_UID = -1; 38 class ImeMirrorTest : public testing::Test { 39 public: 40 static sptr<InputMethodController> imc_; 41 static bool isImeMirrorFeatureEnabled_; 42 static int32_t agentUid_; SetUpTestCase(void)43 static void SetUpTestCase(void) 44 { 45 IMSA_HILOGI("ImeMirrorTest::SetUpTestCase"); 46 TddUtil::StorageSelfTokenID(); 47 TddUtil::InitWindow(true); 48 imc_ = InputMethodController::GetInstance(); 49 50 TddUtil::SetTestTokenID(TddUtil::AllocTestTokenID(true, "ImeProxyTest")); 51 auto listener = std::make_shared<ImeSettingListenerTestImpl>(); 52 ImeEventMonitorManagerImpl::GetInstance().RegisterImeEventListener( 53 EVENT_IME_HIDE_MASK | EVENT_IME_SHOW_MASK | EVENT_IME_CHANGE_MASK, listener); 54 55 ImeSettingListenerTestImpl::ResetParam(); 56 TddUtil::SetTestTokenID( 57 TddUtil::AllocTestTokenID(true, "ImeProxyTest", { "ohos.permission.CONNECT_IME_ABILITY" })); 58 TddUtil::EnabledAllIme(); 59 SubProperty subProp; 60 subProp.name = "com.example.testIme"; 61 subProp.id = "InputMethodExtAbility"; 62 auto ret = imc_->SwitchInputMethod(SwitchTrigger::CURRENT_IME, subProp.name, subProp.id); 63 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 64 EXPECT_TRUE(ImeSettingListenerTestImpl::WaitImeChange(subProp)); 65 TddUtil::RestoreSelfTokenID(); 66 67 // native sa permission 68 TddUtil::GrantNativePermission(); 69 SystemConfig systemConfig; 70 SysCfgParser::ParseSystemConfig(systemConfig); 71 isImeMirrorFeatureEnabled_ = 72 systemConfig.supportedCapacityList.find("ime_mirror") != systemConfig.supportedCapacityList.end(); 73 if (isImeMirrorFeatureEnabled_) { 74 if (systemConfig.proxyImeUidList.empty()) { 75 isImeMirrorFeatureEnabled_ = false; 76 } 77 for (auto id : systemConfig.proxyImeUidList) { 78 agentUid_ = id; 79 } 80 } 81 Attach(); 82 InputMethodEngineListenerImpl::WaitInputStart(); 83 Close(); 84 } TearDownTestCase(void)85 static void TearDownTestCase(void) 86 { 87 IMSA_HILOGI("ImeMirrorTest::TearDownTestCase"); 88 TddUtil::DestroyWindow(); 89 TddUtil::RestoreSelfTokenID(); 90 } SetUp()91 void SetUp() 92 { 93 if (!isImeMirrorFeatureEnabled_) { 94 GTEST_SKIP() << "ime mirror ime feature is not enabled"; 95 } 96 IMSA_HILOGI("ImeMirrorTest::SetUp"); 97 InputMethodAbilityInterface::GetInstance().SetImeListener(std::make_shared<InputMethodEngineListenerImpl>()); 98 InputMethodAbilityInterface::GetInstance().SetKdListener(std::make_shared<KeyboardListenerTestImpl>()); 99 TaskManager::GetInstance().SetInited(true); 100 InputMethodEngineListenerImpl::ResetParam(); 101 KeyboardListenerTestImpl::ResetParam(); 102 } TearDown()103 void TearDown() 104 { 105 IMSA_HILOGI("ImeMirrorTest::TearDown"); 106 // wait ime stop 107 std::this_thread::sleep_for(std::chrono::seconds(1)); 108 TaskManager::GetInstance().Reset(); 109 } 110 Attach(bool isShowKeyboard=true)111 static int32_t Attach(bool isShowKeyboard = true) 112 { 113 TextConfig config; 114 config.cursorInfo = { .left = 0, .top = 1, .width = 0.5, .height = 1.2 }; 115 sptr<OnTextChangedListener> testListener = new TextListener(); 116 auto ret = imc_->Attach(testListener, isShowKeyboard, config); 117 return ret; 118 } Close()119 static void Close() 120 { 121 imc_->Close(); 122 } 123 AttachAndRegisterProxy()124 static void AttachAndRegisterProxy() 125 { 126 { 127 UidScope uidScope(ImeMirrorTest::agentUid_); 128 auto ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 129 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 130 } 131 132 auto ret = Attach(); 133 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 134 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputStart()); 135 } 136 CloseAndUnregisterProxy()137 static void CloseAndUnregisterProxy() 138 { 139 Close(); 140 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputFinish()); 141 { 142 UidScope uidScope(ImeMirrorTest::agentUid_); 143 auto ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 144 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 145 } 146 } 147 }; 148 sptr<InputMethodController> ImeMirrorTest::imc_; 149 bool ImeMirrorTest::isImeMirrorFeatureEnabled_ { false }; 150 int32_t ImeMirrorTest::agentUid_ { INVALID_UID }; 151 152 /** 153 * @tc.name: RegisterUnbindImeMirrorWithoutPermission_fail 154 * @tc.desc: Register or unregister ime mirror without permission 155 * @tc.type: FUNC 156 */ 157 HWTEST_F(ImeMirrorTest, RegisterUnbindImeMirrorWithoutPermission_fail, TestSize.Level1) 158 { 159 IMSA_HILOGI("ImeMirrorTest::RegisterUnbindImeMirrorWithoutPermission_fail start"); 160 auto ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 161 EXPECT_EQ(ret, ErrorCode::ERROR_NOT_AI_APP_IME); 162 ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 163 EXPECT_EQ(ret, ErrorCode::ERROR_NOT_AI_APP_IME); 164 } 165 166 /** 167 * @tc.name: BindImeMirrorAndVerifyTextSelectionConfig_success 168 * @tc.desc: Register ime mirror and verify text\selection\config success 169 * @tc.type: FUNC 170 */ 171 HWTEST_F(ImeMirrorTest, BindImeMirrorAndVerifyTextSelectionConfig_success, TestSize.Level1) 172 { 173 IMSA_HILOGI("ImeMirrorTest::BindImeMirrorAndVerifyTextSelectionConfig_success start"); 174 AttachAndRegisterProxy(); 175 auto instance = InputMethodAbilityInterface::GetInstance(); 176 auto ret = instance.InsertText("1234567890"); 177 EXPECT_EQ(ret, ErrorCode::ERROR_IMA_CHANNEL_NULLPTR); 178 179 ret = ImeMirrorTest::imc_->OnSelectionChange(u"1234567890", 1, 1); 180 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 181 EXPECT_TRUE(KeyboardListenerTestImpl::WaitTextChange("1234567890")); 182 EXPECT_TRUE(KeyboardListenerTestImpl::WaitSelectionChange(1)); 183 184 Configuration config; 185 config.SetEnterKeyType(EnterKeyType::GO); 186 ret = ImeMirrorTest::imc_->OnConfigurationChange(config); 187 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 188 InputAttribute attr; 189 attr.enterKeyType = static_cast<int32_t>(EnterKeyType::GO); 190 EXPECT_FALSE(KeyboardListenerTestImpl::WaitEditorAttributeChange(attr)); 191 192 ret = ImeMirrorTest::imc_->SendFunctionKey(static_cast<int32_t>(EnterKeyType::NEW_LINE)); 193 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 194 EXPECT_TRUE(KeyboardListenerTestImpl::WaitFunctionKey(static_cast<int32_t>(EnterKeyType::NEW_LINE))); 195 196 CloseAndUnregisterProxy(); 197 } 198 199 /** 200 * @tc.name: BindImeMirrorAndVerifyPasswordTextHandling_fail 201 * @tc.desc: Register ime mirror and verify password text handling fail 202 * @tc.type: FUNC 203 */ 204 HWTEST_F(ImeMirrorTest, BindImeMirrorAndVerifyPasswordTextHandling_fail, TestSize.Level1) 205 { 206 IMSA_HILOGI("ImeMirrorTest::BindImeMirrorAndVerifyPasswordTextHandling_fail start"); 207 AttachAndRegisterProxy(); 208 // Test multiple secure input types 209 std::vector<TextInputType> secureTypes = { TextInputType::NEW_PASSWORD, TextInputType::NUMBER_PASSWORD, 210 TextInputType::VISIBLE_PASSWORD, TextInputType::SCREEN_LOCK_PASSWORD }; 211 for (auto type : secureTypes) { 212 Configuration config; 213 config.SetTextInputType(type); 214 auto ret = ImeMirrorTest::imc_->OnConfigurationChange(config); 215 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 216 217 ret = ImeMirrorTest::imc_->OnSelectionChange(u"secure123", 1, 1); 218 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 219 // Secure text should not be received by proxy 220 EXPECT_FALSE(KeyboardListenerTestImpl::WaitTextChange("secure123")); 221 222 ret = ImeMirrorTest::imc_->SendFunctionKey(static_cast<int32_t>(EnterKeyType::NEW_LINE)); 223 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 224 EXPECT_FALSE(KeyboardListenerTestImpl::WaitFunctionKey(static_cast<int32_t>(EnterKeyType::NEW_LINE))); 225 } 226 227 CloseAndUnregisterProxy(); 228 } 229 230 /** 231 * @tc.name: BindImeMirrorAndVerifyLongTextHandling_success 232 * @tc.desc: Verify long text processing capability after registering ime mirror 233 * @tc.type: FUNC 234 */ 235 HWTEST_F(ImeMirrorTest, BindImeMirrorAndVerifyLongTextHandling_success, TestSize.Level1) 236 { 237 IMSA_HILOGI("ImeMirrorTest::BindImeMirrorAndVerifyLongTextHandling_success start"); 238 AttachAndRegisterProxy(); 239 240 // Generate extra-long text 241 std::u16string longText; 242 for (int i = 0; i < 1000; ++i) { 243 longText += u"test"; 244 } 245 auto ret = ImeMirrorTest::imc_->OnSelectionChange(longText, 500, 500); 246 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 247 // Verify long text is received correctly 248 EXPECT_TRUE(KeyboardListenerTestImpl::WaitTextChange(std::string(longText.begin(), longText.end()))); 249 250 CloseAndUnregisterProxy(); 251 } 252 253 /** 254 * @tc.name: RegisterAndUnbindImeMirrorImmediately_VerifyInputListenerInvalid_fail 255 * @tc.desc: Register and immediately unregister ime mirror, verify input start/finish listener is invalid 256 * @tc.type: FUNC 257 */ 258 HWTEST_F(ImeMirrorTest, RegisterAndUnbindImeMirrorImmediately_VerifyInputListenerInvalid_fail, TestSize.Level1) 259 { 260 IMSA_HILOGI("ImeMirrorTest::RegisterAndUnbindImeMirrorImmediately_VerifyInputListenerInvalid_fail start"); 261 { 262 UidScope uidScope(ImeMirrorTest::agentUid_); 263 auto ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 264 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 265 ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 266 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 267 } 268 269 auto ret = Attach(); 270 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 271 EXPECT_FALSE(InputMethodEngineListenerImpl::WaitInputStart()); 272 Close(); 273 EXPECT_FALSE(InputMethodEngineListenerImpl::WaitInputFinish()); 274 } 275 276 /** 277 * @tc.name: UnregisterProxyDuringTextInput_success 278 * @tc.desc: Unregister proxy during text input and verify no further text reception 279 * @tc.type: FUNC 280 */ 281 HWTEST_F(ImeMirrorTest, UnregisterProxyDuringTextInput_success, TestSize.Level1) 282 { 283 IMSA_HILOGI("ImeMirrorTest::UnregisterProxyDuringTextInput_success start"); 284 AttachAndRegisterProxy(); 285 286 // Send text before unregistration 287 auto ret = imc_->OnSelectionChange(u"before unregister", 0, 0); 288 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 289 EXPECT_TRUE(KeyboardListenerTestImpl::WaitTextChange("before unregister")); 290 291 // Unregister proxy 292 { 293 UidScope uidScope(agentUid_); 294 ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 295 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 296 } 297 298 // Send text after unregistration, proxy should not receive it 299 ret = imc_->OnSelectionChange(u"after unregister", 0, 0); 300 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 301 EXPECT_FALSE(KeyboardListenerTestImpl::WaitTextChange("after unregister")); 302 303 InputMethodEngineListenerImpl::ResetParam(); 304 Close(); 305 EXPECT_FALSE(InputMethodEngineListenerImpl::WaitInputFinish()); 306 } 307 308 /** 309 * @tc.name: RepeatBindImeMirror_success 310 * @tc.desc: Test multiple registrations of ime mirror, verify duplicate registration handling logic 311 * @tc.type: FUNC 312 */ 313 HWTEST_F(ImeMirrorTest, RepeatBindImeMirror_success, TestSize.Level1) 314 { 315 IMSA_HILOGI("ImeMirrorTest::RepeatBindImeMirror_success start"); 316 { 317 UidScope uidScope(ImeMirrorTest::agentUid_); 318 // First registration should succeed 319 auto ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 320 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 321 // Duplicate registration should return success (idempotent handling) 322 ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 323 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 324 // Unregister once 325 ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 326 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 327 // Unregister again 328 ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 329 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 330 } 331 } 332 333 /** 334 * @tc.name: RegisterAfterAttach_success 335 * @tc.desc: Verify proxy registration works correctly when Register is called after Attach 336 * @tc.type: FUNC 337 */ 338 HWTEST_F(ImeMirrorTest, RegisterAfterAttach_success, TestSize.Level1) 339 { 340 IMSA_HILOGI("ImeMirrorTest::RegisterAfterAttach_success start"); 341 342 // Step 1: Perform Attach first without registration 343 auto ret = Attach(); 344 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 345 EXPECT_FALSE(InputMethodEngineListenerImpl::WaitInputStart()); 346 347 // Step 2: Register proxy after Attach 348 { 349 UidScope uidScope(agentUid_); 350 ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 351 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 352 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputStart()); 353 } 354 355 // Step 3: Verify text can be received after post-attach registration 356 ret = imc_->OnSelectionChange(u"register after attach", 0, 0); 357 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 358 EXPECT_TRUE(KeyboardListenerTestImpl::WaitTextChange("register after attach")); 359 360 // Cleanup 361 { 362 UidScope uidScope(ImeMirrorTest::agentUid_); 363 auto ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 364 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 365 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputFinish()); 366 } 367 Close(); 368 } 369 370 /** 371 * @tc.name: BindImeMirror_WillNotChangeClientAndImeBinding 372 * @tc.desc: BindImeMirror should not change client and ime binding relationship 373 * @tc.type: FUNC 374 */ 375 HWTEST_F(ImeMirrorTest, BindImeMirror_WillNotChangeClientAndImeBinding, TestSize.Level1) 376 { 377 IMSA_HILOGI("ImeMirrorTest::BindImeMirror_WillNotChangeClientAndImeBinding start"); 378 379 // Step 1: Perform Attach first without bind 380 auto ret = Attach(false); 381 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 382 EXPECT_FALSE(InputMethodEngineListenerImpl::WaitInputStart()); 383 384 // Step 2: bind ime mirror after Attach 385 { 386 UidScope uidScope(agentUid_); 387 ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 388 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 389 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputStart()); 390 } 391 392 ret = imc_->ShowTextInput(); 393 EXPECT_FALSE(InputMethodEngineListenerImpl::WaitKeyboardStatus(true)); 394 395 CloseAndUnregisterProxy(); 396 } 397 398 /** 399 * @tc.name: multiThreadAttachRegisterTest_001 400 * @tc.desc: test ime Attach and Register ime mirror ime in multi-thread 401 * @tc.type: FUNC 402 * @tc.require: 403 */ 404 HWTEST_F(ImeMirrorTest, multiThreadAttachRegisterTest_001, TestSize.Level1) 405 { 406 IMSA_HILOGI("ImeMirrorTest::multiThreadAttachRegisterTest_001"); 407 SET_THREAD_NUM(1); __anone666e82b0102() 408 auto attachTask = []() { 409 auto ret = ImeMirrorTest::Attach(); 410 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 411 ret = ImeMirrorTest::imc_->OnSelectionChange(u"1234567890", 1, 1); 412 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 413 ImeMirrorTest::Close(); 414 }; 415 __anone666e82b0202() 416 auto registerTask = []() { 417 UidScope uidScope(ImeMirrorTest::agentUid_); 418 auto ret = InputMethodAbilityInterface::GetInstance().BindImeMirror(); 419 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 420 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputStart()); 421 EXPECT_TRUE(KeyboardListenerTestImpl::WaitTextChange("1234567890")); 422 ret = InputMethodAbilityInterface::GetInstance().UnbindImeMirror(); 423 EXPECT_EQ(ret, ErrorCode::NO_ERROR); 424 EXPECT_TRUE(InputMethodEngineListenerImpl::WaitInputFinish()); 425 }; 426 MTEST_ADD_TASK(RANDOM_THREAD_ID, attachTask); 427 MTEST_ADD_TASK(RANDOM_THREAD_ID, registerTask); 428 } 429 430 /** 431 * @tc.name: multiThreadAttachRegisterTest_002 432 * @tc.desc: test ime Attach and Register ime mirror ime in multi-thread 433 * @tc.type: FUNC 434 * @tc.require: 435 */ 436 HWTEST_F(ImeMirrorTest, multiThreadAttachRegisterTest_002, TestSize.Level1) 437 { 438 IMSA_HILOGI("ImeMirrorTest::multiThreadAttachRegisterTest_002"); 439 MTEST_POST_RUN(); 440 } 441 } // namespace MiscServices 442 } // namespace OHOS