Merge "Fix SystemInfo cache" into tizen_2.1
[platform/framework/native/appfw.git] / src / server / app / FApp_AulServer.cpp
1 //
2 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
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 /**
18  * @file         FApp_AulServer.cpp
19  * @brief       This is the implementation for the _AulServer.cpp class.
20  */
21 #include <cstdio>
22 #include <cstdlib>
23 #include <new>
24 #include <unistd.h>
25 #include <sys/prctl.h>
26 #include <signal.h>
27 #include <unique_ptr.h>
28
29 #include <aul.h>
30 #include <bundle.h>
31 #include <appsvc/appsvc.h>
32 #include <app_manager.h>
33 #include <heynoti.h>
34
35 #include <FBaseObject.h>
36 #include <FBaseString.h>
37 #include <FBaseUtil.h>
38 #include <FBaseSysLog.h>
39 #include <FBaseColHashMapT.h>
40 #include <FAppPkgPackageInfo.h>
41
42 #include <FBaseRt_Process.h>
43 #include <FBase_StringConverter.h>
44 #include "FAppPkg_PackageManagerImpl.h"
45 #include "FApp_Types.h"
46 #include "FApp_AulServer.h"
47 #include "FApp_TemplateUtil.h"
48
49 #ifdef __cplusplus
50 extern "C" {
51 #endif
52 extern int aul_listen_app_dead_signal(int (* func)(int, void*), void* data);
53 #ifdef __cplusplus
54 }
55 #endif
56
57 using namespace Tizen::App::Package;
58 using namespace Tizen::Base;
59 using namespace Tizen::Base::Collection;
60 using namespace Tizen::Base::Runtime;
61 using namespace Tizen::Base::Utility;
62
63 namespace
64 {
65
66 const char _DESKTOP_FILE_PATH[] = "/opt/share/applications";
67 const char _DESKTOP_FILE_PATH_FORMAT[] = "%s/%s.desktop";
68
69 const char _X_TIZEN_SVC[] = "x-tizen-svc"; //X-TIZEN-SVC=[operation1] | [URI1] | [MIME1] ; [operation2] | [URI2] | [MIME2]
70 const int _MAX_TIZEN_SVC_DESC_LEN = 1024;
71
72 const int _MAX_CATEGORY = 12;
73 const int _MAX_PACKAGE_ID_LENGTH = 10;
74
75 const char _APP_PATH_FORMAT[] = "/opt/usr/apps/0000000000/bin/%ls";
76 const char _APP_PATH_FORMAT2[] = "/opt/apps/0000000000/bin/%ls";
77 const char PATH_ROOT[] = "/opt/usr/apps/";
78 const char PATH_ROOT2[] = "/opt/apps/";
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
101 result
102 _AulServer::SendResult(bundle* b, appsvc_result_val res)
103 {
104         result r = E_SUCCESS;
105
106         int ret = appsvc_send_result(b, res);
107
108         switch (ret)
109         {
110         case AUL_R_EINVAL:
111                 r = E_INVALID_ARG;
112                 SysLogException(NID_APP, r, "Invalid result bundle.");
113                 break;
114
115         case AUL_R_ECOMM:
116                 r = E_SYSTEM;
117                 SysLogException(NID_APP, r, "Internal IPC error.");
118                 break;
119
120         case AUL_R_ERROR:
121                 r = E_SYSTEM;
122                 SysLogException(NID_APP, r, "General error.");
123                 break;
124
125         default:
126                 break;
127         }
128
129         SysLog(NID_APP, "SendResult() ok");
130
131         return r;
132 }
133
134
135 bool
136 _AulServer::IsRunning(const AppId& appId, const String& exeName)
137 {
138         char slpPackageName[MAX_SLP_PACKAGE_ID] = {0, };
139         bool isRunning = false;
140
141         _PackageManagerImpl* pPackageManagerImpl = _PackageManagerImpl::GetInstance();
142         SysTryReturn(NID_APP, pPackageManagerImpl != null, false, E_INVALID_STATE, "[E_INVALID_STATE] Invalid package instance.");
143
144         pPackageManagerImpl->GetPackageName(appId, &exeName, slpPackageName, MAX_SLP_PACKAGE_ID);
145
146         app_manager_is_running(slpPackageName, &isRunning);
147
148         SysLog(NID_APP, "'%s' %s running now.", slpPackageName, (isRunning == true) ? "is" : "is NOT");
149
150         return isRunning;
151 }
152
153 bool
154 _AulServer::IsRunning(const String& packageName)
155 {
156         bool isRunning = false;
157         std::unique_ptr<char[]> pSlpPackageName(_StringConverter::CopyToCharArrayN(packageName));
158
159         app_manager_is_running(pSlpPackageName.get(), &isRunning);
160
161         SysLog(NID_APP, "'%ls' %s running now.", packageName.GetPointer(), (isRunning) ? "is" : "is NOT");
162         return isRunning;
163 }
164
165
166 void
167 _AulServer::SetOnAppTerminatedCb(int (* pf_app_dead_handler)(int pid, void* pData), void* pData)
168 {
169         aul_listen_app_dead_signal(pf_app_dead_handler, pData);
170         SysLog(NID_APP, "'app_dead_handler is set.");
171 }
172
173
174 result
175 _AulServer::TerminateApplicationByPid(int pid)
176 {
177         int ret_aul = aul_terminate_pid(pid);
178         result r = E_SUCCESS;
179
180         switch (ret_aul)
181         {
182         case AUL_R_EINVAL:
183                 r = E_INVALID_ARG;
184                 SysLogException(NID_APP, r, "invaild pid.\n");
185                 break;
186
187         case AUL_R_ECOMM:
188                 r = E_SYSTEM;
189                 SysLogException(NID_APP, r, "internal AUL IPC error.\n");
190                 break;
191
192         case AUL_R_ERROR:
193                 r = E_SYSTEM;
194                 SysLogException(NID_APP, r, "general error.\n");
195                 break;
196
197         default:
198                 SysLog(NID_APP, "'%d' is terminated.", pid);
199                 break;
200         }
201
202         return r;
203 }
204
205 result
206 _AulServer::SetOomAdj(int pid, int adj)
207 {
208         // set oom_adj to -17 for system service
209         result r = E_SUCCESS;
210         char buf[FILENAME_MAX];
211         FILE *fP = NULL;
212
213         snprintf(buf, FILENAME_MAX, "/proc/%d/oom_adj", pid);
214         fP = fopen(buf, "w");
215         SysTryReturnResult(NID_APP, fP != NULL, E_SYSTEM, "oom_adj change failed with %s.", strerror(errno));
216
217         fprintf(fP, "%d", adj);
218         fclose(fP);
219
220         return r;
221 }
222
223 result
224 _AulServer::SetPowerOffNotiListener( void (*powerOffCb)(void *pData), void *pData)
225 {
226         int heyFd = heynoti_init();
227         SysTryReturnResult(NID_APP, heyFd >= 0, E_SYSTEM, "heynoti_init failed.");
228
229         int ret = heynoti_subscribe(heyFd, "power_off_start", powerOffCb, pData);
230         SysTryReturnResult(NID_APP, ret >= 0, E_SYSTEM, "heynoti_subscribe failed.");
231
232         ret = heynoti_attach_handler(heyFd);
233         SysTryReturnResult(NID_APP, ret >= 0, E_SYSTEM, "heynoti_attach_handler failed.");
234
235         return E_SUCCESS;
236 }
237
238 int
239 _AulServer::GetAppType(const String& category)
240 {
241         int ret = 0;
242
243         HashMapT<String, int> map;
244         map.Construct();
245
246         StringTokenizer strTok(category, L';');
247
248         String token;
249         while (strTok.HasMoreTokens())
250         {
251                 result r = strTok.GetNextToken(token);
252                 if (r == E_SUCCESS)
253                 {
254                         map.Add(token, 0);
255                 }
256         }
257
258         SysLog(NID_APP, "%d category items .", map.GetCount());
259
260         String key;
261
262         for (int i = 0; i < _NUM_CATEGORY; i++)
263         {
264                 bool b = false;
265                 key = _CATEGORY_LIST[i].category;
266                 result r = map.ContainsKey(key, b);
267                 if (r == E_SUCCESS && b)
268                 {
269                         ret |= _CATEGORY_LIST[i].type;
270                 }
271         }
272
273         return ret;
274 }
275
276 bool _AulServer::IsInstalled(const AppId& appId)
277 {
278         String packageId;
279         packageId = _PackageManagerImpl::GetPackageIdByAppId(appId);
280
281         return _PackageManagerImpl::GetInstance()->IsPackageInstalled(packageId);
282 }
283
284 int _AulServer::CreateProcess(const AppId& appId)
285 {
286         SysTryReturn(NID_APP, appId.GetLength() > 10 && appId[10] == L'.', -1, E_INVALID_ARG, "[E_INVALID_ARG] Wrong appId %ls.", appId.GetPointer());
287
288         int pid = fork();
289
290         if (pid == -1)
291         {
292                 return -1;
293         }
294         else if (pid == 0)
295         {
296                 char path[FILENAME_MAX];
297                 memset(path, '\0', FILENAME_MAX);
298
299 #if 0
300                 snprintf(path, FILENAME_MAX, _APP_PATH_FORMAT2, appId.GetPointer() + 11);
301
302                 int ret = wcstombs(path + strlen(PATH_ROOT2), appId.GetPointer(), 10);
303                 if (ret == -1)
304                 {
305                         SysLogException(NID_APP, E_SYSTEM, "Launching service (%ls)(%s) failed with [%s].", appId.GetPointer(), path, strerror(errno));
306                         _Process::Exit(1);
307                 }
308 #else
309                 snprintf(path, FILENAME_MAX, _APP_PATH_FORMAT2, appId.GetPointer() + 11);
310
311                 int ret = wcstombs(path + strlen(PATH_ROOT2), appId.GetPointer(), 10);
312                 if (ret == -1)
313                 {
314                         SysLogException(NID_APP, E_SYSTEM, "Launching service (%ls)(%s) failed with [%s].", appId.GetPointer(), path, strerror(errno));
315                         _Process::Exit(1);
316                 }
317
318                 if (euidaccess(path, R_OK) != 0)
319                 {
320                         snprintf(path, FILENAME_MAX, _APP_PATH_FORMAT, appId.GetPointer() + 11);
321
322                         ret = wcstombs(path + strlen(PATH_ROOT), appId.GetPointer(), 10);
323                         if (ret == -1)
324                         {
325                                 SysLogException(NID_APP, E_SYSTEM, "Launching service (%ls)(%s) failed with [%s].", appId.GetPointer(), path, strerror(errno));
326                                 _Process::Exit(1);
327                         }
328                 }
329 #endif
330
331                 SysLog(NID_APP, "Launching %s.", path);
332
333                 int currentPid = getpid();
334
335                 SetOomAdj(currentPid, -17); // set oom_adj to -17 for system service
336
337                 prctl(PR_SET_PDEATHSIG, SIGTERM);
338
339                 setpgid(currentPid, currentPid);
340
341                 ret = execl(path, path, static_cast<char*>(NULL));
342                 if (ret < 0)
343                 {
344                         SysLogException(NID_APP, E_SYSTEM, "Launching service (%ls)(%s) failed with [%s].", appId.GetPointer(), path, strerror(errno));
345                         _Process::Exit(1);
346                 }
347         }
348
349         return pid;
350
351 }
352
353 result
354 _AulServer::_DesktopFile::MakePath(const AppId& appId, const String* pExeName, char* path, int size)
355 {
356         SysTryReturnResult(NID_APP, path != null, E_INVALID_ARG, "");
357
358         char packageName[MAX_SLP_PACKAGE_ID] = {0, };
359         result r = E_SUCCESS;
360         _PackageManagerImpl* pPackageManagerImpl = _PackageManagerImpl::GetInstance();
361         SysTryReturnResult(NID_APP, pPackageManagerImpl, E_INVALID_STATE, "Invalid package manager instance.");
362
363         r = pPackageManagerImpl->GetPackageName(appId, pExeName, packageName, MAX_SLP_PACKAGE_ID);
364         SysTryReturn(NID_APP, !IsFailed(r), r, r, "%s", GetErrorMessage(r));
365
366         snprintf(path, size, _DESKTOP_FILE_PATH_FORMAT, _DESKTOP_FILE_PATH, packageName);
367
368         return r;
369 }
370
371 result
372 _AulServer::_DesktopFile::UpdateService(const AppId& appId, const char* value)
373 {
374         char path[FILENAME_MAX] = {0, };
375         MakePath(appId, null, path, FILENAME_MAX);
376
377         return UpdateField(path, _X_TIZEN_SVC, value);
378 }
379
380
381 result
382 _AulServer::_DesktopFile::RemoveService(const AppId& appId, const char* operationOnlyValue)
383 {
384         char path[FILENAME_MAX] = {0, };
385         MakePath(appId, null, path, FILENAME_MAX);
386
387         return UpdateField(path, _X_TIZEN_SVC, operationOnlyValue, true);
388 }
389
390
391 //
392 // Update value of specified field.
393 // currently only "x-slp-svc" field is supported.
394 //
395 #define BUFFER_SIZE 1024
396 result
397 _AulServer::_DesktopFile::UpdateField(const char* path, const char* fieldName, const char* value, bool isRemove)
398 {
399         SysTryReturnResult(NID_APP, path != null, E_INVALID_ARG, "path should not be null.");
400         SysTryReturnResult(NID_APP, fieldName != null, E_INVALID_ARG, "fieldName should not be null.");
401         SysTryReturnResult(NID_APP, value != null, E_INVALID_ARG, "value should not be null.");
402
403         FILE* fp = fopen(path, "r+");
404         SysTryReturnResult(NID_APP, fp != null, E_SYSTEM, "falied to open '%s' due to %s", path, strerror(errno));
405
406         char buffer[BUFFER_SIZE] = {0, };
407         bool found = false;
408         int len = 0;
409         int pos = 0;
410         int foundpos = 0;
411         result r = E_SUCCESS;
412         int remains = 0;
413
414         ArrayListT<char*> buffers;
415         buffers.Construct();
416
417         char* pCurrent = null;
418
419         while (fgets(buffer, BUFFER_SIZE, fp) != NULL)
420         {
421                 len = strlen(buffer);
422                 SysTryCatch(NID_APP, len < BUFFER_SIZE, , r = E_INVALID_ARG, "strlen returns invalid value. (%d)", len );
423
424                 if (found)
425                 {
426                         pCurrent = new (std::nothrow) char[len + 1];
427                         SysTryCatch(NID_APP, pCurrent != null, , r = E_OUT_OF_MEMORY, "failed to allocate mem pCurrent");
428
429                         strncpy(pCurrent, buffer, len);
430                         buffers.Add(pCurrent);
431                 }
432                 else
433                 {
434                         if (strncmp(buffer, fieldName, len) == 0)
435                         {
436                                 int fieldNameLen = strlen(fieldName);
437                                 SysTryCatch(NID_APP, len > fieldNameLen, , E_INVALID_ARG, "[E_INVALID_ARG] fieldName(%s)", fieldName);
438
439                                 pCurrent = UpdateServiceValueN(buffer + fieldNameLen, value, isRemove);
440                                 SysTryCatch(NID_APP, pCurrent != null, , r = GetLastResult(), "[%s] UpdateServiceValue failed", GetErrorMessage(GetLastResult()));
441
442                                 buffers.Add(pCurrent);
443
444                                 foundpos = pos;
445                                 found = true;
446                         }
447                 }
448
449                 pos += len;
450         }
451
452         if (found)
453         {
454                 fsetpos(fp, (fpos_t*) &foundpos);
455
456                 remains = buffers.GetCount();   // prevent infinite loop
457                 while (buffers.GetCount() > 0 && remains-- > 0)
458                 {
459                         pCurrent = null;
460                         buffers.GetAt(0, pCurrent);
461                         buffers.RemoveAt(0);
462                         SysTryCatch(NID_APP, pCurrent != null, , r = E_INVALID_STATE, "");
463
464                         fputs(pCurrent, fp);
465                         len = strlen(pCurrent);
466                         pos += len;
467                         delete[] pCurrent;
468                 }
469
470                 int ret = truncate(path, pos);
471                 SysTryLog(NID_APP, ret == 0, "Truncate failure (%s).", strerror(errno));
472         }
473         else
474         {
475                 char svctext[_MAX_TIZEN_SVC_DESC_LEN] = {0, };
476                 snprintf(svctext, _MAX_TIZEN_SVC_DESC_LEN, "%s=%s\n", fieldName, value);
477                 fputs(svctext, fp);
478         }
479         fclose(fp);
480
481         return E_SUCCESS;
482
483 CATCH:
484
485         remains = buffers.GetCount();   // prevent infinite loop
486         while (buffers.GetCount() > 0 && remains-- > 0)
487         {
488                 pCurrent = null;
489                 buffers.GetAt(0, pCurrent);
490                 buffers.RemoveAt(0);
491                 if (pCurrent != null)
492                 {
493                         delete[] pCurrent;
494                 }
495         }
496
497         fclose(fp);
498
499         return r;
500 }
501
502 //
503 //      Tizen service string example
504 //      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
505 //
506 char*
507 _AulServer::_DesktopFile::UpdateServiceValueN(char* buffer, const char* newValue, bool isRemove)
508 {
509         SysTryReturn(NID_APP, buffer != null, null, E_INVALID_ARG, "");
510         SysTryReturn(NID_APP, newValue != null, null, E_INVALID_ARG, "");
511
512         SysLog(NID_APP, "current(%s), new(%s), isRemove(%s)", buffer, newValue, (isRemove) ? "true" : "false");
513
514         String buf(buffer);
515         bool found = false;
516
517         const String& servicePattern(L"([A-Za-z&=:/\\.\\-]*);?");
518
519         ArrayList services;
520         String resultString;
521
522         Utility::RegularExpression regex;
523         result r = regex.Construct(servicePattern);
524         SysTryReturn(NID_APP, !IsFailed(r), null, r, "");
525
526         String newOperation;
527         String newUrl;
528         String newMimeType;
529         String newService(newValue);
530
531         if (isRemove == false)
532         {
533                 ParseService(newService, newOperation, newUrl, newMimeType);
534         }
535         else
536         {
537                 newOperation = newValue;
538         }
539
540         services.Construct();
541
542         while (regex.Consume(buf, &services) == true)
543         {
544                 String* pCurrentService = (String*) services.GetAt(1);
545                 services.RemoveAll(false);
546
547                 String operation;
548                 String url;
549                 String mimeType;
550
551                 ParseService(*pCurrentService, operation, url, mimeType);
552
553                 if (operation == newOperation)
554                 {
555                         if (isRemove == true)
556                         {
557                                 SysLog(NID_APP, "opreration '%ls' will be removed", operation.GetPointer());
558                         }
559                         else
560                         {
561                                 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.");
562                                 // replace operation.
563                                 if (found == false) // ( if duplicated operation is already exist, It will be keeped.
564                                 {
565                                         // update value
566                                         AppendServiceValueToString(resultString, newService);
567                                         SysLog(NID_APP, "opreration '%ls;%ls;%ls' will be updated to ;%ls;%ls", operation.GetPointer(), url.GetPointer(), mimeType.GetPointer(), newUrl.GetPointer(), mimeType.GetPointer());
568                                 }
569                         }
570                         found = true;
571                 }
572                 else
573                 {
574                         // add not specified service.
575                         AppendServiceValueToString(resultString, *pCurrentService);
576                 }
577
578                 delete pCurrentService;
579         }
580
581         if (found == false && isRemove == false)
582         {
583                 AppendServiceValueToString(resultString, newService);
584                 SysLog(NID_APP, "opreration '%ls;%ls;%ls' will be added", newOperation.GetPointer(), newUrl.GetPointer(), newMimeType.GetPointer());
585         }
586
587         SysLog(NID_APP, "updated string is '%ls'", resultString.GetPointer());
588         return _StringConverter::CopyToCharArrayN(resultString);
589 }
590
591
592 void
593 _AulServer::_DesktopFile::AppendServiceValueToString(String& serviceString, const String& newVaue)
594 {
595         if (serviceString.GetLength() > 0)
596         {
597                 serviceString += ";";
598         }
599
600         serviceString += newVaue;
601 }
602
603
604 result
605 _AulServer::_DesktopFile::ParseService(const String& service, String& operation, String& url, String& mimeType)
606 {
607         SysLog(NID_APP, "service(%ls)", service.GetPointer());
608
609         const String& serviceDetailPattern(L"([A-Za-z&=/\\.\\-]*):(.*://[A-Za-z&=/\\.\\-]*|[A-Za-z&=/\\.\\-]*):([A-Za-z&=/\\.\\-]*)");
610
611         Utility::RegularExpression regexDetail;
612         result r = regexDetail.Construct(serviceDetailPattern);
613         SysTryReturn(NID_APP, !IsFailed(r), null, r, "[%s] RegularExpression::Construct(L\"%ls\") failed.", GetErrorMessage(r), serviceDetailPattern.GetPointer());
614
615         ArrayList matchedItems;
616         matchedItems.Construct();
617         regexDetail.Match(service, true, &matchedItems);
618
619         int matchedCount = matchedItems.GetCount();
620         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());
621
622         if (matchedCount > 1)
623         {
624                 operation = *(String*) matchedItems.GetAt(1);
625         }
626
627         if (matchedCount > 2)
628         {
629                 url = *(String*) matchedItems.GetAt(2);
630         }
631
632         if (matchedCount > 3)
633         {
634                 mimeType = *(String*) matchedItems.GetAt(3);
635         }
636
637         SysLog(NID_APP, "matched(%d) : (%ls;%ls;%ls)", matchedItems.GetCount(), operation.GetPointer(), url.GetPointer(), mimeType.GetPointer());
638         matchedItems.RemoveAll(true);
639
640         return E_SUCCESS;
641 }
642
643 } } // Tizen::App