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/browser/installer/tizen/package_installer.h"
10 #include <pkgmgr/pkgmgr_parser.h>
14 #include "base/file_util.h"
15 #include "base/files/file_enumerator.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/command_line.h"
19 #include "base/process/launch.h"
20 #include "third_party/libxml/chromium/libxml_utils.h"
21 #include "xwalk/application/common/application_data.h"
22 #include "xwalk/application/common/application_manifest_constants.h"
23 #include "xwalk/application/common/manifest_handlers/tizen_metadata_handler.h"
24 #include "xwalk/application/browser/application_storage.h"
25 #include "xwalk/application/browser/installer/tizen/packageinfo_constants.h"
26 #include "xwalk/runtime/browser/xwalk_runner.h"
28 namespace info = xwalk::application_packageinfo_constants;
32 namespace widget_keys = xwalk::application_widget_keys;
34 const base::FilePath kPkgHelper("/usr/bin/xwalk-pkg-helper");
36 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
38 const base::FilePath kDefaultIcon(
39 "/usr/share/icons/default/small/crosswalk.png");
41 const std::string kServicePrefix("xwalk-service.");
42 const std::string kAppIdPrefix("xwalk.");
46 FileDeleter(const base::FilePath& path, bool recursive)
48 recursive_(recursive) {}
54 base::DeleteFile(path_, recursive_);
66 void WriteMetaDataElement(
67 XmlWriter& writer, // NOLINT
68 xwalk::application::TizenMetaDataInfo* info) {
72 const std::map<std::string, std::string>& metadata = info->metadata();
73 std::map<std::string, std::string>::const_iterator it;
74 for (it = metadata.begin(); it != metadata.end(); ++it) {
75 writer.StartElement("metadata");
76 writer.AddAttribute("key", it->first);
77 writer.AddAttribute("value", it->second);
82 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
83 const std::string& icon_name,
84 const base::FilePath& app_dir,
85 const base::FilePath& xml_path) {
86 if (!base::PathExists(app_dir) &&
87 !base::CreateDirectory(app_dir))
90 std::string package_id = application->ID();
91 std::string tizen_app_id = kAppIdPrefix + package_id;
92 base::FilePath execute_path =
93 app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
94 std::string stripped_name = application->Name();
96 FILE* file = base::OpenFile(xml_path, "w");
99 xml_writer.StartWriting();
100 xml_writer.StartElement("manifest");
101 xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
102 xml_writer.AddAttribute("package", package_id);
103 xml_writer.AddAttribute("version", application->VersionString());
104 xml_writer.WriteElement("label", application->Name());
105 xml_writer.WriteElement("description", application->Description());
107 xml_writer.StartElement("ui-application");
108 xml_writer.AddAttribute("appid", tizen_app_id);
109 xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
110 xml_writer.AddAttribute("type", "c++app");
111 xml_writer.AddAttribute("taskmanage", "true");
112 xml_writer.WriteElement("label", application->Name());
114 xwalk::application::TizenMetaDataInfo* info =
115 static_cast<xwalk::application::TizenMetaDataInfo*>(
116 application->GetManifestData(widget_keys::kTizenMetaDataKey));
117 WriteMetaDataElement(xml_writer, info);
119 if (icon_name.empty())
120 xml_writer.WriteElement("icon", info::kDefaultIconName);
122 xml_writer.WriteElement("icon", kServicePrefix + package_id + ".png");
123 xml_writer.EndElement(); // Ends "ui-application"
125 xml_writer.EndElement(); // Ends "manifest" element.
126 xml_writer.StopWriting();
128 base::WriteFile(xml_path,
129 xml_writer.GetWrittenString().c_str(),
130 xml_writer.GetWrittenString().size());
132 base::CloseFile(file);
133 LOG(INFO) << "Converting manifest.json into "
134 << xml_path.BaseName().MaybeAsASCII()
135 << " for installation. [DONE]";
139 bool CheckRunningMode() {
140 if (xwalk::XWalkRunner::GetInstance()->is_running_as_service())
142 LOG(ERROR) << "Package manipulation on Tizen is only possible in"
143 << "service mode using 'xwalkctl' utility.";
147 bool CreateAppSymbolicLink(const base::FilePath& app_dir,
148 const std::string& tizen_app_id) {
149 base::FilePath execute_path =
150 app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
152 if (!base::CreateDirectory(execute_path.DirName())) {
153 LOG(ERROR) << "Could not create directory '"
154 << execute_path.DirName().value() << "'.";
158 if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
159 LOG(ERROR) << "Could not create symbolic link to launcher from '"
160 << execute_path.value() << "'.";
169 namespace application {
171 bool PackageInstaller::InstallApplication(
172 ApplicationData* application, const base::FilePath& data_dir) {
173 if (!CheckRunningMode())
176 std::string package_id = application->ID();
177 std::string tizen_app_id = kAppIdPrefix + package_id;
178 base::FilePath app_dir =
179 data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
180 base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
181 package_id + std::string(info::kXmlExtension));
183 std::string icon_name;
184 if (!application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
185 LOG(WARNING) << "'icon' not included in manifest";
187 // This will clean everything inside '<data dir>/<app id>'.
188 FileDeleter app_dir_cleaner(app_dir, true);
190 if (!GeneratePkgInfoXml(application, icon_name, app_dir, xml_path)) {
191 LOG(ERROR) << "Could not create XML metadata file '"
192 << xml_path.value() << "'.";
196 if (!CreateAppSymbolicLink(app_dir, tizen_app_id))
199 base::FilePath icon =
200 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
202 CommandLine cmdline(kPkgHelper);
203 cmdline.AppendSwitch("--install");
204 cmdline.AppendArg(package_id);
205 cmdline.AppendArgPath(xml_path);
206 cmdline.AppendArgPath(icon);
211 if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
212 LOG(ERROR) << "Could not launch installer helper.";
216 if (exit_code != 0) {
217 LOG(ERROR) << "Could not install application: "
218 << output << " (" << exit_code << ")";
222 app_dir_cleaner.Dismiss();
227 bool PackageInstaller::UninstallApplication(
228 ApplicationData* application, const base::FilePath& data_dir) {
229 if (!CheckRunningMode())
232 std::string package_id = application->ID();
235 CommandLine cmdline(kPkgHelper);
236 cmdline.AppendSwitch("--uninstall");
237 cmdline.AppendArg(package_id);
242 if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
243 LOG(ERROR) << "Could not launch installer helper";
247 if (exit_code != 0) {
248 LOG(ERROR) << "Could not uninstall application: "
249 << output << " (" << exit_code << ")";
253 base::FilePath app_dir =
254 data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
255 if (!base::DeleteFile(app_dir, true)) {
256 LOG(ERROR) << "Could not remove directory '" << app_dir.value() << "'";
260 base::FilePath xml_path = data_dir.AppendASCII(
261 package_id + std::string(info::kXmlExtension));
262 if (!base::DeleteFile(xml_path, false)) {
263 LOG(ERROR) << "Could not remove file '" << xml_path.value() << "'";
270 bool PackageInstaller::UpdateApplication(
271 ApplicationData* new_application, const base::FilePath& data_dir) {
272 if (!CheckRunningMode())
275 std::string package_id = new_application->ID();
276 std::string tizen_app_id = kAppIdPrefix + package_id;
277 base::FilePath app_dir =
278 data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
279 base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
280 package_id + ".new" + std::string(info::kXmlExtension));
282 std::string icon_name;
283 if (!new_application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
284 LOG(WARNING) << "'icon' not included in manifest";
286 // This will clean everything inside '<data dir>/<app id>' and the new XML.
287 FileDeleter app_dir_cleaner(app_dir, true);
288 FileDeleter new_xml_cleaner(new_xml_path, true);
290 if (!GeneratePkgInfoXml(new_application, icon_name, app_dir, new_xml_path)) {
291 LOG(ERROR) << "Could not create new XML metadata file '"
292 << new_xml_path.value() << "'.";
296 if (!CreateAppSymbolicLink(app_dir, tizen_app_id))
299 base::FilePath icon =
300 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
302 CommandLine cmdline(kPkgHelper);
303 cmdline.AppendSwitch("--update");
304 cmdline.AppendArg(package_id);
305 cmdline.AppendArgPath(new_xml_path);
306 cmdline.AppendArgPath(icon);
311 if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
312 LOG(ERROR) << "Could not launch installer helper";
316 if (exit_code != 0) {
317 LOG(ERROR) << "Could not update application: "
318 << output << " (" << exit_code << ")";
322 base::FilePath old_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
323 package_id + std::string(info::kXmlExtension));
324 base::Move(new_xml_path, old_xml_path);
325 app_dir_cleaner.Dismiss();
329 } // namespace application