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 <unistd.h>
19
20 #include "ability_manager_client.h"
21 #include "application_info.h"
22 #include "bundle_checker.h"
23 #include "combination_key.h"
24 #include "common_event_support.h"
25 #include "errors.h"
26 #include "global.h"
27 #include "im_common_event_manager.h"
28 #include "ime_cfg_manager.h"
29 #include "ime_info_inquirer.h"
30 #include "ipc_skeleton.h"
31 #include "iservice_registry.h"
32 #include "itypes_util.h"
33 #include "key_event.h"
34 #include "message_handler.h"
35 #include "native_token_info.h"
36 #include "os_account_manager.h"
37 #include "sys/prctl.h"
38 #include "system_ability_definition.h"
39
40 namespace OHOS {
41 namespace MiscServices {
42 using namespace MessageID;
43 using namespace AccountSA;
44 REGISTER_SYSTEM_ABILITY_BY_ID(InputMethodSystemAbility, INPUT_METHOD_SYSTEM_ABILITY_ID, true);
45 constexpr std::int32_t INIT_INTERVAL = 10000L;
46 constexpr std::int32_t MAIN_USER_ID = 100;
47 constexpr uint32_t RETRY_INTERVAL = 100;
48 constexpr uint32_t BLOCK_RETRY_TIMES = 100;
49 constexpr uint32_t SWITCH_BLOCK_TIME = 150000;
50 static const std::string PERMISSION_CONNECT_IME_ABILITY = "ohos.permission.CONNECT_IME_ABILITY";
51 std::shared_ptr<AppExecFwk::EventHandler> InputMethodSystemAbility::serviceHandler_;
52
InputMethodSystemAbility(int32_t systemAbilityId,bool runOnCreate)53 InputMethodSystemAbility::InputMethodSystemAbility(int32_t systemAbilityId, bool runOnCreate)
54 : SystemAbility(systemAbilityId, runOnCreate), state_(ServiceRunningState::STATE_NOT_START)
55 {
56 }
57
InputMethodSystemAbility()58 InputMethodSystemAbility::InputMethodSystemAbility() : state_(ServiceRunningState::STATE_NOT_START)
59 {
60 }
61
~InputMethodSystemAbility()62 InputMethodSystemAbility::~InputMethodSystemAbility()
63 {
64 Message *msg = new Message(MessageID::MSG_ID_QUIT_WORKER_THREAD, nullptr);
65 MessageHandler::Instance()->SendMessage(msg);
66 if (workThreadHandler.joinable()) {
67 workThreadHandler.join();
68 }
69 }
70
OnStart()71 void InputMethodSystemAbility::OnStart()
72 {
73 IMSA_HILOGI("InputMethodSystemAbility::OnStart.");
74 if (!InputMethodSysEvent::GetInstance().StartTimerForReport()) {
75 IMSA_HILOGE("Start sysevent timer failed!");
76 }
77 if (state_ == ServiceRunningState::STATE_RUNNING) {
78 IMSA_HILOGI("ImsaService is already running.");
79 return;
80 }
81 Initialize();
82 InitServiceHandler();
83 int32_t ret = Init();
84 if (ret != ErrorCode::NO_ERROR) {
85 InputMethodSysEvent::GetInstance().ServiceFaultReporter("imf", ret);
86 auto callback = [=]() { Init(); };
87 serviceHandler_->PostTask(callback, INIT_INTERVAL);
88 IMSA_HILOGE("Init failed. Try again 10s later");
89 }
90 InitHiTrace();
91 InputMethodSyncTrace tracer("InputMethodController Attach trace.");
92 InputmethodDump::GetInstance().AddDumpAllMethod(
93 std::bind(&InputMethodSystemAbility::DumpAllMethod, this, std::placeholders::_1));
94 IMSA_HILOGI("Start ImsaService ErrorCode::NO_ERROR.");
95 return;
96 }
97
Dump(int fd,const std::vector<std::u16string> & args)98 int InputMethodSystemAbility::Dump(int fd, const std::vector<std::u16string> &args)
99 {
100 IMSA_HILOGI("InputMethodSystemAbility::Dump");
101 std::vector<std::string> argsStr;
102 for (auto item : args) {
103 argsStr.emplace_back(Str16ToStr8(item));
104 }
105 InputmethodDump::GetInstance().Dump(fd, argsStr);
106 return ERR_OK;
107 }
108
DumpAllMethod(int fd)109 void InputMethodSystemAbility::DumpAllMethod(int fd)
110 {
111 IMSA_HILOGI("InputMethodSystemAbility::DumpAllMethod");
112 std::vector<int32_t> ids;
113 int errCode = OsAccountManager::QueryActiveOsAccountIds(ids);
114 if (errCode != ERR_OK) {
115 dprintf(fd, "\n - InputMethodSystemAbility::DumpAllMethod get Active Id failed.\n");
116 return;
117 }
118 dprintf(fd, "\n - DumpAllMethod get Active Id succeed,count=%zu,", ids.size());
119 for (auto id : ids) {
120 const auto ¶ms = ImeInfoInquirer::GetInstance().GetInputMethodParam(id);
121 if (params.empty()) {
122 IMSA_HILOGI("userId: %{public}d The IME properties is empty.", id);
123 dprintf(fd, "\n - The IME properties about the Active Id %d is empty.\n", id);
124 continue;
125 }
126 dprintf(fd, "\n - The Active Id:%d get input method:\n%s\n", id, params.c_str());
127 }
128 IMSA_HILOGI("InputMethodSystemAbility::DumpAllMethod end.");
129 }
130
Init()131 int32_t InputMethodSystemAbility::Init()
132 {
133 bool isSuccess = Publish(this);
134 if (!isSuccess) {
135 return -1;
136 }
137 state_ = ServiceRunningState::STATE_RUNNING;
138 ImeCfgManager::GetInstance().Init();
139 std::vector<int32_t> userIds;
140 if (BlockRetry(RETRY_INTERVAL, BLOCK_RETRY_TIMES, [&userIds]() -> bool {
141 return OsAccountManager::QueryActiveOsAccountIds(userIds) == ERR_OK && !userIds.empty();
142 })) {
143 userId_ = userIds[0];
144 InputMethodSysEvent::GetInstance().SetUserId(userId_);
145 userSession_->UpdateCurrentUserId(userId_);
146 }
147 StartUserIdListener();
148 int32_t ret = InitKeyEventMonitor();
149 IMSA_HILOGI("init KeyEvent monitor %{public}s", ret == ErrorCode::NO_ERROR ? "success" : "failed");
150 ret = InitFocusChangeMonitor();
151 IMSA_HILOGI("init focus change monitor %{public}s", ret ? "success" : "failed");
152 return ErrorCode::NO_ERROR;
153 }
154
OnStop()155 void InputMethodSystemAbility::OnStop()
156 {
157 IMSA_HILOGI("OnStop started.");
158 serviceHandler_ = nullptr;
159 state_ = ServiceRunningState::STATE_NOT_START;
160 }
161
InitServiceHandler()162 void InputMethodSystemAbility::InitServiceHandler()
163 {
164 IMSA_HILOGI("InitServiceHandler started.");
165 if (serviceHandler_ != nullptr) {
166 IMSA_HILOGE("InputMethodSystemAbility, already init.");
167 return;
168 }
169 std::shared_ptr<AppExecFwk::EventRunner> runner = AppExecFwk::EventRunner::Create("InputMethodSystemAbility");
170 serviceHandler_ = std::make_shared<AppExecFwk::EventHandler>(runner);
171
172 IMSA_HILOGI("InitServiceHandler succeeded.");
173 }
174
175 /**
176 * Initialization of Input method management service
177 * \n It's called after the service starts, before any transaction.
178 */
Initialize()179 void InputMethodSystemAbility::Initialize()
180 {
181 IMSA_HILOGI("InputMethodSystemAbility::Initialize");
182 // init work thread to handle the messages
183 workThreadHandler = std::thread([this] { WorkThread(); });
184 userSession_ = std::make_shared<PerUserSession>(MAIN_USER_ID);
185 userId_ = MAIN_USER_ID;
186 }
187
StartUserIdListener()188 void InputMethodSystemAbility::StartUserIdListener()
189 {
190 sptr<ImCommonEventManager> imCommonEventManager = ImCommonEventManager::GetInstance();
191 bool isSuccess = imCommonEventManager->SubscribeEvent(EventFwk::CommonEventSupport::COMMON_EVENT_USER_SWITCHED);
192 if (isSuccess) {
193 IMSA_HILOGI("InputMethodSystemAbility::Initialize subscribe service event success");
194 return;
195 }
196
197 IMSA_HILOGE("StartUserIdListener failed. Try again 10s later");
198 auto callback = [this]() { StartUserIdListener(); };
199 serviceHandler_->PostTask(callback, INIT_INTERVAL);
200 }
201
StartInputService(const std::string & imeId)202 bool InputMethodSystemAbility::StartInputService(const std::string &imeId)
203 {
204 return userSession_->StartInputService(imeId, true);
205 }
206
StopInputService(const std::string & imeId)207 void InputMethodSystemAbility::StopInputService(const std::string &imeId)
208 {
209 IMSA_HILOGE("InputMethodSystemAbility::StopInputService(%{public}s)", imeId.c_str());
210 userSession_->StopInputService(imeId);
211 }
212
PrepareInput(InputClientInfo & clientInfo)213 int32_t InputMethodSystemAbility::PrepareInput(InputClientInfo &clientInfo)
214 {
215 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
216 if (!CheckBrokerTokenID(tokenId)) {
217 if (!BundleChecker::IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
218 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
219 }
220 }
221 auto ret = GenerateClientInfo(clientInfo);
222 if (ret != ErrorCode::NO_ERROR) {
223 return ret;
224 }
225 return userSession_->OnPrepareInput(clientInfo);
226 }
227
GenerateClientInfo(InputClientInfo & clientInfo)228 int32_t InputMethodSystemAbility::GenerateClientInfo(InputClientInfo &clientInfo)
229 {
230 if (clientInfo.client == nullptr || clientInfo.channel == nullptr) {
231 return ErrorCode::ERROR_NULL_POINTER;
232 }
233 auto deathRecipient = new (std::nothrow) InputDeathRecipient();
234 if (deathRecipient == nullptr) {
235 IMSA_HILOGE("failed to new deathRecipient");
236 return ErrorCode::ERROR_EX_NULL_POINTER;
237 }
238 clientInfo.pid = IPCSkeleton::GetCallingPid();
239 clientInfo.uid = IPCSkeleton::GetCallingUid();
240 clientInfo.userID = userId_;
241 clientInfo.deathRecipient = deathRecipient;
242 return ErrorCode::NO_ERROR;
243 }
244
ReleaseInput(sptr<IInputClient> client)245 int32_t InputMethodSystemAbility::ReleaseInput(sptr<IInputClient> client)
246 {
247 if (client == nullptr) {
248 IMSA_HILOGE("InputMethodSystemAbility::client is nullptr");
249 return ErrorCode::ERROR_CLIENT_NULL_POINTER;
250 }
251 return userSession_->OnReleaseInput(client);
252 };
253
StartInput(sptr<IInputClient> client,bool isShowKeyboard,bool attachFlag)254 int32_t InputMethodSystemAbility::StartInput(sptr<IInputClient> client, bool isShowKeyboard, bool attachFlag)
255 {
256 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
257 if (!CheckBrokerTokenID(tokenId)) {
258 if (!BundleChecker::IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
259 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
260 }
261 }
262 if (client == nullptr) {
263 IMSA_HILOGE("InputMethodSystemAbility::client is nullptr");
264 return ErrorCode::ERROR_CLIENT_NULL_POINTER;
265 }
266 return userSession_->OnStartInput(client, isShowKeyboard, attachFlag);
267 };
268
StopInput(sptr<IInputClient> client)269 int32_t InputMethodSystemAbility::StopInput(sptr<IInputClient> client)
270 {
271 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
272 if (!CheckBrokerTokenID(tokenId)) {
273 if (!userSession_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
274 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
275 }
276 }
277 if (client == nullptr) {
278 IMSA_HILOGE("InputMethodSystemAbility::client is nullptr");
279 return ErrorCode::ERROR_CLIENT_NULL_POINTER;
280 }
281 return userSession_->OnStopInput(client);
282 };
283
StopInputSession()284 int32_t InputMethodSystemAbility::StopInputSession()
285 {
286 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
287 if (!CheckBrokerTokenID(tokenId)) {
288 if (!userSession_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
289 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
290 }
291 }
292 return userSession_->OnHideKeyboardSelf();
293 }
294
SetCoreAndAgent(sptr<IInputMethodCore> core,sptr<IInputMethodAgent> agent)295 int32_t InputMethodSystemAbility::SetCoreAndAgent(sptr<IInputMethodCore> core, sptr<IInputMethodAgent> agent)
296 {
297 IMSA_HILOGD("InputMethodSystemAbility run in");
298 auto currentImeCfg = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_);
299 if (currentImeCfg == nullptr) {
300 IMSA_HILOGE("failed to get current ime");
301 return ErrorCode::ERROR_NULL_POINTER;
302 }
303 if (!BundleChecker::IsCurrentIme(IPCSkeleton::GetCallingTokenID(), currentImeCfg->bundleName)) {
304 return ErrorCode::ERROR_NOT_CURRENT_IME;
305 }
306 if (core == nullptr || agent == nullptr) {
307 IMSA_HILOGE("InputMethodSystemAbility::core or agent is nullptr");
308 return ErrorCode::ERROR_NULL_POINTER;
309 }
310 return userSession_->OnSetCoreAndAgent(core, agent);
311 };
312
HideCurrentInput()313 int32_t InputMethodSystemAbility::HideCurrentInput()
314 {
315 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
316 if (CheckBrokerTokenID(tokenId)) {
317 return userSession_->OnHideKeyboardSelf();
318 }
319 if (!BundleChecker::CheckPermission(tokenId, PERMISSION_CONNECT_IME_ABILITY)) {
320 return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
321 }
322
323 if (!userSession_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
324 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
325 }
326 return userSession_->OnHideKeyboardSelf();
327 };
328
ShowCurrentInput()329 int32_t InputMethodSystemAbility::ShowCurrentInput()
330 {
331 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
332 if (CheckBrokerTokenID(tokenId)) {
333 return userSession_->OnShowKeyboardSelf();
334 }
335
336 if (!BundleChecker::CheckPermission(tokenId, PERMISSION_CONNECT_IME_ABILITY)) {
337 return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
338 }
339
340 if (!userSession_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
341 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
342 }
343 return userSession_->OnShowKeyboardSelf();
344 };
345
PanelStatusChange(const InputWindowStatus & status,const InputWindowInfo & windowInfo)346 int32_t InputMethodSystemAbility::PanelStatusChange(const InputWindowStatus &status, const InputWindowInfo &windowInfo)
347 {
348 auto currentImeCfg = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_);
349 if (currentImeCfg == nullptr) {
350 IMSA_HILOGE("failed to get current ime");
351 return ErrorCode::ERROR_NULL_POINTER;
352 }
353 if (!BundleChecker::IsCurrentIme(IPCSkeleton::GetCallingTokenID(), currentImeCfg->bundleName)) {
354 IMSA_HILOGE("not current ime");
355 return ErrorCode::ERROR_NOT_CURRENT_IME;
356 }
357 return userSession_->OnPanelStatusChange(status, windowInfo);
358 }
359
UpdateListenEventFlag(InputClientInfo & clientInfo,EventType eventType)360 int32_t InputMethodSystemAbility::UpdateListenEventFlag(InputClientInfo &clientInfo, EventType eventType)
361 {
362 IMSA_HILOGI("eventType: %{public}u, eventFlag: %{public}u", eventType, clientInfo.eventFlag);
363 if ((eventType == IME_SHOW || eventType == IME_HIDE)
364 && !BundleChecker::IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
365 IMSA_HILOGE("not system application");
366 return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
367 }
368 auto ret = GenerateClientInfo(clientInfo);
369 if (ret != ErrorCode::NO_ERROR) {
370 return ret;
371 }
372 return userSession_->OnUpdateListenEventFlag(clientInfo);
373 }
374
DisplayOptionalInputMethod()375 int32_t InputMethodSystemAbility::DisplayOptionalInputMethod()
376 {
377 IMSA_HILOGD("InputMethodSystemAbility run in");
378 if (!BundleChecker::CheckPermission(IPCSkeleton::GetCallingTokenID(), PERMISSION_CONNECT_IME_ABILITY)) {
379 return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
380 }
381 return OnDisplayOptionalInputMethod();
382 };
383
SwitchInputMethod(const std::string & bundleName,const std::string & subName)384 int32_t InputMethodSystemAbility::SwitchInputMethod(const std::string &bundleName, const std::string &subName)
385 {
386 SwitchInfo switchInfo = { std::chrono::system_clock::now(), bundleName, subName };
387 switchQueue_.Push(switchInfo);
388 return OnSwitchInputMethod(switchInfo, true);
389 }
390
OnSwitchInputMethod(const SwitchInfo & switchInfo,bool isCheckPermission)391 int32_t InputMethodSystemAbility::OnSwitchInputMethod(const SwitchInfo &switchInfo, bool isCheckPermission)
392 {
393 IMSA_HILOGD("run in, switchInfo: %{public}s|%{public}s", switchInfo.bundleName.c_str(), switchInfo.subName.c_str());
394 InputMethodSysEvent::GetInstance().RecordEvent(IMEBehaviour::CHANGE_IME);
395 if (!switchQueue_.IsReady(switchInfo)) {
396 IMSA_HILOGD("start wait");
397 switchQueue_.Wait(switchInfo);
398 usleep(SWITCH_BLOCK_TIME);
399 }
400 IMSA_HILOGD("start switch %{public}s", (switchInfo.bundleName + '/' + switchInfo.subName).c_str());
401 // if currentIme is switching subtype, permission verification is not performed.
402 auto currentIme = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->bundleName;
403 if (isCheckPermission
404 && !BundleChecker::CheckPermission(IPCSkeleton::GetCallingTokenID(), PERMISSION_CONNECT_IME_ABILITY)
405 && !(switchInfo.bundleName == currentIme
406 && BundleChecker::IsCurrentIme(IPCSkeleton::GetCallingTokenID(), currentIme))) {
407 switchQueue_.Pop();
408 InputMethodSysEvent::GetInstance().InputmethodFaultReporter(
409 ErrorCode::ERROR_STATUS_PERMISSION_DENIED, switchInfo.bundleName, "switch inputmethod failed!");
410 return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
411 }
412 if (!IsNeedSwitch(switchInfo.bundleName, switchInfo.subName)) {
413 switchQueue_.Pop();
414 return ErrorCode::NO_ERROR;
415 }
416 ImeInfo info;
417 int32_t ret = ImeInfoInquirer::GetInstance().GetImeInfo(userId_, switchInfo.bundleName, switchInfo.subName, info);
418 if (ret != ErrorCode::NO_ERROR) {
419 switchQueue_.Pop();
420 return ret;
421 }
422 ret = info.isNewIme ? Switch(switchInfo.bundleName, info) : SwitchExtension(info);
423 switchQueue_.Pop();
424 if (ret != ErrorCode::NO_ERROR) {
425 InputMethodSysEvent::GetInstance().InputmethodFaultReporter(
426 ret, switchInfo.bundleName, "switch inputmethod failed!");
427 }
428 return ret;
429 }
430
IsNeedSwitch(const std::string & bundleName,const std::string & subName)431 bool InputMethodSystemAbility::IsNeedSwitch(const std::string &bundleName, const std::string &subName)
432 {
433 auto currentImeCfg = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_);
434 IMSA_HILOGI("currentIme: %{public}s, targetIme: %{public}s",
435 (currentImeCfg->bundleName + "/" + currentImeCfg->subName).c_str(), (bundleName + "/" + subName).c_str());
436 if ((subName.empty() && bundleName == currentImeCfg->bundleName)
437 || (!subName.empty() && subName == currentImeCfg->subName && currentImeCfg->bundleName == bundleName)) {
438 IMSA_HILOGI("no need to switch");
439 return false;
440 }
441 return true;
442 }
443
Switch(const std::string & bundleName,const ImeInfo & info)444 int32_t InputMethodSystemAbility::Switch(const std::string &bundleName, const ImeInfo &info)
445 {
446 auto currentImeBundleName = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->bundleName;
447 return bundleName != currentImeBundleName ? SwitchExtension(info) : SwitchSubType(info);
448 }
449
450 // Switch the current InputMethodExtension to the new InputMethodExtension
SwitchExtension(const ImeInfo & info)451 int32_t InputMethodSystemAbility::SwitchExtension(const ImeInfo &info)
452 {
453 auto currentIme = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->imeId;
454 StopInputService(currentIme);
455 std::string targetIme = info.prop.name + "/" + info.prop.id;
456 ImeCfgManager::GetInstance().ModifyImeCfg({ userId_, targetIme, info.subProp.id });
457 ImeInfoInquirer::GetInstance().SetCurrentImeInfo(info);
458 if (!StartInputService(targetIme)) {
459 IMSA_HILOGE("start input method failed");
460 return ErrorCode::ERROR_IME_START_FAILED;
461 }
462 userSession_->OnSwitchIme(info.prop, info.subProp, false);
463 return ErrorCode::NO_ERROR;
464 }
465
466 // Inform current InputMethodExtension to switch subtype
SwitchSubType(const ImeInfo & info)467 int32_t InputMethodSystemAbility::SwitchSubType(const ImeInfo &info)
468 {
469 auto ret = userSession_->OnSwitchIme(info.prop, info.subProp, true);
470 if (ret != ErrorCode::NO_ERROR) {
471 return ret;
472 }
473 auto currentIme = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->imeId;
474 ImeCfgManager::GetInstance().ModifyImeCfg({ userId_, currentIme, info.subProp.id });
475 ImeInfoInquirer::GetInstance().SetCurrentImeInfo(info);
476 return ErrorCode::NO_ERROR;
477 }
478
479 // Deprecated because of no permission check, kept for compatibility
HideCurrentInputDeprecated()480 int32_t InputMethodSystemAbility::HideCurrentInputDeprecated()
481 {
482 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
483 if (!CheckBrokerTokenID(tokenId)) {
484 if (!userSession_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
485 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
486 }
487 }
488 return userSession_->OnHideKeyboardSelf();
489 };
490
ShowCurrentInputDeprecated()491 int32_t InputMethodSystemAbility::ShowCurrentInputDeprecated()
492 {
493 AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
494 if (!CheckBrokerTokenID(tokenId)) {
495 if (!userSession_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId)) {
496 return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
497 }
498 }
499 return userSession_->OnShowKeyboardSelf();
500 };
501
DisplayOptionalInputMethodDeprecated()502 int32_t InputMethodSystemAbility::DisplayOptionalInputMethodDeprecated()
503 {
504 return OnDisplayOptionalInputMethod();
505 };
506
GetCurrentInputMethod()507 std::shared_ptr<Property> InputMethodSystemAbility::GetCurrentInputMethod()
508 {
509 return ImeInfoInquirer::GetInstance().GetCurrentInputMethod(userId_);
510 }
511
GetCurrentInputMethodSubtype()512 std::shared_ptr<SubProperty> InputMethodSystemAbility::GetCurrentInputMethodSubtype()
513 {
514 return ImeInfoInquirer::GetInstance().GetCurrentInputMethodSubtype(userId_);
515 }
516
ListInputMethod(InputMethodStatus status,std::vector<Property> & props)517 int32_t InputMethodSystemAbility::ListInputMethod(InputMethodStatus status, std::vector<Property> &props)
518 {
519 return ImeInfoInquirer::GetInstance().ListInputMethod(userId_, status, props);
520 }
521
ListCurrentInputMethodSubtype(std::vector<SubProperty> & subProps)522 int32_t InputMethodSystemAbility::ListCurrentInputMethodSubtype(std::vector<SubProperty> &subProps)
523 {
524 return ImeInfoInquirer::GetInstance().ListCurrentInputMethodSubtype(userId_, subProps);
525 }
526
ListInputMethodSubtype(const std::string & bundleName,std::vector<SubProperty> & subProps)527 int32_t InputMethodSystemAbility::ListInputMethodSubtype(
528 const std::string &bundleName, std::vector<SubProperty> &subProps)
529 {
530 return ImeInfoInquirer::GetInstance().ListInputMethodSubtype(userId_, bundleName, subProps);
531 }
532
533 /**
534 * Work Thread of input method management service
535 * \n Remote commands which may change the state or data in the service will be handled sequentially in this thread.
536 */
WorkThread()537 void InputMethodSystemAbility::WorkThread()
538 {
539 prctl(PR_SET_NAME, "IMSAWorkThread");
540 while (1) {
541 Message *msg = MessageHandler::Instance()->GetMessage();
542 switch (msg->msgId_) {
543 case MSG_ID_USER_START: {
544 OnUserStarted(msg);
545 break;
546 }
547 case MSG_ID_USER_REMOVED: {
548 OnUserRemoved(msg);
549 break;
550 }
551 case MSG_ID_PACKAGE_REMOVED: {
552 OnPackageRemoved(msg);
553 break;
554 }
555 case MSG_ID_HIDE_KEYBOARD_SELF: {
556 userSession_->OnHideKeyboardSelf();
557 break;
558 }
559 case MSG_ID_START_INPUT_SERVICE: {
560 StartInputService(ImeInfoInquirer::GetInstance().GetStartedIme(userId_));
561 break;
562 }
563 case MSG_ID_QUIT_WORKER_THREAD: {
564 IMSA_HILOGD("Quit Sa work thread.");
565 return;
566 }
567 default: {
568 break;
569 }
570 }
571 delete msg;
572 }
573 }
574
575 /**
576 * Called when a user is started. (EVENT_USER_STARTED is received)
577 * \n Run in work thread of input method management service
578 * \param msg the parameters are saved in msg->msgContent_
579 * \return ErrorCode
580 */
OnUserStarted(const Message * msg)581 int32_t InputMethodSystemAbility::OnUserStarted(const Message *msg)
582 {
583 if (msg->msgContent_ == nullptr) {
584 IMSA_HILOGE("msgContent is nullptr.");
585 return ErrorCode::ERROR_NULL_POINTER;
586 }
587 int32_t oldUserId = userId_;
588 userId_ = msg->msgContent_->ReadInt32();
589 userSession_->UpdateCurrentUserId(userId_);
590 if (oldUserId == userId_) {
591 IMSA_HILOGI("device boot, userId: %{public}d", userId_);
592 return ErrorCode::NO_ERROR;
593 }
594 IMSA_HILOGI("%{public}d switch to %{public}d.", oldUserId, userId_);
595 auto currentIme = ImeCfgManager::GetInstance().GetCurrentImeCfg(oldUserId)->imeId;
596 StopInputService(currentIme);
597 // user switch, reset currentImeInfo_ = nullptr
598 ImeInfoInquirer::GetInstance().ResetCurrentImeInfo();
599 auto newIme = ImeInfoInquirer::GetInstance().GetStartedIme(userId_);
600 InputMethodSysEvent::GetInstance().SetUserId(userId_);
601 if (!StartInputService(newIme)) {
602 IMSA_HILOGE("start input method failed");
603 InputMethodSysEvent::GetInstance().InputmethodFaultReporter(
604 ErrorCode::ERROR_IME_START_FAILED, newIme, "user start ime failed!");
605 return ErrorCode::ERROR_IME_START_FAILED;
606 }
607 return ErrorCode::NO_ERROR;
608 }
609
OnUserRemoved(const Message * msg)610 int32_t InputMethodSystemAbility::OnUserRemoved(const Message *msg)
611 {
612 if (msg->msgContent_ == nullptr) {
613 IMSA_HILOGE("Aborted! Message is nullptr.");
614 return ErrorCode::ERROR_NULL_POINTER;
615 }
616 auto userId = msg->msgContent_->ReadInt32();
617 IMSA_HILOGI("Start: %{public}d", userId);
618 ImeCfgManager::GetInstance().DeleteImeCfg(userId);
619 return ErrorCode::NO_ERROR;
620 }
621
622 /**
623 * Called when a package is removed.
624 * \n Run in work thread of input method management service
625 * \param msg the parameters are saved in msg->msgContent_
626 * \return ErrorCode::NO_ERROR
627 * \return ErrorCode::ERROR_USER_NOT_UNLOCKED user not unlocked
628 * \return ErrorCode::ERROR_BAD_PARAMETERS bad parameter
629 */
OnPackageRemoved(const Message * msg)630 int32_t InputMethodSystemAbility::OnPackageRemoved(const Message *msg)
631 {
632 IMSA_HILOGI("Start...\n");
633 MessageParcel *data = msg->msgContent_;
634 if (data == nullptr) {
635 IMSA_HILOGD("data is nullptr");
636 return ErrorCode::ERROR_NULL_POINTER;
637 }
638 int32_t userId = 0;
639 std::string packageName = "";
640 if (!ITypesUtil::Unmarshal(*data, userId, packageName)) {
641 IMSA_HILOGE("Failed to read message parcel");
642 return ErrorCode::ERROR_EX_PARCELABLE;
643 }
644 // 用户移除也会有该通知,如果移除的app用户不是当前用户,则不处理
645 if (userId != userId_) {
646 IMSA_HILOGI("InputMethodSystemAbility::userId: %{public}d, currentUserId: %{public}d,", userId, userId_);
647 return ErrorCode::NO_ERROR;
648 }
649 auto currentImeBundle = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId)->bundleName;
650 if (packageName == currentImeBundle) {
651 // Switch to the default ime
652 auto info = ImeInfoInquirer::GetInstance().GetDefaultImeInfo(userId);
653 if (info == nullptr) {
654 return ErrorCode::ERROR_PERSIST_CONFIG;
655 }
656 int32_t ret = SwitchExtension(*info);
657 IMSA_HILOGI("InputMethodSystemAbility::OnPackageRemoved ret = %{public}d", ret);
658 }
659 return ErrorCode::NO_ERROR;
660 }
661
OnDisplayOptionalInputMethod()662 int32_t InputMethodSystemAbility::OnDisplayOptionalInputMethod()
663 {
664 IMSA_HILOGI("InputMethodSystemAbility::OnDisplayOptionalInputMethod");
665 AAFwk::Want want;
666 want.SetAction(SELECT_DIALOG_ACTION);
667 want.SetElementName(SELECT_DIALOG_HAP, SELECT_DIALOG_ABILITY);
668 int32_t ret = AAFwk::AbilityManagerClient::GetInstance()->StartAbility(want);
669 if (ret != ErrorCode::NO_ERROR && ret != START_SERVICE_ABILITY_ACTIVATING) {
670 IMSA_HILOGE("InputMethodSystemAbility::Start InputMethod ability failed, err = %{public}d", ret);
671 return ErrorCode::ERROR_EX_SERVICE_SPECIFIC;
672 }
673 IMSA_HILOGI("InputMethodSystemAbility::Start InputMethod ability success.");
674 return ErrorCode::NO_ERROR;
675 }
676
SwitchByCombinationKey(uint32_t state)677 int32_t InputMethodSystemAbility::SwitchByCombinationKey(uint32_t state)
678 {
679 IMSA_HILOGI("InputMethodSystemAbility::SwitchByCombinationKey");
680 if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_MODE, state)) {
681 IMSA_HILOGI("switch mode");
682 return SwitchMode();
683 }
684 if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_LANGUAGE, state)) {
685 IMSA_HILOGI("switch language");
686 return SwitchLanguage();
687 }
688 if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_IME, state)) {
689 IMSA_HILOGI("switch ime");
690 return SwitchType();
691 }
692 IMSA_HILOGE("keycode undefined");
693 return ErrorCode::ERROR_EX_UNSUPPORTED_OPERATION;
694 }
695
SwitchMode()696 int32_t InputMethodSystemAbility::SwitchMode()
697 {
698 ImeInfo info;
699 auto bundleName = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->bundleName;
700 auto subName = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->subName;
701 auto ret = ImeInfoInquirer::GetInstance().GetImeInfo(userId_, bundleName, subName, info);
702 if (ret != ErrorCode::NO_ERROR) {
703 IMSA_HILOGE("current ime is abnormal, ret: %{public}d", ret);
704 return ret;
705 }
706 auto condition = info.subProp.mode == "upper" ? Condition::LOWER : Condition::UPPER;
707 auto target = ImeInfoInquirer::GetInstance().GetImeSubProp(info.subProps, condition);
708 if (target == nullptr) {
709 IMSA_HILOGE("target is empty");
710 return ErrorCode::ERROR_BAD_PARAMETERS;
711 }
712 SwitchInfo switchInfo = { std::chrono::system_clock::now(), target->name, target->id };
713 switchQueue_.Push(switchInfo);
714 return OnSwitchInputMethod(switchInfo, false);
715 }
716
SwitchLanguage()717 int32_t InputMethodSystemAbility::SwitchLanguage()
718 {
719 ImeInfo info;
720 auto bundleName = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->bundleName;
721 auto subName = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->subName;
722 auto ret = ImeInfoInquirer::GetInstance().GetImeInfo(userId_, bundleName, subName, info);
723 if (ret != ErrorCode::NO_ERROR) {
724 IMSA_HILOGE("current ime is abnormal, ret: %{public}d", ret);
725 return ret;
726 }
727 if (info.subProp.language != "chinese" && info.subProp.language != "english") {
728 return ErrorCode::NO_ERROR;
729 }
730 auto condition = info.subProp.language == "chinese" ? Condition::ENGLISH : Condition::CHINESE;
731 auto target = ImeInfoInquirer::GetInstance().GetImeSubProp(info.subProps, condition);
732 if (target == nullptr) {
733 IMSA_HILOGE("target is empty");
734 return ErrorCode::ERROR_BAD_PARAMETERS;
735 }
736 SwitchInfo switchInfo = { std::chrono::system_clock::now(), target->name, target->id };
737 switchQueue_.Push(switchInfo);
738 return OnSwitchInputMethod(switchInfo, false);
739 }
740
SwitchType()741 int32_t InputMethodSystemAbility::SwitchType()
742 {
743 std::vector<Property> props = {};
744 auto ret = ImeInfoInquirer::GetInstance().ListInputMethod(userId_, ALL, props);
745 if (ret != ErrorCode::NO_ERROR) {
746 IMSA_HILOGE("ListProperty failed");
747 return ret;
748 }
749 auto currentImeBundle = ImeCfgManager::GetInstance().GetCurrentImeCfg(userId_)->bundleName;
750 auto iter = std::find_if(props.begin(), props.end(),
751 [¤tImeBundle](const Property &property) { return property.name != currentImeBundle; });
752 if (iter != props.end()) {
753 SwitchInfo switchInfo = { std::chrono::system_clock::now(), iter->name, "" };
754 switchQueue_.Push(switchInfo);
755 return OnSwitchInputMethod(switchInfo, false);
756 }
757 return ErrorCode::NO_ERROR;
758 }
759
InitKeyEventMonitor()760 int32_t InputMethodSystemAbility::InitKeyEventMonitor()
761 {
762 IMSA_HILOGI("InputMethodSystemAbility::InitKeyEventMonitor");
763 bool ret = ImCommonEventManager::GetInstance()->SubscribeKeyboardEvent(
764 [this](uint32_t keyCode) { return SwitchByCombinationKey(keyCode); });
765 return ret ? ErrorCode::NO_ERROR : ErrorCode::ERROR_SERVICE_START_FAILED;
766 }
767
InitFocusChangeMonitor()768 bool InputMethodSystemAbility::InitFocusChangeMonitor()
769 {
770 return ImCommonEventManager::GetInstance()->SubscribeWindowManagerService(
771 [this](bool isOnFocused, int32_t pid, int32_t uid) {
772 return isOnFocused ? userSession_->OnFocused(pid, uid) : userSession_->OnUnfocused(pid, uid);
773 },
774 [this]() { StartInputService(ImeInfoInquirer::GetInstance().GetStartedIme(userId_)); });
775 }
776
CheckBrokerTokenID(AccessTokenID tokenId)777 bool InputMethodSystemAbility::CheckBrokerTokenID(AccessTokenID tokenId)
778 {
779 NativeTokenInfo nativeTokenInfoRes;
780 AccessTokenKit::GetNativeTokenInfo(tokenId, nativeTokenInfoRes);
781 if (AccessTokenKit::GetTokenType(tokenId) == TypeATokenTypeEnum::TOKEN_NATIVE
782 && nativeTokenInfoRes.processName == "broker" && nativeTokenInfoRes.apl == ATokenAplEnum::APL_SYSTEM_BASIC) {
783 return true;
784 }
785 return false;
786 }
787 } // namespace MiscServices
788 } // namespace OHOS
789