c74eebffd8931a2fa7f7285d021e0c5f642f04ab
[platform/framework/web/crosswalk.git] / src / xwalk / application / tools / tizen / xwalk_package_installer.cc
1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "xwalk/application/tools/tizen/xwalk_package_installer.h"
7
8 #include <sys/types.h>
9 #include <pwd.h>
10 #include <ss_manager.h>
11 #include <unistd.h>
12 #include <pkgmgr/pkgmgr_parser.h>
13
14 #include <algorithm>
15 #include <cctype>
16 #include <map>
17 #include <string>
18
19 #include "base/file_util.h"
20 #include "base/files/file_enumerator.h"
21 #include "base/logging.h"
22 #include "base/path_service.h"
23 #include "base/version.h"
24 #include "crypto/symmetric_key.h"
25 #include "third_party/libxml/chromium/libxml_utils.h"
26 #include "xwalk/application/common/application_data.h"
27 #include "xwalk/application/common/application_file_util.h"
28 #include "xwalk/application/common/application_manifest_constants.h"
29 #include "xwalk/application/common/id_util.h"
30 #include "xwalk/application/common/manifest_handlers/tizen_application_handler.h"
31 #include "xwalk/application/common/manifest_handlers/tizen_metadata_handler.h"
32 #include "xwalk/application/common/manifest_handlers/tizen_setting_handler.h"
33 #include "xwalk/application/common/permission_policy_manager.h"
34 #include "xwalk/application/common/tizen/application_storage.h"
35 #include "xwalk/application/common/tizen/encryption.h"
36 #include "xwalk/application/tools/tizen/xwalk_packageinfo_constants.h"
37 #include "xwalk/application/tools/tizen/xwalk_platform_installer.h"
38 #include "xwalk/runtime/common/xwalk_paths.h"
39
40 namespace info = application_packageinfo_constants;
41
42 using xwalk::application::ApplicationData;
43 using xwalk::application::ApplicationStorage;
44 using xwalk::application::FileDeleter;
45 using xwalk::application::Manifest;
46 using xwalk::application::Package;
47
48 namespace {
49
50 const base::FilePath::CharType kApplicationsDir[] =
51     FILE_PATH_LITERAL("applications");
52
53 const base::FilePath::CharType kInstallTempDir[] =
54     FILE_PATH_LITERAL("install_temp");
55
56 const base::FilePath::CharType kUpdateTempDir[] =
57     FILE_PATH_LITERAL("update_temp");
58
59 namespace widget_keys = xwalk::application_widget_keys;
60
61 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
62
63 const base::FilePath kDefaultIcon(
64     "/usr/share/icons/default/small/crosswalk.png");
65
66 const std::string kServicePrefix("xwalk-service.");
67 const std::string kAppIdPrefix("xwalk.");
68
69 bool CopyDirectoryContents(const base::FilePath& from,
70     const base::FilePath& to) {
71   base::FileEnumerator iter(from, false,
72       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
73   for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
74     if (iter.GetInfo().IsDirectory()) {
75       if (!base::CopyDirectory(path, to, true))
76         return false;
77     } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
78         return false;
79     }
80   }
81
82   return true;
83 }
84
85 void WriteMetaDataElement(
86     XmlWriter& writer, // NOLINT
87     xwalk::application::TizenMetaDataInfo* info) {
88   if (!info)
89     return;
90
91   const std::map<std::string, std::string>& metadata = info->metadata();
92   std::map<std::string, std::string>::const_iterator it;
93   for (it = metadata.begin(); it != metadata.end(); ++it) {
94     writer.StartElement("metadata");
95     writer.AddAttribute("key", it->first);
96     writer.AddAttribute("value", it->second);
97     writer.EndElement();
98   }
99 }
100
101 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
102                         const std::string& icon_name,
103                         const base::FilePath& app_dir,
104                         const base::FilePath& xml_path) {
105   if (!base::PathExists(app_dir) &&
106       !base::CreateDirectory(app_dir))
107     return false;
108
109   std::string package_id =
110       xwalk::application::AppIdToPkgId(application->ID());
111
112   base::FilePath execute_path =
113       app_dir.AppendASCII("bin/").AppendASCII(application->ID());
114   std::string stripped_name = application->Name();
115
116   FILE* file = base::OpenFile(xml_path, "w");
117
118   XmlWriter xml_writer;
119   xml_writer.StartWriting();
120   xml_writer.StartElement("manifest");
121   xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
122   xml_writer.AddAttribute("package", package_id);
123   xml_writer.AddAttribute("type", "wgt");
124   xml_writer.AddAttribute("version", application->VersionString());
125   xml_writer.WriteElement("label", application->Name());
126   xml_writer.WriteElement("description", application->Description());
127
128   xml_writer.StartElement("ui-application");
129   xml_writer.AddAttribute("appid", application->ID());
130   xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
131   xml_writer.AddAttribute("type", "webapp");
132   xml_writer.AddAttribute("taskmanage", "true");
133   xml_writer.WriteElement("label", application->Name());
134
135   xwalk::application::TizenMetaDataInfo* info =
136       static_cast<xwalk::application::TizenMetaDataInfo*>(
137       application->GetManifestData(widget_keys::kTizenMetaDataKey));
138   WriteMetaDataElement(xml_writer, info);
139
140   if (icon_name.empty())
141     xml_writer.WriteElement("icon", info::kDefaultIconName);
142   else
143     xml_writer.WriteElement("icon",
144                             kServicePrefix + application->ID() + ".png");
145   xml_writer.EndElement();  // Ends "ui-application"
146
147   xml_writer.EndElement();  // Ends "manifest" element.
148   xml_writer.StopWriting();
149
150   base::WriteFile(xml_path,
151                   xml_writer.GetWrittenString().c_str(),
152                   xml_writer.GetWrittenString().size());
153
154   base::CloseFile(file);
155   LOG(INFO) << "Converting manifest.json into "
156             << xml_path.BaseName().MaybeAsASCII()
157             << " for installation. [DONE]";
158   return true;
159 }
160
161 bool CreateAppSymbolicLink(const base::FilePath& app_dir,
162                            const std::string& app_id) {
163   base::FilePath execute_path =
164       app_dir.AppendASCII("bin/").AppendASCII(app_id);
165
166   if (!base::CreateDirectory(execute_path.DirName())) {
167     LOG(ERROR) << "Could not create directory '"
168                << execute_path.DirName().value() << "'.";
169     return false;
170   }
171
172   if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
173     LOG(ERROR) << "Could not create symbolic link to launcher from '"
174                << execute_path.value() << "'.";
175     return false;
176   }
177   return true;
178 }
179
180 }  // namespace
181
182 PackageInstaller::PackageInstaller(ApplicationStorage* storage)
183   : storage_(storage),
184     quiet_(false) {
185 }
186
187 PackageInstaller::~PackageInstaller() {
188 }
189
190 scoped_ptr<PackageInstaller> PackageInstaller::Create(
191     ApplicationStorage* storage) {
192   return scoped_ptr<PackageInstaller>(new PackageInstaller(storage));
193 }
194
195 void PackageInstaller::SetQuiet(bool quiet) {
196   quiet_ = quiet;
197 }
198
199 void PackageInstaller::SetInstallationKey(const std::string& key) {
200   key_ = key;
201 }
202
203 std::string PackageInstaller::PrepareUninstallationID(
204     const std::string& id) {
205   // this function fix pkg_id to app_id
206   // if installer was launched with pkg_id
207   if (xwalk::application::IsValidPkgID(id)) {
208     LOG(INFO) << "The package id is given " << id << " Will find app_id...";
209     std::string appid = xwalk::application::PkgIdToAppId(id);
210     if (!appid.empty())
211       return appid;
212   }
213   return id;
214 }
215
216 bool PackageInstaller::PlatformInstall(ApplicationData* app_data) {
217   std::string app_id(app_data->ID());
218   base::FilePath data_dir;
219   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
220
221   base::FilePath app_dir =
222       data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
223   base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
224       app_id + std::string(info::kXmlExtension));
225
226   std::string icon_name;
227   if (!app_data->GetManifest()->GetString(
228       GetIcon128Key(app_data->manifest_type()), &icon_name))
229     LOG(WARNING) << "'icon' not included in manifest";
230
231   // This will clean everything inside '<data dir>/<app id>'.
232   FileDeleter app_dir_cleaner(app_dir, true);
233
234   if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, xml_path)) {
235     LOG(ERROR) << "Failed to create XML metadata file '"
236                << xml_path.value() << "'.";
237     return false;
238   }
239
240   if (!CreateAppSymbolicLink(app_dir, app_id)) {
241     LOG(ERROR) << "Failed to create symbolic link for " << app_id;
242     return false;
243   }
244
245   base::FilePath icon =
246       icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
247
248   // args for pkgmgr
249   const char* pkgmgr_argv[5];
250   pkgmgr_argv[2] = "-k";
251   pkgmgr_argv[3] = key_.c_str();
252   pkgmgr_argv[4] = "-q";
253
254   PlatformInstaller platform_installer(app_id);
255
256   if (xml_path.empty() || icon.empty()) {
257     LOG(ERROR) << "Xml or icon path is empty";
258     return false;
259   }
260
261   if (!key_.empty()) {
262     pkgmgr_argv[0] = "-i";
263     pkgmgr_argv[1] = app_id.c_str();  // this value is ignored by pkgmgr
264     platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
265   }
266
267   if (!platform_installer.InstallApplication(xml_path, icon))
268     return false;
269
270   app_dir_cleaner.Dismiss();
271
272   return true;
273 }
274
275 bool PackageInstaller::PlatformUninstall(const std::string& app_id) {
276   base::FilePath data_dir;
277   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
278
279   // args for pkgmgr
280   const char* pkgmgr_argv[5];
281   pkgmgr_argv[2] = "-k";
282   pkgmgr_argv[3] = key_.c_str();
283   pkgmgr_argv[4] = "-q";
284
285   PlatformInstaller platform_installer(app_id);
286
287   if (!key_.empty()) {
288     pkgmgr_argv[0] = "-d";
289     pkgmgr_argv[1] = app_id.c_str();  // this value is ignored by pkgmgr
290     platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
291   }
292
293   return platform_installer.UninstallApplication();
294 }
295
296 bool PackageInstaller::PlatformUpdate(ApplicationData* app_data) {
297   std::string app_id(app_data->ID());
298   base::FilePath data_dir;
299   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
300
301   base::FilePath app_dir =
302       data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
303   base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
304       app_id + ".new" + std::string(info::kXmlExtension));
305
306   std::string icon_name;
307   if (!app_data->GetManifest()->GetString(
308       GetIcon128Key(app_data->manifest_type()), &icon_name))
309     LOG(WARNING) << "'icon' not included in manifest";
310
311   // This will clean everything inside '<data dir>/<app id>' and the new XML.
312   FileDeleter app_dir_cleaner(app_dir, true);
313   FileDeleter new_xml_cleaner(new_xml_path, true);
314
315   if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, new_xml_path)) {
316     LOG(ERROR) << "Could not create new XML metadata file '"
317                << new_xml_path.value() << "'.";
318     return false;
319   }
320
321   if (!CreateAppSymbolicLink(app_dir, app_id))
322     return false;
323
324   base::FilePath icon =
325       icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
326
327   // args for pkgmgr
328   const char* pkgmgr_argv[5];
329   pkgmgr_argv[2] = "-k";
330   pkgmgr_argv[3] = key_.c_str();
331   pkgmgr_argv[4] = "-q";
332
333   PlatformInstaller platform_installer(app_id);
334
335   if (!key_.empty()) {
336     pkgmgr_argv[0] = "-i";
337     pkgmgr_argv[1] = app_id.c_str();  // this value is ignored by pkgmgr
338     platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
339   }
340
341   if (!platform_installer.InstallApplication(new_xml_path, icon))
342     return false;
343
344   app_dir_cleaner.Dismiss();
345   return true;
346 }
347
348 bool PackageInstaller::PlatformReinstall(const base::FilePath& path) {
349   // args for pkgmgr
350   const char* pkgmgr_argv[5];
351   pkgmgr_argv[2] = "-k";
352   pkgmgr_argv[3] = key_.c_str();
353   pkgmgr_argv[4] = "-q";
354
355   PlatformInstaller platform_installer;
356
357   if (!key_.empty()) {
358     pkgmgr_argv[0] = "-r";
359     pkgmgr_argv[1] = path.value().c_str();  // this value is ignored by pkgmgr
360     platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
361   }
362
363   return platform_installer.ReinstallApplication();
364 }
365
366 bool PackageInstaller::Install(const base::FilePath& path, std::string* id) {
367   // FIXME(leandro): Installation is not robust enough -- should any step
368   // fail, it can't roll back to a consistent state.
369   if (!base::PathExists(path))
370     return false;
371
372   base::FilePath data_dir, install_temp_dir;
373   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
374   install_temp_dir = data_dir.Append(kInstallTempDir);
375   data_dir = data_dir.Append(kApplicationsDir);
376
377   // Make sure the kApplicationsDir exists under data_path, otherwise,
378   // the installation will always fail because of moving application
379   // resources into an invalid directory.
380   if (!base::DirectoryExists(data_dir) &&
381       !base::CreateDirectory(data_dir))
382     return false;
383
384   if (!base::DirectoryExists(install_temp_dir) &&
385       !base::CreateDirectory(install_temp_dir))
386     return false;
387
388   std::string app_id;
389   base::FilePath unpacked_dir;
390   scoped_ptr<Package> package;
391   FileDeleter tmp_path(install_temp_dir.Append(path.BaseName()), false);
392   if (!base::DirectoryExists(path)) {
393     if (tmp_path.path() != path &&
394         !base::CopyFile(path, tmp_path.path()))
395       return false;
396     package = Package::Create(tmp_path.path());
397     if (!package || !package->IsValid())
398       return false;
399     package->ExtractToTemporaryDir(&unpacked_dir);
400     app_id = package->Id();
401   } else {
402     unpacked_dir = path;
403   }
404
405   std::string error;
406   scoped_refptr<ApplicationData> app_data = LoadApplication(
407       unpacked_dir, app_id, ApplicationData::LOCAL_DIRECTORY,
408       package->manifest_type(), &error);
409   if (!app_data.get()) {
410     LOG(ERROR) << "Error during application installation: " << error;
411     return false;
412   }
413
414   // FIXME: Probably should be removed, as we should not handle permissions
415   // inside XWalk.
416   xwalk::application::PermissionPolicyManager permission_policy_handler;
417   if (!permission_policy_handler.
418       InitApplicationPermission(app_data.get())) {
419     LOG(ERROR) << "Application permission data is invalid";
420     return false;
421   }
422
423   if (storage_->Contains(app_data->ID())) {
424     *id = app_data->ID();
425     LOG(INFO) << "Already installed: " << *id;
426     return false;
427   }
428
429   base::FilePath app_dir = data_dir.AppendASCII(app_data->ID());
430   if (base::DirectoryExists(app_dir)) {
431     if (!base::DeleteFile(app_dir, true))
432       return false;
433   }
434   if (!package) {
435     if (!base::CreateDirectory(app_dir))
436       return false;
437     if (!CopyDirectoryContents(unpacked_dir, app_dir))
438       return false;
439   } else {
440     if (!base::Move(unpacked_dir, app_dir))
441       return false;
442   }
443
444   xwalk::application::TizenSettingInfo* info =
445       static_cast<xwalk::application::TizenSettingInfo*>(
446           app_data->GetManifestData(widget_keys::kTizenSettingKey));
447   if (info && info->encryption_enabled()) {
448     // Generate encryption key.
449     scoped_ptr<crypto::SymmetricKey> key(
450         crypto::SymmetricKey::GenerateRandomKey(
451             crypto::SymmetricKey::AES, 256));
452
453     std::string str_key;
454     key->GetRawKey(&str_key);
455
456     std::string appId = app_data->ID();
457     std::transform(appId.begin(), appId.end(), appId.begin(), tolower);
458     scoped_ptr<char[], base::FreeDeleter> buffer =
459         scoped_ptr<char[], base::FreeDeleter>(strdup(str_key.c_str()));
460     const char* filename = appId.c_str();
461     int ret = ssm_write_buffer(
462         buffer.get(), str_key.size(),
463         filename,
464         SSM_FLAG_SECRET_OPERATION,
465         filename);
466     // Encrypt the resources if needed.
467     base::FileEnumerator iter(app_dir, true, base::FileEnumerator::FILES);
468     for (base::FilePath file_path = iter.Next();
469          !file_path.empty();
470          file_path = iter.Next()) {
471       if (!ret && xwalk::application::RequiresEncryption(file_path) &&
472           base::PathIsWritable(file_path)) {
473         std::string content;
474         std::string encrypted;
475         if (!base::ReadFileToString(file_path, &content)) {
476           LOG(ERROR) << "Failed to read " << file_path.MaybeAsASCII();
477           return false;
478         }
479         if (!xwalk::application::EncryptData(content.data(),
480                                              content.size(),
481                                              str_key,
482                                              &encrypted)
483             || !base::WriteFile(file_path,
484                                 encrypted.data(),
485                                 encrypted.size())) {
486           LOG(ERROR) << "Failed to encrypt " << file_path.MaybeAsASCII();
487           return false;
488         }
489       }
490     }
491   }
492
493   app_data->set_path(app_dir);
494
495   if (!storage_->AddApplication(app_data)) {
496     LOG(ERROR) << "Application with id " << app_data->ID()
497                << " couldn't be installed due to a Storage error";
498     base::DeleteFile(app_dir, true);
499     return false;
500   }
501
502   if (!PlatformInstall(app_data.get())) {
503     LOG(ERROR) << "Application with id " << app_data->ID()
504                << " couldn't be installed due to a platform error";
505     storage_->RemoveApplication(app_data->ID());
506     base::DeleteFile(app_dir, true);
507     return false;
508   }
509
510   LOG(INFO) << "Installed application with id: " << app_data->ID()
511             << " to " << app_dir.MaybeAsASCII() << " successfully.";
512   *id = app_data->ID();
513
514   return true;
515 }
516
517 bool PackageInstaller::Update(const std::string& app_id,
518                               const base::FilePath& path) {
519   if (!xwalk::application::IsValidApplicationID(app_id)) {
520     LOG(ERROR) << "The given application id " << app_id << " is invalid.";
521     return false;
522   }
523
524   if (!base::PathExists(path)) {
525     LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
526     return false;
527   }
528
529   if (base::DirectoryExists(path)) {
530     LOG(WARNING) << "Cannot update an unpacked XPK/WGT package.";
531     return false;
532   }
533
534   base::FilePath unpacked_dir, update_temp_dir;
535   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &update_temp_dir));
536   update_temp_dir = update_temp_dir.Append(kUpdateTempDir);
537   if (!base::DirectoryExists(update_temp_dir) &&
538       !base::CreateDirectory(update_temp_dir))
539     return false;
540
541   FileDeleter tmp_path(update_temp_dir.Append(path.BaseName()), false);
542   if (tmp_path.path() != path &&
543       !base::CopyFile(path, tmp_path.path()))
544     return false;
545
546   scoped_ptr<Package> package = Package::Create(tmp_path.path());
547   if (!package) {
548     LOG(ERROR) << "XPK/WGT file is invalid.";
549     return false;
550   }
551
552   if (app_id.compare(package->Id()) != 0) {
553     LOG(ERROR) << "The XPK/WGT file is invalid, the application id is not the"
554                << "same as the installed application has.";
555     return false;
556   }
557
558   if (!package->ExtractToTemporaryDir(&unpacked_dir))
559     return false;
560
561   std::string error;
562   scoped_refptr<ApplicationData> new_app_data =
563       LoadApplication(unpacked_dir, app_id, ApplicationData::TEMP_DIRECTORY,
564                       package->manifest_type(), &error);
565   if (!new_app_data.get()) {
566     LOG(ERROR) << "An error occurred during application updating: " << error;
567     return false;
568   }
569
570   scoped_refptr<ApplicationData> old_app_data =
571       storage_->GetApplicationData(app_id);
572   if (!old_app_data.get()) {
573     LOG(INFO) << "Application haven't installed yet: " << app_id;
574     return false;
575   }
576
577   // For Tizen WGT package, downgrade to a lower version or reinstall
578   // is permitted when using Tizen WRT, Crosswalk runtime need to follow
579   // this behavior on Tizen platform.
580   if (package->manifest_type() != Manifest::TYPE_WIDGET &&
581       old_app_data->Version()->CompareTo(
582           *(new_app_data->Version())) >= 0) {
583     LOG(INFO) << "The version number of new XPK/WGT package "
584                  "should be higher than "
585               << old_app_data->VersionString();
586     return false;
587   }
588
589   const base::FilePath& app_dir = old_app_data->path();
590   const base::FilePath tmp_dir(app_dir.value()
591                                + FILE_PATH_LITERAL(".tmp"));
592
593   if (!base::Move(app_dir, tmp_dir) ||
594       !base::Move(unpacked_dir, app_dir))
595     return false;
596
597   new_app_data = LoadApplication(
598       app_dir, app_id, ApplicationData::LOCAL_DIRECTORY,
599       package->manifest_type(), &error);
600   if (!new_app_data.get()) {
601     LOG(ERROR) << "Error during loading new package: " << error;
602     base::DeleteFile(app_dir, true);
603     base::Move(tmp_dir, app_dir);
604     return false;
605   }
606
607   if (!storage_->UpdateApplication(new_app_data)) {
608     LOG(ERROR) << "Fail to update application, roll back to the old one.";
609     base::DeleteFile(app_dir, true);
610     base::Move(tmp_dir, app_dir);
611     return false;
612   }
613
614   if (!PlatformUpdate(new_app_data.get())) {
615     LOG(ERROR) << "Fail to update application, roll back to the old one.";
616     base::DeleteFile(app_dir, true);
617     if (!storage_->UpdateApplication(old_app_data)) {
618       LOG(ERROR) << "Fail to revert old application info, "
619                  << "remove the application as a last resort.";
620       storage_->RemoveApplication(old_app_data->ID());
621       base::DeleteFile(tmp_dir, true);
622       return false;
623     }
624     base::Move(tmp_dir, app_dir);
625     return false;
626   }
627
628   base::DeleteFile(tmp_dir, true);
629
630   return true;
631 }
632
633 bool PackageInstaller::Uninstall(const std::string& id) {
634   std::string app_id = PrepareUninstallationID(id);
635
636   if (!xwalk::application::IsValidApplicationID(app_id)) {
637     LOG(ERROR) << "The given application id " << app_id << " is invalid.";
638     return false;
639   }
640
641   bool result = true;
642   if (!storage_->RemoveApplication(app_id)) {
643     LOG(ERROR) << "Cannot uninstall application with id " << app_id
644                << "; application is not installed.";
645     result = false;
646   }
647
648   base::FilePath resources;
649   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &resources));
650   resources = resources.Append(kApplicationsDir).AppendASCII(app_id);
651   if (base::DirectoryExists(resources) &&
652       !base::DeleteFile(resources, true)) {
653     LOG(ERROR) << "Error occurred while trying to remove application with id "
654                << app_id << "; Cannot remove all resources.";
655     result = false;
656   }
657
658   if (!PlatformUninstall(app_id))
659     result = false;
660
661   return result;
662 }
663
664 bool PackageInstaller::Reinstall(const base::FilePath& path) {
665   return PlatformReinstall(path);
666 }
667
668 void PackageInstaller::ContinueUnfinishedTasks() {
669   base::FilePath config_dir;
670   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &config_dir));
671
672   base::FilePath install_temp_dir = config_dir.Append(kInstallTempDir),
673       update_temp_dir = config_dir.Append(kUpdateTempDir);
674   FileDeleter install_cleaner(install_temp_dir, true),
675       update_cleaner(update_temp_dir, true);
676
677   if (base::DirectoryExists(install_temp_dir)) {
678     base::FileEnumerator install_iter(
679         install_temp_dir, false, base::FileEnumerator::FILES);
680     for (base::FilePath file = install_iter.Next();
681          !file.empty(); file = install_iter.Next()) {
682       std::string app_id;
683       Install(file, &app_id);
684     }
685   }
686
687   if (base::DirectoryExists(update_temp_dir)) {
688     base::FileEnumerator update_iter(
689         update_temp_dir, false, base::FileEnumerator::FILES);
690     for (base::FilePath file = update_iter.Next();
691          !file.empty(); file = update_iter.Next()) {
692       std::string app_id;
693       if (!Install(file, &app_id) && storage_->Contains(app_id)) {
694         LOG(INFO) << "trying to update %s" << app_id;
695         Update(app_id, file);
696       }
697     }
698   }
699 }