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"
9 #include <ss_manager.h>
10 #include <sys/types.h>
13 #include <pkgmgr/pkgmgr_parser.h>
20 #include "base/file_util.h"
21 #include "base/files/file_enumerator.h"
22 #include "base/logging.h"
23 #include "base/path_service.h"
24 #include "base/version.h"
25 #include "crypto/symmetric_key.h"
26 #include "third_party/libxml/chromium/libxml_utils.h"
27 #include "xwalk/application/common/application_data.h"
28 #include "xwalk/application/common/application_file_util.h"
29 #include "xwalk/application/common/application_manifest_constants.h"
30 #include "xwalk/application/common/id_util.h"
31 #include "xwalk/application/common/manifest_handlers/tizen_app_control_handler.h"
32 #include "xwalk/application/common/manifest_handlers/tizen_application_handler.h"
33 #include "xwalk/application/common/manifest_handlers/tizen_metadata_handler.h"
34 #include "xwalk/application/common/manifest_handlers/tizen_setting_handler.h"
35 #include "xwalk/application/common/permission_policy_manager.h"
36 #include "xwalk/application/common/tizen/application_storage.h"
37 #include "xwalk/application/common/tizen/encryption.h"
38 #include "xwalk/application/common/tizen/package_query.h"
39 #include "xwalk/application/tools/tizen/xwalk_packageinfo_constants.h"
40 #include "xwalk/application/tools/tizen/xwalk_platform_installer.h"
41 #include "xwalk/application/tools/tizen/xwalk_rds_delta_parser.h"
42 #include "xwalk/runtime/common/xwalk_paths.h"
44 namespace info = application_packageinfo_constants;
46 using xwalk::application::ApplicationData;
47 using xwalk::application::ApplicationStorage;
48 using xwalk::application::FileDeleter;
49 using xwalk::application::Manifest;
50 using xwalk::application::Package;
54 const base::FilePath::CharType kApplicationsDir[] =
55 FILE_PATH_LITERAL("applications");
57 const base::FilePath::CharType kInstallTempDir[] =
58 FILE_PATH_LITERAL("install_temp");
60 const base::FilePath::CharType kUpdateTempDir[] =
61 FILE_PATH_LITERAL("update_temp");
63 namespace widget_keys = xwalk::application_widget_keys;
65 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
67 const base::FilePath kDefaultIcon(
68 "/usr/share/icons/default/small/crosswalk.png");
70 const std::string kServicePrefix("xwalk-service.");
71 const std::string kAppIdPrefix("xwalk.");
73 bool CopyDirectoryContents(const base::FilePath& from,
74 const base::FilePath& to) {
75 base::FileEnumerator iter(from, false,
76 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
77 for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
78 if (iter.GetInfo().IsDirectory()) {
79 if (!base::CopyDirectory(path, to, true))
81 } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
89 void WriteMetaDataElement(
90 XmlWriter& writer, // NOLINT
91 xwalk::application::TizenMetaDataInfo* info) {
95 const std::map<std::string, std::string>& metadata = info->metadata();
96 std::map<std::string, std::string>::const_iterator it;
97 for (it = metadata.begin(); it != metadata.end(); ++it) {
98 writer.StartElement("metadata");
99 writer.AddAttribute("key", it->first);
100 writer.AddAttribute("value", it->second);
105 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
106 const std::string& icon_name,
107 const base::FilePath& app_dir,
108 const base::FilePath& xml_path) {
109 if (!base::PathExists(app_dir) &&
110 !base::CreateDirectory(app_dir))
113 std::string package_id =
114 xwalk::application::AppIdToPkgId(application->ID());
116 base::FilePath execute_path =
117 app_dir.AppendASCII("bin/").AppendASCII(application->ID());
118 std::string stripped_name = application->Name();
120 FILE* file = base::OpenFile(xml_path, "w");
122 XmlWriter xml_writer;
123 xml_writer.StartWriting();
124 xml_writer.StartElement("manifest");
125 xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
126 xml_writer.AddAttribute("package", package_id);
127 xml_writer.AddAttribute("type",
128 application->GetManifest()->type() == Manifest::TYPE_MANIFEST
130 xml_writer.AddAttribute("version", application->VersionString());
131 xml_writer.WriteElement("label", application->Name());
132 xml_writer.WriteElement("description", application->Description());
134 xml_writer.StartElement("ui-application");
135 xml_writer.AddAttribute("appid", application->ID());
136 xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
137 xml_writer.AddAttribute("type", "webapp");
138 xml_writer.AddAttribute("taskmanage", "true");
140 const xwalk::application::AppControlInfoList* aplist =
141 static_cast<const xwalk::application::AppControlInfoList*>(
142 application->GetManifestData(
143 widget_keys::kTizenApplicationAppControlsKey));
145 for (const auto& item : aplist->controls) {
146 xml_writer.StartElement("app-control");
148 xml_writer.StartElement("operation");
149 xml_writer.AddAttribute("name", item.operation());
150 xml_writer.EndElement();
152 xml_writer.StartElement("uri");
153 if (!item.uri().empty()) {
154 xml_writer.AddAttribute("name", item.uri());
156 xml_writer.AddAttribute("name", "*/*");
158 xml_writer.EndElement();
160 xml_writer.StartElement("mime");
161 if (!item.mime().empty()) {
162 xml_writer.AddAttribute("name", item.mime());
164 xml_writer.AddAttribute("name", "*/*");
166 xml_writer.EndElement();
168 xml_writer.EndElement();
172 xml_writer.WriteElement("label", application->Name());
174 xwalk::application::TizenMetaDataInfo* info =
175 static_cast<xwalk::application::TizenMetaDataInfo*>(
176 application->GetManifestData(widget_keys::kTizenMetaDataKey));
177 WriteMetaDataElement(xml_writer, info);
179 if (icon_name.empty())
180 xml_writer.WriteElement("icon", info::kDefaultIconName);
182 xml_writer.WriteElement("icon",
183 kServicePrefix + application->ID() + ".png");
184 xml_writer.EndElement(); // Ends "ui-application"
186 xml_writer.EndElement(); // Ends "manifest" element.
187 xml_writer.StopWriting();
189 base::WriteFile(xml_path,
190 xml_writer.GetWrittenString().c_str(),
191 xml_writer.GetWrittenString().size());
193 base::CloseFile(file);
194 LOG(INFO) << "Converting manifest.json into "
195 << xml_path.BaseName().MaybeAsASCII()
196 << " for installation. [DONE]";
200 bool CreateAppSymbolicLink(const base::FilePath& app_dir,
201 const std::string& app_id) {
202 base::FilePath execute_path =
203 app_dir.AppendASCII("bin/").AppendASCII(app_id);
205 if (!base::CreateDirectory(execute_path.DirName())) {
206 LOG(ERROR) << "Could not create directory '"
207 << execute_path.DirName().value() << "'.";
211 if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
212 LOG(ERROR) << "Could not create symbolic link to launcher from '"
213 << execute_path.value() << "'.";
221 PackageInstaller::PackageInstaller(ApplicationStorage* storage)
226 PackageInstaller::~PackageInstaller() {
229 scoped_ptr<PackageInstaller> PackageInstaller::Create(
230 ApplicationStorage* storage) {
231 return scoped_ptr<PackageInstaller>(new PackageInstaller(storage));
234 void PackageInstaller::SetQuiet(bool quiet) {
238 void PackageInstaller::SetInstallationKey(const std::string& key) {
242 std::string PackageInstaller::PrepareUninstallationID(
243 const std::string& id) {
244 // this function fix pkg_id to app_id
245 // if installer was launched with pkg_id
246 if (xwalk::application::IsValidPkgID(id)) {
247 LOG(INFO) << "The package id is given " << id << " Will find app_id...";
248 std::string appid = xwalk::application::PkgIdToAppId(id);
255 bool PackageInstaller::PlatformInstall(ApplicationData* app_data) {
256 std::string app_id(app_data->ID());
257 base::FilePath data_dir;
258 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
260 base::FilePath app_dir =
261 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
262 base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
263 app_id + std::string(info::kXmlExtension));
265 std::string icon_name;
266 if (!app_data->GetManifest()->GetString(
267 GetIcon128Key(app_data->manifest_type()), &icon_name))
268 LOG(WARNING) << "'icon' not included in manifest";
270 // This will clean everything inside '<data dir>/<app id>'.
271 FileDeleter app_dir_cleaner(app_dir, true);
273 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, xml_path)) {
274 LOG(ERROR) << "Failed to create XML metadata file '"
275 << xml_path.value() << "'.";
279 if (!CreateAppSymbolicLink(app_dir, app_id)) {
280 LOG(ERROR) << "Failed to create symbolic link for " << app_id;
284 base::FilePath icon =
285 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
287 if (xml_path.empty() || icon.empty()) {
288 LOG(ERROR) << "Xml or icon path is empty";
292 PlatformInstaller platform_installer(app_id);
294 InitializePkgmgrSignal(&platform_installer, "-i", app_id);
296 if (!platform_installer.InstallApplication(xml_path, icon))
299 app_dir_cleaner.Dismiss();
304 bool PackageInstaller::PlatformUninstall(const std::string& app_id) {
305 PlatformInstaller platform_installer(app_id);
307 InitializePkgmgrSignal(&platform_installer, "-d", app_id);
309 return platform_installer.UninstallApplication();
312 bool PackageInstaller::PlatformUpdate(ApplicationData* app_data) {
313 std::string app_id(app_data->ID());
314 base::FilePath data_dir;
315 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
317 base::FilePath app_dir =
318 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
319 base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
320 app_id + ".new" + std::string(info::kXmlExtension));
322 std::string icon_name;
323 if (!app_data->GetManifest()->GetString(
324 GetIcon128Key(app_data->manifest_type()), &icon_name))
325 LOG(WARNING) << "'icon' not included in manifest";
327 // This will clean everything inside '<data dir>/<app id>' and the new XML.
328 FileDeleter app_dir_cleaner(app_dir, true);
329 FileDeleter new_xml_cleaner(new_xml_path, true);
331 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, new_xml_path)) {
332 LOG(ERROR) << "Could not create new XML metadata file '"
333 << new_xml_path.value() << "'.";
337 if (!CreateAppSymbolicLink(app_dir, app_id))
340 base::FilePath icon =
341 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
343 PlatformInstaller platform_installer(app_id);
345 InitializePkgmgrSignal(&platform_installer, "-i", app_id);
347 if (!platform_installer.UpdateApplication(new_xml_path, icon))
350 app_dir_cleaner.Dismiss();
354 bool PackageInstaller::PlatformReinstall(const std::string& pkgid) {
355 PlatformInstaller platform_installer;
356 InitializePkgmgrSignal(&platform_installer, "-r", pkgid);
358 return platform_installer.ReinstallApplication(pkgid);
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)) {
365 LOG(ERROR) << "The specified XPK/WGT package file is invalid.";
368 if (base::DirectoryExists(path)) {
369 LOG(WARNING) << "Cannot install from directory.";
373 base::FilePath data_dir, install_temp_dir;
374 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
375 install_temp_dir = data_dir.Append(kInstallTempDir);
376 data_dir = data_dir.Append(kApplicationsDir);
378 // Make sure the kApplicationsDir exists under data_path, otherwise,
379 // the installation will always fail because of moving application
380 // resources into an invalid directory.
381 if (!base::DirectoryExists(data_dir) &&
382 !base::CreateDirectory(data_dir))
385 if (!base::DirectoryExists(install_temp_dir) &&
386 !base::CreateDirectory(install_temp_dir))
390 base::FilePath unpacked_dir;
391 scoped_ptr<Package> package;
392 FileDeleter tmp_path(install_temp_dir.Append(path.BaseName()), false);
393 if (!base::DirectoryExists(path)) {
394 if (tmp_path.path() != path &&
395 !base::CopyFile(path, tmp_path.path()))
397 package = Package::Create(tmp_path.path());
398 if (!package || !package->IsValid())
400 package->ExtractToTemporaryDir(&unpacked_dir);
401 app_id = package->Id();
406 base::FilePath app_dir = data_dir.AppendASCII(app_id);
407 base::FilePath manifest_path =
408 xwalk::application::GetManifestPath(app_dir, package->manifest_type());
409 if (!base::PathExists(manifest_path)) {
410 if (!base::DirectoryExists(app_dir))
411 if (!base::CreateDirectory(app_dir))
414 xwalk::application::GetManifestPath(
415 unpacked_dir, package->manifest_type()), manifest_path))
420 scoped_refptr<ApplicationData> app_data = LoadApplication(
421 app_dir, app_id, ApplicationData::LOCAL_DIRECTORY,
422 package->manifest_type(), &error);
423 if (!app_data.get()) {
424 LOG(ERROR) << "Error during application installation: " << error;
428 // FIXME: Probably should be removed, as we should not handle permissions
430 xwalk::application::PermissionPolicyManager permission_policy_handler;
431 if (!permission_policy_handler.
432 InitApplicationPermission(app_data.get())) {
433 LOG(ERROR) << "Application permission data is invalid";
437 if (storage_->Contains(app_data->ID())) {
438 *id = app_data->ID();
439 LOG(INFO) << "Already installed: " << *id;
443 if (base::DirectoryExists(app_dir)) {
444 if (!base::DeleteFile(app_dir, true))
448 if (!base::CreateDirectory(app_dir))
450 if (!CopyDirectoryContents(unpacked_dir, app_dir))
453 if (!base::Move(unpacked_dir, app_dir))
457 xwalk::application::TizenSettingInfo* info =
458 static_cast<xwalk::application::TizenSettingInfo*>(
459 app_data->GetManifestData(widget_keys::kTizenSettingKey));
460 if (info && info->encryption_enabled()) {
461 // Generate encryption key.
462 scoped_ptr<crypto::SymmetricKey> key(
463 crypto::SymmetricKey::GenerateRandomKey(
464 crypto::SymmetricKey::AES, 256));
467 key->GetRawKey(&str_key);
469 std::string appId = app_data->ID();
470 std::transform(appId.begin(), appId.end(), appId.begin(), tolower);
471 scoped_ptr<char[], base::FreeDeleter> buffer =
472 scoped_ptr<char[], base::FreeDeleter>(strdup(str_key.c_str()));
473 const char* filename = appId.c_str();
474 int ret = ssm_write_buffer(
475 buffer.get(), str_key.size(),
477 SSM_FLAG_SECRET_OPERATION,
479 // Encrypt the resources if needed.
480 base::FileEnumerator iter(app_dir, true, base::FileEnumerator::FILES);
481 for (base::FilePath file_path = iter.Next();
483 file_path = iter.Next()) {
484 if (!ret && xwalk::application::RequiresEncryption(file_path) &&
485 base::PathIsWritable(file_path)) {
487 std::string encrypted;
488 if (!base::ReadFileToString(file_path, &content)) {
489 LOG(ERROR) << "Failed to read " << file_path.MaybeAsASCII();
492 if (!xwalk::application::EncryptData(content.data(),
496 || !base::WriteFile(file_path,
499 LOG(ERROR) << "Failed to encrypt " << file_path.MaybeAsASCII();
506 if (!storage_->AddApplication(app_data)) {
507 LOG(ERROR) << "Application with id " << app_data->ID()
508 << " couldn't be installed due to a Storage error";
509 base::DeleteFile(app_dir, true);
513 if (!PlatformInstall(app_data.get())) {
514 LOG(ERROR) << "Application with id " << app_data->ID()
515 << " couldn't be installed due to a platform error";
516 storage_->RemoveApplication(app_data->ID());
517 base::DeleteFile(app_dir, true);
521 LOG(INFO) << "Installed application with id: " << app_data->ID()
522 << " to " << app_dir.MaybeAsASCII() << " successfully.";
523 *id = app_data->ID();
528 bool PackageInstaller::Update(const std::string& app_id,
529 const base::FilePath& path) {
530 if (!xwalk::application::IsValidApplicationID(app_id)) {
531 LOG(ERROR) << "The given application id " << app_id << " is invalid.";
535 if (!base::PathExists(path)) {
536 LOG(ERROR) << "The specified XPK/WGT package file is invalid.";
540 if (base::DirectoryExists(path)) {
541 LOG(WARNING) << "Cannot update from directory.";
545 base::FilePath unpacked_dir, update_temp_dir;
546 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &update_temp_dir));
547 update_temp_dir = update_temp_dir.Append(kUpdateTempDir);
548 if (!base::DirectoryExists(update_temp_dir) &&
549 !base::CreateDirectory(update_temp_dir))
552 FileDeleter tmp_path(update_temp_dir.Append(path.BaseName()), false);
553 if (tmp_path.path() != path &&
554 !base::CopyFile(path, tmp_path.path()))
557 scoped_ptr<Package> package = Package::Create(tmp_path.path());
559 LOG(ERROR) << "XPK/WGT file is invalid.";
563 if (app_id.compare(package->Id()) != 0) {
564 LOG(ERROR) << "The XPK/WGT file is invalid, the application id is not the"
565 << "same as the installed application has.";
569 if (!package->ExtractToTemporaryDir(&unpacked_dir))
573 scoped_refptr<ApplicationData> new_app_data =
574 LoadApplication(unpacked_dir, app_id, ApplicationData::TEMP_DIRECTORY,
575 package->manifest_type(), &error);
576 if (!new_app_data.get()) {
577 LOG(ERROR) << "An error occurred during application updating: " << error;
581 scoped_refptr<ApplicationData> old_app_data =
582 storage_->GetApplicationData(app_id);
583 if (!old_app_data.get()) {
584 LOG(INFO) << "Application haven't installed yet: " << app_id;
588 // For Tizen WGT package, downgrade to a lower version or reinstall
589 // is permitted when using Tizen WRT, Crosswalk runtime need to follow
590 // this behavior on Tizen platform.
591 if (package->manifest_type() != Manifest::TYPE_WIDGET &&
592 old_app_data->Version()->CompareTo(
593 *(new_app_data->Version())) >= 0) {
594 LOG(INFO) << "The version number of new XPK/WGT package "
595 "should be higher than "
596 << old_app_data->VersionString();
600 const base::FilePath& app_dir = old_app_data->path();
601 const base::FilePath tmp_dir(app_dir.value()
602 + FILE_PATH_LITERAL(".tmp"));
604 if (!base::Move(app_dir, tmp_dir) ||
605 !base::Move(unpacked_dir, app_dir))
608 new_app_data = LoadApplication(
609 app_dir, app_id, ApplicationData::LOCAL_DIRECTORY,
610 package->manifest_type(), &error);
611 if (!new_app_data.get()) {
612 LOG(ERROR) << "Error during loading new package: " << error;
613 base::DeleteFile(app_dir, true);
614 base::Move(tmp_dir, app_dir);
618 if (!storage_->UpdateApplication(new_app_data)) {
619 LOG(ERROR) << "Fail to update application, roll back to the old one.";
620 base::DeleteFile(app_dir, true);
621 base::Move(tmp_dir, app_dir);
625 if (!PlatformUpdate(new_app_data.get())) {
626 LOG(ERROR) << "Fail to update application, roll back to the old one.";
627 base::DeleteFile(app_dir, true);
628 if (!storage_->UpdateApplication(old_app_data)) {
629 LOG(ERROR) << "Fail to revert old application info, "
630 << "remove the application as a last resort.";
631 storage_->RemoveApplication(old_app_data->ID());
632 base::DeleteFile(tmp_dir, true);
635 base::Move(tmp_dir, app_dir);
639 base::DeleteFile(tmp_dir, true);
644 bool PackageInstaller::Uninstall(const std::string& id) {
645 std::string app_id = PrepareUninstallationID(id);
647 if (!xwalk::application::IsValidApplicationID(app_id)) {
648 LOG(ERROR) << "The given application id '" << app_id << "' is invalid.";
653 if (!storage_->RemoveApplication(app_id)) {
654 LOG(ERROR) << "Cannot uninstall application with id " << app_id
655 << "; application is not installed.";
659 base::FilePath resources;
660 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &resources));
661 resources = resources.Append(kApplicationsDir).AppendASCII(app_id);
662 if (base::DirectoryExists(resources) &&
663 !base::DeleteFile(resources, true)) {
664 LOG(ERROR) << "Error occurred while trying to remove application with id "
665 << app_id << "; Cannot remove all resources.";
669 if (!PlatformUninstall(app_id))
675 bool PackageInstaller::Reinstall(const std::string& pkgid) {
676 if (!xwalk::application::IsValidPkgID(pkgid)) {
677 LOG(ERROR) << "The given package id '" << pkgid << "' is invalid.";
681 base::FilePath app_dir = xwalk::application::GetPackagePath(pkgid);
682 if (!base::DirectoryExists(app_dir)) {
683 LOG(ERROR) << "Application directory " << app_dir.value()
684 << " does not exist!";
688 RDSDeltaParser rds_parser(app_dir, pkgid);
689 if (!rds_parser.Parse())
692 if (!rds_parser.ApplyParsedData())
695 if (!PlatformReinstall(pkgid)) {
696 LOG(ERROR) << "Reinstallation of package " << pkgid
697 << " has failed due to a platform error!";
701 LOG(INFO) << "Package " << pkgid << " has been reinstalled successfully";
705 void PackageInstaller::ContinueUnfinishedTasks() {
706 base::FilePath config_dir;
707 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &config_dir));
709 base::FilePath install_temp_dir = config_dir.Append(kInstallTempDir),
710 update_temp_dir = config_dir.Append(kUpdateTempDir);
711 FileDeleter install_cleaner(install_temp_dir, true),
712 update_cleaner(update_temp_dir, true);
714 if (base::DirectoryExists(install_temp_dir)) {
715 base::FileEnumerator install_iter(
716 install_temp_dir, false, base::FileEnumerator::FILES);
717 for (base::FilePath file = install_iter.Next();
718 !file.empty(); file = install_iter.Next()) {
720 Install(file, &app_id);
724 if (base::DirectoryExists(update_temp_dir)) {
725 base::FileEnumerator update_iter(
726 update_temp_dir, false, base::FileEnumerator::FILES);
727 for (base::FilePath file = update_iter.Next();
728 !file.empty(); file = update_iter.Next()) {
730 if (!Install(file, &app_id) && storage_->Contains(app_id)) {
731 LOG(INFO) << "trying to update %s" << app_id;
732 Update(app_id, file);
738 void PackageInstaller::InitializePkgmgrSignal(
739 PlatformInstaller* platform_installer, const std::string& action,
740 const std::string& action_arg) {
741 DCHECK(platform_installer);
742 DCHECK(!action.empty());
743 DCHECK(!action_arg.empty());
748 const char* pkgmgr_argv[5];
749 pkgmgr_argv[0] = action.c_str();
750 pkgmgr_argv[1] = action_arg.c_str(); // this value is ignored by pkgmgr
751 pkgmgr_argv[2] = "-k";
752 pkgmgr_argv[3] = key_.c_str();
753 pkgmgr_argv[4] = "-q";
755 platform_installer->InitializePkgmgrSignal((quiet_ ? 5 : 4), pkgmgr_argv);