sync with tizen_2.0
[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
76 // borrowed from app-svc/include/pri_key.h
77 #define APP_SVC_K_RES_VAL       "__APP_SVC_K_RES_VAL__"
78
79 }
80
81 namespace Tizen { namespace App
82 {
83
84 struct _CategoryList
85 {
86         const char category[_MAX_CATEGORY];
87         _AppType type;
88 };
89
90 static const _CategoryList _CATEGORY_LIST[] =
91 {
92         {"home-screen", _APP_TYPE_HOME_APP},
93         {"lock-screen", _APP_TYPE_LOCK_APP},
94         {"ime", _APP_TYPE_IME_APP},
95 };
96
97 static const int _NUM_CATEGORY = sizeof(_CATEGORY_LIST) / sizeof(_CategoryList);
98
99
100 result
101 _Aul::SendResult(bundle* b, appsvc_result_val res)
102 {
103         result r = E_SUCCESS;
104
105         // to skip error handling, of appsvc_send_result, use aul_send_service_result() directly.
106         //int ret = appsvc_send_result(b, res);
107
108         char tmp[32] = {0, };
109         snprintf(tmp, 32, "%d", static_cast<int>(res));
110         appsvc_add_data(b, APP_SVC_K_RES_VAL, tmp);
111
112         int ret = aul_send_service_result(b);
113
114         switch (ret)
115         {
116         case AUL_R_EINVAL:
117                 r = E_INVALID_ARG;
118                 SysLogException(NID_APP, r, "Invalid result bundle.");
119                 break;
120
121         case AUL_R_ECOMM:
122                 r = E_SYSTEM;
123                 SysLogException(NID_APP, r, "Internal IPC error.");
124                 break;
125
126         case AUL_R_ERROR:
127                 r = E_SYSTEM;
128                 SysLogException(NID_APP, r, "General error.");
129                 break;
130
131         default:
132                 SysLog(NID_APP, "SendResult() ok");
133                 break;
134         }
135
136         return r;
137 }
138
139
140 bool
141 _Aul::IsRunning(const AppId& appId, const String& exeName)
142 {
143         char slpPackageName[MAX_SLP_PACKAGE_ID] = {0, };
144         bool isRunning = false;
145
146         _PackageManagerImpl* pPackageManagerImpl = _PackageManagerImpl::GetInstance();
147         SysTryReturn(NID_APP, pPackageManagerImpl != null, false, E_INVALID_STATE, "[E_INVALID_STATE] Invalid package instance.");
148
149         pPackageManagerImpl->GetPackageName(appId, &exeName, slpPackageName, MAX_SLP_PACKAGE_ID);
150
151         app_manager_is_running(slpPackageName, &isRunning);
152
153         SysLog(NID_APP, "'%s' %s running now.", slpPackageName, (isRunning == true) ? "is" : "is NOT");
154
155         return isRunning;
156 }
157
158 bool
159 _Aul::IsRunning(const String& packageName)
160 {
161         bool isRunning = false;
162         std::unique_ptr<char[]> pSlpPackageName(_StringConverter::CopyToCharArrayN(packageName));
163
164         app_manager_is_running(pSlpPackageName.get(), &isRunning);
165
166         SysLog(NID_APP, "'%ls' %s running now.", packageName.GetPointer(), (isRunning) ? "is" : "is NOT");
167         return isRunning;
168 }
169
170
171 void
172 _Aul::SetOnAppTerminatedCb(int (* pf_app_dead_handler)(int pid, void* pData), void* pData)
173 {
174         aul_listen_app_dead_signal(pf_app_dead_handler, pData);
175         SysLog(NID_APP, "'app_dead_handler is set.");
176 }
177
178
179 result
180 _Aul::TerminateApplicationByPid(int pid)
181 {
182         int ret_aul = aul_terminate_pid(pid);
183         result r = E_SUCCESS;
184
185         switch (ret_aul)
186         {
187         case AUL_R_EINVAL:
188                 r = E_INVALID_ARG;
189                 SysLogException(NID_APP, r, "invaild pid.\n");
190                 break;
191
192         case AUL_R_ECOMM:
193                 r = E_SYSTEM;
194                 SysLogException(NID_APP, r, "internal AUL IPC error.\n");
195                 break;
196
197         case AUL_R_ERROR:
198                 r = E_SYSTEM;
199                 SysLogException(NID_APP, r, "general error.\n");
200                 break;
201
202         default:
203                 SysLog(NID_APP, "'%d' is terminated.", pid);
204                 break;
205         }
206
207         return r;
208 }
209
210 result
211 _Aul::SetOomAdj(int pid, int adj)
212 {
213         // set oom_adj to -17 for system service
214         result r = E_SUCCESS;
215         char buf[FILENAME_MAX];
216         FILE *fP = NULL;
217
218         snprintf(buf, FILENAME_MAX, "/proc/%d/oom_adj", pid);
219         fP = fopen(buf, "w");
220         SysTryReturnResult(NID_APP, fP != NULL, E_SYSTEM, "oom_adj change failed with %s.", strerror(errno));
221
222         fprintf(fP, "%d", adj);
223         fclose(fP);
224
225         return r;
226 }
227
228 result
229 _Aul::SetPowerOffNotiListener( void (*powerOffCb)(void *pData), void *pData)
230 {
231         int heyFd = heynoti_init();
232         SysTryReturnResult(NID_APP, heyFd >= 0, E_SYSTEM, "heynoti_init failed.");
233
234         int ret = heynoti_subscribe(heyFd, "power_off_start", powerOffCb, pData);
235         SysTryReturnResult(NID_APP, ret >= 0, E_SYSTEM, "heynoti_subscribe failed.");
236
237         ret = heynoti_attach_handler(heyFd);
238         SysTryReturnResult(NID_APP, ret >= 0, E_SYSTEM, "heynoti_attach_handler failed.");
239
240         return E_SUCCESS;
241 }
242
243 int
244 _Aul::GetAppType(const String& category)
245 {
246         int ret = 0;
247
248         HashMapT<String, int> map;
249         map.Construct();
250
251         StringTokenizer strTok(category, L';');
252
253         String token;
254         while (strTok.HasMoreTokens())
255         {
256                 result r = strTok.GetNextToken(token);
257                 if (r == E_SUCCESS)
258                 {
259                         map.Add(token, 0);
260                 }
261         }
262
263         SysLog(NID_APP, "%d category items .", map.GetCount());
264
265         String key;
266
267         for (int i = 0; i < _NUM_CATEGORY; i++)
268         {
269                 bool b = false;
270                 key = _CATEGORY_LIST[i].category;
271                 result r = map.ContainsKey(key, b);
272                 if (r == E_SUCCESS && b)
273                 {
274                         ret |= _CATEGORY_LIST[i].type;
275                 }
276         }
277
278         return ret;
279 }
280
281 bool _Aul::IsInstalled(const AppId& appId)
282 {
283         String packageId;
284         packageId = _PackageManagerImpl::GetPackageIdByAppId(appId);
285
286         return _PackageManagerImpl::GetInstance()->IsPackageInstalled(packageId);
287 }
288
289 result
290 _Aul::_DesktopFile::MakePath(const AppId& appId, const String* pExeName, char* path, int size)
291 {
292         SysTryReturnResult(NID_APP, path != null, E_INVALID_ARG, "");
293
294         char packageName[MAX_SLP_PACKAGE_ID] = {0, };
295         result r = E_SUCCESS;
296         _PackageManagerImpl* pPackageManagerImpl = _PackageManagerImpl::GetInstance();
297         SysTryReturnResult(NID_APP, pPackageManagerImpl, E_INVALID_STATE, "Invalid package manager instance.");
298
299         r = pPackageManagerImpl->GetPackageName(appId, pExeName, packageName, MAX_SLP_PACKAGE_ID);
300         SysTryReturn(NID_APP, !IsFailed(r), r, r, "%s", GetErrorMessage(r));
301
302         snprintf(path, size, _DESKTOP_FILE_PATH_FORMAT, _DESKTOP_FILE_PATH, packageName);
303
304         return r;
305 }
306
307 result
308 _Aul::_DesktopFile::UpdateService(const AppId& appId, const char* value)
309 {
310         char path[FILENAME_MAX] = {0, };
311         MakePath(appId, null, path, FILENAME_MAX);
312
313         return UpdateField(path, _X_TIZEN_SVC, value);
314 }
315
316
317 result
318 _Aul::_DesktopFile::RemoveService(const AppId& appId, const char* operationOnlyValue)
319 {
320         char path[FILENAME_MAX] = {0, };
321         MakePath(appId, null, path, FILENAME_MAX);
322
323         return UpdateField(path, _X_TIZEN_SVC, operationOnlyValue, true);
324 }
325
326
327 //
328 // Update value of specified field.
329 // currently only "x-slp-svc" field is supported.
330 //
331 #define BUFFER_SIZE 1024
332 result
333 _Aul::_DesktopFile::UpdateField(const char* path, const char* fieldName, const char* value, bool isRemove)
334 {
335         SysTryReturnResult(NID_APP, path != null, E_INVALID_ARG, "path should not be null.");
336         SysTryReturnResult(NID_APP, fieldName != null, E_INVALID_ARG, "fieldName should not be null.");
337         SysTryReturnResult(NID_APP, value != null, E_INVALID_ARG, "value should not be null.");
338
339         FILE* fp = fopen(path, "r+");
340         SysTryReturnResult(NID_APP, fp != null, E_SYSTEM, "falied to open '%s' due to %s", path, strerror(errno));
341
342         char buffer[BUFFER_SIZE] = {0, };
343         bool found = false;
344         int len = 0;
345         int pos = 0;
346         int foundpos = 0;
347         result r = E_SUCCESS;
348         int remains = 0;
349
350         ArrayListT<char*> buffers;
351         buffers.Construct();
352
353         char* pCurrent = null;
354
355         while (fgets(buffer, BUFFER_SIZE, fp) != NULL)
356         {
357                 len = strlen(buffer);
358                 SysTryCatch(NID_APP, len < BUFFER_SIZE, , r = E_INVALID_ARG, "strlen returns invalid value. (%d)", len );
359
360                 if (found)
361                 {
362                         pCurrent = new (std::nothrow) char[len + 1];
363                         SysTryCatch(NID_APP, pCurrent != null, , r = E_OUT_OF_MEMORY, "failed to allocate mem pCurrent");
364
365                         strncpy(pCurrent, buffer, len);
366                         buffers.Add(pCurrent);
367                 }
368                 else
369                 {
370                         if (strncmp(buffer, fieldName, len) == 0)
371                         {
372                                 int fieldNameLen = strlen(fieldName);
373                                 SysTryCatch(NID_APP, len > fieldNameLen, , E_INVALID_ARG, "[E_INVALID_ARG] fieldName(%s)", fieldName);
374
375                                 pCurrent = UpdateServiceValueN(buffer + fieldNameLen, value, isRemove);
376                                 SysTryCatch(NID_APP, pCurrent != null, , r = GetLastResult(), "[%s] UpdateServiceValue failed", GetErrorMessage(GetLastResult()));
377
378                                 buffers.Add(pCurrent);
379
380                                 foundpos = pos;
381                                 found = true;
382                         }
383                 }
384
385                 pos += len;
386         }
387
388         if (found)
389         {
390                 fsetpos(fp, (fpos_t*) &foundpos);
391
392                 remains = buffers.GetCount();   // prevent infinite loop
393                 while (buffers.GetCount() > 0 && remains-- > 0)
394                 {
395                         pCurrent = null;
396                         buffers.GetAt(0, pCurrent);
397                         buffers.RemoveAt(0);
398                         SysTryCatch(NID_APP, pCurrent != null, , r = E_INVALID_STATE, "");
399
400                         fputs(pCurrent, fp);
401                         len = strlen(pCurrent);
402                         pos += len;
403                         delete[] pCurrent;
404                 }
405
406                 int ret = truncate(path, pos);
407                 SysTryLog(NID_APP, ret == 0, "Truncate failure (%s).", strerror(errno));
408         }
409         else
410         {
411                 char svctext[_MAX_TIZEN_SVC_DESC_LEN] = {0, };
412                 snprintf(svctext, _MAX_TIZEN_SVC_DESC_LEN, "%s=%s\n", fieldName, value);
413                 fputs(svctext, fp);
414         }
415         fclose(fp);
416
417         return E_SUCCESS;
418
419 CATCH:
420
421         remains = buffers.GetCount();   // prevent infinite loop
422         while (buffers.GetCount() > 0 && remains-- > 0)
423         {
424                 pCurrent = null;
425                 buffers.GetAt(0, pCurrent);
426                 buffers.RemoveAt(0);
427                 if (pCurrent != null)
428                 {
429                         delete[] pCurrent;
430                 }
431         }
432
433         fclose(fp);
434
435         return r;
436 }
437
438 //
439 //      Tizen service string example
440 //      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
441 //
442 char*
443 _Aul::_DesktopFile::UpdateServiceValueN(char* buffer, const char* newValue, bool isRemove)
444 {
445         SysTryReturn(NID_APP, buffer != null, null, E_INVALID_ARG, "");
446         SysTryReturn(NID_APP, newValue != null, null, E_INVALID_ARG, "");
447
448         SysLog(NID_APP, "current(%s), new(%s), isRemove(%s)", buffer, newValue, (isRemove) ? "true" : "false");
449
450         String buf(buffer);
451         bool found = false;
452
453         const String& servicePattern(L"([A-Za-z&=:/\\.\\-]*);?");
454
455         ArrayList services;
456         String resultString;
457
458         Utility::RegularExpression regex;
459         result r = regex.Construct(servicePattern);
460         SysTryReturn(NID_APP, !IsFailed(r), null, r, "");
461
462         String newOperation;
463         String newUrl;
464         String newMimeType;
465         String newService(newValue);
466
467         if (isRemove == false)
468         {
469                 ParseService(newService, newOperation, newUrl, newMimeType);
470         }
471         else
472         {
473                 newOperation = newValue;
474         }
475
476         services.Construct();
477
478         while (regex.Consume(buf, &services) == true)
479         {
480                 String* pCurrentService = (String*) services.GetAt(1);
481                 services.RemoveAll(false);
482
483                 String operation;
484                 String url;
485                 String mimeType;
486
487                 ParseService(*pCurrentService, operation, url, mimeType);
488
489                 if (operation == newOperation)
490                 {
491                         if (isRemove == true)
492                         {
493                                 SysLog(NID_APP, "opreration '%ls' will be removed", operation.GetPointer());
494                         }
495                         else
496                         {
497                                 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.");
498                                 // replace operation.
499                                 if (found == false) // ( if duplicated operation is already exist, It will be keeped.
500                                 {
501                                         // update value
502                                         AppendServiceValueToString(resultString, newService);
503                                         SysLog(NID_APP, "opreration '%ls;%ls;%ls' will be updated to ;%ls;%ls", operation.GetPointer(), url.GetPointer(), mimeType.GetPointer(), newUrl.GetPointer(), mimeType.GetPointer());
504                                 }
505                         }
506                         found = true;
507                 }
508                 else
509                 {
510                         // add not specified service.
511                         AppendServiceValueToString(resultString, *pCurrentService);
512                 }
513
514                 delete pCurrentService;
515         }
516
517         if (found == false && isRemove == false)
518         {
519                 AppendServiceValueToString(resultString, newService);
520                 SysLog(NID_APP, "opreration '%ls;%ls;%ls' will be added", newOperation.GetPointer(), newUrl.GetPointer(), newMimeType.GetPointer());
521         }
522
523         SysLog(NID_APP, "updated string is '%ls'", resultString.GetPointer());
524         return _StringConverter::CopyToCharArrayN(resultString);
525 }
526
527
528 void
529 _Aul::_DesktopFile::AppendServiceValueToString(String& serviceString, const String& newVaue)
530 {
531         if (serviceString.GetLength() > 0)
532         {
533                 serviceString += ";";
534         }
535
536         serviceString += newVaue;
537 }
538
539
540 result
541 _Aul::_DesktopFile::ParseService(const String& service, String& operation, String& url, String& mimeType)
542 {
543         SysLog(NID_APP, "service(%ls)", service.GetPointer());
544
545         const String& serviceDetailPattern(L"([A-Za-z&=/\\.\\-]*):(.*://[A-Za-z&=/\\.\\-]*|[A-Za-z&=/\\.\\-]*):([A-Za-z&=/\\.\\-]*)");
546
547         Utility::RegularExpression regexDetail;
548         result r = regexDetail.Construct(serviceDetailPattern);
549         SysTryReturn(NID_APP, !IsFailed(r), null, r, "[%s] RegularExpression::Construct(L\"%ls\") failed.", GetErrorMessage(r), serviceDetailPattern.GetPointer());
550
551         ArrayList matchedItems;
552         matchedItems.Construct();
553         regexDetail.Match(service, true, &matchedItems);
554
555         int matchedCount = matchedItems.GetCount();
556         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());
557
558         if (matchedCount > 1)
559         {
560                 operation = *(String*) matchedItems.GetAt(1);
561         }
562
563         if (matchedCount > 2)
564         {
565                 url = *(String*) matchedItems.GetAt(2);
566         }
567
568         if (matchedCount > 3)
569         {
570                 mimeType = *(String*) matchedItems.GetAt(3);
571         }
572
573         SysLog(NID_APP, "matched(%d) : (%ls;%ls;%ls)", matchedItems.GetCount(), operation.GetPointer(), url.GetPointer(), mimeType.GetPointer());
574         matchedItems.RemoveAll(true);
575
576         return E_SUCCESS;
577 }
578
579 } } // Tizen::App