Move function definition to aul header
[platform/core/appfw/aul-1.git] / src / status.cc
1 /*
2  * Copyright (c) 2000 - 2022 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "include/aul.h"
18
19 #include <sys/types.h>
20 #include <unistd.h>
21
22 #include <bundle_cpp.h>
23
24 #include <list>
25 #include <memory>
26 #include <mutex>
27
28 #include "aul_api.h"
29 #include "aul_util.h"
30 #include "include/aul_app_com.h"
31 #include "include/aul_error.h"
32 #include "include/aul_sock.h"
33 #include "launch.h"
34
35 #include "app_request.h"
36 #include "aul/common/exception.hh"
37
38 using namespace aul::internal;
39
40 struct status_listen_s {
41   void* dummy;
42 };
43
44 namespace {
45 using namespace aul;
46
47 class AppStatusEvent {
48  public:
49   AppStatusEvent(std::string appid, uid_t uid, app_status_cb cb,
50       void* user_data)
51       : appid_(std::move(appid)),
52         uid_(uid),
53         cb_(cb),
54         user_data_(user_data) {
55     std::string endpoint = "app_status_event:" + appid_;
56     if (uid_ < REGULAR_UID_MIN)
57       endpoint += ":" + std::to_string(uid_);
58
59     int ret = aul_app_com_create(endpoint.c_str(), nullptr, OnAppComCb, this,
60         &connection_);
61     if (ret != AUL_R_OK) {
62       _E("aul_app_com_create() is failed. endpoint(%s), error(%d)",
63           endpoint.c_str(), ret);
64       THROW(ret);
65     }
66   }
67
68   ~AppStatusEvent() {
69     if (connection_ != nullptr)
70       aul_app_com_leave(connection_);
71   }
72
73  private:
74   static int OnAppComCb(const char* endpoint, aul_app_com_result_e res,
75       bundle* envelope, void* user_data) {
76     tizen_base::Bundle b(envelope);
77     aul_app_info app_info = { 0, };
78     app_info.appid = const_cast<char*>(b.GetString(AUL_K_APPID).c_str());
79     app_info.pkgid = const_cast<char*>(b.GetString(AUL_K_PKGID).c_str());
80     app_info.app_path = const_cast<char*>(b.GetString(AUL_K_EXEC).c_str());
81     app_info.instance_id = const_cast<char*>(
82         b.GetString(AUL_K_INSTANCE_ID).c_str());
83     app_info.pid = std::stoi(b.GetString(AUL_K_PID));
84     app_info.status = std::stoi(b.GetString(AUL_K_STATUS));
85     app_info.is_sub_app = std::stoi(b.GetString(AUL_K_IS_SUBAPP));
86     int context_status = std::stoi(b.GetString("__CONTEXT_STATUS__"));
87
88     auto* event = static_cast<AppStatusEvent*>(user_data);
89     event->cb_(&app_info, context_status, event->user_data_);
90     return 0;
91   }
92
93  private:
94   std::string appid_;
95   uid_t uid_;
96   app_status_cb cb_;
97   void* user_data_;
98   aul_app_com_connection_h connection_ = nullptr;
99 };
100
101 class StatusLocalCb {
102  public:
103   StatusLocalCb(aul_status_local_cb cb, void* user_data)
104       : cb_(cb), user_data_(user_data) {
105   }
106
107   aul_status_local_cb GetCb() const {
108     return cb_;
109   }
110
111   void* GetUserData() const {
112     return user_data_;
113   }
114
115   int Invoke(int status) {
116     if (cb_ != nullptr)
117       return cb_(status, user_data_);
118
119     return 0;
120   }
121
122  private:
123   aul_status_local_cb cb_;
124   void* user_data_;
125 };
126
127 class Context {
128  public:
129   Context() = default;
130
131   int GetStatus() const {
132     return status_;
133   }
134
135   void SetStatus(int status) {
136     status_ = status;
137   }
138
139   void AddLocalCb(aul_status_local_cb cb, void* user_data) {
140     std::lock_guard<std::recursive_mutex> lock(mutex_);
141     cbs_.emplace_back(new StatusLocalCb(cb, user_data));
142   }
143
144   std::size_t RemoveLocalCb(aul_status_local_cb cb, void* user_data) {
145     std::lock_guard<std::recursive_mutex> lock(mutex_);
146     auto count = cbs_.size();
147     cbs_.remove_if([&](const std::unique_ptr<StatusLocalCb>& local_cb) {
148           return local_cb->GetCb() == cb &&
149               local_cb->GetUserData() == user_data;
150         });
151     return count - cbs_.size();
152   }
153
154   void InvokeLocalCb(int status) {
155     std::lock_guard<std::recursive_mutex> lock(mutex_);
156     auto iter = cbs_.begin();
157     while (iter != cbs_.end()) {
158       if ((*iter)->Invoke(status) < 0)
159         iter = cbs_.erase(iter);
160       else
161         iter++;
162     }
163   }
164
165  private:
166   int status_ = STATUS_LAUNCHING;
167   std::list<std::unique_ptr<StatusLocalCb>> cbs_;
168   std::recursive_mutex mutex_;
169 };
170
171 Context context;
172
173 }  // namespace
174
175 extern "C" API int aul_status_update(int status) {
176   if (status < STATUS_LAUNCHING || status > STATUS_TERMINATE) {
177     _E("Invalid parameter");
178     return AUL_R_EINVAL;
179   }
180
181   if (context.GetStatus() == status)
182     return AUL_R_OK;
183
184   context.SetStatus(status);
185
186   int ret = aul_sock_send_raw(AUL_UTIL_PID, getuid(), APP_STATUS_UPDATE,
187       reinterpret_cast<unsigned char*>(&status), sizeof(status),
188       AUL_SOCK_NOREPLY);
189   if (ret == 0)
190     context.InvokeLocalCb(status);
191   else
192     ret = aul_error_convert(ret);
193
194   return ret;
195 }
196
197 extern "C" API int aul_app_get_status_bypid(int pid) {
198   return aul_app_get_status_bypid_for_uid(pid, getuid());
199 }
200
201 extern "C" API int aul_app_get_status_bypid_for_uid(int pid, uid_t uid) {
202   if (pid < 0) {
203     _E("Invalid parameter");
204     return AUL_R_EINVAL;
205   }
206
207   if (pid == getpid())
208     return context.GetStatus();
209
210   return AppRequest(APP_GET_STATUS, uid)
211       .SetPid(pid)
212       .SendSimply();
213 }
214
215 extern "C" API int aul_app_get_status(const char* appid) {
216   return aul_app_get_status_for_uid(appid, getuid());
217 }
218
219 extern "C" API int aul_app_get_status_for_uid(const char* appid, uid_t uid) {
220   if (appid == nullptr) {
221     _E("Invalid parameter");
222     return AUL_R_EINVAL;
223   }
224
225   return AppRequest(APP_GET_STATUS_BY_APPID, uid)
226       .SetAppId(appid)
227       .SendSimply();
228 }
229
230 extern "C" API int aul_add_status_local_cb(aul_status_local_cb callback,
231     void* user_data) {
232   if (callback == nullptr) {
233     _E("Invalid parameter");
234     return AUL_R_ERROR;
235   }
236
237   context.RemoveLocalCb(callback, user_data);
238   context.AddLocalCb(callback, user_data);
239   return AUL_R_OK;
240 }
241
242 extern "C" API int aul_remove_status_local_cb(aul_status_local_cb callback,
243     void* user_data) {
244   if (callback == nullptr) {
245     _E("Invalid parameter");
246     return AUL_R_ERROR;
247   }
248
249   if (context.RemoveLocalCb(callback, user_data) > 0)
250     return AUL_R_OK;
251
252   return AUL_R_EINVAL;
253 }
254
255 extern "C" API int aul_invoke_status_local_cb(int status) {
256   if (status < STATUS_LAUNCHING || status > STATUS_TERMINATE) {
257     _E("Invalid parameter");
258     return AUL_R_EINVAL;
259   }
260
261   context.InvokeLocalCb(status);
262   return AUL_R_OK;
263 }
264
265 extern "C" API int aul_set_process_group(int owner_pid, int child_pid) {
266   if (owner_pid < 1 || child_pid < 1) {
267     _E("Invalid parameter");
268     return AUL_R_EINVAL;
269   }
270
271   tizen_base::Bundle b {
272     { AUL_K_OWNER_PID, std::to_string(owner_pid) },
273     { AUL_K_CHILD_PID, std::to_string(child_pid) }
274   };
275
276   return AppRequest(APP_SET_PROCESS_GROUP, getuid())
277       .With(std::move(b))
278       .SendSimply();
279 }
280
281 extern "C" API int aul_listen_app_status(const char* appid,
282     app_status_cb callback, void* user_data, status_listen_h* handle) {
283   return aul_listen_app_status_for_uid(appid, callback, user_data, handle,
284       getuid());
285 }
286
287 extern "C" API int aul_listen_app_status_for_uid(const char* appid,
288     app_status_cb callback, void* user_data, status_listen_h* handle,
289     uid_t uid) {
290   if (appid == nullptr || callback == nullptr || handle == nullptr) {
291     _E("Invalid parameter");
292     return AUL_R_EINVAL;
293   }
294
295   try {
296     auto* event = new (std::nothrow) AppStatusEvent(
297         appid, uid, callback, user_data);
298     if (event == nullptr) {
299       _E("Out of memory");
300       return AUL_R_ENOMEM;
301     }
302
303     *handle = reinterpret_cast<status_listen_h>(event);
304   } catch (const Exception& e) {
305     _E("Exception occurs. error(%s)", e.what());
306     return e.GetErrorCode();
307   }
308
309   return AUL_R_OK;
310 }
311
312 extern "C" API int aul_ignore_app_status(status_listen_h handle) {
313   if (handle == nullptr) {
314     _E("Invalid parameter");
315     return AUL_R_EINVAL;
316   }
317
318   auto* event = reinterpret_cast<AppStatusEvent*>(handle);
319   delete event;
320   return AUL_R_OK;
321 }
322
323 extern "C" API int aul_notify_exit(void) {
324   return AppRequest(APP_NOTIFY_EXIT, getuid())
325       .SendCmdOnly(AUL_SOCK_NOREPLY);
326 }
327
328 extern "C" API int aul_notify_start(void) {
329   return AppRequest(APP_NOTIFY_START, getuid())
330       .SendCmdOnly(AUL_SOCK_NOREPLY);
331 }
332
333 extern "C" API const char* aul_app_status_convert_to_string(int status) {
334   switch (status) {
335     case STATUS_LAUNCHING:
336       return "STATUS_LAUNCHING";
337     case STATUS_CREATED:
338       return "STATUS_CREATED";
339     case STATUS_FOCUS:
340       return "STATUS_FOCUS";
341     case STATUS_VISIBLE:
342       return "STATUS_VISIBLE";
343     case STATUS_BG:
344       return "STATUS_BG";
345     case STATUS_DYING:
346       return "STATUS_DYING";
347     case STATUS_HOME:
348       return "STATUS_HOME";
349     case STATUS_NORESTART:
350       return "STATUS_NORESTART";
351     case STATUS_SERVICE:
352       return "STATUS_SERVICE";
353     case STATUS_TERMINATE:
354       return "STATUS_TERMINATE";
355     default:
356       return "Unknown status";
357   }
358 }
359
360 extern "C" API int aul_status_update_v2(int status) {
361   if (status < STATUS_LAUNCHING || status > STATUS_TERMINATE) {
362     _E("Invalid parameter");
363     return AUL_R_EINVAL;
364   }
365
366   if (context.GetStatus() == status)
367     return AUL_R_OK;
368
369   context.SetStatus(status);
370
371   tizen_base::Bundle b { { AUL_K_STATUS, std::to_string(status) } };
372   int ret = AppRequest(APP_STATUS_UPDATE_V2, getuid())
373       .With(std::move(b))
374       .SendSimply();
375   if (ret != 0) {
376     _E("Failed to update app status. error(%d)", ret);
377     return ret;
378   }
379
380   context.InvokeLocalCb(status);
381   return AUL_R_OK;
382 }