/* * Copyright (C) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "peruser_session.h" #include #include "ability_connect_callback_proxy.h" #include "ability_manager_interface.h" #include "element_name.h" #include "input_client_proxy.h" #include "input_control_channel_proxy.h" #include "input_data_channel_proxy.h" #include "input_method_ability_connection_stub.h" #include "input_method_agent_proxy.h" #include "input_method_core_proxy.h" #include "ipc_skeleton.h" #include "message_parcel.h" #include "para_handle.h" #include "parcel.h" #include "platform.h" #include "sa_mgr_client.h" #include "system_ability_definition.h" #include "unistd.h" #include "utils.h" #include "want.h" namespace OHOS { namespace MiscServices { using namespace MessageID; /*! Constructor \param userId the id of the given user \param msgId the msg id can be MessageID::MSG_ID_CLIENT_DIED (to monitor input client) \n or MessageID::MSG_ID_IMS_DIED (to monitor input method service) */ RemoteObjectDeathRecipient::RemoteObjectDeathRecipient(int userId, int msgId) { userId_ = userId; msgId_ = msgId; } RemoteObjectDeathRecipient::~RemoteObjectDeathRecipient() { } /*! Notify that a remote object died. \n It's called when the linked remote object died. \param who the IRemoteObject handler of the remote object died. */ void RemoteObjectDeathRecipient::OnRemoteDied(const wptr &who) { auto parcel = new (std::nothrow) MessageParcel(); if (parcel == nullptr) { IMSA_HILOGE("parcel is nullptr"); return; } parcel->WriteInt32(userId_); parcel->WritePointer(reinterpret_cast(who.GetRefPtr())); auto msg = new (std::nothrow) Message(msgId_, parcel); if (msg == nullptr) { IMSA_HILOGE("msg is nullptr"); delete parcel; return; } MessageHandler::Instance()->SendMessage(msg); } /*! Constructor \param userId the user id of this user whose session are managed by this instance of PerUserSession. */ PerUserSession::PerUserSession(int userId) { userState = UserState::USER_STATE_STARTED; userId_ = userId; currentIme[0] = nullptr; currentIme[1] = nullptr; needReshowClient = nullptr; imsDeathRecipient = new RemoteObjectDeathRecipient(userId, MSG_ID_IMS_DIED); } /*! Destructor */ PerUserSession::~PerUserSession() { if (userState == UserState::USER_STATE_UNLOCKED) { OnUserLocked(); } imsDeathRecipient = nullptr; if (workThreadHandler.joinable()) { workThreadHandler.join(); } } /*! Create work thread for this user \param handle message handle to receive the message */ void PerUserSession::CreateWorkThread(MessageHandler& handler) { msgHandler = &handler; workThreadHandler = std::thread([this] {WorkThread();}); } /*! Wait till work thread exits */ void PerUserSession::JoinWorkThread() { if (workThreadHandler.joinable()) { workThreadHandler.join(); } } /*! Work thread for this user */ void PerUserSession::WorkThread() { if (msgHandler == nullptr) { return; } while (1) { Message *msg = msgHandler->GetMessage(); std::unique_lock lock(mtx); switch (msg->msgId_) { case MSG_ID_USER_LOCK: case MSG_ID_EXIT_SERVICE: { OnUserLocked(); delete msg; msg = nullptr; return; } case MSG_ID_PREPARE_INPUT: { OnPrepareInput(msg); break; } case MSG_ID_RELEASE_INPUT: { OnReleaseInput(msg); break; } case MSG_ID_START_INPUT: { OnStartInput(msg); break; } case MSG_ID_STOP_INPUT: { OnStopInput(msg); break; } case MSG_ID_SET_CORE_AND_AGENT: { SetCoreAndAgent(msg); break; } case MSG_ID_CLIENT_DIED: { auto *remoteObject = reinterpret_cast(msg->msgContent_->ReadPointer()); if (remoteObject == nullptr) { IMSA_HILOGE("MSG_ID_CLIENT_DIED remoteObject is nullptr"); break; } OnClientDied(remoteObject); break; } case MSG_ID_IMS_DIED: { auto *remoteObject = reinterpret_cast(msg->msgContent_->ReadPointer()); if (remoteObject == nullptr) { IMSA_HILOGE("MSG_ID_IMS_DIED remoteObject is nullptr"); break; } OnImsDied(remoteObject); break; } case MSG_ID_HIDE_KEYBOARD_SELF: { int flag = msg->msgContent_->ReadInt32(); OnHideKeyboardSelf(flag); break; } case MSG_ID_ADVANCE_TO_NEXT: { OnAdvanceToNext(); break; } case MSG_ID_SET_DISPLAY_MODE: { int mode = msg->msgContent_->ReadInt32(); OnSetDisplayMode(mode); break; } case MSG_ID_RESTART_IMS: { int index = msg->msgContent_->ReadInt32(); std::u16string imeId = msg->msgContent_->ReadString16(); OnRestartIms(index, imeId); break; } case MSG_HIDE_CURRENT_INPUT: { OnHideKeyboardSelf(0); } default: { break; } } delete msg; msg = nullptr; } } /*! Set display Id \param displayId the Id of display screen on which the input method keyboard show. */ void PerUserSession::SetDisplayId(int displayId) { this->displayId = displayId; } /*! Set the current input method engine \param ime the current (default) IME pointer referred to the instance in PerUserSetting. */ void PerUserSession::SetCurrentIme(InputMethodProperty *ime) { currentIme[DEFAULT_IME] = ime; userState = UserState::USER_STATE_UNLOCKED; } /*! Set the system security input method engine \param ime system security IME pointer referred to the instance in PerUserSetting. */ void PerUserSession::SetSecurityIme(InputMethodProperty *ime) { currentIme[SECURITY_IME] = ime; } /*! Set the input method setting data \param setting InputMethodSetting pointer referred to the instance in PerUserSetting. */ void PerUserSession::SetInputMethodSetting(InputMethodSetting *setting) { inputMethodSetting = setting; } /*! Reset input method engine \param defaultIme default ime pointer referred to the instance in PerUserSetting \param security security ime pointer referred to the instance in PerUserSetting \n Two input method engines can be running at the same time for one user. \n One is the default ime, another is security ime */ void PerUserSession::ResetIme(InputMethodProperty *defaultIme, InputMethodProperty *securityIme) { IMSA_HILOGI("PerUserSession::ResetIme"); std::unique_lock lock(mtx); InputMethodProperty *ime[] = {defaultIme, securityIme}; for (int i = 0; i < MIN_IME; i++) { if (currentIme[i] == ime[i] && ime[i] != nullptr) { continue; } if (imsCore[i]) { StopInputMethod(i); } ResetImeError(i); currentIme[i] = ime[i]; if (currentIme[i] == nullptr) { if (needReshowClient && GetImeIndex(needReshowClient) == i) { needReshowClient = nullptr; } continue; } bool flag = false; for (auto mapClient : mapClients) { if ((i == DEFAULT_IME && !mapClient.second->attribute.GetSecurityFlag()) || (i == SECURITY_IME && mapClient.second->attribute.GetSecurityFlag())) { flag = true; break; } } if (flag) { int ret = StartInputMethod(i); if (ret != ErrorCode::NO_ERROR) { needReshowClient = nullptr; break; } if (needReshowClient && GetImeIndex(needReshowClient) == i) { ShowKeyboard(needReshowClient); needReshowClient = nullptr; } } } } /*! Called when a package is removed \param packageName the name of package removed */ void PerUserSession::OnPackageRemoved(const std::u16string& packageName) { IMSA_HILOGI("PerUserSession::OnPackageRemoved"); InputMethodSetting tmpSetting; bool flag = false; std::unique_lock lock(mtx); for (int i = 0; i < MAX_IME; i++) { sptr client = GetCurrentClient(); if (currentIme[i] && currentIme[i]->mPackageName == packageName) { if (client != nullptr && GetImeIndex(client) == i) { needReshowClient = client; HideKeyboard(client); } StopInputMethod(i); currentIme[i] = nullptr; if (i == DEFAULT_IME) { tmpSetting.SetCurrentKeyboardType(-1); inputMethodSetting->SetCurrentKeyboardType(-1); } else if (i == SECURITY_IME) { tmpSetting.SetCurrentSysKeyboardType(-1); inputMethodSetting->SetCurrentSysKeyboardType(-1); } currentKbdIndex[i] = 0; flag = true; } } if (flag) { Platform::Instance()->SetInputMethodSetting(userId_, tmpSetting); } } /*! Add an input client \param pid the process pid of the input client \param uid the uid of the the input client \param displayId the display id of the input client \param inputClient the remote object handler of the input client \param channel the remote InputDataChannel object handler for the input client. \n It will be transferred to input method service \param attribute the input attribute of the input client. \return \li ErrorCode::NO_ERROR no error \return \li ErrorCode::ERROR_CLIENT_DUPLICATED client is duplicated */ int PerUserSession::AddClient(const ClientInfo &clientInfo) { IMSA_HILOGI("PerUserSession::AddClient"); ClientInfo *info = GetClientInfo(clientInfo.client); if (info != nullptr) { IMSA_HILOGE("info is exist, not need add."); return ErrorCode::NO_ERROR; } sptr obj = clientInfo.client->AsObject(); if (obj == nullptr) { IMSA_HILOGE("inputClient AsObject is nullptr"); return ErrorCode::ERROR_REMOTE_CLIENT_DIED; } bool ret = obj->AddDeathRecipient(clientInfo.deathRecipient); IMSA_HILOGI("Add death recipient %{public}s", ret ? "success" : "failed"); info = new (std::nothrow) ClientInfo(clientInfo); if (info == nullptr) { IMSA_HILOGE("failed to create ClientInfo"); return ErrorCode::ERROR_NULL_POINTER; } mapClients.insert({ obj, info }); return ErrorCode::NO_ERROR; } /*! Remove an input client \param inputClient remote object handler of the input client \param[out] remainClientNum remained count of the same kinds of clients for this user \n (i.e. if inputClient is an normal client, remainClientNum is the count of remained normal clients. \n if inputClient is a security client, remainClientNum is the count of remained security clients.) \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_CLIENT_NOT_FOUND client is not found */ void PerUserSession::RemoveClient(IRemoteObject *inputClient) { IMSA_HILOGI("PerUserSession::RemoveClient"); auto it = mapClients.find(inputClient); if (it == mapClients.end()) { IMSA_HILOGE("PerUserSession::RemoveClient client not found"); return; } ClientInfo *clientInfo = it->second; inputClient->RemoveDeathRecipient(clientInfo->deathRecipient); delete clientInfo; clientInfo = nullptr; mapClients.erase(it); } /*! Start input method service \param index it can be 0 or 1. 0 - default ime, 1 - security ime \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_IME_BIND_FAILED failed to bind ime \return ErrorCode::ERROR_IME_NOT_AVAILABLE no ime is available \return ErrorCode::ERROR_SECURITY_IME_NOT_AVAILABLE no security ime is available \return ErrorCode::ERROR_TOKEN_CREATE_FAILED failed to create window token \return other errors returned by binder driver */ int PerUserSession::StartInputMethod(int index) { IMSA_HILOGI("PerUserSession::StartInputMethod index = %{public}d [%{public}d]\n", index, userId_); if (imsCore[index] == nullptr) { IMSA_HILOGI("PerUserSession::StartInputMethod imscore is null"); return ErrorCode::ERROR_IME_BIND_FAILED; } sptr b = imsCore[index]->AsObject(); inputMethodToken[index] = IPCSkeleton::GetInstance().GetContextObject(); localControlChannel[index] = new InputControlChannelStub(userId_); inputControlChannel[index] = localControlChannel[index]; int ret_init = imsCore[index]->initializeInput(inputMethodToken[index], displayId, inputControlChannel[index]); if (ret_init != ErrorCode::NO_ERROR) { IMSA_HILOGE("PerUserSession::StartInputMethod initializeInput fail %{public}s", ErrorCode::ToString(ret_init)); localControlChannel[index] = nullptr; inputControlChannel[index] = nullptr; return ret_init; } return ErrorCode::NO_ERROR; } /*! Stop input method service \param index it can be 0 or 1. 0 - default ime, 1 - security ime \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_IME_NOT_STARTED ime not started \return ErrorCode::ERROR_IME_UNBIND_FAILED failed to unbind ime \return ErrorCode::ERROR_TOKEN_DESTROY_FAILED failed to destroy window token \return other errors returned by binder driver */ int PerUserSession::StopInputMethod(int index) { IMSA_HILOGI("Start... index = %{public}d [%{public}d]\n", index, userId_); if (index >= MAX_IME || index < 0) { IMSA_HILOGE("Aborted! %{public}s", ErrorCode::ToString(ErrorCode::ERROR_BAD_PARAMETERS)); return ErrorCode::ERROR_BAD_PARAMETERS; } if (imsCore[index] == nullptr || currentIme[index] == nullptr) { IMSA_HILOGE("Aborted! %{public}s", ErrorCode::ToString(ErrorCode::ERROR_IME_NOT_STARTED)); return ErrorCode::ERROR_IME_NOT_STARTED; } if (currentIme[index] == currentIme[1 - index] && imsCore[1 - index] != nullptr) { imsCore[index] = nullptr; inputControlChannel[index] = nullptr; localControlChannel[index] = nullptr; IMSA_HILOGI("End...[%{public}d]\n", userId_); return ErrorCode::NO_ERROR; } IMSA_HILOGD("unbindInputMethodService...\n"); IMSA_HILOGD("destroyWindowToken...\n"); int errorCode = ErrorCode::NO_ERROR; int ret = Platform::Instance()->DestroyWindowToken(userId_, currentIme[index]->mPackageName); inputMethodToken[index] = nullptr; if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("destroyWindowToken return : %{public}s [%{public}d]\n", ErrorCode::ToString(ret), userId_); errorCode = ErrorCode::ERROR_TOKEN_DESTROY_FAILED; } sptr b = imsCore[index]->AsObject(); ret = b->RemoveDeathRecipient(imsDeathRecipient); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("RemoveDeathRecipient return : %{public}s [%{public}d]\n", ErrorCode::ToString(ret), userId_); } imsCore[index] = nullptr; inputControlChannel[index] = nullptr; localControlChannel[index] = nullptr; IMSA_HILOGI("End...[%{public}d]\n", userId_); return errorCode; } /*! Show keyboard \param inputClient the remote object handler of the input client. \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_IME_NOT_STARTED ime not started \return ErrorCode::ERROR_KBD_IS_OCCUPIED keyboard is showing by other client \return ErrorCode::ERROR_CLIENT_NOT_FOUND the input client is not found \return ErrorCode::ERROR_IME_START_FAILED failed to start input method service \return ErrorCode::ERROR_KBD_SHOW_FAILED failed to show keyboard \return other errors returned by binder driver */ int PerUserSession::ShowKeyboard(const sptr& inputClient) { IMSA_HILOGI("PerUserSession::ShowKeyboard"); ClientInfo *clientInfo = GetClientInfo(inputClient); int index = GetImeIndex(inputClient); if (index == -1 || clientInfo == nullptr) { IMSA_HILOGE("PerUserSession::ShowKeyboard Aborted! index = -1 or clientInfo is nullptr"); return ErrorCode::ERROR_CLIENT_NOT_FOUND; } if (imsCore[0] == nullptr) { IMSA_HILOGE("PerUserSession::ShowKeyboard Aborted! imsCore[%{public}d] is nullptr", index); return ErrorCode::ERROR_NULL_POINTER; } imsCore[0]->showKeyboard(clientInfo->channel); SetCurrentClient(inputClient); return ErrorCode::NO_ERROR; } /*! hide keyboard \param inputClient the remote object handler of the input client. \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_IME_NOT_STARTED ime not started \return ErrorCode::ERROR_KBD_IS_NOT_SHOWING keyboard has not been showing \return ErrorCode::ERROR_CLIENT_NOT_FOUND the input client is not found \return ErrorCode::ERROR_KBD_HIDE_FAILED failed to hide keyboard \return other errors returned by binder driver */ int PerUserSession::HideKeyboard(const sptr& inputClient) { IMSA_HILOGI("PerUserSession::HideKeyboard"); int index = GetImeIndex(inputClient); if (index == -1) { IMSA_HILOGE("PerUserSession::HideKeyboard Aborted! ErrorCode::ERROR_CLIENT_NOT_FOUND"); return ErrorCode::ERROR_CLIENT_NOT_FOUND; } ClientInfo *clientInfo = GetClientInfo(inputClient); if (clientInfo == nullptr) { IMSA_HILOGE("PerUserSession::HideKeyboard GetClientInfo pointer nullptr"); } if (imsCore[0] == nullptr) { IMSA_HILOGE("PerUserSession::HideKeyboard imsCore[index] is nullptr"); return ErrorCode::ERROR_IME_NOT_STARTED; } bool ret = imsCore[0]->hideKeyboard(1); if (!ret) { IMSA_HILOGE("PerUserSession::HideKeyboard [imsCore->hideKeyboard] failed"); ret = ErrorCode::ERROR_KBD_HIDE_FAILED; } return ErrorCode::NO_ERROR; } /*! Get the display mode of the current keyboard showing \return return display mode. \n 0 - part sceen mode, 1 - full sceen mode */ int PerUserSession::GetDisplayMode() { return currentDisplayMode; } /*! Get the keyboard window height \param[out] retHeight the height of keyboard window showing or showed returned to caller \return ErrorCode */ int PerUserSession::GetKeyboardWindowHeight(int retHeight) { if (imsCore[lastImeIndex] != nullptr) { int ret = imsCore[lastImeIndex]->getKeyboardWindowHeight(retHeight); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("getKeyboardWindowHeight return : %{public}s", ErrorCode::ToString(ret)); } return ret; } IMSA_HILOGW("No IME is started [%{public}d]\n", userId_); return ErrorCode::ERROR_IME_NOT_STARTED; } /*! Get the current keyboard type \return return the pointer of the object of current keyboard type. \n null if no keyboard type supported by the current ime. \note The returned pointer should NOT be freed by the caller. */ KeyboardType *PerUserSession::GetCurrentKeyboardType() { if (inputMethodSetting == nullptr || currentIme[DEFAULT_IME] == nullptr) { IMSA_HILOGI("Ime has not started ! [%{public}d]\n", userId_); return nullptr; } if (currentIme[DEFAULT_IME] == currentIme[SECURITY_IME]) { return nullptr; } int hashCode = inputMethodSetting->GetCurrentKeyboardType(); // To be checked. if (hashCode == -1) { std::vector hashCodeList = inputMethodSetting->GetEnabledKeyboardTypes(currentIme[DEFAULT_IME]->mImeId); if (hashCodeList.size() == 0) { IMSA_HILOGE("Cannot find any keyboard types for the current ime [%{public}d]\n", userId_); return nullptr; } hashCode = hashCodeList[0]; } for (int i = 0; i < (int)currentIme[DEFAULT_IME]->mTypes.size(); i++) { if (currentIme[DEFAULT_IME]->mTypes[i]->getHashCode() == hashCode) { return currentIme[DEFAULT_IME]->mTypes[i]; } } return nullptr; } /*! Handle the situation a remote input client died\n It's called when a remote input client died \param remoteObject the remote object handler of the input client died. */ void PerUserSession::OnClientDied(IRemoteObject *remoteObject) { IMSA_HILOGI("PerUserSession::OnClientDied Start...[%{public}d]\n", userId_); auto it = mapClients.find(remoteObject); if (it == mapClients.end()) { IMSA_HILOGE("PerUserSession::RemoveClient client not found"); return; } sptr client = GetCurrentClient(); if (client == nullptr) { IMSA_HILOGE("current client is nullptr"); RemoveClient(it->first); return; } if (client->AsObject().GetRefPtr() == remoteObject) { int ret = HideKeyboard(client); IMSA_HILOGI("hide keyboard ret: %{public}s", ErrorCode::ToString(ret)); } RemoveClient(it->first); } /*! Handle the situation a input method service died\n It's called when an input method service died \param remoteObject the remote object handler of input method service remoteObject died. */ void PerUserSession::OnImsDied(IRemoteObject *remoteObject) { (void)remoteObject; IMSA_HILOGI("Start...[%{public}d]\n", userId_); int index = 0; for (int i = 0; i < MAX_IME; i++) { if (imsCore[i] == nullptr) { continue; } auto b = imsCore[i]->AsObject(); if (b.GetRefPtr() == remoteObject) { index = i; break; } } ClearImeData(index); if (!IsRestartIme(index)) { IMSA_HILOGI("Restart ime over max num"); return; } IMSA_HILOGI("IME died. Restart input method...[%{public}d]\n", userId_); const auto &ime = ParaHandle::GetDefaultIme(userId_); auto *parcel = new (std::nothrow) MessageParcel(); if (parcel == nullptr) { IMSA_HILOGE("parcel is nullptr"); return; } parcel->WriteString(ime); auto *msg = new (std::nothrow) Message(MSG_ID_START_INPUT_SERVICE, parcel); if (msg == nullptr) { IMSA_HILOGE("msg is nullptr"); delete parcel; return; } usleep(MAX_RESET_WAIT_TIME); MessageHandler::Instance()->SendMessage(msg); IMSA_HILOGI("End...[%{public}d]\n", userId_); } /*! It's called when input method setting data in the system is changed \param key the name of setting item changed. \param value the value of setting item changed. \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_SETTING_SAME_VALUE the current value is same as the one in the system. */ int PerUserSession::OnSettingChanged(const std::u16string& key, const std::u16string& value) { IMSA_HILOGI("Start...[%{public}d]\n", userId_); std::unique_lock lock(mtx); if (inputMethodSetting == nullptr) { return ErrorCode::ERROR_NULL_POINTER; } std::u16string currentValue = inputMethodSetting->GetValue(key); IMSA_HILOGD("PerUserSession::OnSettingChanged key = %{public}s", Utils::to_utf8(key).c_str()); IMSA_HILOGD("PerUserSession::OnSettingChanged value = %{public}s", Utils::to_utf8(value).c_str()); IMSA_HILOGD("PerUserSession::OnSettingChanged currentValue = %{public}s", Utils::to_utf8(currentValue).c_str()); if (currentValue == value) { IMSA_HILOGI("End...[%{public}d]\n", userId_); return ErrorCode::ERROR_SETTING_SAME_VALUE; } sptr client = GetCurrentClient(); if (key == InputMethodSetting::CURRENT_KEYBOARD_TYPE_TAG) { return OnCurrentKeyboardTypeChanged(DEFAULT_IME, value); } else if (key == InputMethodSetting::CURRENT_SYS_KEYBOARD_TYPE_TAG) { return OnCurrentKeyboardTypeChanged(SECURITY_IME, value); } else if (key == InputMethodSetting::CURRENT_INPUT_METHOD_TAG) { if (currentIme[DEFAULT_IME] == nullptr || value == currentIme[DEFAULT_IME]->mImeId) { return ErrorCode::NO_ERROR; } if (client != nullptr && GetImeIndex(client) == DEFAULT_IME) { needReshowClient = client; HideKeyboard(client); } StopInputMethod(DEFAULT_IME); currentIme[DEFAULT_IME] = nullptr; currentKbdIndex[DEFAULT_IME] = 0; inputMethodSetting->SetCurrentKeyboardType(-1); } else if (key == InputMethodSetting::ENABLED_INPUT_METHODS_TAG) { if (currentIme[DEFAULT_IME] != nullptr && currentIme[DEFAULT_IME] != currentIme[SECURITY_IME] && value.find(currentIme[DEFAULT_IME]->mImeId) == std::string::npos) { if (client != nullptr && GetImeIndex(client) == DEFAULT_IME) { needReshowClient = client; HideKeyboard(client); } StopInputMethod(DEFAULT_IME); currentIme[DEFAULT_IME] = nullptr; currentKbdIndex[DEFAULT_IME] = 0; inputMethodSetting->SetCurrentKeyboardType(-1); } } IMSA_HILOGI("End...[%{public}d]\n", userId_); return ErrorCode::NO_ERROR; } /*! Change current keyboard type. \param index it can be 0 or 1. 0 - default ime, 1 - security ime. \param value the hash code of keyboard type \return ErrorCode::NO_ERROR no error \return ErrorCode::ERROR_SETTING_SAME_VALUE the current value is same as the one in the system. */ int PerUserSession::OnCurrentKeyboardTypeChanged(int index, const std::u16string& value) { std::string str = Utils::to_utf8(value); int hashCode = std::atoi(str.c_str()); if (hashCode == -1) { return ErrorCode::ERROR_SETTING_SAME_VALUE;; } // switch within the current ime. if (index == SECURITY_IME || currentIme[DEFAULT_IME] == currentIme[SECURITY_IME]) { int num = currentKbdIndex[index]; if (currentIme[index]->mTypes[num]->getHashCode() == hashCode) { return ErrorCode::ERROR_SETTING_SAME_VALUE; } for (int i = 0; i < (int)currentIme[index]->mTypes.size(); i++) { if (currentIme[index]->mTypes[i]->getHashCode() == hashCode) { currentKbdIndex[index] = i; break; } } } else { std::u16string imeId = currentIme[index]->mImeId; std::vector currentKbdTypes = inputMethodSetting->GetEnabledKeyboardTypes(imeId); int num = currentKbdIndex[index]; if (currentKbdTypes[num] == hashCode) { return ErrorCode::ERROR_SETTING_SAME_VALUE; } for (int i = 0; i < (int)currentKbdTypes.size(); i++) { if (currentKbdTypes[i] == hashCode) { currentKbdIndex[index] = i; break; } } } KeyboardType *type = GetKeyboardType(index, currentKbdIndex[index]); if (type != nullptr) { if (GetCurrentClient() != nullptr) { int ret = imsCore[index]->setKeyboardType(*type); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("setKeyboardType return : %{public}s [%{public}d]\n", ErrorCode::ToString(ret), userId_); } } if (imsCore[index] == imsCore[1 - index]) { inputMethodSetting->SetCurrentKeyboardType(type->getHashCode()); inputMethodSetting->SetCurrentSysKeyboardType(type->getHashCode()); currentKbdIndex[1 - index] = currentKbdIndex[index]; } else if (index == DEFAULT_IME) { inputMethodSetting->SetCurrentKeyboardType(type->getHashCode()); } else { inputMethodSetting->SetCurrentSysKeyboardType(type->getHashCode()); } } return ErrorCode::NO_ERROR; } /*! Hide current keyboard \param flag the flag to hide keyboard. */ void PerUserSession::OnHideKeyboardSelf(int flags) { IMSA_HILOGW("PerUserSession::OnHideKeyboardSelf"); (void) flags; sptr client = GetCurrentClient(); if (client == nullptr) { IMSA_HILOGE("current client is nullptr"); return; } HideKeyboard(client); } /*! Switch to next keyboard type */ void PerUserSession::OnAdvanceToNext() { sptr client = GetCurrentClient(); if (client == nullptr) { IMSA_HILOGE("current client is nullptr"); return; } int index = GetImeIndex(client); if (index == -1) { IMSA_HILOGW("%{public}s [%{public}d]\n", ErrorCode::ToString(ErrorCode::ERROR_CLIENT_NOT_FOUND), userId_); return; } int size = 0; if (index == SECURITY_IME || currentIme[DEFAULT_IME] == currentIme[SECURITY_IME]) { size = currentIme[index]->mTypes.size(); } else { std::u16string imeId = currentIme[index]->mImeId; std::vector currentKbdTypes = inputMethodSetting->GetEnabledKeyboardTypes(imeId); size = currentKbdTypes.size(); } if (size < MIN_IME) { IMSA_HILOGW("No next keyboard is available. [%{public}d]\n", userId_); return; } int num = currentKbdIndex[index]+1; if (size != 0) { num %= size; } KeyboardType *type = GetKeyboardType(index, num); if (type == nullptr) { IMSA_HILOGW("No next keyboard is available. [%{public}d]\n", userId_); return; } InputMethodSetting tmpSetting; if (imsCore[index] == imsCore[1 - index]) { tmpSetting.SetCurrentKeyboardType(type->getHashCode()); tmpSetting.SetCurrentSysKeyboardType(type->getHashCode()); } else if (index == DEFAULT_IME) { tmpSetting.SetCurrentKeyboardType(type->getHashCode()); } else { tmpSetting.SetCurrentSysKeyboardType(type->getHashCode()); } Platform::Instance()->SetInputMethodSetting(userId_, tmpSetting); } /*! Set display mode \param mode the display mode of soft keyboard UI. \n 0 - part sceen mode, 1 - full sceen mode */ void PerUserSession::OnSetDisplayMode(int mode) { currentDisplayMode = mode; sptr client = GetCurrentClient(); if (client == nullptr) { IMSA_HILOGE("current client is nullptr"); return; } ClientInfo *clientInfo = GetClientInfo(client); if (clientInfo == nullptr) { IMSA_HILOGE("%{public}s [%{public}d]\n", ErrorCode::ToString(ErrorCode::ERROR_CLIENT_NOT_FOUND), userId_); return; } int ret = clientInfo->client->setDisplayMode(mode); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("setDisplayMode return : %{public}s [%{public}d]\n", ErrorCode::ToString(ret), userId_); } } /*! Restart input method service \param index it can be DEFAULT_IME or SECURITY_IME \param imeId the id of the input method service going to restart */ void PerUserSession::OnRestartIms(int index, const std::u16string& imeId) { if (index < 0 || index >= MAX_IME) { return; } IMSA_HILOGI("Start...[%{public}d]\n", userId_); if (currentIme[index] && currentIme[index]->mImeId == imeId) { int ret = StartInputMethod(index); if (needReshowClient && GetImeIndex(needReshowClient) == index) { if (ret == ErrorCode::NO_ERROR) { ShowKeyboard(needReshowClient); } needReshowClient = nullptr; } } IMSA_HILOGI("End...[%{public}d]\n", userId_); } /*! It's called when this user is locked */ void PerUserSession::OnUserLocked() { IMSA_HILOGI("PerUserSession::OnUserLocked"); if (userState == UserState::USER_STATE_STARTED) { IMSA_HILOGI("End...[%{public}d]\n", userId_); return; } userState = UserState::USER_STATE_STARTED; // hide current keyboard sptr client = GetCurrentClient(); if (client != nullptr) { HideKeyboard(client); } for (int i = 0; i < MIN_IME; i++) { StopInputMethod(i); currentIme[i] = nullptr; } // disconnect all clients. for (auto it = mapClients.begin(); it != mapClients.end();) { auto object = it->first; ClientInfo *clientInfo = it->second; if (clientInfo != nullptr) { object->RemoveDeathRecipient(clientInfo->deathRecipient); int ret = clientInfo->client->onInputReleased(0); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("2-onInputReleased return : %{public}s", ErrorCode::ToString(ret)); } delete clientInfo; clientInfo = nullptr; } IMSA_HILOGD("erase client..\n"); it = mapClients.erase(it); } mapClients.clear(); // reset values inputMethodSetting = nullptr; SetCurrentClient(nullptr); needReshowClient = nullptr; } /*! Get keyboard type \param imeIndex it can be 0 or 1. 0 - default ime, 1 - security ime \param typeIndex the index of keyboard type. \return a KeyboardType pointer when it's found. \return null when it's not found. \note The returned pointer should not be freed by caller. */ KeyboardType *PerUserSession::GetKeyboardType(int imeIndex, int typeIndex) { if (typeIndex < 0) { return nullptr; } if (imeIndex == SECURITY_IME || currentIme[DEFAULT_IME] == currentIme[SECURITY_IME]) { if (typeIndex >= (int)currentIme[imeIndex]->mTypes.size()) { return nullptr; } return currentIme[imeIndex]->mTypes[typeIndex]; } else { std::u16string imeId = currentIme[imeIndex]->mImeId; std::vector currentKbdTypes = inputMethodSetting->GetEnabledKeyboardTypes(imeId); int size = currentKbdTypes.size(); if (typeIndex >= size) { return nullptr; } int hashCode = currentKbdTypes[typeIndex]; for (int i = 0; i < (int)currentIme[imeIndex]->mTypes.size(); i++) { if (currentIme[imeIndex]->mTypes[i]->getHashCode() == hashCode) { return currentIme[imeIndex]->mTypes[i]; } } } return nullptr; } /*! Reset current keyboard type \param imeIndex it can be 0 or 1. 0 - default ime, 1 - security ime */ void PerUserSession::ResetCurrentKeyboardType(int imeIndex) { if (imeIndex < 0 || imeIndex > 1) { return; } currentKbdIndex[imeIndex] = 0; int hashCode = 0; if (imeIndex == DEFAULT_IME) { hashCode = inputMethodSetting->GetCurrentKeyboardType(); } else { hashCode = inputMethodSetting->GetCurrentSysKeyboardType(); } KeyboardType *type = nullptr; if (hashCode == -1) { type = GetKeyboardType(imeIndex, currentKbdIndex[imeIndex]); } else { bool flag = false; if (imeIndex == SECURITY_IME || currentIme[DEFAULT_IME] == currentIme[SECURITY_IME]) { for (int i = 0; i < (int)currentIme[imeIndex]->mTypes.size(); i++) { if (currentIme[imeIndex]->mTypes[i]->getHashCode() == hashCode) { currentKbdIndex[imeIndex] = i; flag = true; break; } } } else { std::vector hashCodeList = inputMethodSetting->GetEnabledKeyboardTypes(currentIme[imeIndex]->mImeId); for (int i = 0; i < (int)hashCodeList.size(); i++) { if (hashCode == hashCodeList[i]) { currentKbdIndex[imeIndex] = i; flag = true; break; } } } if (flag == false) { IMSA_HILOGW("The current keyboard type is not found in the current IME. Reset it!"); type = GetKeyboardType(imeIndex, currentKbdIndex[imeIndex]); } else if (imsCore[imeIndex] == imsCore[1 - imeIndex]) { currentKbdIndex[1 - imeIndex] = currentKbdIndex[imeIndex]; } } if (type != nullptr) { InputMethodSetting tmpSetting; if (imsCore[imeIndex] == imsCore[1 - imeIndex]) { inputMethodSetting->SetCurrentKeyboardType(type->getHashCode()); inputMethodSetting->SetCurrentSysKeyboardType(type->getHashCode()); currentKbdIndex[1 - imeIndex] = currentKbdIndex[imeIndex]; tmpSetting.SetCurrentKeyboardType(type->getHashCode()); tmpSetting.SetCurrentSysKeyboardType(type->getHashCode()); } else if (imeIndex == DEFAULT_IME) { tmpSetting.SetCurrentKeyboardType(type->getHashCode()); inputMethodSetting->SetCurrentKeyboardType(type->getHashCode()); } else { tmpSetting.SetCurrentSysKeyboardType(type->getHashCode()); inputMethodSetting->SetCurrentSysKeyboardType(type->getHashCode()); } Platform::Instance()->SetInputMethodSetting(userId_, tmpSetting); } } /*! Get ime index for the input client \param inputClient the remote object handler of an input client. \return 0 - default ime \return 1 - security ime \return -1 - input client is not found */ int PerUserSession::GetImeIndex(const sptr& inputClient) { if (inputClient == nullptr) { IMSA_HILOGW("PerUserSession::GetImeIndex inputClient is nullptr"); return -1; } ClientInfo *clientInfo = GetClientInfo(inputClient); if (clientInfo == nullptr) { IMSA_HILOGW("PerUserSession::GetImeIndex clientInfo is nullptr"); return -1; } if (clientInfo->attribute.GetSecurityFlag() == true) { return SECURITY_IME; } return DEFAULT_IME; } /*! Copy session data from one IME to another IME \param imeIndex it can be 0 or 1. \n 0 - default ime, 1 - security ime */ void PerUserSession::CopyInputMethodService(int imeIndex) { imsCore[imeIndex] = imsCore[1 - imeIndex]; localControlChannel[imeIndex] = localControlChannel[1 - imeIndex]; inputControlChannel[imeIndex] = inputControlChannel[1 - imeIndex]; inputMethodToken[imeIndex] = inputMethodToken[1 - imeIndex]; currentKbdIndex[imeIndex] = currentKbdIndex[1 - imeIndex]; int hashCode[2]; hashCode[0] = inputMethodSetting->GetCurrentKeyboardType(); hashCode[1] = inputMethodSetting->GetCurrentSysKeyboardType(); if (hashCode[imeIndex] != hashCode[1 - imeIndex]) { hashCode[imeIndex] = hashCode[1 - imeIndex]; inputMethodSetting->SetCurrentKeyboardType(hashCode[0]); inputMethodSetting->SetCurrentSysKeyboardType(hashCode[1]); InputMethodSetting tmpSetting; tmpSetting.ClearData(); tmpSetting.SetCurrentKeyboardType(hashCode[0]); tmpSetting.SetCurrentSysKeyboardType(hashCode[1]); Platform::Instance()->SetInputMethodSetting(userId_, tmpSetting); } } /*! Get ClientInfo \param inputClient the IInputClient remote handler of given input client \return a pointer of ClientInfo if client is found \n null if client is not found \note the clientInfo pointer should not be freed by caller */ ClientInfo *PerUserSession::GetClientInfo(const sptr &inputClient) { if (inputClient == nullptr) { IMSA_HILOGE("PerUserSession::GetClientInfo inputClient is nullptr"); return nullptr; } sptr object = Platform::RemoteBrokerToObject(inputClient); auto it = mapClients.find(object); if (it == mapClients.end()) { IMSA_HILOGE("client not found"); return nullptr; } return it->second; } bool PerUserSession::StartInputService() { IMSA_HILOGE("PerUserSession::StartInputService"); sptr ams = GetAbilityManagerService(); if (ams == nullptr) { return false; } AAFwk::Want want; want.SetAction("action.system.inputmethod"); want.SetElementName("com.example.kikakeyboard", "com.example.kikakeyboard.ServiceExtAbility"); int32_t result = ams->StartAbility(want); if (result != 0) { IMSA_HILOGE("PerUserSession::StartInputService fail. result = %{public}d", result); return false; } return true; } sptr PerUserSession::GetAbilityManagerService() { IMSA_HILOGE("GetAbilityManagerService start"); sptr abilityMsObj = OHOS::DelayedSingleton::GetInstance()->GetSystemAbility(ABILITY_MGR_SERVICE_ID); if (abilityMsObj == nullptr) { IMSA_HILOGE("failed to get ability manager service"); return nullptr; } return iface_cast(abilityMsObj); } /*! Prepare input. Called by an input client. \n Run in work thread of this user \param msg the parameters from remote client are saved in msg->msgContent_ \return ErrorCode */ void PerUserSession::OnPrepareInput(Message *msg) { IMSA_HILOGI("PerUserSession::OnPrepareInput Start...[%{public}d]\n", userId_); MessageParcel *data = msg->msgContent_; int pid = data->ReadInt32(); int uid = data->ReadInt32(); int displayId = data->ReadInt32(); sptr clientObject = data->ReadRemoteObject(); if (clientObject == nullptr) { IMSA_HILOGI("PerUserSession::OnPrepareInput clientObject is null"); return; } sptr client = new InputClientProxy(clientObject); sptr channelObject = data->ReadRemoteObject(); if (channelObject == nullptr) { IMSA_HILOGI("PerUserSession::OnPrepareInput channelObject is null"); return; } sptr channel = new InputDataChannelProxy(channelObject); InputAttribute *attribute = data->ReadParcelable(); if (attribute == nullptr) { IMSA_HILOGI("PerUserSession::OnPrepareInput attribute is nullptr"); return; } sptr clientDeathRecipient = new (std::nothrow) RemoteObjectDeathRecipient(Utils::GetUserId(), MSG_ID_CLIENT_DIED); if (clientDeathRecipient == nullptr) { IMSA_HILOGE("failed to create clientDeathRecipient"); } int ret = AddClient({ pid, uid, userId_, displayId, client, channel, *attribute, clientDeathRecipient }); delete attribute; if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("PerUserSession::OnPrepareInput Aborted! %{public}s", ErrorCode::ToString(ret)); return; } SendAgentToSingleClient(client); } void PerUserSession::SendAgentToSingleClient(const sptr& inputClient) { IMSA_HILOGI("PerUserSession::SendAgentToSingleClient"); if (imsAgent == nullptr) { IMSA_HILOGI("PerUserSession::SendAgentToSingleClient imsAgent is nullptr"); return; } ClientInfo *clientInfo = GetClientInfo(inputClient); if (clientInfo == nullptr) { IMSA_HILOGE("PerUserSession::SendAgentToSingleClient clientInfo is nullptr"); return; } clientInfo->client->onInputReady(imsAgent); } /*! Release input. Called by an input client. \n Run in work thread of this user \param msg the parameters from remote client are saved in msg->msgContent_ \return ErrorCode */ void PerUserSession::OnReleaseInput(Message *msg) { IMSA_HILOGI("PerUserSession::OnReleaseInput Start...[%{public}d]\n", userId_); MessageParcel *data = msg->msgContent_; sptr clientObject = data->ReadRemoteObject(); sptr client = new InputClientProxy(clientObject); sptr interface = client; if (imsCore[0] != nullptr) { imsCore[0]->SetClientState(false); } HideKeyboard(client); RemoveClient(clientObject.GetRefPtr()); IMSA_HILOGI("PerUserSession::OnReleaseInput End...[%{public}d]\n", userId_); } /*! Start input. Called by an input client. \n Run in work thread of this user \param msg the parameters from remote client are saved in msg->msgContent_ \return ErrorCode */ void PerUserSession::OnStartInput(Message *msg) { IMSA_HILOGI("PerUserSession::OnStartInput"); MessageParcel *data = msg->msgContent_; sptr clientObject = data->ReadRemoteObject(); sptr client = new InputClientProxy(clientObject); if (imsCore[0] != nullptr) { imsCore[0]->SetClientState(true); } ShowKeyboard(client); } void PerUserSession::SetCoreAndAgent(Message *msg) { IMSA_HILOGI("PerUserSession::SetCoreAndAgent Start...[%{public}d]\n", userId_); auto data = msg->msgContent_; auto coreObject = data->ReadRemoteObject(); if (coreObject == nullptr) { IMSA_HILOGE("coreObject is nullptr"); return; } auto core = new (std::nothrow) InputMethodCoreProxy(coreObject); if (core == nullptr) { IMSA_HILOGE("core is nullptr"); return; } if (imsCore[0] != nullptr) { IMSA_HILOGI("PerUserSession::SetCoreAndAgent Input Method Service has already been started ! "); } imsCore[0] = core; bool ret = coreObject->AddDeathRecipient(imsDeathRecipient); IMSA_HILOGI("Add death recipient %{public}s", ret ? "success" : "failed"); auto agentObject = data->ReadRemoteObject(); auto proxy = new (std::nothrow) InputMethodAgentProxy(agentObject); if (proxy == nullptr) { IMSA_HILOGE("proxy is nullptr"); return; } imsAgent = proxy; InitInputControlChannel(); SendAgentToAllClients(); } void PerUserSession::SendAgentToAllClients() { IMSA_HILOGI("PerUserSession::SendAgentToAllClients"); if (imsAgent == nullptr) { IMSA_HILOGI("PerUserSession::SendAgentToAllClients imsAgent is nullptr"); return; } for (auto & mapClient : mapClients) { auto clientInfo = mapClient.second; if (clientInfo != nullptr) { clientInfo->client->onInputReady(imsAgent); } } } void PerUserSession::InitInputControlChannel() { IMSA_HILOGI("PerUserSession::InitInputControlChannel"); sptr inputControlChannel = new InputControlChannelStub(userId_); int ret = imsCore[0]->InitInputControlChannel(inputControlChannel); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGI("PerUserSession::InitInputControlChannel fail %{public}s", ErrorCode::ToString(ret)); } } /*! Stop input. Called by an input client. \n Run in work thread of this user \param msg the parameters from remote client are saved in msg->msgContent_ \return ErrorCode */ void PerUserSession::OnStopInput(Message *msg) { IMSA_HILOGI("PerUserSession::OnStopInput"); MessageParcel *data = msg->msgContent_; sptr clientObject = data->ReadRemoteObject(); sptr client = new InputClientProxy(clientObject); HideKeyboard(client); } void PerUserSession::StopInputService(std::string imeId) { IMSA_HILOGI("PerUserSession::StopInputService"); if (imsCore[0] == nullptr) { IMSA_HILOGE("imsCore[0] is nullptr"); return; } IMSA_HILOGI("Remove death recipient"); imsCore[0]->AsObject()->RemoveDeathRecipient(imsDeathRecipient); imsCore[0]->StopInputService(imeId); } bool PerUserSession::IsRestartIme(uint32_t index) { IMSA_HILOGI("PerUserSession::IsRestartIme"); std::lock_guard lock(resetLock); auto now = time(nullptr); if (difftime(now, manager[index].last) > IME_RESET_TIME_OUT) { manager[index] = { 0, now }; } ++manager[index].num; return manager[index].num <= MAX_RESTART_NUM; } void PerUserSession::ResetImeError(uint32_t index) { IMSA_HILOGI("PerUserSession::ResetImeError index = %{public}d", index); std::lock_guard lock(resetLock); manager[index] = { 0, 0 }; } void PerUserSession::ClearImeData(uint32_t index) { IMSA_HILOGI("Clear ime...index = %{public}d", index); if (imsCore[index] != nullptr) { imsCore[index]->AsObject()->RemoveDeathRecipient(imsDeathRecipient); imsCore[index] = nullptr; } inputControlChannel[index] = nullptr; localControlChannel[index] = nullptr; inputMethodToken[index] = nullptr; } void PerUserSession::SetCurrentClient(sptr client) { IMSA_HILOGI("set current client"); std::lock_guard lock(clientLock_); currentClient = client; } sptr PerUserSession::GetCurrentClient() { std::lock_guard lock(clientLock_); return currentClient; } } // namespace MiscServices } // namespace OHOS