diff --git a/services/include/global.h b/services/include/global.h index dc2db6e..5e89bdd 100644 --- a/services/include/global.h +++ b/services/include/global.h @@ -136,6 +136,7 @@ namespace ErrorCode { ERROR_NO_NEXT_IME, // no next ime is available ERROR_CLIENTWINDOW_NOT_FOCUSED, // the input client window is not focused ERROR_CLIENT_NOT_WINDOW, // the input client is not from a valid window + ERROR_IME_PROPERTY_MARSHALL, // failed to marshall the ime property // error from ime ERROR_REMOTE_IME_DIED, // remote input method service died abnormally ERROR_RESTART_IME_FAILED, // failed to restart input method service @@ -154,15 +155,15 @@ static constexpr HiviewDFX::HiLogLabel g_SMALL_SERVICES_LABEL = { }; #define IMSA_HILOGD(fmt, ...) (void)OHOS::HiviewDFX::HiLog::Debug(OHOS::MiscServices::g_SMALL_SERVICES_LABEL, \ - "line: %d, function: %s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) + "line: %{public}d, function: %{public}s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) #define IMSA_HILOGE(fmt, ...) (void)OHOS::HiviewDFX::HiLog::Error(OHOS::MiscServices::g_SMALL_SERVICES_LABEL, \ - "line: %d, function: %s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) + "line: %{public}d, function: %{public}s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) #define IMSA_HILOGF(fmt, ...) (void)OHOS::HiviewDFX::HiLog::Fatal(OHOS::MiscServices::g_SMALL_SERVICES_LABEL, \ - "line: %d, function: %s," fmt, __LINE__FILE__, __FUNCTION__, ##__VA_ARGS__) + "line: %{public}d, function: %{public}s," fmt, __LINE__FILE__, __FUNCTION__, ##__VA_ARGS__) #define IMSA_HILOGI(fmt, ...) (void)OHOS::HiviewDFX::HiLog::Info(OHOS::MiscServices::g_SMALL_SERVICES_LABEL, \ - "line: %d, function: %s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) + "line: %{public}d, function: %{public}s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) #define IMSA_HILOGW(fmt, ...) (void)OHOS::HiviewDFX::HiLog::Warn(OHOS::MiscServices::g_SMALL_SERVICES_LABEL, \ - "line: %d, function: %s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) + "line: %{public}d, function: %{public}s," fmt, __LINE__, __FUNCTION__, ##__VA_ARGS__) } } #endif // SERVICES_INCLUDE_GLOBAL_H diff --git a/services/include/i_input_method_system_ability.h b/services/include/i_input_method_system_ability.h index 53ea3ef..dcf3407 100644 --- a/services/include/i_input_method_system_ability.h +++ b/services/include/i_input_method_system_ability.h @@ -47,6 +47,7 @@ namespace MiscServices { LIST_KEYBOARD_TYPE, SET_CORE_AND_AGENT, DISPLAY_OPTIONAL_INPUT_METHOD, + SWITCH_INPUT_METHOD, }; DECLARE_INTERFACE_DESCRIPTOR(u"ohos.miscservices.inputmethod.IInputMethodSystemAbility"); diff --git a/services/include/input_method_system_ability.h b/services/include/input_method_system_ability.h index 784d2d4..3fc22fe 100644 --- a/services/include/input_method_system_ability.h +++ b/services/include/input_method_system_ability.h @@ -73,7 +73,7 @@ namespace MiscServices { void WorkThread(); PerUserSetting *GetUserSetting(int32_t userId); PerUserSession *GetUserSession(int32_t userId); - void StartInputService(std::string imeId); + bool StartInputService(std::string imeId); void StopInputService(std::string imeId); int32_t OnUserStarted(const Message *msg); int32_t OnUserStopped(const Message *msg); @@ -90,6 +90,7 @@ namespace MiscServices { static sptr GetAbilityManagerService(); OHOS::sptr GetBundleMgr(); void StartUserIdListener(); + int32_t OnSwitchInputMethod(int32_t userId, InputMethodProperty *target); ServiceRunningState state_; void InitServiceHandler(); diff --git a/services/include/input_method_system_ability_stub.h b/services/include/input_method_system_ability_stub.h index ee18380..20f3386 100644 --- a/services/include/input_method_system_ability_stub.h +++ b/services/include/input_method_system_ability_stub.h @@ -37,6 +37,7 @@ namespace MiscServices { void HideCurrentInput(MessageParcel& data) override; void displayOptionalInputMethod(MessageParcel& data) override; virtual int32_t listInputMethodByUserId(int32_t userId, std::vector *properties) = 0; + int32_t SwitchInputMethod(MessageParcel &data, MessageParcel &reply); protected: int32_t getUserId(int32_t uid); diff --git a/services/include/message.h b/services/include/message.h index 47815a1..5038e1a 100644 --- a/services/include/message.h +++ b/services/include/message.h @@ -25,7 +25,9 @@ namespace MiscServices { public: int32_t msgId_; // message id MessageParcel *msgContent_ = nullptr; // message content + MessageParcel *msgReply_ = nullptr; // message reply Message(int32_t msgId, MessageParcel *msgContent); + Message(int32_t msgId, MessageParcel *msgContent, MessageParcel *msgReply); explicit Message(const Message& msg); Message& operator =(const Message& msg); ~Message(); diff --git a/services/include/message_handler.h b/services/include/message_handler.h index 8c00a8e..9ab10d6 100644 --- a/services/include/message_handler.h +++ b/services/include/message_handler.h @@ -46,6 +46,7 @@ namespace MessageID { MSG_ID_RELEASE_INPUT, // release input MSG_ID_SET_CORE_AND_AGENT, MSG_HIDE_CURRENT_INPUT, + MSG_ID_SWITCH_INPUT_METHOD, // switch input method // the request to handle the condition that the remote object died MSG_ID_CLIENT_DIED, // input client died diff --git a/services/src/input_method_system_ability.cpp b/services/src/input_method_system_ability.cpp index b1742d9..3ca16d6 100644 --- a/services/src/input_method_system_ability.cpp +++ b/services/src/input_method_system_ability.cpp @@ -198,7 +198,7 @@ namespace MiscServices { serviceHandler_->PostTask(callback, INIT_INTERVAL); } - void InputMethodSystemAbility::StartInputService(std::string imeId) + bool InputMethodSystemAbility::StartInputService(std::string imeId) { IMSA_HILOGE("InputMethodSystemAbility::StartInputService() ime:%{public}s", imeId.c_str()); @@ -236,6 +236,7 @@ namespace MiscServices { auto callback = [this, imeId]() { StartInputService(imeId); }; serviceHandler_->PostTask(callback, INIT_INTERVAL); } + return isStartSuccess; } void InputMethodSystemAbility::StopInputService(std::string imeId) @@ -562,6 +563,14 @@ namespace MiscServices { msg = nullptr; return; } + case MSG_ID_SWITCH_INPUT_METHOD: { + MessageParcel *data = msg->msgContent_; + int32_t userId = data->ReadInt32(); + InputMethodProperty *target = InputMethodProperty::Unmarshalling(*data); + auto ret = OnSwitchInputMethod(userId, target); + msg->msgReply_->WriteInt32(ret); + break; + } default: { break; } @@ -890,6 +899,49 @@ namespace MiscServices { return ErrorCode::NO_ERROR; } + int32_t InputMethodSystemAbility::OnSwitchInputMethod(int32_t userId, InputMethodProperty *target) + { + IMSA_HILOGI("InputMethodSystemAbility::OnSwitchInputMethod"); + std::vector properties; + listInputMethodByUserId(userId, &properties); + if (!properties.size()) { + IMSA_HILOGE("InputMethodSystemAbility::OnSwitchInputMethod has no ime"); + return ErrorCode::ERROR_BAD_PARAMETERS; + } + bool isTargetFound = false; + for (auto it = properties.begin(); it < properties.end(); ++it) { + InputMethodProperty *temp = (InputMethodProperty *)*it; + if (temp->mPackageName == target->mPackageName) { + *target = *temp; + isTargetFound = true; + IMSA_HILOGI("InputMethodSystemAbility::OnSwitchInputMethod target is found in installed packages!"); + } + delete temp; + } + if (!isTargetFound) { + IMSA_HILOGE("InputMethodSystemAbility::OnSwitchInputMethod target is not an installed package !"); + return ErrorCode::ERROR_NOT_IME_PACKAGE; + } + + std::string defaultIme = ParaHandle::GetDefaultIme(userId_); + std::string targetIme = ""; + std::string imeId = Str16ToStr8(target->mPackageName) + "/" + Str16ToStr8(target->mAbilityName); + targetIme += imeId; + IMSA_HILOGI("InputMethodSystemAbility::OnSwitchInputMethod DefaultIme : %{public}s, TargetIme : %{public}s", + defaultIme.c_str(), targetIme.c_str()); + if (defaultIme != targetIme) { + IMSA_HILOGI("InputMethodSystemAbility::OnSwitchInputMethod DefaultIme is not target! Start Switching IME !"); + StopInputService(defaultIme); + if (!StartInputService(targetIme)) { + return ErrorCode::ERROR_IME_START_FAILED; + } + ParaHandle::SetDefaultIme(userId_, targetIme); + } else { + IMSA_HILOGI("InputMethodSystemAbility::OnSwitchInputMethod DefaultIme and TargetIme are the same one!"); + } + return ErrorCode::NO_ERROR; + } + void InputMethodSystemAbility::OnDisplayOptionalInputMethod(int32_t userId) { IMSA_HILOGI("InputMethodSystemAbility::OnDisplayOptionalInputMethod"); diff --git a/services/src/input_method_system_ability_stub.cpp b/services/src/input_method_system_ability_stub.cpp index 2dd37f0..3ca4210 100644 --- a/services/src/input_method_system_ability_stub.cpp +++ b/services/src/input_method_system_ability_stub.cpp @@ -170,6 +170,10 @@ namespace MiscServices { reply.WriteInt32(NO_ERROR); break; } + case SWITCH_INPUT_METHOD: { + SwitchInputMethod(data, reply); + break; + } default: { return BRemoteObject::OnRemoteRequest(code, data, reply, option); } @@ -304,6 +308,27 @@ namespace MiscServices { MessageHandler::Instance()->SendMessage(msg); } + int32_t InputMethodSystemAbilityStub::SwitchInputMethod(MessageParcel &data, MessageParcel &reply) + { + IMSA_HILOGI("InputMethodSystemAbilityStub::switchInputMethod"); + int32_t uid = IPCSkeleton::GetCallingUid(); + int32_t userId = getUserId(uid); + + MessageParcel *parcel = new MessageParcel(); + InputMethodProperty *target = InputMethodProperty::Unmarshalling(data); + parcel->WriteInt32(userId); + if (!target->Marshalling(*parcel)) { + IMSA_HILOGE("InputMethodSystemAbilityStub::switchInputMethod Failed to marshall the target! "); + delete target; + delete parcel; + return ErrorCode::ERROR_IME_PROPERTY_MARSHALL; + } + delete target; + Message *msg = new Message(MSG_ID_SWITCH_INPUT_METHOD, parcel, &reply); + MessageHandler::Instance()->SendMessage(msg); + return ErrorCode::NO_ERROR; + } + /*! Get user id from uid \param uid the uid from which the remote call is \return return user id of the remote caller diff --git a/services/src/message.cpp b/services/src/message.cpp index e39f48c..9421575 100644 --- a/services/src/message.cpp +++ b/services/src/message.cpp @@ -30,6 +30,10 @@ namespace MiscServices { } } + Message::Message(int32_t msgId, MessageParcel *msgContent, MessageParcel *msgReply) : Message(msgId, msgContent) + { + msgReply_ = msgReply; + } /*! Constructor \param msg a source message */ diff --git a/unitest/src/input_method_switch_test.cpp b/unitest/src/input_method_switch_test.cpp new file mode 100644 index 0000000..8d0d4de --- /dev/null +++ b/unitest/src/input_method_switch_test.cpp @@ -0,0 +1,77 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +#include "global.h" +#include "input_method_controller.h" +#include "input_method_property.h" +#include "message_handler.h" + +using namespace testing::ext; +namespace OHOS { +namespace MiscServices { +class InputMethodSwitchTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void InputMethodSwitchTest::SetUpTestCase(void) +{ + IMSA_HILOGI("InputMethodSwitchTest::SetUpTestCase"); +} + +void InputMethodSwitchTest::TearDownTestCase(void) +{ + IMSA_HILOGI("InputMethodSwitchTest::TearDownTestCase"); +} + +void InputMethodSwitchTest::SetUp(void) +{ + IMSA_HILOGI("InputMethodSwitchTest::SetUp"); +} + +void InputMethodSwitchTest::TearDown(void) +{ + IMSA_HILOGI("InputMethodSwitchTest::TearDown"); +} + +/** +* @tc.name: intputMethodSwitch_sub_001 +* @tc.desc: Checkout SwitchInputMethod. +* @tc.type: FUNC +*/ +HWTEST_F(InputMethodSwitchTest, intputMethodSwitch_sub_001, TestSize.Level0) +{ + InputMethodProperty *target = new InputMethodProperty(); + std::string packageName = "com.ohos.inputApp"; + std::string imeId = "InputDemoService"; + target->mPackageName = std::wstring_convert, char16_t>{}.from_bytes(packageName); + target->mImeId = std::wstring_convert, char16_t>{}.from_bytes(imeId); + int32_t ret = InputMethodController::GetInstance()->SwitchInputMethod(target); + delete target; + EXPECT_TRUE(0 == ret); +} +} // namespace MiscServices +} // namespace OHOS