1 /*
2  * Copyright (c) 2024 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 "uitest_ffi.h"
17 
18 #include <cstdlib>
19 #include <grp.h>
20 #include <pthread.h>
21 #include <pwd.h>
22 #include <sched.h>
23 #include <future>
24 #include <queue>
25 #include <set>
26 #include <string>
27 #include <sys/resource.h>
28 #include <sys/syscall.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include "json.hpp"
32 #include "fcntl.h"
33 #include "common_utilities_hpp.h"
34 #include "frontend_api_defines.h"
35 #include "ipc_transactor.h"
36 #include "ui_event_observer_impl.h"
37 #include "test_server_client.h"
38 
39 namespace OHOS::cjuitest {
40     using namespace nlohmann;
41     using namespace std;
42     using namespace OHOS::uitest;
43 
44     static constexpr size_t BACKEND_OBJ_GC_BATCH = 100;
45     /**For gc usage, records the backend objRefs about to delete. */
46     static queue<string> g_backendObjsAboutToDelete;
47     static mutex g_gcQueueMutex;
48     /**IPC client. */
49     static ApiTransactor g_apiTransactClient(false);
50     static future<void> g_establishConnectionFuture;
51 
MallocCString(const string &origin)52     char *MallocCString(const string &origin)
53     {
54         if (origin.empty()) {
55             return nullptr;
56         }
57         auto len = origin.length() + 1;
58         char *res = static_cast<char *>(malloc(sizeof(char) * len));
59         if (res == nullptr) {
60             return nullptr;
61         }
62         return char_traits<char>::copy(res, origin.c_str(), len);
63     }
64 
65     /**Wait connection result sync if need.*/
WaitForConnectionIfNeed()66     static void WaitForConnectionIfNeed()
67     {
68         if (g_establishConnectionFuture.valid()) {
69             LOG_I("Begin WaitForConnection");
70             g_establishConnectionFuture.get();
71         }
72     }
73 
SetPasteBoardData(string_view text)74     static void SetPasteBoardData(string_view text)
75     {
76         OHOS::testserver::TestServerClient::GetInstance().SetPasteData(string(text));
77     }
78 
PreprocessTransaction(ApiCallInfo &callInfo, ApiCallErr &error)79     void PreprocessTransaction(ApiCallInfo &callInfo, ApiCallErr &error)
80     {
81         auto &paramList = callInfo.paramList_;
82         const auto &id = callInfo.apiId_;
83         if (id == "Component.inputText" && paramList.size() > 0) {
84             if (paramList.at(INDEX_ZERO).type() == nlohmann::detail::value_t::string) {
85                 SetPasteBoardData(paramList.at(INDEX_ZERO).get<string>());
86             }
87         } else if (id  == "Driver.inputText" && paramList.size() > 1) {
88             if (paramList.at(INDEX_ONE).type() == nlohmann::detail::value_t::string) {
89                 SetPasteBoardData(paramList.at(INDEX_ONE).get<string>());
90             }
91         } else if (id == "Driver.screenCap" || id == "UiDriver.screenCap" || id == "Driver.screenCapture") {
92             if (paramList.size() < 1 || paramList.at(0).type() != nlohmann::detail::value_t::string) {
93                 LOG_E("Missing file path argument");
94                 error = ApiCallErr{ERR_INVALID_INPUT, "Missing file path argument"};
95                 return;
96             }
97             auto path = paramList.at(INDEX_ZERO).get<string>();
98             auto fd = open(path.c_str(), O_RDWR | O_CREAT, 0666);
99             if (fd == -1) {
100                 LOG_E("Invalid file path: %{public}s", path.data());
101                 error = ApiCallErr{ERR_INVALID_INPUT, "Invalid file path:" + path};
102                 return;
103             }
104             paramList[INDEX_ZERO] = fd;
105             callInfo.fdParamIndex_ = INDEX_ZERO;
106         } else if (id  == "UIEventObserver.once") {
107             LOG_I("preprocess callback");
108             int64_t callbackId = paramList.at(1).get<int64_t>();
109             UiEventObserverImpl::Get().PreprocessCallOnce(callInfo, callbackId, error);
110         }
111     }
112 
113     /**Call api with parameters out, wait for and return result value or throw raised exception.*/
CJTransact(ApiCallInfo callInfo)114     ApiReplyInfo CJTransact(ApiCallInfo callInfo)
115     {
116         WaitForConnectionIfNeed();
117         LOG_D("TargetApi=%{public}s", callInfo.apiId_.data());
118         auto reply = ApiReplyInfo();
119         g_apiTransactClient.Transact(callInfo, reply);
120         LOG_I("return value: %{public}s", reply.resultValue_.dump().c_str());
121         // notify backend objects deleting
122         if (g_backendObjsAboutToDelete.size() >= BACKEND_OBJ_GC_BATCH) {
123             auto gcCall = ApiCallInfo {.apiId_ = "BackendObjectsCleaner"};
124             unique_lock<mutex> lock(g_gcQueueMutex);
125             for (size_t count = 0; count < BACKEND_OBJ_GC_BATCH; count++) {
126                 gcCall.paramList_.emplace_back(g_backendObjsAboutToDelete.front());
127                 g_backendObjsAboutToDelete.pop();
128             }
129             lock.unlock();
130             auto gcReply = ApiReplyInfo();
131             g_apiTransactClient.Transact(gcCall, gcReply);
132         }
133         return reply;
134     }
135 
GetUid()136     int32_t GetUid()
137     {
138         auto processGetuid = static_cast<int32_t>(getuid());
139         return processGetuid;
140     }
141 
GetPid()142     int32_t GetPid()
143     {
144         auto proPid = static_cast<int32_t>(getpid());
145         return proPid;
146     }
147 
GetTid()148     int32_t GetTid()
149     {
150         auto proTid = static_cast<int32_t>(gettid());
151         return proTid;
152     }
153 
154     extern "C" {
CJ_InitConnection(char *token)155         void CJ_InitConnection(char *token)
156         {
157             string realToken{token};
158             LOG_I("connect token is %{public}s", token);
159             g_establishConnectionFuture = async(launch::async, [realToken]() {
160                 auto &instance = UiEventObserverImpl::Get();
161                 using namespace std::placeholders;
162                 auto callbackHandler = std::bind(&UiEventObserverImpl::HandleEventCallback, &instance, _1, _2);
163                 auto result = g_apiTransactClient.InitAndConnectPeer(realToken, callbackHandler);
164                 LOG_I("End setup transaction connection, result=%{public}d", result);
165             });
166         }
167 
CJ_ApiCall(ApiCallParams params)168         RetDataCString CJ_ApiCall(ApiCallParams params)
169         {
170             RetDataCString ret{.code = uitest::ErrCode::NO_ERROR, .data = nullptr};
171             ApiCallInfo callInfo_;
172             LOG_D("apiId: %{public}s", params.apiId);
173             callInfo_.apiId_ = string{params.apiId};
174             if (params.callerObjRef != nullptr) {
175                 LOG_D("callerObjRef_: %{public}s", params.callerObjRef);
176                 callInfo_.callerObjRef_ = string{params.callerObjRef};
177             }
178             if (params.paramList == nullptr) {
179                 LOG_D("paramList_: \"\"");
180                 callInfo_.paramList_ = "";
181             } else {
182                 LOG_D("paramList_: %{public}s", params.paramList);
183                 callInfo_.paramList_ = nlohmann::json::parse(string{params.paramList});
184             }
185             ApiCallErr err{uitest::ErrCode::NO_ERROR};
186             PreprocessTransaction(callInfo_, err);
187             if (err.code_ != uitest::ErrCode::NO_ERROR) {
188                 ret.code = err.code_;
189                 ret.data = MallocCString(err.message_);
190                 return ret;
191             }
192             auto result = CJTransact(callInfo_);
193             if (callInfo_.fdParamIndex_ >= 0) {
194                 auto fd = callInfo_.paramList_.at(INDEX_ZERO).get<int>();
195                 (void) close(fd);
196             }
197             if (result.exception_.code_ != uitest::ErrCode::NO_ERROR) {
198                 ret.code = result.exception_.code_;
199                 ret.data = MallocCString(result.exception_.message_);
200                 return ret;
201             }
202             ret.data = MallocCString(result.resultValue_.dump());
203             return ret;
204         }
205 
CJ_UITestObjDelete(char *objref)206         void CJ_UITestObjDelete(char *objref)
207         {
208             unique_lock<mutex> lock(g_gcQueueMutex);
209             g_backendObjsAboutToDelete.push(string(objref));
210         }
211 
FfiOHOSProcessManagerGetUid()212         int32_t FfiOHOSProcessManagerGetUid()
213         {
214             auto result = GetUid();
215             return result;
216         }
217 
FfiOHOSProcessManagerGetPid()218         int32_t FfiOHOSProcessManagerGetPid()
219         {
220             auto result = GetPid();
221             return result;
222         }
223 
FfiOHOSProcessManagerGetTid()224         int32_t FfiOHOSProcessManagerGetTid()
225         {
226             auto result = GetTid();
227             return result;
228         }
229     }
230 } // namespace OHOS::uitest
231