• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "input_method_system_ability.h"
17 
18 #include <global.h>
19 #include <utils.h>
20 
21 #include "ability_connect_callback_proxy.h"
22 #include "ability_manager_errors.h"
23 #include "ability_manager_interface.h"
24 #include "application_info.h"
25 #include "bundle_checker.h"
26 #include "bundle_mgr_proxy.h"
27 #include "combination_key.h"
28 #include "common_event_support.h"
29 #include "errors.h"
30 #include "global.h"
31 #include "im_common_event_manager.h"
32 #include "ime_cfg_manager.h"
33 #include "input_method_status.h"
34 #include "ipc_skeleton.h"
35 #include "iservice_registry.h"
36 #include "itypes_util.h"
37 #include "key_event.h"
38 #include "message_handler.h"
39 #include "os_account_manager.h"
40 #include "resource_manager.h"
41 #include "system_ability.h"
42 #include "system_ability_definition.h"
43 #include "ui_service_mgr_client.h"
44 
45 namespace OHOS {
46 namespace MiscServices {
47     using namespace MessageID;
48     using namespace AccountSA;
49     REGISTER_SYSTEM_ABILITY_BY_ID(InputMethodSystemAbility, INPUT_METHOD_SYSTEM_ABILITY_ID, true);
50     const std::int32_t INIT_INTERVAL = 10000L;
51     const std::int32_t MAIN_USER_ID = 100;
52     constexpr int32_t INVALID_USER_ID = -1;
53     static const std::string PERMISSION_CONNECT_IME_ABILITY = "ohos.permission.CONNECT_IME_ABILITY";
54     std::shared_ptr<AppExecFwk::EventHandler> InputMethodSystemAbility::serviceHandler_;
55 
56     /**
57      * constructor
58      * @param systemAbilityId
59      * @param runOnCreate
60      */
InputMethodSystemAbility(int32_t systemAbilityId,bool runOnCreate)61     InputMethodSystemAbility::InputMethodSystemAbility(int32_t systemAbilityId, bool runOnCreate)
62         : SystemAbility(systemAbilityId, runOnCreate), state_(ServiceRunningState::STATE_NOT_START)
63     {
64     }
65 
66     /**
67      * constructor
68      */
InputMethodSystemAbility()69     InputMethodSystemAbility::InputMethodSystemAbility() : state_(ServiceRunningState::STATE_NOT_START)
70     {
71     }
72 
73     /**
74      * Destructor
75      */
~InputMethodSystemAbility()76     InputMethodSystemAbility::~InputMethodSystemAbility()
77     {
78         if (workThreadHandler.joinable()) {
79             workThreadHandler.join();
80         }
81         userSessions.clear();
82         std::map<int32_t, MessageHandler*>::const_iterator it2;
83         for (it2 = msgHandlers.cbegin(); it2 != msgHandlers.cend();) {
84             MessageHandler *handler = it2->second;
85             it2 = msgHandlers.erase(it2);
86             delete handler;
87             handler = nullptr;
88         }
89         msgHandlers.clear();
90     }
91 
OnStart()92     void InputMethodSystemAbility::OnStart()
93     {
94         IMSA_HILOGI("InputMethodSystemAbility::OnStart.");
95         if (state_ == ServiceRunningState::STATE_RUNNING) {
96             IMSA_HILOGI("ImsaService is already running.");
97             return;
98         }
99         Initialize();
100         InitServiceHandler();
101         if (Init() != ErrorCode::NO_ERROR) {
102             auto callback = [=]() { Init(); };
103             serviceHandler_->PostTask(callback, INIT_INTERVAL);
104             IMSA_HILOGE("Init failed. Try again 10s later");
105             return;
106         }
107         InitHiTrace();
108         InputmethodTrace tracer("InputMethodController Attach trace.");
109         InputmethodDump::GetInstance().AddDumpAllMethod(
110             std::bind(&InputMethodSystemAbility::DumpAllMethod, this, std::placeholders::_1));
111         IMSA_HILOGI("Start ImsaService ErrorCode::NO_ERROR.");
112         return;
113     }
114 
Dump(int fd,const std::vector<std::u16string> & args)115     int InputMethodSystemAbility::Dump(int fd, const std::vector<std::u16string> &args)
116     {
117         IMSA_HILOGI("InputMethodSystemAbility::Dump");
118         std::vector<std::string> argsStr;
119         for (auto item : args) {
120             argsStr.emplace_back(Str16ToStr8(item));
121         }
122         InputmethodDump::GetInstance().Dump(fd, argsStr);
123         return ERR_OK;
124     }
125 
GetInputMethodParam(const std::vector<InputMethodInfo> & properties)126     std::string InputMethodSystemAbility::GetInputMethodParam(const std::vector<InputMethodInfo> &properties)
127     {
128         auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId_);
129         auto &currentIme = cfg.currentIme;
130         bool isBegin = true;
131         std::string params = "{\"imeList\":[";
132         for (const auto &property : properties) {
133             params += isBegin ? "" : "},";
134             isBegin = false;
135 
136             std::string imeId = Str16ToStr8(property.mPackageName) + "/" + Str16ToStr8(property.mAbilityName);
137             params += "{\"ime\": \"" + imeId + "\",";
138             params += "\"labelId\": \"" + std::to_string(property.labelId) + "\",";
139             params += "\"descriptionId\": \"" + std::to_string(property.descriptionId) + "\",";
140             std::string isCurrentIme = currentIme == imeId ? "true" : "false";
141             params += "\"isCurrentIme\": \"" + isCurrentIme + "\",";
142             params += "\"label\": \"" + Str16ToStr8(property.label) + "\",";
143             params += "\"description\": \"" + Str16ToStr8(property.description) + "\"";
144         }
145         params += "}]}";
146         return params;
147     }
148 
DumpAllMethod(int fd)149     void InputMethodSystemAbility::DumpAllMethod(int fd)
150     {
151         IMSA_HILOGI("InputMethodSystemAbility::DumpAllMethod");
152         std::vector<int32_t> ids;
153         int errCode = OsAccountManager::QueryActiveOsAccountIds(ids);
154         if (errCode != ERR_OK) {
155             dprintf(fd, "\n - InputMethodSystemAbility::DumpAllMethod get Active Id failed.\n");
156             return;
157         }
158         dprintf(fd, "\n - DumpAllMethod get Active Id succeed,count=%zu,", ids.size());
159         for (auto id : ids) {
160             const auto &properties = ListInputMethodInfo(id);
161             if (properties.empty()) {
162                 IMSA_HILOGI("The IME properties is empty.");
163                 dprintf(fd, "\n - The IME properties about the Active Id %d is empty.\n", id);
164                 continue;
165             }
166             const auto &params = GetInputMethodParam(properties);
167             dprintf(fd, "\n - The Active Id:%d get input method:\n%s\n", id, params.c_str());
168         }
169         IMSA_HILOGI("InputMethodSystemAbility::DumpAllMethod end.");
170     }
171 
Init()172     int32_t InputMethodSystemAbility::Init()
173     {
174         bool isSuccess = Publish(this);
175         if (!isSuccess) {
176             return -1;
177         }
178         IMSA_HILOGI("Publish ErrorCode::NO_ERROR.");
179         state_ = ServiceRunningState::STATE_RUNNING;
180         ImeCfgManager::GetInstance().Init();
181         // If restart is caused by exception, userId can be got without 'OnUserStarted'.
182         // It may be possible that sa can not get userId when device startup, then wait for 'OnUserStarted'.
183         std::vector<int32_t> userIds;
184         if (OsAccountManager::QueryActiveOsAccountIds(userIds) == ERR_OK && !userIds.empty()) {
185             userId_ = userIds[0];
186             IMSA_HILOGI("InputMethodSystemAbility::get current userId success, userId: %{public}d", userId_);
187             StartInputService(GetNewUserIme(userId_));
188         }
189         StartUserIdListener();
190         int32_t ret = InitKeyEventMonitor();
191         IMSA_HILOGI("init KeyEvent monitor %{public}s", ret == ErrorCode::NO_ERROR ? "success" : "failed");
192         return ErrorCode::NO_ERROR;
193     }
194 
OnStop()195     void InputMethodSystemAbility::OnStop()
196     {
197         IMSA_HILOGI("OnStop started.");
198         if (state_ != ServiceRunningState::STATE_RUNNING) {
199             return;
200         }
201         serviceHandler_ = nullptr;
202 
203         state_ = ServiceRunningState::STATE_NOT_START;
204         IMSA_HILOGI("OnStop end.");
205     }
206 
InitServiceHandler()207     void InputMethodSystemAbility::InitServiceHandler()
208     {
209         IMSA_HILOGI("InitServiceHandler started.");
210         if (serviceHandler_) {
211             IMSA_HILOGI("InitServiceHandler already init.");
212             return;
213         }
214         std::shared_ptr<AppExecFwk::EventRunner> runner = AppExecFwk::EventRunner::Create("InputMethodSystemAbility");
215         serviceHandler_ = std::make_shared<AppExecFwk::EventHandler>(runner);
216 
217         IMSA_HILOGI("InitServiceHandler succeeded.");
218     }
219 
220     /*! Initialization of Input method management service
221     \n It's called after the service starts, before any transaction.
222     */
Initialize()223     void InputMethodSystemAbility::Initialize()
224     {
225         IMSA_HILOGI("InputMethodSystemAbility::Initialize");
226         // init work thread to handle the messages
227         workThreadHandler = std::thread([this] { WorkThread(); });
228         userSessions.insert({ MAIN_USER_ID, std::make_shared<PerUserSession>(MAIN_USER_ID) });
229 
230         userId_ = INVALID_USER_ID;
231     }
232 
StartUserIdListener()233     void InputMethodSystemAbility::StartUserIdListener()
234     {
235         sptr<ImCommonEventManager> imCommonEventManager = ImCommonEventManager::GetInstance();
236         bool isSuccess = imCommonEventManager->SubscribeEvent(EventFwk::CommonEventSupport::COMMON_EVENT_USER_SWITCHED);
237         if (isSuccess) {
238             IMSA_HILOGI("InputMethodSystemAbility::Initialize subscribe service event success");
239             return;
240         }
241 
242         IMSA_HILOGE("StartUserIdListener failed. Try again 10s later");
243         auto callback = [this]() { StartUserIdListener(); };
244         serviceHandler_->PostTask(callback, INIT_INTERVAL);
245     }
246 
StartInputService(std::string imeId)247     bool InputMethodSystemAbility::StartInputService(std::string imeId)
248     {
249         IMSA_HILOGE("InputMethodSystemAbility::StartInputService() ime:%{public}s", imeId.c_str());
250         bool isStartSuccess = false;
251         sptr<AAFwk::IAbilityManager> abms = GetAbilityManagerService();
252         if (abms) {
253             AAFwk::Want want;
254             want.SetAction("action.system.inputmethod");
255             std::string::size_type pos = imeId.find("/");
256             want.SetElementName(imeId.substr(0, pos), imeId.substr(pos + 1));
257             int32_t result = abms->StartAbility(want);
258             if (result) {
259                 IMSA_HILOGE("InputMethodSystemAbility::StartInputService failed, result = %{public}d", result);
260                 isStartSuccess = false;
261             } else {
262                 IMSA_HILOGE("InputMethodSystemAbility::StartInputService success.");
263                 isStartSuccess = true;
264             }
265         }
266         if (!isStartSuccess) {
267             IMSA_HILOGE("StartInputService failed. Try again 10s later");
268             auto callback = [this, imeId]() { StartInputService(imeId); };
269             serviceHandler_->PostTask(callback, INIT_INTERVAL);
270         }
271         return isStartSuccess;
272     }
273 
StopInputService(std::string imeId)274     void InputMethodSystemAbility::StopInputService(std::string imeId)
275     {
276         IMSA_HILOGE("InputMethodSystemAbility::StopInputService(%{public}s)", imeId.c_str());
277         auto session = GetUserSession(MAIN_USER_ID);
278         if (session == nullptr) {
279             IMSA_HILOGE("InputMethodSystemAbility::StopInputService abort session is nullptr");
280             return;
281         }
282         session->StopInputService(imeId);
283     }
284 
285     /*! Handle the transaction from the remote binder
286     \n Run in binder thread
287     \param code transaction code number
288     \param data the params from remote binder
289     \param[out] reply the result of the transaction replied to the remote binder
290     \param flags the flags of handling transaction
291     \return int32_t
292     */
OnRemoteRequest(uint32_t code,MessageParcel & data,MessageParcel & reply,MessageOption & option)293     int32_t InputMethodSystemAbility::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply,
294         MessageOption &option)
295     {
296         return InputMethodSystemAbilityStub::OnRemoteRequest(code, data, reply, option);
297     }
298 
PrepareInput(int32_t displayId,sptr<IInputClient> client,sptr<IInputDataChannel> channel,InputAttribute & attribute)299     int32_t InputMethodSystemAbility::PrepareInput(int32_t displayId, sptr<IInputClient> client,
300         sptr<IInputDataChannel> channel, InputAttribute &attribute)
301     {
302         int32_t pid = IPCSkeleton::GetCallingPid();
303         int32_t uid = IPCSkeleton::GetCallingUid();
304         auto session = GetUserSession(MAIN_USER_ID);
305         if (session == nullptr) {
306             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
307             return ErrorCode::ERROR_NULL_POINTER;
308         }
309         sptr<RemoteObjectDeathRecipient> clientDeathRecipient = new (std::nothrow) RemoteObjectDeathRecipient();
310         if (clientDeathRecipient == nullptr) {
311             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput clientDeathRecipient is nullptr");
312             return ErrorCode::ERROR_EX_NULL_POINTER;
313         }
314         return session->OnPrepareInput(
315             { pid, uid, userId_, displayId, client, channel, clientDeathRecipient, attribute });
316     };
317 
ReleaseInput(sptr<IInputClient> client)318     int32_t InputMethodSystemAbility::ReleaseInput(sptr<IInputClient> client)
319     {
320         auto session = GetUserSession(MAIN_USER_ID);
321         if (session == nullptr) {
322             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
323             return ErrorCode::ERROR_NULL_POINTER;
324         }
325         return session->OnReleaseInput(client);
326     };
327 
StartInput(sptr<IInputClient> client,bool isShowKeyboard)328     int32_t InputMethodSystemAbility::StartInput(sptr<IInputClient> client, bool isShowKeyboard)
329     {
330         auto session = GetUserSession(MAIN_USER_ID);
331         if (session == nullptr) {
332             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
333             return ErrorCode::ERROR_NULL_POINTER;
334         }
335         auto currentSubtype = GetCurrentInputMethodSubtype();
336         if (currentSubtype == nullptr) {
337             IMSA_HILOGE("currentSubtype is nullptr");
338             return ErrorCode::ERROR_NULL_POINTER;
339         }
340         session->SetCurrentSubProperty(*currentSubtype);
341         return session->OnStartInput(client, isShowKeyboard);
342     };
343 
StopInput(sptr<IInputClient> client)344     int32_t InputMethodSystemAbility::StopInput(sptr<IInputClient> client)
345     {
346         auto session = GetUserSession(MAIN_USER_ID);
347         if (session == nullptr) {
348             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
349             return ErrorCode::ERROR_NULL_POINTER;
350         }
351         return session->OnStopInput(client);
352     };
353 
StopInputSession()354     int32_t InputMethodSystemAbility::StopInputSession()
355     {
356         auto session = GetUserSession(MAIN_USER_ID);
357         if (session == nullptr) {
358             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
359             return ErrorCode::ERROR_NULL_POINTER;
360         }
361         return session->OnHideKeyboardSelf();
362     }
363 
SetCoreAndAgent(sptr<IInputMethodCore> core,sptr<IInputMethodAgent> agent)364     int32_t InputMethodSystemAbility::SetCoreAndAgent(sptr<IInputMethodCore> core, sptr<IInputMethodAgent> agent)
365     {
366         IMSA_HILOGD("InputMethodSystemAbility run in");
367         auto currentIme = GetCurrentInputMethod();
368         if (currentIme == nullptr) {
369             return ErrorCode::ERROR_EX_NULL_POINTER;
370         }
371         if (!BundleChecker::IsCurrentIme(IPCSkeleton::GetCallingTokenID(), currentIme->name)) {
372             return ErrorCode::ERROR_NOT_CURRENT_IME;
373         }
374         auto session = GetUserSession(MAIN_USER_ID);
375         if (session == nullptr) {
376             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
377             return ErrorCode::ERROR_NULL_POINTER;
378         }
379         return session->OnSetCoreAndAgent(core, agent);
380     };
381 
HideCurrentInput()382     int32_t InputMethodSystemAbility::HideCurrentInput()
383     {
384         IMSA_HILOGD("InputMethodSystemAbility run in");
385         if (!BundleChecker::CheckPermission(IPCSkeleton::GetCallingTokenID(), PERMISSION_CONNECT_IME_ABILITY)) {
386             return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
387         }
388         auto session = GetUserSession(MAIN_USER_ID);
389         if (session == nullptr) {
390             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
391             return ErrorCode::ERROR_NULL_POINTER;
392         }
393         return session->OnHideKeyboardSelf();
394     };
395 
ShowCurrentInput()396     int32_t InputMethodSystemAbility::ShowCurrentInput()
397     {
398         IMSA_HILOGD("InputMethodSystemAbility run in");
399         if (!BundleChecker::CheckPermission(IPCSkeleton::GetCallingTokenID(), PERMISSION_CONNECT_IME_ABILITY)) {
400             return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
401         }
402         auto session = GetUserSession(MAIN_USER_ID);
403         if (session == nullptr) {
404             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
405             return ErrorCode::ERROR_NULL_POINTER;
406         }
407         return session->OnShowKeyboardSelf();
408     };
409 
DisplayOptionalInputMethod()410     int32_t InputMethodSystemAbility::DisplayOptionalInputMethod()
411     {
412         IMSA_HILOGD("InputMethodSystemAbility run in");
413         if (!BundleChecker::CheckPermission(IPCSkeleton::GetCallingTokenID(), PERMISSION_CONNECT_IME_ABILITY)) {
414             return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
415         }
416         return OnDisplayOptionalInputMethod(userId_);
417     };
418 
ListInputMethod(InputMethodStatus status,std::vector<Property> & props)419     int32_t InputMethodSystemAbility::ListInputMethod(InputMethodStatus status, std::vector<Property> &props)
420     {
421         return ListInputMethodByUserId(userId_, status, props);
422     }
423 
ListAllInputMethod(int32_t userId,std::vector<Property> & props)424     int32_t InputMethodSystemAbility::ListAllInputMethod(int32_t userId, std::vector<Property> &props)
425     {
426         IMSA_HILOGI("InputMethodSystemAbility::listAllInputMethod");
427         return ListProperty(userId, props);
428     }
429 
ListEnabledInputMethod(std::vector<Property> & props)430     int32_t InputMethodSystemAbility::ListEnabledInputMethod(std::vector<Property> &props)
431     {
432         IMSA_HILOGI("InputMethodSystemAbility::listEnabledInputMethod");
433         auto current = GetCurrentInputMethod();
434         if (current == nullptr) {
435             IMSA_HILOGE("GetCurrentInputMethod current is nullptr");
436             return ErrorCode::ERROR_NULL_POINTER;
437         }
438         auto property = FindProperty(current->name);
439         props = { property };
440         return ErrorCode::NO_ERROR;
441     }
442 
ListDisabledInputMethod(int32_t userId,std::vector<Property> & props)443     int32_t InputMethodSystemAbility::ListDisabledInputMethod(int32_t userId, std::vector<Property> &props)
444     {
445         IMSA_HILOGI("InputMethodSystemAbility::listDisabledInputMethod");
446         auto ret = ListProperty(userId, props);
447         if (ret != ErrorCode::NO_ERROR) {
448             IMSA_HILOGE("list property is failed, ret = %{public}d", ret);
449             return ret;
450         }
451         auto filter = GetCurrentInputMethod();
452         if (filter == nullptr) {
453             IMSA_HILOGE("GetCurrentInputMethod property is nullptr");
454             return ErrorCode::ERROR_NULL_POINTER;
455         }
456         for (auto iter = props.begin(); iter != props.end();) {
457             if (iter->name == filter->name) {
458                 iter = props.erase(iter);
459                 continue;
460             }
461             ++iter;
462         }
463         return ErrorCode::NO_ERROR;
464     }
465 
ListCurrentInputMethodSubtype(std::vector<SubProperty> & subProps)466     int32_t InputMethodSystemAbility::ListCurrentInputMethodSubtype(std::vector<SubProperty> &subProps)
467     {
468         IMSA_HILOGI("InputMethodSystemAbility::ListCurrentInputMethodSubtype");
469         auto filter = GetCurrentInputMethod();
470         if (filter == nullptr) {
471             IMSA_HILOGE("GetCurrentInputMethod failed");
472             return ErrorCode::ERROR_NULL_POINTER;
473         }
474         return ListSubtypeByBundleName(userId_, filter->name, subProps);
475     }
476 
ListInputMethodSubtype(const std::string & name,std::vector<SubProperty> & subProps)477     int32_t InputMethodSystemAbility::ListInputMethodSubtype(
478         const std::string &name, std::vector<SubProperty> &subProps)
479     {
480         IMSA_HILOGI("InputMethodSystemAbility::ListInputMethodSubtype");
481         return ListSubtypeByBundleName(userId_, name, subProps);
482     }
483 
ListSubtypeByBundleName(int32_t userId,const std::string & name,std::vector<SubProperty> & subProps)484     int32_t InputMethodSystemAbility::ListSubtypeByBundleName(
485         int32_t userId, const std::string &name, std::vector<SubProperty> &subProps)
486     {
487         IMSA_HILOGI("InputMethodSystemAbility::ListSubtypeByBundleName");
488         std::vector<AppExecFwk::ExtensionAbilityInfo> subtypeInfos;
489         int32_t ret = QueryImeInfos(userId, subtypeInfos);
490         if (ret != ErrorCode::NO_ERROR) {
491             IMSA_HILOGE("Failed to query inputmethod infos");
492             return ret;
493         }
494         auto bundleMgr = GetBundleMgr();
495         if (bundleMgr == nullptr) {
496             IMSA_HILOGE("Failed to GetBundleMgr");
497             return ErrorCode::ERROR_NULL_POINTER;
498         }
499         for (const auto &subtypeInfo : subtypeInfos) {
500             if (subtypeInfo.bundleName == name) {
501                 std::vector<Metadata> extends = subtypeInfo.metadata;
502                 auto property = GetExtends(extends);
503                 auto label = bundleMgr->GetStringById(subtypeInfo.bundleName, subtypeInfo.moduleName,
504                                                       subtypeInfo.labelId, userId);
505                 subProps.push_back({ .id = subtypeInfo.name,
506                     .label = label,
507                     .name = subtypeInfo.bundleName,
508                     .iconId = subtypeInfo.iconId,
509                     .language = property.language,
510                     .mode = property.mode,
511                     .locale = property.locale,
512                     .icon = property.icon });
513             }
514         }
515         return ErrorCode::NO_ERROR;
516     }
517 
GetExtends(const std::vector<Metadata> & metaData)518     SubProperty InputMethodSystemAbility::GetExtends(const std::vector<Metadata> &metaData)
519     {
520         IMSA_HILOGI("InputMethodSystemAbility::GetExtends");
521         SubProperty property;
522         for (const auto &data : metaData) {
523             if (data.name == "language") {
524                 property.language = data.value;
525                 continue;
526             }
527             if (data.name == "mode") {
528                 property.mode = data.value;
529                 continue;
530             }
531             if (data.name == "locale") {
532                 property.locale = data.value;
533                 continue;
534             }
535             if (data.name == "icon") {
536                 property.icon = data.value;
537             }
538         }
539         return property;
540     }
541 
SwitchInputMethod(const std::string & name,const std::string & subName)542     int32_t InputMethodSystemAbility::SwitchInputMethod(const std::string &name, const std::string &subName)
543     {
544         IMSA_HILOGI("InputMethodSystemAbility::SwitchInputMethod");
545         if (!BundleChecker::CheckPermission(IPCSkeleton::GetCallingTokenID(), PERMISSION_CONNECT_IME_ABILITY)) {
546             return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
547         }
548         return subName.empty() ? SwitchInputMethodType(name) : SwitchInputMethodSubtype(name, subName);
549     }
550 
SwitchInputMethodType(const std::string & name)551     int32_t InputMethodSystemAbility::SwitchInputMethodType(const std::string &name)
552     {
553         IMSA_HILOGI("InputMethodSystemAbility::SwitchInputMethodType");
554         std::vector<Property> properties = {};
555         auto ret = ListInputMethodByUserId(userId_, ALL, properties);
556         if (ret != ErrorCode::NO_ERROR) {
557             IMSA_HILOGE("ListInputMethodByUserId failed, ret = %{public}d", ret);
558             return ret;
559         }
560         if (properties.empty()) {
561             IMSA_HILOGE("InputMethodSystemAbility::SwitchInputMethodType has no ime");
562             return ErrorCode::ERROR_BAD_PARAMETERS;
563         }
564         for (const auto &property : properties) {
565             if (property.name == name) {
566                 IMSA_HILOGI("target is installed, start switching");
567                 return OnSwitchInputMethod(property.name, property.id);
568             }
569         }
570         IMSA_HILOGE("target is not installed, switch failed");
571         return ErrorCode::ERROR_SWITCH_IME;
572     }
573 
SwitchInputMethodSubtype(const std::string & bundleName,const std::string & name)574     int32_t InputMethodSystemAbility::SwitchInputMethodSubtype(const std::string &bundleName, const std::string &name)
575     {
576         IMSA_HILOGI("InputMethodSystemAbility::SwitchInputMethodSubtype");
577         std::vector<SubProperty> subProps = {};
578         auto ret = ListSubtypeByBundleName(userId_, bundleName, subProps);
579         if (ret != ErrorCode::NO_ERROR) {
580             IMSA_HILOGE("ListSubtypeByBundleName failed, ret = %{public}d", ret);
581             return ret;
582         }
583         if (subProps.empty()) {
584             IMSA_HILOGE("InputMethodSystemAbility::SwitchInputMethodSubtype has no ime");
585             return ErrorCode::ERROR_BAD_PARAMETERS;
586         }
587         for (const auto &subProp : subProps) {
588             if (subProp.id == name) {
589                 IMSA_HILOGI("target is installed, start switching");
590                 return OnSwitchInputMethod(bundleName, name);
591             }
592         }
593         IMSA_HILOGE("target is not installed, switch failed");
594         return ErrorCode::ERROR_SWITCH_IME;
595     }
596 
OnSwitchInputMethod(const std::string & bundleName,const std::string & name)597     int32_t InputMethodSystemAbility::OnSwitchInputMethod(const std::string &bundleName, const std::string &name)
598     {
599         IMSA_HILOGI("InputMethodSystemAbility::OnSwitchInputMethod");
600         std::string targetIme = bundleName + "/" + name;
601         auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId_);
602         auto &currentIme = cfg.currentIme;
603         if (currentIme.empty()) {
604             IMSA_HILOGE("currentIme is empty");
605             return ErrorCode::ERROR_PERSIST_CONFIG;
606         }
607         IMSA_HILOGI("CurrentIme : %{public}s, TargetIme : %{public}s", currentIme.c_str(), targetIme.c_str());
608         if (currentIme == targetIme) {
609             IMSA_HILOGI("currentIme and TargetIme are the same one");
610             return ErrorCode::NO_ERROR;
611         }
612         StopInputService(currentIme);
613         ImeCfgManager::GetInstance().ModifyImeCfg({ userId_, targetIme });
614         if (!StartInputService(targetIme)) {
615             IMSA_HILOGE("start input method failed");
616             return ErrorCode::ERROR_IME_START_FAILED;
617         }
618         auto session = GetUserSession(MAIN_USER_ID);
619         if (session == nullptr) {
620             IMSA_HILOGE("session is nullptr");
621             return ErrorCode::ERROR_NULL_POINTER;
622         }
623         session->OnInputMethodSwitched(FindProperty(bundleName), FindSubProperty(bundleName, name));
624         return ErrorCode::NO_ERROR;
625     }
626 
FindProperty(const std::string & name)627     Property InputMethodSystemAbility::FindProperty(const std::string &name)
628     {
629         IMSA_HILOGI("InputMethodSystemAbility::FindProperty");
630         std::vector<Property> props = {};
631         auto ret = ListAllInputMethod(userId_, props);
632         if (ret != ErrorCode::NO_ERROR) {
633             IMSA_HILOGE("ListAllInputMethod failed");
634             return {};
635         }
636         for (const auto &prop : props) {
637             if (prop.name == name) {
638                 return prop;
639             }
640         }
641         IMSA_HILOGE("InputMethodSystemAbility::FindProperty failed");
642         return {};
643     }
644 
FindSubProperty(const std::string & bundleName,const std::string & name)645     SubProperty InputMethodSystemAbility::FindSubProperty(const std::string &bundleName, const std::string &name)
646     {
647         IMSA_HILOGI("InputMethodSystemAbility::FindSubProperty");
648         std::vector<SubProperty> subProps = {};
649         auto ret = ListSubtypeByBundleName(userId_, bundleName, subProps);
650         if (ret != ErrorCode::NO_ERROR) {
651             IMSA_HILOGE("ListSubtypeByBundleName failed, ret = %{public}d", ret);
652             return {};
653         }
654         for (const auto &subProp : subProps) {
655             if (subProp.id == name) {
656                 return subProp;
657             }
658         }
659         IMSA_HILOGE("InputMethodSystemAbility::FindSubProperty failed");
660         return {};
661     }
662 
663     // Deprecated because of no permission check, kept for compatibility
HideCurrentInputDeprecated()664     int32_t InputMethodSystemAbility::HideCurrentInputDeprecated()
665     {
666         auto session = GetUserSession(MAIN_USER_ID);
667         if (session == nullptr) {
668             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
669             return ErrorCode::ERROR_NULL_POINTER;
670         }
671         return session->OnHideKeyboardSelf();
672     };
673 
ShowCurrentInputDeprecated()674     int32_t InputMethodSystemAbility::ShowCurrentInputDeprecated()
675     {
676         auto session = GetUserSession(MAIN_USER_ID);
677         if (session == nullptr) {
678             IMSA_HILOGE("InputMethodSystemAbility::PrepareInput session is nullptr");
679             return ErrorCode::ERROR_NULL_POINTER;
680         }
681         return session->OnShowKeyboardSelf();
682     };
683 
DisplayOptionalInputMethodDeprecated()684     int32_t InputMethodSystemAbility::DisplayOptionalInputMethodDeprecated()
685     {
686         return OnDisplayOptionalInputMethod(userId_);
687     };
688 
689     /*! Get all of the input method engine list installed in the system
690     \n Run in binder thread
691     \param[out] properties input method engine list returned to the caller
692     \return ErrorCode::NO_ERROR no error
693     \return ErrorCode::ERROR_USER_NOT_UNLOCKED user not unlocked
694     */
ListInputMethodByUserId(int32_t userId,InputMethodStatus status,std::vector<Property> & props)695     int32_t InputMethodSystemAbility::ListInputMethodByUserId(
696         int32_t userId, InputMethodStatus status, std::vector<Property> &props)
697     {
698         IMSA_HILOGI("InputMethodSystemAbility::ListInputMethodByUserId");
699         if (status == InputMethodStatus::ALL) {
700             return ListAllInputMethod(userId, props);
701         }
702         if (status == InputMethodStatus::ENABLE) {
703             return ListEnabledInputMethod(props);
704         }
705         if (status == InputMethodStatus::DISABLE) {
706             return ListDisabledInputMethod(userId, props);
707         }
708         return ErrorCode::ERROR_BAD_PARAMETERS;
709     }
710 
ListInputMethodInfo(int32_t userId)711     std::vector<InputMethodInfo> InputMethodSystemAbility::ListInputMethodInfo(int32_t userId)
712     {
713         IMSA_HILOGI("InputMethodSystemAbility::ListInputMethodInfo userId = %{public}d", userId);
714         std::vector<AppExecFwk::ExtensionAbilityInfo> extensionInfos;
715         if (QueryImeInfos(userId, extensionInfos) != ErrorCode::NO_ERROR) {
716             IMSA_HILOGE("Failed to query inputmethod infos");
717             return {};
718         }
719         std::vector<InputMethodInfo> properties;
720         for (auto extension : extensionInfos) {
721             std::shared_ptr<Global::Resource::ResourceManager> resourceManager(
722                 Global::Resource::CreateResourceManager());
723             if (resourceManager == nullptr) {
724                 IMSA_HILOGE("InputMethodSystemAbility::ListInputMethodInfo resourcemanager is nullptr");
725                 break;
726             }
727             AppExecFwk::ApplicationInfo applicationInfo = extension.applicationInfo;
728             std::string path = extension.hapPath.empty() ? extension.resourcePath : extension.hapPath;
729             resourceManager->AddResource(path.c_str());
730             std::string labelString;
731             resourceManager->GetStringById(applicationInfo.labelId, labelString);
732             std::string descriptionString;
733             resourceManager->GetStringById(applicationInfo.descriptionId, descriptionString);
734             InputMethodInfo property;
735             property.mPackageName = Str8ToStr16(extension.bundleName);
736             property.mAbilityName = Str8ToStr16(extension.name);
737             property.labelId = applicationInfo.labelId;
738             property.descriptionId = applicationInfo.descriptionId;
739             property.label = Str8ToStr16(labelString);
740             property.description = Str8ToStr16(descriptionString);
741             properties.emplace_back(property);
742         }
743         return properties;
744     }
745 
ListProperty(int32_t userId,std::vector<Property> & props)746     int32_t InputMethodSystemAbility::ListProperty(int32_t userId, std::vector<Property> &props)
747     {
748         IMSA_HILOGI("InputMethodSystemAbility::ListProperty userId = %{public}d", userId);
749         std::vector<AppExecFwk::ExtensionAbilityInfo> extensionInfos;
750         int32_t ret = QueryImeInfos(userId, extensionInfos);
751         if (ret != ErrorCode::NO_ERROR) {
752             IMSA_HILOGE("Failed to query inputmethod infos");
753             return ret;
754         }
755         for (const auto &extension : extensionInfos) {
756             bool isInVector = false;
757             for (const auto &prop : props) {
758                 if (extension.bundleName == prop.name) {
759                     isInVector = true;
760                     break;
761                 }
762             }
763             if (isInVector) {
764                 continue;
765             }
766             props.push_back({ .name = extension.bundleName,
767                 .id = extension.name,
768                 .label = extension.applicationInfo.label,
769                 .icon = extension.applicationInfo.icon,
770                 .iconId = extension.applicationInfo.iconId });
771         }
772         return ErrorCode::NO_ERROR;
773     }
774 
GetCurrentInputMethod()775     std::shared_ptr<Property> InputMethodSystemAbility::GetCurrentInputMethod()
776     {
777         IMSA_HILOGI("InputMethodSystemAbility::GetCurrentInputMethod");
778         auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId_);
779         auto &currentIme = cfg.currentIme;
780         if (currentIme.empty()) {
781             IMSA_HILOGE("InputMethodSystemAbility::GetCurrentInputMethod currentIme is empty");
782             return nullptr;
783         }
784 
785         auto pos = currentIme.find('/');
786         if (pos == std::string::npos) {
787             IMSA_HILOGE("InputMethodSystemAbility::GetCurrentInputMethod currentIme can not find '/'");
788             return nullptr;
789         }
790 
791         auto property = std::make_shared<Property>();
792         if (property == nullptr) {
793             IMSA_HILOGE("InputMethodSystemAbility property is nullptr");
794             return nullptr;
795         }
796         property->name = currentIme.substr(0, pos);
797         property->id = currentIme.substr(pos + 1, currentIme.length() - pos - 1);
798         return property;
799     }
800 
GetCurrentInputMethodSubtype()801     std::shared_ptr<SubProperty> InputMethodSystemAbility::GetCurrentInputMethodSubtype()
802     {
803         IMSA_HILOGI("InputMethodSystemAbility::GetCurrentInputMethodSubtype");
804         auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId_);
805         auto &currentIme = cfg.currentIme;
806         if (currentIme.empty()) {
807             IMSA_HILOGE("InputMethodSystemAbility currentIme is empty");
808             return nullptr;
809         }
810         auto pos = currentIme.find('/');
811         if (pos == std::string::npos) {
812             IMSA_HILOGE("InputMethodSystemAbility:: currentIme can not find '/'");
813             return nullptr;
814         }
815         auto property = std::make_shared<SubProperty>(
816             FindSubProperty(currentIme.substr(0, pos), currentIme.substr(pos + 1, currentIme.length() - pos - 1)));
817         if (property == nullptr) {
818             IMSA_HILOGE("property is nullptr");
819             return nullptr;
820         }
821         return property;
822     }
823 
824     /*! Get the instance of PerUserSession for the given user
825     \param userId the user id of the given user
826     \return a pointer of the instance if the user is found
827     \return null if the user is not found
828     */
GetUserSession(int32_t userId)829     std::shared_ptr<PerUserSession> InputMethodSystemAbility::GetUserSession(int32_t userId)
830     {
831         auto it = userSessions.find(userId);
832         if (it == userSessions.end()) {
833             IMSA_HILOGE("not found session");
834             return nullptr;
835         }
836         return it->second;
837     }
838 
839     /*! Work Thread of input method management service
840     \n Remote commands which may change the state or data in the service will be handled sequentially in this thread.
841     */
WorkThread()842     void InputMethodSystemAbility::WorkThread()
843     {
844         while (1) {
845             Message *msg = MessageHandler::Instance()->GetMessage();
846             switch (msg->msgId_) {
847                 case MSG_ID_USER_START : {
848                     OnUserStarted(msg);
849                     break;
850                 }
851                 case MSG_ID_USER_REMOVED: {
852                     OnUserRemoved(msg);
853                     break;
854                 }
855                 case MSG_ID_PACKAGE_REMOVED: {
856                     OnPackageRemoved(msg);
857                     break;
858                 }
859                 case MSG_ID_HIDE_KEYBOARD_SELF:{
860                     auto session = GetUserSession(MAIN_USER_ID);
861                     if (session == nullptr) {
862                         IMSA_HILOGE("session is nullptr");
863                         break;
864                     }
865                     session->OnHideKeyboardSelf();
866                     break;
867                 }
868                 case MSG_ID_START_INPUT_SERVICE: {
869                     MessageParcel *data = msg->msgContent_;
870                     const auto &ime = data->ReadString();
871                     StartInputService(ime);
872                     break;
873                 }
874                 default: {
875                     break;
876                 }
877             }
878             delete msg;
879         }
880     }
881 
IsImeInstalled(int32_t userId,std::string & imeId)882 bool InputMethodSystemAbility::IsImeInstalled(int32_t userId, std::string &imeId)
883 {
884     std::vector<Property> props;
885     ListAllInputMethod(userId, props);
886     for (auto const &prop : props) {
887         std::string ime = prop.name + "/" + prop.id;
888         if (ime == imeId) {
889             IMSA_HILOGI("true");
890             return true;
891         }
892     }
893     IMSA_HILOGI("false");
894     return false;
895 }
896 
GetNewUserIme(int32_t userId)897 std::string InputMethodSystemAbility::GetNewUserIme(int32_t userId)
898 {
899     auto defaultIme = ImeCfgManager::GetDefaultIme();
900     if (defaultIme.empty()) {
901         IMSA_HILOGE("InputMethodSystemAbility::defaultIme is empty");
902         return "";
903     }
904     auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId);
905     auto newUserIme = cfg.currentIme;
906     if (newUserIme.empty()) {
907         newUserIme = defaultIme;
908         ImeCfgManager::GetInstance().AddImeCfg({ userId, newUserIme });
909     } else if (!IsImeInstalled(userId, newUserIme)) {
910         newUserIme = defaultIme;
911         ImeCfgManager::GetInstance().ModifyImeCfg({ userId, newUserIme });
912     }
913     return newUserIme;
914 }
915 
916 /*! Called when a user is started. (EVENT_USER_STARTED is received)
917     \n Run in work thread of input method management service
918     \param msg the parameters are saved in msg->msgContent_
919     \return ErrorCode
920     */
OnUserStarted(const Message * msg)921 int32_t InputMethodSystemAbility::OnUserStarted(const Message *msg)
922 {
923     if (!msg->msgContent_) {
924         IMSA_HILOGE("InputMethodSystemAbility::Aborted! %s\n", ErrorCode::ToString(ErrorCode::ERROR_BAD_PARAMETERS));
925         return ErrorCode::ERROR_BAD_PARAMETERS;
926     }
927     std::string lastUserIme;
928     if (userId_ != INVALID_USER_ID) {
929         auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId_);
930         lastUserIme = cfg.currentIme;
931         if (lastUserIme.empty()) {
932             IMSA_HILOGE("InputMethodSystemAbility::lastUserIme is empty");
933         }
934     }
935     int32_t newUserId = msg->msgContent_->ReadInt32();
936     IMSA_HILOGI("lastUserId: %{public}d, newUserId: %{public}d", userId_, newUserId);
937     userId_ = newUserId;
938     auto it = userSessions.find(MAIN_USER_ID);
939     if (it != userSessions.end()) {
940         it->second->UpdateCurrentUserId(newUserId);
941     }
942     if (!lastUserIme.empty()) {
943         IMSA_HILOGI("service restart or user switch");
944         StopInputService(lastUserIme);
945     }
946     StartInputService(GetNewUserIme(newUserId));
947     return ErrorCode::NO_ERROR;
948 }
949 
OnUserRemoved(const Message * msg)950 int32_t InputMethodSystemAbility::OnUserRemoved(const Message *msg)
951 {
952     if (!msg->msgContent_) {
953         IMSA_HILOGE("Aborted! %s", ErrorCode::ToString(ErrorCode::ERROR_BAD_PARAMETERS));
954         return ErrorCode::ERROR_BAD_PARAMETERS;
955     }
956     auto userId = msg->msgContent_->ReadInt32();
957     IMSA_HILOGI("Start: %{public}d", userId);
958     ImeCfgManager::GetInstance().DeleteImeCfg(userId);
959     return ErrorCode::NO_ERROR;
960 }
961 
962     /*! Called when a package is removed.
963     \n Run in work thread of input method management service
964     \param msg the parameters are saved in msg->msgContent_
965     \return ErrorCode::NO_ERROR
966     \return ErrorCode::ERROR_USER_NOT_UNLOCKED user not unlocked
967     \return ErrorCode::ERROR_BAD_PARAMETERS bad parameter
968     */
OnPackageRemoved(const Message * msg)969 int32_t InputMethodSystemAbility::OnPackageRemoved(const Message *msg)
970 {
971     IMSA_HILOGI("Start...\n");
972     MessageParcel *data = msg->msgContent_;
973     if (data == nullptr) {
974         IMSA_HILOGI("InputMethodSystemAbility::OnPackageRemoved data is nullptr");
975         return ErrorCode::ERROR_NULL_POINTER;
976     }
977     int32_t userId = 0;
978     std::string packageName = "";
979     if (!ITypesUtil::Unmarshal(*data, userId, packageName)) {
980         IMSA_HILOGE("Failed to read message parcel");
981         return ErrorCode::ERROR_EX_PARCELABLE;
982     }
983     // This will also be called when a user is removed. Do not process if removed user is not current one.
984     if (userId != userId_) {
985         return ErrorCode::NO_ERROR;
986     }
987     auto cfg = ImeCfgManager::GetInstance().GetImeCfg(userId);
988     auto &currentIme = cfg.currentIme;
989     if (currentIme.empty()) {
990         IMSA_HILOGE("InputMethodSystemAbility::currentIme is empty");
991         return ErrorCode::ERROR_PERSIST_CONFIG;
992     }
993     std::string::size_type pos = currentIme.find("/");
994     std::string currentImeBundle = currentIme.substr(0, pos);
995     if (packageName == currentImeBundle) {
996         std::string defaultIme = ImeCfgManager::GetDefaultIme();
997         if (defaultIme.empty()) {
998             IMSA_HILOGE("InputMethodSystemAbility::defaultIme is empty");
999             return ErrorCode::ERROR_PERSIST_CONFIG;
1000         }
1001         pos = defaultIme.find("/");
1002         int32_t ret =
1003             OnSwitchInputMethod(defaultIme.substr(0, pos), defaultIme.substr(pos + 1, defaultIme.length() - pos - 1));
1004         IMSA_HILOGI("InputMethodSystemAbility::OnPackageRemoved ret = %{public}d", ret);
1005     }
1006     return ErrorCode::NO_ERROR;
1007 }
OnDisplayOptionalInputMethod(int32_t userId)1008     int32_t InputMethodSystemAbility::OnDisplayOptionalInputMethod(int32_t userId)
1009     {
1010         IMSA_HILOGI("InputMethodSystemAbility::OnDisplayOptionalInputMethod");
1011         auto abilityManager = GetAbilityManagerService();
1012         if (abilityManager == nullptr) {
1013             IMSA_HILOGE("InputMethodSystemAbility::get ability manager failed");
1014             return ErrorCode::ERROR_EX_SERVICE_SPECIFIC;
1015         }
1016         AAFwk::Want want;
1017         want.SetAction(SELECT_DIALOG_ACTION);
1018         want.SetElementName(SELECT_DIALOG_HAP, SELECT_DIALOG_ABILITY);
1019         int32_t ret = abilityManager->StartAbility(want);
1020         if (ret == START_SERVICE_ABILITY_ACTIVATING) {
1021             return ErrorCode::NO_ERROR;
1022         }
1023         if (ret != ErrorCode::NO_ERROR) {
1024             IMSA_HILOGE("InputMethodSystemAbility::Start InputMethod ability failed, err = %{public}d", ret);
1025             return ErrorCode::ERROR_EX_SERVICE_SPECIFIC;
1026         }
1027         IMSA_HILOGI("InputMethodSystemAbility::Start InputMethod ability success.");
1028         return ErrorCode::NO_ERROR;
1029     }
GetBundleMgr()1030     sptr<OHOS::AppExecFwk::IBundleMgr> InputMethodSystemAbility::GetBundleMgr()
1031     {
1032         IMSA_HILOGI("InputMethodSystemAbility::GetBundleMgr");
1033         sptr<ISystemAbilityManager> systemAbilityManager =
1034         SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
1035         if (!systemAbilityManager) {
1036             IMSA_HILOGI("InputMethodSystemAbility::GetBundleMgr systemAbilityManager is nullptr");
1037             return nullptr;
1038         }
1039         sptr<IRemoteObject> remoteObject =
1040         systemAbilityManager->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
1041         return iface_cast<AppExecFwk::IBundleMgr>(remoteObject);
1042     }
1043 
QueryImeInfos(int32_t userId,std::vector<AppExecFwk::ExtensionAbilityInfo> & infos)1044     int32_t InputMethodSystemAbility::QueryImeInfos(
1045         int32_t userId, std::vector<AppExecFwk::ExtensionAbilityInfo> &infos)
1046     {
1047         IMSA_HILOGI("InputMethodSystemAbility::QueryImeInfos");
1048         auto bundleMgr = GetBundleMgr();
1049         if (bundleMgr == nullptr) {
1050             IMSA_HILOGE("Failed to GetBundleMgr");
1051             return ErrorCode::ERROR_NULL_POINTER;
1052         }
1053         if (!bundleMgr->QueryExtensionAbilityInfos(AbilityType::INPUTMETHOD, userId, infos)) {
1054             IMSA_HILOGE("QueryExtensionAbilityInfos failed");
1055             return ErrorCode::ERROR_PACKAGE_MANAGER;
1056         }
1057         return ErrorCode::NO_ERROR;
1058     }
1059 
GetAbilityManagerService()1060     sptr<AAFwk::IAbilityManager> InputMethodSystemAbility::GetAbilityManagerService()
1061     {
1062         IMSA_HILOGD("InputMethodSystemAbility::GetAbilityManagerService start");
1063         auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
1064         if (systemAbilityManager == nullptr) {
1065             IMSA_HILOGE("SystemAbilityManager is nullptr.");
1066             return nullptr;
1067         }
1068 
1069         auto abilityMsObj = systemAbilityManager->GetSystemAbility(ABILITY_MGR_SERVICE_ID);
1070         if (abilityMsObj == nullptr) {
1071             IMSA_HILOGE("Failed to get ability manager service.");
1072             return nullptr;
1073         }
1074 
1075         return iface_cast<AAFwk::IAbilityManager>(abilityMsObj);
1076     }
1077 
FindSubPropertyByCompare(const std::string & bundleName,CompareHandler compare)1078     SubProperty InputMethodSystemAbility::FindSubPropertyByCompare(
1079         const std::string &bundleName, CompareHandler compare)
1080     {
1081         IMSA_HILOGI("InputMethodSystemAbility::FindSubPropertyByCompare");
1082         std::vector<SubProperty> subProps = {};
1083         auto ret = ListSubtypeByBundleName(userId_, bundleName, subProps);
1084         if (ret != ErrorCode::NO_ERROR) {
1085             IMSA_HILOGE("ListSubtypeByBundleName failed, ret = %{public}d", ret);
1086             return {};
1087         }
1088         for (const auto &subProp : subProps) {
1089             if (compare(subProp)) {
1090                 return subProp;
1091             }
1092         }
1093         IMSA_HILOGE("InputMethodSystemAbility::FindSubPropertyByCompare failed");
1094         return {};
1095     }
1096 
SwitchByCombinationKey(uint32_t state)1097     int32_t InputMethodSystemAbility::SwitchByCombinationKey(uint32_t state)
1098     {
1099         IMSA_HILOGI("InputMethodSystemAbility::SwitchByCombinationKey");
1100         auto current = GetCurrentInputMethodSubtype();
1101         if (current == nullptr) {
1102             IMSA_HILOGE("GetCurrentInputMethodSubtype failed");
1103             return ErrorCode::ERROR_EX_NULL_POINTER;
1104         }
1105         if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_MODE, state)) {
1106             IMSA_HILOGI("switch mode");
1107             auto target = current->mode == "upper"
1108                               ? FindSubPropertyByCompare(current->name,
1109                                   [&current](const SubProperty &property) { return property.mode == "lower"; })
1110                               : FindSubPropertyByCompare(current->name,
1111                                   [&current](const SubProperty &property) { return property.mode == "upper"; });
1112             return SwitchInputMethodSubtype(target.name, target.id);
1113         }
1114         if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_LANGUAGE, state)) {
1115             IMSA_HILOGI("switch language");
1116             auto target = current->language == "chinese"
1117                               ? FindSubPropertyByCompare(current->name,
1118                                   [&current](const SubProperty &property) { return property.language == "english"; })
1119                               : FindSubPropertyByCompare(current->name,
1120                                   [&current](const SubProperty &property) { return property.language == "chinese"; });
1121             return SwitchInputMethodSubtype(target.name, target.id);
1122         }
1123         if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_IME, state)) {
1124             IMSA_HILOGI("switch ime");
1125             std::vector<Property> props = {};
1126             auto ret = ListProperty(userId_, props);
1127             if (ret != ErrorCode::NO_ERROR) {
1128                 IMSA_HILOGE("ListProperty failed");
1129                 return ret;
1130             }
1131             auto iter = std::find_if(props.begin(), props.end(),
1132                 [&current](const Property &property) { return property.name != current->name; });
1133             if (iter != props.end()) {
1134                 return SwitchInputMethodSubtype(iter->name, iter->id);
1135             }
1136         }
1137         IMSA_HILOGD("keycode undefined");
1138         return ErrorCode::ERROR_EX_UNSUPPORTED_OPERATION;
1139     }
1140 
InitKeyEventMonitor()1141     int32_t InputMethodSystemAbility::InitKeyEventMonitor()
1142     {
1143         IMSA_HILOGI("InputMethodSystemAbility::InitKeyEventMonitor");
1144         bool ret = ImCommonEventManager::GetInstance()->SubscribeKeyboardEvent(
1145             [this](uint32_t keyCode) { return SwitchByCombinationKey(keyCode); });
1146         return ret ? ErrorCode::NO_ERROR : ErrorCode::ERROR_SERVICE_START_FAILED;
1147     }
1148 } // namespace MiscServices
1149 } // namespace OHOS
1150