• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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