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