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.
6 #include "xwalk/application/tools/tizen/xwalk_package_installer.h"
10 #include <ss_manager.h>
12 #include <pkgmgr/pkgmgr_parser.h>
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"
40 namespace info = application_packageinfo_constants;
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;
50 const base::FilePath::CharType kApplicationsDir[] =
51 FILE_PATH_LITERAL("applications");
53 const base::FilePath::CharType kInstallTempDir[] =
54 FILE_PATH_LITERAL("install_temp");
56 const base::FilePath::CharType kUpdateTempDir[] =
57 FILE_PATH_LITERAL("update_temp");
59 namespace widget_keys = xwalk::application_widget_keys;
61 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
63 const base::FilePath kDefaultIcon(
64 "/usr/share/icons/default/small/crosswalk.png");
66 const std::string kServicePrefix("xwalk-service.");
67 const std::string kAppIdPrefix("xwalk.");
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))
77 } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
85 void WriteMetaDataElement(
86 XmlWriter& writer, // NOLINT
87 xwalk::application::TizenMetaDataInfo* info) {
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);
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))
109 std::string package_id =
110 xwalk::application::AppIdToPkgId(application->ID());
112 base::FilePath execute_path =
113 app_dir.AppendASCII("bin/").AppendASCII(application->ID());
114 std::string stripped_name = application->Name();
116 FILE* file = base::OpenFile(xml_path, "w");
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());
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());
135 xwalk::application::TizenMetaDataInfo* info =
136 static_cast<xwalk::application::TizenMetaDataInfo*>(
137 application->GetManifestData(widget_keys::kTizenMetaDataKey));
138 WriteMetaDataElement(xml_writer, info);
140 if (icon_name.empty())
141 xml_writer.WriteElement("icon", info::kDefaultIconName);
143 xml_writer.WriteElement("icon",
144 kServicePrefix + application->ID() + ".png");
145 xml_writer.EndElement(); // Ends "ui-application"
147 xml_writer.EndElement(); // Ends "manifest" element.
148 xml_writer.StopWriting();
150 base::WriteFile(xml_path,
151 xml_writer.GetWrittenString().c_str(),
152 xml_writer.GetWrittenString().size());
154 base::CloseFile(file);
155 LOG(INFO) << "Converting manifest.json into "
156 << xml_path.BaseName().MaybeAsASCII()
157 << " for installation. [DONE]";
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);
166 if (!base::CreateDirectory(execute_path.DirName())) {
167 LOG(ERROR) << "Could not create directory '"
168 << execute_path.DirName().value() << "'.";
172 if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
173 LOG(ERROR) << "Could not create symbolic link to launcher from '"
174 << execute_path.value() << "'.";
182 PackageInstaller::PackageInstaller(ApplicationStorage* storage)
187 PackageInstaller::~PackageInstaller() {
190 scoped_ptr<PackageInstaller> PackageInstaller::Create(
191 ApplicationStorage* storage) {
192 return scoped_ptr<PackageInstaller>(new PackageInstaller(storage));
195 void PackageInstaller::SetQuiet(bool quiet) {
199 void PackageInstaller::SetInstallationKey(const std::string& key) {
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);
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));
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));
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";
231 // This will clean everything inside '<data dir>/<app id>'.
232 FileDeleter app_dir_cleaner(app_dir, true);
234 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, xml_path)) {
235 LOG(ERROR) << "Failed to create XML metadata file '"
236 << xml_path.value() << "'.";
240 if (!CreateAppSymbolicLink(app_dir, app_id)) {
241 LOG(ERROR) << "Failed to create symbolic link for " << app_id;
245 base::FilePath icon =
246 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
249 const char* pkgmgr_argv[5];
250 pkgmgr_argv[2] = "-k";
251 pkgmgr_argv[3] = key_.c_str();
252 pkgmgr_argv[4] = "-q";
254 PlatformInstaller platform_installer(app_id);
256 if (xml_path.empty() || icon.empty()) {
257 LOG(ERROR) << "Xml or icon path is 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);
267 if (!platform_installer.InstallApplication(xml_path, icon))
270 app_dir_cleaner.Dismiss();
275 bool PackageInstaller::PlatformUninstall(const std::string& app_id) {
276 base::FilePath data_dir;
277 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
280 const char* pkgmgr_argv[5];
281 pkgmgr_argv[2] = "-k";
282 pkgmgr_argv[3] = key_.c_str();
283 pkgmgr_argv[4] = "-q";
285 PlatformInstaller platform_installer(app_id);
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);
293 return platform_installer.UninstallApplication();
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));
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));
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";
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);
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() << "'.";
321 if (!CreateAppSymbolicLink(app_dir, app_id))
324 base::FilePath icon =
325 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
328 const char* pkgmgr_argv[5];
329 pkgmgr_argv[2] = "-k";
330 pkgmgr_argv[3] = key_.c_str();
331 pkgmgr_argv[4] = "-q";
333 PlatformInstaller platform_installer(app_id);
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);
341 if (!platform_installer.InstallApplication(new_xml_path, icon))
344 app_dir_cleaner.Dismiss();
348 bool PackageInstaller::PlatformReinstall(const base::FilePath& path) {
350 const char* pkgmgr_argv[5];
351 pkgmgr_argv[2] = "-k";
352 pkgmgr_argv[3] = key_.c_str();
353 pkgmgr_argv[4] = "-q";
355 PlatformInstaller platform_installer;
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);
363 return platform_installer.ReinstallApplication();
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))
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);
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))
384 if (!base::DirectoryExists(install_temp_dir) &&
385 !base::CreateDirectory(install_temp_dir))
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()))
396 package = Package::Create(tmp_path.path());
397 if (!package || !package->IsValid())
399 package->ExtractToTemporaryDir(&unpacked_dir);
400 app_id = package->Id();
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;
414 // FIXME: Probably should be removed, as we should not handle permissions
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";
423 if (storage_->Contains(app_data->ID())) {
424 *id = app_data->ID();
425 LOG(INFO) << "Already installed: " << *id;
429 base::FilePath app_dir = data_dir.AppendASCII(app_data->ID());
430 if (base::DirectoryExists(app_dir)) {
431 if (!base::DeleteFile(app_dir, true))
435 if (!base::CreateDirectory(app_dir))
437 if (!CopyDirectoryContents(unpacked_dir, app_dir))
440 if (!base::Move(unpacked_dir, app_dir))
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));
454 key->GetRawKey(&str_key);
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(),
464 SSM_FLAG_SECRET_OPERATION,
466 // Encrypt the resources if needed.
467 base::FileEnumerator iter(app_dir, true, base::FileEnumerator::FILES);
468 for (base::FilePath file_path = iter.Next();
470 file_path = iter.Next()) {
471 if (!ret && xwalk::application::RequiresEncryption(file_path) &&
472 base::PathIsWritable(file_path)) {
474 std::string encrypted;
475 if (!base::ReadFileToString(file_path, &content)) {
476 LOG(ERROR) << "Failed to read " << file_path.MaybeAsASCII();
479 if (!xwalk::application::EncryptData(content.data(),
483 || !base::WriteFile(file_path,
486 LOG(ERROR) << "Failed to encrypt " << file_path.MaybeAsASCII();
493 app_data->set_path(app_dir);
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);
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);
510 LOG(INFO) << "Installed application with id: " << app_data->ID()
511 << " to " << app_dir.MaybeAsASCII() << " successfully.";
512 *id = app_data->ID();
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.";
524 if (!base::PathExists(path)) {
525 LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
529 if (base::DirectoryExists(path)) {
530 LOG(WARNING) << "Cannot update an unpacked XPK/WGT package.";
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))
541 FileDeleter tmp_path(update_temp_dir.Append(path.BaseName()), false);
542 if (tmp_path.path() != path &&
543 !base::CopyFile(path, tmp_path.path()))
546 scoped_ptr<Package> package = Package::Create(tmp_path.path());
548 LOG(ERROR) << "XPK/WGT file is invalid.";
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.";
558 if (!package->ExtractToTemporaryDir(&unpacked_dir))
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;
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;
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();
589 const base::FilePath& app_dir = old_app_data->path();
590 const base::FilePath tmp_dir(app_dir.value()
591 + FILE_PATH_LITERAL(".tmp"));
593 if (!base::Move(app_dir, tmp_dir) ||
594 !base::Move(unpacked_dir, app_dir))
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);
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);
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);
624 base::Move(tmp_dir, app_dir);
628 base::DeleteFile(tmp_dir, true);
633 bool PackageInstaller::Uninstall(const std::string& id) {
634 std::string app_id = PrepareUninstallationID(id);
636 if (!xwalk::application::IsValidApplicationID(app_id)) {
637 LOG(ERROR) << "The given application id " << app_id << " is invalid.";
642 if (!storage_->RemoveApplication(app_id)) {
643 LOG(ERROR) << "Cannot uninstall application with id " << app_id
644 << "; application is not installed.";
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.";
658 if (!PlatformUninstall(app_id))
664 bool PackageInstaller::Reinstall(const base::FilePath& path) {
665 return PlatformReinstall(path);
668 void PackageInstaller::ContinueUnfinishedTasks() {
669 base::FilePath config_dir;
670 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &config_dir));
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);
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()) {
683 Install(file, &app_id);
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()) {
693 if (!Install(file, &app_id) && storage_->Contains(app_id)) {
694 LOG(INFO) << "trying to update %s" << app_id;
695 Update(app_id, file);