1/* 2 * Copyright (c) 2021-2022 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 <common_event_manager.h> 17#include <common_event_subscribe_info.h> 18#include <functional> 19#include <future> 20#include <unistd.h> 21#include "common_utilities_hpp.h" 22#include "test_server_client.h" 23#include "json.hpp" 24#include "ipc_transactor.h" 25 26namespace OHOS::uitest { 27 using namespace std; 28 using namespace chrono; 29 using namespace nlohmann; 30 using namespace OHOS; 31 using namespace OHOS::AAFwk; 32 using namespace OHOS::EventFwk; 33 using Message = MessageParcel &; 34 35 using CommonEventHandler = function<void(const CommonEventData &)>; 36 class CommonEventForwarder : public CommonEventSubscriber { 37 public: 38 explicit CommonEventForwarder(const CommonEventSubscribeInfo &info, CommonEventHandler handler) 39 : CommonEventSubscriber(info), handler_(handler) 40 { 41 } 42 43 virtual ~CommonEventForwarder() {} 44 45 void UpdateHandler(CommonEventHandler handler) 46 { 47 handler_ = handler; 48 } 49 50 void OnReceiveEvent(const CommonEventData &data) override 51 { 52 if (handler_ != nullptr) { 53 handler_(data); 54 } 55 } 56 57 private: 58 CommonEventHandler handler_ = nullptr; 59 }; 60 61 using RemoteDiedHandler = function<void()>; 62 class DeathRecipientForwarder : public IRemoteObject::DeathRecipient { 63 public: 64 explicit DeathRecipientForwarder(RemoteDiedHandler handler) : handler_(handler) {}; 65 ~DeathRecipientForwarder() = default; 66 void OnRemoteDied(const wptr<IRemoteObject> &remote) override 67 { 68 if (handler_ != nullptr) { 69 handler_(); 70 } 71 } 72 73 private: 74 const RemoteDiedHandler handler_; 75 }; 76 77 int ApiCaller::OnRemoteRequest(uint32_t code, Message data, Message reply, MessageOption &option) 78 { 79 if (data.ReadInterfaceToken() != GetDescriptor()) { 80 return -1; 81 } 82 if (code == TRANS_ID_CALL) { 83 // IPC io: verify on write 84 ApiCallInfo call; 85 string paramListStr; 86 call.apiId_ = data.ReadString(); 87 call.callerObjRef_ = data.ReadString(); 88 paramListStr = data.ReadString(); 89 call.fdParamIndex_ = data.ReadInt32(); 90 call.paramList_ = nlohmann::json::parse(paramListStr, nullptr, false); 91 DCHECK(!call.paramList_.is_discarded()); 92 if (call.fdParamIndex_ >= 0) { 93 call.paramList_.at(call.fdParamIndex_) = data.ReadFileDescriptor(); 94 } 95 ApiReplyInfo result; 96 Call(call, result); 97 auto ret = reply.WriteString(result.resultValue_.dump()) && reply.WriteUint32(result.exception_.code_) && 98 reply.WriteString(result.exception_.message_); 99 return ret ? 0 : -1; 100 } else if (code == TRANS_ID_SET_BACKCALLER) { 101 reply.WriteBool(SetBackCaller(data.ReadRemoteObject())); 102 return 0; 103 } else { 104 return IPCObjectStub::OnRemoteRequest(code, data, reply, option); 105 } 106 } 107 108 void ApiCaller::Call(const ApiCallInfo &call, ApiReplyInfo &result) 109 { 110 DCHECK(handler_ != nullptr); 111 handler_(call, result); 112 } 113 114 bool ApiCaller::SetBackCaller(const sptr<IRemoteObject> &caller) 115 { 116 if (backcallerHandler_ == nullptr) { 117 LOG_W("No backcallerHandler set!"); 118 return false; 119 } 120 backcallerHandler_(caller); 121 return true; 122 } 123 124 void ApiCaller::SetCallHandler(ApiCallHandler handler) 125 { 126 handler_ = handler; 127 } 128 129 void ApiCaller::SetBackCallerHandler(function<void(sptr<IRemoteObject>)> handler) 130 { 131 backcallerHandler_ = handler; 132 } 133 134 ApiCallerProxy::ApiCallerProxy(const sptr<IRemoteObject> &impl) : IRemoteProxy<IApiCaller>(impl) {} 135 136 void ApiCallerProxy::Call(const ApiCallInfo &call, ApiReplyInfo &result) 137 { 138 MessageOption option; 139 MessageParcel data; 140 MessageParcel reply; 141 // IPC io: verify on write 142 auto ret = data.WriteInterfaceToken(GetDescriptor()) && data.WriteString(call.apiId_) && 143 data.WriteString(call.callerObjRef_) && data.WriteString(call.paramList_.dump()) && 144 data.WriteInt32(call.fdParamIndex_); 145 auto fdIndex = call.fdParamIndex_; 146 if (ret && fdIndex >= 0) { 147 DCHECK(static_cast<size_t>(fdIndex) < call.paramList_.size()); 148 DCHECK(call.paramList_.at(fdIndex).type() == nlohmann::detail::value_t::number_integer); 149 if (!data.WriteFileDescriptor(call.paramList_.at(fdIndex).get<uint32_t>())) { 150 ret = false; 151 LOG_E("Failed to write file descriptor param"); 152 } 153 } 154 if (!ret || Remote()->SendRequest(TRANS_ID_CALL, data, reply, option) != 0) { 155 result.exception_ = ApiCallErr(ERR_INTERNAL, "IPC SendRequest failed"); 156 result.resultValue_ = nullptr; 157 } else { 158 result.resultValue_ = json::parse(reply.ReadString(), nullptr, false); 159 DCHECK(!result.resultValue_.is_discarded()); 160 result.exception_.code_ = static_cast<ErrCode>(reply.ReadUint32()); 161 result.exception_.message_ = reply.ReadString(); 162 } 163 } 164 165 bool ApiCallerProxy::SetBackCaller(const OHOS::sptr<IRemoteObject> &caller) 166 { 167 MessageOption option; 168 MessageParcel data; 169 MessageParcel reply; 170 auto writeStat = data.WriteInterfaceToken(GetDescriptor()) && data.WriteRemoteObject(caller); 171 if (!writeStat || (Remote()->SendRequest(TRANS_ID_SET_BACKCALLER, data, reply, option) != 0)) { 172 LOG_E("IPC SendRequest failed"); 173 return false; 174 } 175 return reply.ReadBool(); 176 } 177 178 bool ApiCallerProxy::SetRemoteDeathCallback(const sptr<IRemoteObject::DeathRecipient> &callback) 179 { 180 return Remote()->AddDeathRecipient(callback); 181 } 182 183 bool ApiCallerProxy::UnsetRemoteDeathCallback(const sptr<OHOS::IRemoteObject::DeathRecipient> &callback) 184 { 185 return Remote()->RemoveDeathRecipient(callback); 186 } 187 188 constexpr string_view PUBLISH_EVENT_PREFIX = "uitest.api.caller.publish#"; 189 constexpr uint32_t PUBLISH_MAX_RETIES = 10; 190 constexpr uint32_t WAIT_CONN_TIMEOUT_MS = 5000; 191 constexpr uint32_t WAIT_DUMP_TIMEOUT_MS = 30000; 192 193 static sptr<IRemoteObject> PublishCallerAndWaitForBackcaller(const sptr<ApiCaller> &caller, string_view token) 194 { 195 CommonEventData event; 196 Want want; 197 want.SetAction(string(PUBLISH_EVENT_PREFIX) + token.data()); 198 want.SetParam(string(token), caller->AsObject()); 199 event.SetWant(want); 200 // wait backcaller object registeration from client 201 mutex mtx; 202 unique_lock<mutex> lock(mtx); 203 condition_variable condition; 204 sptr<IRemoteObject> remoteCallerObject = nullptr; 205 caller->SetBackCallerHandler([&remoteCallerObject, &condition](const sptr<IRemoteObject> &remote) { 206 remoteCallerObject = remote; 207 condition.notify_one(); 208 }); 209 constexpr auto period = chrono::milliseconds(WAIT_CONN_TIMEOUT_MS / PUBLISH_MAX_RETIES); 210 uint32_t tries = 0; 211 do { 212 // publish caller with retries 213 if (!OHOS::testserver::TestServerClient::GetInstance().PublishCommonEvent(event)) { 214 LOG_E("Pulbish commonEvent failed"); 215 } 216 tries++; 217 } while (tries < PUBLISH_MAX_RETIES && condition.wait_for(lock, period) == cv_status::timeout); 218 caller->SetBackCallerHandler(nullptr); 219 return remoteCallerObject; 220 } 221 222 static sptr<IRemoteObject> WaitForPublishedCaller(string_view token) 223 { 224 MatchingSkills matchingSkills; 225 matchingSkills.AddEvent(string(PUBLISH_EVENT_PREFIX) + token.data()); 226 CommonEventSubscribeInfo info(matchingSkills); 227 mutex mtx; 228 unique_lock<mutex> lock(mtx); 229 condition_variable condition; 230 sptr<IRemoteObject> remoteObject = nullptr; 231 auto onEvent = [&condition, &remoteObject, &token](const CommonEventData &data) { 232 LOG_D("Received commonEvent"); 233 const auto &want = data.GetWant(); 234 remoteObject = want.GetRemoteObject(string(token)); 235 if (remoteObject == nullptr) { 236 LOG_W("Not a proxy object!"); 237 remoteObject = nullptr; 238 } else { 239 condition.notify_one(); 240 } 241 }; 242 shared_ptr<CommonEventForwarder> subscriber = make_shared<CommonEventForwarder>(info, onEvent); 243 if (!CommonEventManager::SubscribeCommonEvent(subscriber)) { 244 LOG_E("Fail to subscribe commonEvent"); 245 return nullptr; 246 } 247 const auto timeout = chrono::milliseconds(WAIT_CONN_TIMEOUT_MS); 248 auto ret = condition.wait_for(lock, timeout); 249 CommonEventManager::UnSubscribeCommonEvent(subscriber); 250 subscriber->UpdateHandler(nullptr); // unset handler 251 if (ret == cv_status::timeout) { 252 LOG_E("Wait for ApiCaller publish by server timeout"); 253 } else if (remoteObject == nullptr) { 254 LOG_E("Published ApiCaller object is null"); 255 } 256 return remoteObject; 257 } 258 259 ApiTransactor::ApiTransactor(bool asServer) : asServer_(asServer) {}; 260 261 void ApiTransactor::SetDeathCallback(function<void()> callback) 262 { 263 if (singlenessMode_) { 264 LOG_E("Cannot SetDeathCallback in singleness mode"); 265 return; 266 } 267 onDeathCallback_ = callback; 268 } 269 270 void ApiTransactor::OnPeerDeath() 271 { 272 LOG_W("Connection with peer died!"); 273 connectState_ = DISCONNECTED; 274 if (onDeathCallback_ != nullptr) { 275 onDeathCallback_(); 276 } 277 } 278 279 ApiTransactor::~ApiTransactor() 280 { 281 if (connectState_ == UNINIT) { 282 return; 283 } 284 if (remoteCaller_ != nullptr && peerDeathCallback_ != nullptr) { 285 remoteCaller_->UnsetRemoteDeathCallback(peerDeathCallback_); 286 } 287 caller_ = nullptr; 288 remoteCaller_ = nullptr; 289 peerDeathCallback_ = nullptr; 290 } 291 292 bool ApiTransactor::InitAndConnectPeer(string_view token, ApiCallHandler handler) 293 { 294 LOG_I("Begin"); 295 DCHECK(connectState_ == UNINIT); 296 connectState_ = DISCONNECTED; 297 caller_ = new ApiCaller(); 298 caller_->SetCallHandler(handler); 299 sptr<IRemoteObject> remoteObject = nullptr; 300 if (asServer_) { 301 // public caller object, and wait for backcaller registration from client 302 remoteObject = PublishCallerAndWaitForBackcaller(caller_, token); 303 if (remoteObject != nullptr) { 304 remoteCaller_ = new ApiCallerProxy(remoteObject); 305 } 306 } else { 307 // wait for published caller object, then register backcaller to server 308 remoteObject = WaitForPublishedCaller(token); 309 if (remoteObject != nullptr) { 310 remoteCaller_ = new ApiCallerProxy(remoteObject); 311 if (!remoteCaller_->SetBackCaller(caller_)) { 312 LOG_E("Failed to set backcaller to server"); 313 return false; 314 } 315 } 316 } 317 if (remoteObject == nullptr || remoteCaller_ == nullptr) { 318 LOG_E("Failed to get apiCaller object from peer"); 319 return false; 320 } 321 // in singleness mode, C/S runs in the same shell process and the remoteObject is a stub instead of proxy 322 singlenessMode_ = !remoteObject->IsProxyObject(); 323 // link connectionState to it to remoteCaller 324 if (!singlenessMode_) { 325 peerDeathCallback_ = new DeathRecipientForwarder([this]() { this->OnPeerDeath(); }); 326 if (!remoteCaller_->SetRemoteDeathCallback(peerDeathCallback_)) { 327 LOG_E("Failed to register remote caller DeathRecipient"); 328 return false; 329 } 330 } 331 // connect done 332 connectState_ = CONNECTED; 333 LOG_I("Done"); 334 return true; 335 } 336 337 ConnectionStat ApiTransactor::GetConnectionStat() const 338 { 339 return connectState_; 340 } 341 342 void ApiTransactor::Finalize() {} 343 344 void ApiTransactor::Transact(const ApiCallInfo &call, ApiReplyInfo &reply) 345 { 346 // check connection state 347 DCHECK(connectState_ != UNINIT); 348 if (connectState_ == DISCONNECTED) { 349 reply.exception_ = ApiCallErr(ERR_INTERNAL, "ipc connection is dead"); 350 return; 351 } 352 // check concurrent call 353 if (!processingApi_.empty()) { 354 constexpr auto msg = "uitest-api dose not allow calling concurrently, current processing:"; 355 reply.exception_.code_ = ERR_API_USAGE; 356 reply.exception_.message_ = string(msg) + processingApi_ + ", incoming: " + call.apiId_; 357 return; 358 } 359 processingApi_ = call.apiId_; 360 // forward to peer 361 DCHECK(remoteCaller_ != nullptr); 362 remoteCaller_->Call(call, reply); 363 processingApi_.clear(); 364 } 365 366 // functions for sending/handling broadcast commands 367 BroadcastCommandHandler g_broadcastCommandHandler = nullptr; 368 shared_ptr<CommonEventForwarder> g_broadcastCommandSubscriber = nullptr; 369 void ApiTransactor::SendBroadcastCommand(const OHOS::AAFwk::Want &cmd, ApiCallErr &err) 370 { 371 LOG_I("Send uitest.broadcast.command begin"); 372 CommonEventData event; 373 auto want = OHOS::AAFwk::Want(cmd); 374 want.SetAction("uitest.broadcast.command"); 375 event.SetWant(want); 376 if (!OHOS::testserver::TestServerClient::GetInstance().PublishCommonEvent(event)) { 377 err = ApiCallErr(ERR_INTERNAL, "Failed to publish uitest.broadcast.command"); 378 return; 379 } 380 LOG_I("Send uitest.broadcast.command end"); 381 MatchingSkills matchingSkills; 382 matchingSkills.AddEvent("uitest.broadcast.command.reply"); 383 CommonEventSubscribeInfo info(matchingSkills); 384 mutex mtx; 385 unique_lock<mutex> lock(mtx); 386 condition_variable condition; 387 auto onEvent = [&err, &condition](const CommonEventData &data) { 388 const auto &reply = data.GetWant(); 389 auto code = static_cast<ErrCode>(reply.GetIntParam("code", 0)); 390 err = ApiCallErr(code, reply.GetStringParam("message")); 391 condition.notify_one(); 392 }; 393 auto broadcastReplySubscriber = make_shared<CommonEventForwarder>(info, onEvent); 394 if (!CommonEventManager::SubscribeCommonEvent(broadcastReplySubscriber)) { 395 err = ApiCallErr(INTERNAL_ERROR, "Fail to subscribe uitest.broadcast.command.reply"); 396 } 397 const auto timeout = chrono::milliseconds(WAIT_DUMP_TIMEOUT_MS); 398 if (condition.wait_for(lock, timeout) == cv_status::timeout) { 399 err = ApiCallErr(INTERNAL_ERROR, "Wait for subscribe uitest.broadcast.command.reply timeout"); 400 } 401 CommonEventManager::UnSubscribeCommonEvent(broadcastReplySubscriber); 402 LOG_I("Receive uitest.broadcast.command.reply end"); 403 } 404 405 void ApiTransactor::SetBroadcastCommandHandler(BroadcastCommandHandler handler) 406 { 407 if (handler == nullptr) { 408 LOG_W("BroadcastCommandHandler is null"); 409 return; 410 } 411 g_broadcastCommandHandler = handler; 412 if (g_broadcastCommandSubscriber != nullptr) { 413 return; 414 } 415 MatchingSkills matchingSkills; 416 matchingSkills.AddEvent("uitest.broadcast.command"); 417 CommonEventSubscribeInfo info(matchingSkills); 418 auto onEvent = [](const CommonEventData &commandData) { 419 auto commandWant = OHOS::AAFwk::Want(commandData.GetWant()); 420 // handle command in new thread, do not block in CommonEvent dispatching thread 421 auto _ = async(launch::async, [&commandWant]() { 422 LOG_I("HandleBroadcastCommand begin"); 423 auto replyWant = OHOS::AAFwk::Want(); 424 ApiCallErr err = ApiCallErr(NO_ERROR); 425 if (g_broadcastCommandHandler == nullptr) { 426 err = ApiCallErr(INTERNAL_ERROR, "Received uitest.broadcast.command but handler is null!"); 427 } else { 428 g_broadcastCommandHandler(commandWant, err); 429 } 430 if (err.code_ != NO_ERROR) { 431 LOG_E("Cannot handle this."); 432 return; 433 } 434 replyWant.SetAction("uitest.broadcast.command.reply"); 435 replyWant.SetParam("code", (int)(err.code_)); 436 replyWant.SetParam("message", err.message_); 437 CommonEventData replyData; 438 replyData.SetWant(replyWant); 439 if (!OHOS::testserver::TestServerClient::GetInstance().PublishCommonEvent(replyData)) { 440 LOG_E("Fail to publish uitest.broadcast.command.reply"); 441 } 442 LOG_I("HandleBroadcastCommand end"); 443 }); 444 }; 445 g_broadcastCommandSubscriber = make_shared<CommonEventForwarder>(info, onEvent); 446 if (!CommonEventManager::SubscribeCommonEvent(g_broadcastCommandSubscriber)) { 447 LOG_E("Fail to subscribe uitest.broadcast.command"); 448 } 449 } 450 451 void ApiTransactor::UnsetBroadcastCommandHandler() 452 { 453 if (g_broadcastCommandSubscriber != nullptr) { 454 CommonEventManager::UnSubscribeCommonEvent(g_broadcastCommandSubscriber); 455 } 456 if (g_broadcastCommandHandler != nullptr) { 457 g_broadcastCommandHandler = nullptr; 458 } 459 } 460} // namespace OHOS::uitest