sync with master
[platform/framework/native/appfw.git] / src / app / FApp_Aul.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /**
19  * @file         FApp_Aul.cpp
20  * @brief       This is the implementation for the _Aul.cpp class.
21  */
22 #include <cstdio>
23 #include <cstdlib>
24 #include <new>
25 #include <unistd.h>
26 #include <sys/prctl.h>
27 #include <signal.h>
28 #include <unique_ptr.h>
29
30 #include <aul.h>
31 #include <bundle.h>
32 #include <appsvc/appsvc.h>
33 #include <app_manager.h>
34 #include <heynoti.h>
35
36 #include <FBaseObject.h>
37 #include <FBaseString.h>
38 #include <FBaseUtil.h>
39 #include <FBaseSysLog.h>
40 #include <FBaseColHashMapT.h>
41 #include <FAppPkgPackageInfo.h>
42
43 #include <FBaseRt_Process.h>
44 #include <FBase_StringConverter.h>
45 #include "FAppPkg_PackageManagerImpl.h"
46 #include "FApp_Types.h"
47 #include "FApp_Aul.h"
48 #include "FApp_TemplateUtil.h"
49
50 #ifdef __cplusplus
51 extern "C" {
52 #endif
53 extern int aul_listen_app_dead_signal(int (* func)(int, void*), void* data);
54 #ifdef __cplusplus
55 }
56 #endif
57
58 using namespace Tizen::App::Package;
59 using namespace Tizen::Base;
60 using namespace Tizen::Base::Collection;
61 using namespace Tizen::Base::Runtime;
62 using namespace Tizen::Base::Utility;
63
64 namespace
65 {
66
67 const char _DESKTOP_FILE_PATH[] = "/opt/share/applications";
68 const char _DESKTOP_FILE_PATH_FORMAT[] = "%s/%s.desktop";
69
70 const char _X_TIZEN_SVC[] = "x-tizen-svc"; //X-TIZEN-SVC=[operation1] | [URI1] | [MIME1] ; [operation2] | [URI2] | [MIME2]
71 const int _MAX_TIZEN_SVC_DESC_LEN = 1024;
72
73 const int _MAX_CATEGORY = 12;
74 const int _MAX_PACKAGE_ID_LENGTH = 10;
75 const int _MAX_DATA_ARGUMENT_LENGTH = 32768; // 32KB
76
77 // borrowed from app-svc/include/pri_key.h
78 #define APP_SVC_K_RES_VAL       "__APP_SVC_K_RES_VAL__"
79
80 }
81
82 namespace Tizen { namespace App
83 {
84
85 struct _CategoryList
86 {
87         const char category[_MAX_CATEGORY];
88         _AppType type;
89 };
90
91 static const _CategoryList _CATEGORY_LIST[] =
92 {
93         {"home-screen", _APP_TYPE_HOME_APP},
94         {"lock-screen", _APP_TYPE_LOCK_APP},
95         {"ime", _APP_TYPE_IME_APP},
96 };
97
98 static const int _NUM_CATEGORY = sizeof(_CATEGORY_LIST) / sizeof(_CategoryList);
99
100 result
101 _Aul::GetConvertedResult(const int aul_ret, const char* pFunctionName)
102 {
103         result r = E_SUCCESS;
104
105         switch (aul_ret)
106         {
107         case AUL_R_EINVAL:
108                 r = E_INVALID_ARG;
109                 SysLogException(NID_APP, r, "%s : Invalid argument.", pFunctionName);
110                 break;
111
112         case AUL_R_ECOMM:
113                 r = E_SYSTEM;
114                 SysLogException(NID_APP, r, "%s : Internal IPC error.", pFunctionName);
115                 break;
116
117         case AUL_R_ERROR:
118                 r = E_SYSTEM;
119                 SysLogException(NID_APP, r, "%s : General error.", pFunctionName);
120                 break;
121
122         default:
123                 SysLog(NID_APP, "%s : successed.", pFunctionName);
124                 break;
125         }
126
127         return r;
128 }
129
130 result
131 _Aul::SendResult(bundle* b, appsvc_result_val res)
132 {
133         // to skip error handling, of appsvc_send_result, use aul_send_service_result() directly.
134         //int ret = appsvc_send_result(b, res);
135
136         char tmp[32] = {0, };
137         snprintf(tmp, 32, "%d", static_cast<int>(res));
138         appsvc_add_data(b, APP_SVC_K_RES_VAL, tmp);
139
140         bundle_raw* dataBuf = null;
141         int dataLen = 0;
142         int ret = bundle_encode(b, &dataBuf, &dataLen);
143         bundle_free_encoded_rawdata(&dataBuf);
144         SysTryReturnResult(NID_APP, ret == 0, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient.");
145         SysTryReturnResult(NID_APP, dataLen <= _MAX_DATA_ARGUMENT_LENGTH, E_MAX_EXCEEDED,
146                         "The data length (%d) exceeds the maximum limit.", dataLen);
147
148         int aul_ret = aul_send_service_result(b);
149
150         return GetConvertedResult(aul_ret, "SendResult");
151 }
152
153
154 bool
155 _Aul::IsRunning(const AppId& appId, const String& exeName)
156 {
157         char slpPackageName[MAX_SLP_PACKAGE_ID] = {0, };
158         bool isRunning = false;
159
160         _PackageManagerImpl* pPackageManagerImpl = _PackageManagerImpl::GetInstance();
161         SysTryReturn(NID_APP, pPackageManagerImpl != null, false, E_INVALID_STATE, "[E_INVALID_STATE] Invalid package instance.");
162
163         pPackageManagerImpl->GetPackageName(appId, &exeName, slpPackageName, MAX_SLP_PACKAGE_ID);
164
165         app_manager_is_running(slpPackageName, &isRunning);
166
167         SysLog(NID_APP, "'%s' %s running now.", slpPackageName, (isRunning == true) ? "is" : "is NOT");
168
169         return isRunning;
170 }
171
172 bool
173 _Aul::IsRunning(const String& packageName)
174 {
175         bool isRunning = false;
176         std::unique_ptr<char[]> pSlpPackageName(_StringConverter::CopyToCharArrayN(packageName));
177
178         app_manager_is_running(pSlpPackageName.get(), &isRunning);
179
180         SysLog(NID_APP, "'%ls' %s running now.", packageName.GetPointer(), (isRunning) ? "is" : "is NOT");
181         return isRunning;
182 }
183
184
185 void
186 _Aul::SetOnAppTerminatedCb(int (* pf_app_dead_handler)(int pid, void* pData), void* pData)
187 {
188         aul_listen_app_dead_signal(pf_app_dead_handler, pData);
189         SysLog(NID_APP, "'app_dead_handler is set.");
190 }
191
192
193 result
194 _Aul::TerminateApplicationByPid(int pid)
195 {
196         int ret_aul = aul_terminate_pid(pid);
197
198         return GetConvertedResult(ret_aul, "TerminateApplicationByPid");
199 }
200
201 static int
202 TerminateApplicationIterFnCb(const aul_app_info* pAppInfo, void* pData)
203 {
204         const char* pStr = static_cast<const char*>(pData);
205
206         if (pStr && strncmp(pStr, pAppInfo->pkg_name, NATIVE_APP_MAX_APPID_LENGTH) == 0)
207         {
208                 SysLog(NID_APP, "%s(%d) is terminated.", pAppInfo->pkg_name, pAppInfo->pid);
209                 int ret_aul = aul_terminate_pid( pAppInfo->pid );
210                 SetLastResult(_Aul::GetConvertedResult(ret_aul, "TerminateApplication"));
211         }
212         return 0;
213 }
214
215 result
216 _Aul::TerminateApplication(const AppId& appId)
217 {
218         SetLastResult(E_OBJ_NOT_FOUND);
219         std::unique_ptr<char[]> pAppId(_StringConverter::CopyToCharArrayN(appId));
220         aul_app_get_running_app_info(TerminateApplicationIterFnCb, static_cast<void*>(pAppId.get()));
221
222         SysLog(NID_APP, "%ls terminated.", appId.GetPointer());
223         return GetLastResult();
224 }
225
226 result
227 _Aul::SetOomAdj(int pid, int adj)
228 {
229         // set oom_adj to -17 for system service
230         result r = E_SUCCESS;
231         char buf[FILENAME_MAX];
232         FILE *fP = NULL;
233
234         snprintf(buf, FILENAME_MAX, "/proc/%d/oom_adj", pid);
235         fP = fopen(buf, "w");
236         SysTryReturnResult(NID_APP, fP != NULL, E_SYSTEM, "oom_adj change failed with %s.", strerror(errno));
237
238         fprintf(fP, "%d", adj);
239         fclose(fP);
240
241         return r;
242 }
243
244 result
245 _Aul::SetPowerOffNotiListener( void (*powerOffCb)(void *pData), void *pData)
246 {
247         int heyFd = heynoti_init();
248         SysTryReturnResult(NID_APP, heyFd >= 0, E_SYSTEM, "heynoti_init failed.");
249
250         int ret = heynoti_subscribe(heyFd, "power_off_start", powerOffCb, pData);
251         SysTryReturnResult(NID_APP, ret >= 0, E_SYSTEM, "heynoti_subscribe failed.");
252
253         ret = heynoti_attach_handler(heyFd);
254         SysTryReturnResult(NID_APP, ret >= 0, E_SYSTEM, "heynoti_attach_handler failed.");
255
256         return E_SUCCESS;
257 }
258
259 int
260 _Aul::GetAppType(const String& category)
261 {
262         int ret = 0;
263
264         HashMapT<String, int> map;
265         map.Construct();
266
267         StringTokenizer strTok(category, L';');
268
269         String token;
270         while (strTok.HasMoreTokens())
271         {
272                 result r = strTok.GetNextToken(token);
273                 if (r == E_SUCCESS)
274                 {
275                         map.Add(token, 0);
276                 }
277         }
278
279         SysLog(NID_APP, "%d category items .", map.GetCount());
280
281         String key;
282
283         for (int i = 0; i < _NUM_CATEGORY; i++)
284         {
285                 bool b = false;
286                 key = _CATEGORY_LIST[i].category;
287                 result r = map.ContainsKey(key, b);
288                 if (r == E_SUCCESS && b)
289                 {
290                         ret |= _CATEGORY_LIST[i].type;
291                 }
292         }
293
294         return ret;
295 }
296
297 bool _Aul::IsInstalled(const AppId& appId)
298 {
299         String packageId;
300         packageId = _PackageManagerImpl::GetPackageIdByAppId(appId);
301
302         return _PackageManagerImpl::GetInstance()->IsPackageInstalled(packageId);
303 }
304
305 result
306 _Aul::_DesktopFile::MakePath(const AppId& appId, char* path, int size)
307 {
308         SysTryReturnResult(NID_APP, path != null, E_INVALID_ARG, "");
309
310         std::unique_ptr<char[]> pAppId(_StringConverter::CopyToCharArrayN(appId));
311         SysTryReturnResult(NID_APP, pAppId != null, E_OUT_OF_MEMORY, "Failed to allocate memory for 'pAppId'");
312
313         snprintf(path, size, _DESKTOP_FILE_PATH_FORMAT, _DESKTOP_FILE_PATH, pAppId.get());
314
315         return E_SUCCESS;
316 }
317
318 result
319 _Aul::_DesktopFile::UpdateService(const AppId& appId, const char* value)
320 {
321         char path[FILENAME_MAX] = {0, };
322         MakePath(appId, path, FILENAME_MAX);
323
324         return UpdateField(path, _X_TIZEN_SVC, value);
325 }
326
327
328 result
329 _Aul::_DesktopFile::RemoveService(const AppId& appId, const char* operationOnlyValue)
330 {
331         char path[FILENAME_MAX] = {0, };
332         MakePath(appId, path, FILENAME_MAX);
333
334         return UpdateField(path, _X_TIZEN_SVC, operationOnlyValue, true);
335 }
336
337 //
338 // Update value of specified field.
339 // currently only "x-slp-svc" field is supported.
340 //
341 #define BUFFER_SIZE 1024
342 result
343 _Aul::_DesktopFile::UpdateField(const char* path, const char* fieldName, const char* value, bool isRemove)
344 {
345         SysTryReturnResult(NID_APP, path != null, E_INVALID_ARG, "path should not be null.");
346         SysTryReturnResult(NID_APP, fieldName != null, E_INVALID_ARG, "fieldName should not be null.");
347         SysTryReturnResult(NID_APP, value != null, E_INVALID_ARG, "value should not be null.");
348
349         FILE* fp = fopen(path, "r+");
350         SysTryReturnResult(NID_APP, fp != null, E_SYSTEM, "falied to open '%s' due to %s", path, strerror(errno));
351
352         char buffer[BUFFER_SIZE] = {0, };
353         bool found = false;
354         int len = 0;
355         int pos = 0;
356         int foundpos = 0;
357         result r = E_SUCCESS;
358         int remains = 0;
359
360         ArrayListT<char*> buffers;
361         buffers.Construct();
362
363         char* pCurrent = null;
364
365         while (fgets(buffer, BUFFER_SIZE, fp) != NULL)
366         {
367                 len = strlen(buffer);
368                 SysTryCatch(NID_APP, len < BUFFER_SIZE, , r = E_INVALID_ARG, "strlen returns invalid value. (%d)", len );
369
370                 if (found)
371                 {
372                         pCurrent = new (std::nothrow) char[len + 1];
373                         SysTryCatch(NID_APP, pCurrent != null, , r = E_OUT_OF_MEMORY, "failed to allocate mem pCurrent");
374
375                         strncpy(pCurrent, buffer, len);
376                         buffers.Add(pCurrent);
377                 }
378                 else
379                 {
380                         if (strncmp(buffer, fieldName, len) == 0)
381                         {
382                                 int fieldNameLen = strlen(fieldName);
383                                 SysTryCatch(NID_APP, len > fieldNameLen, , E_INVALID_ARG, "[E_INVALID_ARG] fieldName(%s)", fieldName);
384
385                                 pCurrent = UpdateServiceValueN(buffer + fieldNameLen, value, isRemove);
386                                 SysTryCatch(NID_APP, pCurrent != null, , r = GetLastResult(), "[%s] UpdateServiceValue failed", GetErrorMessage(GetLastResult()));
387
388                                 buffers.Add(pCurrent);
389
390                                 foundpos = pos;
391                                 found = true;
392                         }
393                 }
394
395                 pos += len;
396         }
397
398         if (found)
399         {
400                 fsetpos(fp, (fpos_t*) &foundpos);
401
402                 remains = buffers.GetCount();   // prevent infinite loop
403                 while (buffers.GetCount() > 0 && remains-- > 0)
404                 {
405                         pCurrent = null;
406                         buffers.GetAt(0, pCurrent);
407                         buffers.RemoveAt(0);
408                         SysTryCatch(NID_APP, pCurrent != null, , r = E_INVALID_STATE, "");
409
410                         fputs(pCurrent, fp);
411                         len = strlen(pCurrent);
412                         pos += len;
413                         delete[] pCurrent;
414                 }
415
416                 int ret = truncate(path, pos);
417                 SysTryLog(NID_APP, ret == 0, "Truncate failure (%s).", strerror(errno));
418         }
419         else
420         {
421                 char svctext[_MAX_TIZEN_SVC_DESC_LEN] = {0, };
422                 snprintf(svctext, _MAX_TIZEN_SVC_DESC_LEN, "%s=%s\n", fieldName, value);
423                 fputs(svctext, fp);
424         }
425         fclose(fp);
426
427         return E_SUCCESS;
428
429 CATCH:
430
431         remains = buffers.GetCount();   // prevent infinite loop
432         while (buffers.GetCount() > 0 && remains-- > 0)
433         {
434                 pCurrent = null;
435                 buffers.GetAt(0, pCurrent);
436                 buffers.RemoveAt(0);
437                 if (pCurrent != null)
438                 {
439                         delete[] pCurrent;
440                 }
441         }
442
443         fclose(fp);
444
445         return r;
446 }
447
448 //
449 //      Tizen service string example
450 //      X-TIZEN-SVC= http://tizen.org/appcontrol/operation/pick|NULL|image/jpge; http://tizen.org/appcontrol/operation/pick|NULL|video/mp4; http://tizen.org/appcontrol/operation/pick|NULL|NULL; http://tizen.org/appcontrol/operation/pview|NULL|NULL
451 //
452 char*
453 _Aul::_DesktopFile::UpdateServiceValueN(char* buffer, const char* newValue, bool isRemove)
454 {
455         SysTryReturn(NID_APP, buffer != null, null, E_INVALID_ARG, "");
456         SysTryReturn(NID_APP, newValue != null, null, E_INVALID_ARG, "");
457
458         SysLog(NID_APP, "current(%s), new(%s), isRemove(%s)", buffer, newValue, (isRemove) ? "true" : "false");
459
460         String buf(buffer);
461         bool found = false;
462
463         const String& servicePattern(L"([A-Za-z&=:/\\.\\-]*);?");
464
465         ArrayList services;
466         String resultString;
467
468         Utility::RegularExpression regex;
469         result r = regex.Construct(servicePattern);
470         SysTryReturn(NID_APP, !IsFailed(r), null, r, "");
471
472         String newOperation;
473         String newUrl;
474         String newMimeType;
475         String newService(newValue);
476
477         if (isRemove == false)
478         {
479                 ParseService(newService, newOperation, newUrl, newMimeType);
480         }
481         else
482         {
483                 newOperation = newValue;
484         }
485
486         services.Construct();
487
488         while (regex.Consume(buf, &services) == true)
489         {
490                 String* pCurrentService = (String*) services.GetAt(1);
491                 services.RemoveAll(false);
492
493                 String operation;
494                 String url;
495                 String mimeType;
496
497                 ParseService(*pCurrentService, operation, url, mimeType);
498
499                 if (operation == newOperation)
500                 {
501                         if (isRemove == true)
502                         {
503                                 SysLog(NID_APP, "opreration '%ls' will be removed", operation.GetPointer());
504                         }
505                         else
506                         {
507                                 SysAssertf(found == false, "It's assumed that service doesn't have duplicated operation in tizen desktop file. But it isn't, so now we have to check this case.");
508                                 // replace operation.
509                                 if (found == false) // ( if duplicated operation is already exist, It will be keeped.
510                                 {
511                                         // update value
512                                         AppendServiceValueToString(resultString, newService);
513                                         SysLog(NID_APP, "opreration '%ls;%ls;%ls' will be updated to ;%ls;%ls", operation.GetPointer(), url.GetPointer(), mimeType.GetPointer(), newUrl.GetPointer(), mimeType.GetPointer());
514                                 }
515                         }
516                         found = true;
517                 }
518                 else
519                 {
520                         // add not specified service.
521                         AppendServiceValueToString(resultString, *pCurrentService);
522                 }
523
524                 delete pCurrentService;
525         }
526
527         if (found == false && isRemove == false)
528         {
529                 AppendServiceValueToString(resultString, newService);
530                 SysLog(NID_APP, "opreration '%ls;%ls;%ls' will be added", newOperation.GetPointer(), newUrl.GetPointer(), newMimeType.GetPointer());
531         }
532
533         SysLog(NID_APP, "updated string is '%ls'", resultString.GetPointer());
534         return _StringConverter::CopyToCharArrayN(resultString);
535 }
536
537
538 void
539 _Aul::_DesktopFile::AppendServiceValueToString(String& serviceString, const String& newVaue)
540 {
541         if (serviceString.GetLength() > 0)
542         {
543                 serviceString += ";";
544         }
545
546         serviceString += newVaue;
547 }
548
549
550 result
551 _Aul::_DesktopFile::ParseService(const String& service, String& operation, String& url, String& mimeType)
552 {
553         SysLog(NID_APP, "service(%ls)", service.GetPointer());
554
555         const String& serviceDetailPattern(L"([A-Za-z&=/\\.\\-]*):(.*://[A-Za-z&=/\\.\\-]*|[A-Za-z&=/\\.\\-]*):([A-Za-z&=/\\.\\-]*)");
556
557         Utility::RegularExpression regexDetail;
558         result r = regexDetail.Construct(serviceDetailPattern);
559         SysTryReturn(NID_APP, !IsFailed(r), null, r, "[%s] RegularExpression::Construct(L\"%ls\") failed.", GetErrorMessage(r), serviceDetailPattern.GetPointer());
560
561         ArrayList matchedItems;
562         matchedItems.Construct();
563         regexDetail.Match(service, true, &matchedItems);
564
565         int matchedCount = matchedItems.GetCount();
566         SysTryLog(NID_APP, matchedCount == 4, "It's assumed that x-slp-svc value always have operation:url:mime in tizen desktop file. But it isn't or our parser is invalid. so now we have to check this case. %d", matchedItems.GetCount());
567
568         if (matchedCount > 1)
569         {
570                 operation = *(String*) matchedItems.GetAt(1);
571         }
572
573         if (matchedCount > 2)
574         {
575                 url = *(String*) matchedItems.GetAt(2);
576         }
577
578         if (matchedCount > 3)
579         {
580                 mimeType = *(String*) matchedItems.GetAt(3);
581         }
582
583         SysLog(NID_APP, "matched(%d) : (%ls;%ls;%ls)", matchedItems.GetCount(), operation.GetPointer(), url.GetPointer(), mimeType.GetPointer());
584         matchedItems.RemoveAll(true);
585
586         return E_SUCCESS;
587 }
588
589 } } // Tizen::App