1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "xwalk/application/tools/tizen/xwalk_package_installer.h"
10 #include <pkgmgr/pkgmgr_parser.h>
16 #include "base/file_util.h"
17 #include "base/files/file_enumerator.h"
18 #include "base/logging.h"
19 #include "base/path_service.h"
20 #include "base/version.h"
21 #include "third_party/libxml/chromium/libxml_utils.h"
22 #include "xwalk/application/common/application_data.h"
23 #include "xwalk/application/common/application_file_util.h"
24 #include "xwalk/application/common/application_manifest_constants.h"
25 #include "xwalk/application/common/id_util.h"
26 #include "xwalk/application/common/manifest_handlers/tizen_application_handler.h"
27 #include "xwalk/application/common/manifest_handlers/tizen_metadata_handler.h"
28 #include "xwalk/application/common/permission_policy_manager.h"
29 #include "xwalk/application/common/tizen/application_storage.h"
30 #include "xwalk/application/tools/tizen/xwalk_packageinfo_constants.h"
31 #include "xwalk/application/tools/tizen/xwalk_platform_installer.h"
32 #include "xwalk/runtime/common/xwalk_paths.h"
34 namespace info = application_packageinfo_constants;
36 using xwalk::application::ApplicationData;
37 using xwalk::application::ApplicationStorage;
38 using xwalk::application::FileDeleter;
39 using xwalk::application::Manifest;
40 using xwalk::application::Package;
44 const base::FilePath::CharType kApplicationsDir[] =
45 FILE_PATH_LITERAL("applications");
47 const base::FilePath::CharType kInstallTempDir[] =
48 FILE_PATH_LITERAL("install_temp");
50 const base::FilePath::CharType kUpdateTempDir[] =
51 FILE_PATH_LITERAL("update_temp");
53 namespace widget_keys = xwalk::application_widget_keys;
55 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
57 const base::FilePath kDefaultIcon(
58 "/usr/share/icons/default/small/crosswalk.png");
60 const std::string kServicePrefix("xwalk-service.");
61 const std::string kAppIdPrefix("xwalk.");
63 bool CopyDirectoryContents(const base::FilePath& from,
64 const base::FilePath& to) {
65 base::FileEnumerator iter(from, false,
66 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
67 for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
68 if (iter.GetInfo().IsDirectory()) {
69 if (!base::CopyDirectory(path, to, true))
71 } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
79 void WriteMetaDataElement(
80 XmlWriter& writer, // NOLINT
81 xwalk::application::TizenMetaDataInfo* info) {
85 const std::map<std::string, std::string>& metadata = info->metadata();
86 std::map<std::string, std::string>::const_iterator it;
87 for (it = metadata.begin(); it != metadata.end(); ++it) {
88 writer.StartElement("metadata");
89 writer.AddAttribute("key", it->first);
90 writer.AddAttribute("value", it->second);
95 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
96 const std::string& icon_name,
97 const base::FilePath& app_dir,
98 const base::FilePath& xml_path) {
99 if (!base::PathExists(app_dir) &&
100 !base::CreateDirectory(app_dir))
103 std::string package_id =
104 xwalk::application::AppIdToPkgId(application->ID());
106 base::FilePath execute_path =
107 app_dir.AppendASCII("bin/").AppendASCII(application->ID());
108 std::string stripped_name = application->Name();
110 FILE* file = base::OpenFile(xml_path, "w");
112 XmlWriter xml_writer;
113 xml_writer.StartWriting();
114 xml_writer.StartElement("manifest");
115 xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
116 xml_writer.AddAttribute("package", package_id);
117 xml_writer.AddAttribute("type", "wgt");
118 xml_writer.AddAttribute("version", application->VersionString());
119 xml_writer.WriteElement("label", application->Name());
120 xml_writer.WriteElement("description", application->Description());
122 xml_writer.StartElement("ui-application");
123 xml_writer.AddAttribute("appid", application->ID());
124 xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
125 xml_writer.AddAttribute("type", "webapp");
126 xml_writer.AddAttribute("taskmanage", "true");
127 xml_writer.WriteElement("label", application->Name());
129 xwalk::application::TizenMetaDataInfo* info =
130 static_cast<xwalk::application::TizenMetaDataInfo*>(
131 application->GetManifestData(widget_keys::kTizenMetaDataKey));
132 WriteMetaDataElement(xml_writer, info);
134 if (icon_name.empty())
135 xml_writer.WriteElement("icon", info::kDefaultIconName);
137 xml_writer.WriteElement("icon",
138 kServicePrefix + application->ID() + ".png");
139 xml_writer.EndElement(); // Ends "ui-application"
141 xml_writer.EndElement(); // Ends "manifest" element.
142 xml_writer.StopWriting();
144 base::WriteFile(xml_path,
145 xml_writer.GetWrittenString().c_str(),
146 xml_writer.GetWrittenString().size());
148 base::CloseFile(file);
149 LOG(INFO) << "Converting manifest.json into "
150 << xml_path.BaseName().MaybeAsASCII()
151 << " for installation. [DONE]";
155 bool CreateAppSymbolicLink(const base::FilePath& app_dir,
156 const std::string& app_id) {
157 base::FilePath execute_path =
158 app_dir.AppendASCII("bin/").AppendASCII(app_id);
160 if (!base::CreateDirectory(execute_path.DirName())) {
161 LOG(ERROR) << "Could not create directory '"
162 << execute_path.DirName().value() << "'.";
166 if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
167 LOG(ERROR) << "Could not create symbolic link to launcher from '"
168 << execute_path.value() << "'.";
176 PackageInstaller::PackageInstaller(ApplicationStorage* storage)
181 PackageInstaller::~PackageInstaller() {
184 scoped_ptr<PackageInstaller> PackageInstaller::Create(
185 ApplicationStorage* storage) {
186 return scoped_ptr<PackageInstaller>(new PackageInstaller(storage));
189 void PackageInstaller::SetQuiet(bool quiet) {
193 void PackageInstaller::SetInstallationKey(const std::string& key) {
197 std::string PackageInstaller::PrepareUninstallationID(
198 const std::string& id) {
199 // this function fix pkg_id to app_id
200 // if installer was launched with pkg_id
201 if (xwalk::application::IsValidPkgID(id)) {
202 LOG(INFO) << "The package id is given " << id << " Will find app_id...";
203 std::string appid = xwalk::application::PkgIdToAppId(id);
210 bool PackageInstaller::PlatformInstall(ApplicationData* app_data) {
211 std::string app_id(app_data->ID());
212 base::FilePath data_dir;
213 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
215 base::FilePath app_dir =
216 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
217 base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
218 app_id + std::string(info::kXmlExtension));
220 std::string icon_name;
221 if (!app_data->GetManifest()->GetString(
222 GetIcon128Key(app_data->manifest_type()), &icon_name))
223 LOG(WARNING) << "'icon' not included in manifest";
225 // This will clean everything inside '<data dir>/<app id>'.
226 FileDeleter app_dir_cleaner(app_dir, true);
228 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, xml_path)) {
229 LOG(ERROR) << "Failed to create XML metadata file '"
230 << xml_path.value() << "'.";
234 if (!CreateAppSymbolicLink(app_dir, app_id)) {
235 LOG(ERROR) << "Failed to create symbolic link for " << app_id;
239 base::FilePath icon =
240 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
243 const char* pkgmgr_argv[5];
244 pkgmgr_argv[2] = "-k";
245 pkgmgr_argv[3] = key_.c_str();
246 pkgmgr_argv[4] = "-q";
248 PlatformInstaller platform_installer(app_id);
250 if (xml_path.empty() || icon.empty()) {
251 LOG(ERROR) << "Xml or icon path is empty";
256 pkgmgr_argv[0] = "-i";
257 pkgmgr_argv[1] = app_id.c_str(); // this value is ignored by pkgmgr
258 platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
261 if (!platform_installer.InstallApplication(xml_path, icon))
264 app_dir_cleaner.Dismiss();
269 bool PackageInstaller::PlatformUninstall(ApplicationData* app_data) {
270 std::string app_id(app_data->ID());
271 base::FilePath data_dir;
272 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
275 const char* pkgmgr_argv[5];
276 pkgmgr_argv[2] = "-k";
277 pkgmgr_argv[3] = key_.c_str();
278 pkgmgr_argv[4] = "-q";
280 PlatformInstaller platform_installer(app_id);
283 pkgmgr_argv[0] = "-d";
284 pkgmgr_argv[1] = app_id.c_str(); // this value is ignored by pkgmgr
285 platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
288 return platform_installer.UninstallApplication();
291 bool PackageInstaller::PlatformUpdate(ApplicationData* app_data) {
292 std::string app_id(app_data->ID());
293 base::FilePath data_dir;
294 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
296 base::FilePath app_dir =
297 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
298 base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
299 app_id + ".new" + std::string(info::kXmlExtension));
301 std::string icon_name;
302 if (!app_data->GetManifest()->GetString(
303 GetIcon128Key(app_data->manifest_type()), &icon_name))
304 LOG(WARNING) << "'icon' not included in manifest";
306 // This will clean everything inside '<data dir>/<app id>' and the new XML.
307 FileDeleter app_dir_cleaner(app_dir, true);
308 FileDeleter new_xml_cleaner(new_xml_path, true);
310 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, new_xml_path)) {
311 LOG(ERROR) << "Could not create new XML metadata file '"
312 << new_xml_path.value() << "'.";
316 if (!CreateAppSymbolicLink(app_dir, app_id))
319 base::FilePath icon =
320 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
323 const char* pkgmgr_argv[5];
324 pkgmgr_argv[2] = "-k";
325 pkgmgr_argv[3] = key_.c_str();
326 pkgmgr_argv[4] = "-q";
328 PlatformInstaller platform_installer(app_id);
331 pkgmgr_argv[0] = "-i";
332 pkgmgr_argv[1] = app_id.c_str(); // this value is ignored by pkgmgr
333 platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
336 if (!platform_installer.InstallApplication(new_xml_path, icon))
339 app_dir_cleaner.Dismiss();
343 bool PackageInstaller::PlatformReinstall(const base::FilePath& path) {
345 const char* pkgmgr_argv[5];
346 pkgmgr_argv[2] = "-k";
347 pkgmgr_argv[3] = key_.c_str();
348 pkgmgr_argv[4] = "-q";
350 PlatformInstaller platform_installer;
353 pkgmgr_argv[0] = "-r";
354 pkgmgr_argv[1] = path.value().c_str(); // this value is ignored by pkgmgr
355 platform_installer.InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);
358 return platform_installer.ReinstallApplication();
361 bool PackageInstaller::Install(const base::FilePath& path, std::string* id) {
362 // FIXME(leandro): Installation is not robust enough -- should any step
363 // fail, it can't roll back to a consistent state.
364 if (!base::PathExists(path))
367 base::FilePath data_dir, install_temp_dir;
368 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
369 install_temp_dir = data_dir.Append(kInstallTempDir);
370 data_dir = data_dir.Append(kApplicationsDir);
372 // Make sure the kApplicationsDir exists under data_path, otherwise,
373 // the installation will always fail because of moving application
374 // resources into an invalid directory.
375 if (!base::DirectoryExists(data_dir) &&
376 !base::CreateDirectory(data_dir))
379 if (!base::DirectoryExists(install_temp_dir) &&
380 !base::CreateDirectory(install_temp_dir))
384 base::FilePath unpacked_dir;
385 scoped_ptr<Package> package;
386 FileDeleter tmp_path(install_temp_dir.Append(path.BaseName()), false);
387 if (!base::DirectoryExists(path)) {
388 if (tmp_path.path() != path &&
389 !base::CopyFile(path, tmp_path.path()))
391 package = Package::Create(tmp_path.path());
392 if (!package->IsValid())
394 package->ExtractToTemporaryDir(&unpacked_dir);
395 app_id = package->Id();
401 scoped_refptr<ApplicationData> app_data = LoadApplication(
402 unpacked_dir, app_id, ApplicationData::LOCAL_DIRECTORY,
403 package->manifest_type(), &error);
405 LOG(ERROR) << "Error during application installation: " << error;
409 // FIXME: Probably should be removed, as we should not handle permissions
411 xwalk::application::PermissionPolicyManager permission_policy_handler;
412 if (!permission_policy_handler.
413 InitApplicationPermission(app_data)) {
414 LOG(ERROR) << "Application permission data is invalid";
418 if (storage_->Contains(app_data->ID())) {
419 *id = app_data->ID();
420 LOG(INFO) << "Already installed: " << *id;
424 base::FilePath app_dir = data_dir.AppendASCII(app_data->ID());
425 if (base::DirectoryExists(app_dir)) {
426 if (!base::DeleteFile(app_dir, true))
430 if (!base::CreateDirectory(app_dir))
432 if (!CopyDirectoryContents(unpacked_dir, app_dir))
435 if (!base::Move(unpacked_dir, app_dir))
439 app_data->set_path(app_dir);
441 if (!storage_->AddApplication(app_data)) {
442 LOG(ERROR) << "Application with id " << app_data->ID()
443 << " couldn't be installed due to a Storage error";
444 base::DeleteFile(app_dir, true);
448 if (!PlatformInstall(app_data)) {
449 LOG(ERROR) << "Application with id " << app_data->ID()
450 << " couldn't be installed due to a platform error";
451 storage_->RemoveApplication(app_data->ID());
452 base::DeleteFile(app_dir, true);
456 LOG(INFO) << "Installed application with id: " << app_data->ID()
457 << "to" << app_dir.MaybeAsASCII() << " successfully.";
458 *id = app_data->ID();
463 bool PackageInstaller::Update(const std::string& app_id,
464 const base::FilePath& path) {
465 if (!xwalk::application::IsValidApplicationID(app_id)) {
466 LOG(ERROR) << "The given application id " << app_id << " is invalid.";
470 if (!base::PathExists(path)) {
471 LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
475 if (base::DirectoryExists(path)) {
476 LOG(WARNING) << "Cannot update an unpacked XPK/WGT package.";
480 base::FilePath unpacked_dir, update_temp_dir;
481 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &update_temp_dir));
482 update_temp_dir = update_temp_dir.Append(kUpdateTempDir);
483 if (!base::DirectoryExists(update_temp_dir) &&
484 !base::CreateDirectory(update_temp_dir))
487 FileDeleter tmp_path(update_temp_dir.Append(path.BaseName()), false);
488 if (tmp_path.path() != path &&
489 !base::CopyFile(path, tmp_path.path()))
492 scoped_ptr<Package> package = Package::Create(tmp_path.path());
494 LOG(ERROR) << "XPK/WGT file is invalid.";
498 if (app_id.compare(package->Id()) != 0) {
499 LOG(ERROR) << "The XPK/WGT file is invalid, the application id is not the"
500 << "same as the installed application has.";
504 if (!package->ExtractToTemporaryDir(&unpacked_dir))
508 scoped_refptr<ApplicationData> new_app_data =
509 LoadApplication(unpacked_dir, app_id, ApplicationData::TEMP_DIRECTORY,
510 package->manifest_type(), &error);
512 LOG(ERROR) << "An error occurred during application updating: " << error;
516 scoped_refptr<ApplicationData> old_app_data =
517 storage_->GetApplicationData(app_id);
519 LOG(INFO) << "Application haven't installed yet: " << app_id;
523 // For Tizen WGT package, downgrade to a lower version or reinstall
524 // is permitted when using Tizen WRT, Crosswalk runtime need to follow
525 // this behavior on Tizen platform.
526 if (package->manifest_type() != Manifest::TYPE_WIDGET &&
527 old_app_data->Version()->CompareTo(
528 *(new_app_data->Version())) >= 0) {
529 LOG(INFO) << "The version number of new XPK/WGT package "
530 "should be higher than "
531 << old_app_data->VersionString();
535 const base::FilePath& app_dir = old_app_data->path();
536 const base::FilePath tmp_dir(app_dir.value()
537 + FILE_PATH_LITERAL(".tmp"));
539 if (!base::Move(app_dir, tmp_dir) ||
540 !base::Move(unpacked_dir, app_dir))
543 new_app_data = LoadApplication(
544 app_dir, app_id, ApplicationData::LOCAL_DIRECTORY,
545 package->manifest_type(), &error);
547 LOG(ERROR) << "Error during loading new package: " << error;
548 base::DeleteFile(app_dir, true);
549 base::Move(tmp_dir, app_dir);
553 if (!storage_->UpdateApplication(new_app_data)) {
554 LOG(ERROR) << "Fail to update application, roll back to the old one.";
555 base::DeleteFile(app_dir, true);
556 base::Move(tmp_dir, app_dir);
560 if (!PlatformUpdate(new_app_data)) {
561 LOG(ERROR) << "Fail to update application, roll back to the old one.";
562 base::DeleteFile(app_dir, true);
563 if (!storage_->UpdateApplication(old_app_data)) {
564 LOG(ERROR) << "Fail to revert old application info, "
565 << "remove the application as a last resort.";
566 storage_->RemoveApplication(old_app_data->ID());
567 base::DeleteFile(tmp_dir, true);
570 base::Move(tmp_dir, app_dir);
574 base::DeleteFile(tmp_dir, true);
579 bool PackageInstaller::Uninstall(const std::string& id) {
580 std::string app_id = PrepareUninstallationID(id);
582 if (!xwalk::application::IsValidApplicationID(app_id)) {
583 LOG(ERROR) << "The given application id " << app_id << " is invalid.";
588 scoped_refptr<ApplicationData> app_data =
589 storage_->GetApplicationData(app_id);
591 LOG(ERROR) << "Failed to find application with id " << app_id
592 << " among the installed ones.";
596 if (!storage_->RemoveApplication(app_id)) {
597 LOG(ERROR) << "Cannot uninstall application with id " << app_id
598 << "; application is not installed.";
602 base::FilePath resources;
603 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &resources));
604 resources = resources.Append(kApplicationsDir).AppendASCII(app_id);
605 if (base::DirectoryExists(resources) &&
606 !base::DeleteFile(resources, true)) {
607 LOG(ERROR) << "Error occurred while trying to remove application with id "
608 << app_id << "; Cannot remove all resources.";
612 if (!PlatformUninstall(app_data))
618 bool PackageInstaller::Reinstall(const base::FilePath& path) {
619 return PlatformReinstall(path);
622 void PackageInstaller::ContinueUnfinishedTasks() {
623 base::FilePath config_dir;
624 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &config_dir));
626 base::FilePath install_temp_dir = config_dir.Append(kInstallTempDir),
627 update_temp_dir = config_dir.Append(kUpdateTempDir);
628 FileDeleter install_cleaner(install_temp_dir, true),
629 update_cleaner(update_temp_dir, true);
631 if (base::DirectoryExists(install_temp_dir)) {
632 base::FileEnumerator install_iter(
633 install_temp_dir, false, base::FileEnumerator::FILES);
634 for (base::FilePath file = install_iter.Next();
635 !file.empty(); file = install_iter.Next()) {
637 Install(file, &app_id);
641 if (base::DirectoryExists(update_temp_dir)) {
642 base::FileEnumerator update_iter(
643 update_temp_dir, false, base::FileEnumerator::FILES);
644 for (base::FilePath file = update_iter.Next();
645 !file.empty(); file = update_iter.Next()) {
647 if (!Install(file, &app_id) && storage_->Contains(app_id)) {
648 LOG(INFO) << "trying to update %s" << app_id;
649 Update(app_id, file);