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