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/common/installer/package_installer_tizen.h"
10 #include <pkgmgr/pkgmgr_parser.h>
15 #include "base/file_util.h"
16 #include "base/files/file_enumerator.h"
17 #include "base/logging.h"
18 #include "base/path_service.h"
19 #include "base/command_line.h"
20 #include "base/process/launch.h"
21 #include "third_party/libxml/chromium/libxml_utils.h"
22 #include "xwalk/application/common/application_data.h"
23 #include "xwalk/application/common/application_manifest_constants.h"
24 #include "xwalk/application/common/manifest_handlers/tizen_metadata_handler.h"
25 #include "xwalk/application/common/application_storage.h"
26 #include "xwalk/application/common/installer/tizen/packageinfo_constants.h"
27 #include "xwalk/runtime/common/xwalk_paths.h"
29 namespace info = xwalk::application_packageinfo_constants;
33 namespace widget_keys = xwalk::application_widget_keys;
35 const base::FilePath kPkgHelper("/usr/bin/xwalk-pkg-helper");
37 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
39 const base::FilePath kDefaultIcon(
40 "/usr/share/icons/default/small/crosswalk.png");
42 const std::string kServicePrefix("xwalk-service.");
43 const std::string kAppIdPrefix("xwalk.");
47 FileDeleter(const base::FilePath& path, bool recursive)
49 recursive_(recursive) {}
55 base::DeleteFile(path_, recursive_);
67 void WriteMetaDataElement(
68 XmlWriter& writer, // NOLINT
69 xwalk::application::TizenMetaDataInfo* info) {
73 const std::map<std::string, std::string>& metadata = info->metadata();
74 std::map<std::string, std::string>::const_iterator it;
75 for (it = metadata.begin(); it != metadata.end(); ++it) {
76 writer.StartElement("metadata");
77 writer.AddAttribute("key", it->first);
78 writer.AddAttribute("value", it->second);
83 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
84 const std::string& icon_name,
85 const base::FilePath& app_dir,
86 const base::FilePath& xml_path) {
87 if (!base::PathExists(app_dir) &&
88 !base::CreateDirectory(app_dir))
91 std::string package_id = application->ID();
92 std::string tizen_app_id = kAppIdPrefix + package_id;
93 base::FilePath execute_path =
94 app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
95 std::string stripped_name = application->Name();
97 FILE* file = base::OpenFile(xml_path, "w");
100 xml_writer.StartWriting();
101 xml_writer.StartElement("manifest");
102 xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
103 xml_writer.AddAttribute("package", package_id);
104 xml_writer.AddAttribute("version", application->VersionString());
105 xml_writer.WriteElement("label", application->Name());
106 xml_writer.WriteElement("description", application->Description());
108 xml_writer.StartElement("ui-application");
109 xml_writer.AddAttribute("appid", tizen_app_id);
110 xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
111 xml_writer.AddAttribute("type", "c++app");
112 xml_writer.AddAttribute("taskmanage", "true");
113 xml_writer.WriteElement("label", application->Name());
115 xwalk::application::TizenMetaDataInfo* info =
116 static_cast<xwalk::application::TizenMetaDataInfo*>(
117 application->GetManifestData(widget_keys::kTizenMetaDataKey));
118 WriteMetaDataElement(xml_writer, info);
120 if (icon_name.empty())
121 xml_writer.WriteElement("icon", info::kDefaultIconName);
123 xml_writer.WriteElement("icon", kServicePrefix + package_id + ".png");
124 xml_writer.EndElement(); // Ends "ui-application"
126 xml_writer.EndElement(); // Ends "manifest" element.
127 xml_writer.StopWriting();
129 base::WriteFile(xml_path,
130 xml_writer.GetWrittenString().c_str(),
131 xml_writer.GetWrittenString().size());
133 base::CloseFile(file);
134 LOG(INFO) << "Converting manifest.json into "
135 << xml_path.BaseName().MaybeAsASCII()
136 << " for installation. [DONE]";
140 bool CreateAppSymbolicLink(const base::FilePath& app_dir,
141 const std::string& tizen_app_id) {
142 base::FilePath execute_path =
143 app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
145 if (!base::CreateDirectory(execute_path.DirName())) {
146 LOG(ERROR) << "Could not create directory '"
147 << execute_path.DirName().value() << "'.";
151 if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
152 LOG(ERROR) << "Could not create symbolic link to launcher from '"
153 << execute_path.value() << "'.";
162 namespace application {
164 PackageInstallerTizen::PackageInstallerTizen(ApplicationStorage* storage)
165 : PackageInstaller(storage) {
168 bool PackageInstallerTizen::PlatformInstall(ApplicationData* app_data) {
169 std::string app_id(app_data->ID());
170 base::FilePath data_dir;
171 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
173 std::string tizen_app_id = kAppIdPrefix + app_id;
174 base::FilePath app_dir =
175 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
176 base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
177 app_id + std::string(info::kXmlExtension));
179 std::string icon_name;
180 if (!app_data->GetManifest()->GetString(info::kIconKey, &icon_name)) {
181 LOG(WARNING) << "'icon' not included in manifest";
183 // This will clean everything inside '<data dir>/<app id>'.
184 FileDeleter app_dir_cleaner(app_dir, true);
186 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, xml_path)) {
187 LOG(ERROR) << "Failed to create XML metadata file '"
188 << xml_path.value() << "'.";
192 if (!CreateAppSymbolicLink(app_dir, tizen_app_id)) {
193 LOG(ERROR) << "Failed to create symbolic link for " << app_id;
197 base::FilePath icon =
198 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
200 CommandLine cmdline(kPkgHelper);
201 cmdline.AppendSwitch("--install");
202 cmdline.AppendArg(app_id);
203 cmdline.AppendArgPath(xml_path);
204 cmdline.AppendArgPath(icon);
209 if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
210 LOG(ERROR) << "Could not launch the installation helper process.";
214 if (exit_code != 0) {
215 LOG(ERROR) << "Could not install application: "
216 << output << " (" << exit_code << ")";
220 app_dir_cleaner.Dismiss();
225 bool PackageInstallerTizen::PlatformUninstall(ApplicationData* app_data) {
227 std::string app_id(app_data->ID());
228 base::FilePath data_dir;
229 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
231 CommandLine cmdline(kPkgHelper);
232 cmdline.AppendSwitch("--uninstall");
233 cmdline.AppendArg(app_id);
238 if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
239 LOG(ERROR) << "Could not launch installer helper";
243 if (exit_code != 0) {
244 LOG(ERROR) << "Could not uninstall application: "
245 << output << " (" << exit_code << ")";
249 base::FilePath app_dir =
250 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
251 if (!base::DeleteFile(app_dir, true)) {
252 LOG(ERROR) << "Could not remove directory '" << app_dir.value() << "'";
256 base::FilePath xml_path = data_dir.AppendASCII(
257 app_id + std::string(info::kXmlExtension));
258 if (!base::DeleteFile(xml_path, false)) {
259 LOG(ERROR) << "Could not remove file '" << xml_path.value() << "'";
266 bool PackageInstallerTizen::PlatformUpdate(ApplicationData* app_data) {
267 std::string app_id(app_data->ID());
268 base::FilePath data_dir;
269 CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
271 std::string tizen_app_id = kAppIdPrefix + app_id;
272 base::FilePath app_dir =
273 data_dir.AppendASCII(info::kAppDir).AppendASCII(app_id);
274 base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
275 app_id + ".new" + std::string(info::kXmlExtension));
277 std::string icon_name;
278 if (!app_data->GetManifest()->GetString(info::kIconKey, &icon_name)) {
279 LOG(WARNING) << "'icon' not included in manifest";
281 // This will clean everything inside '<data dir>/<app id>' and the new XML.
282 FileDeleter app_dir_cleaner(app_dir, true);
283 FileDeleter new_xml_cleaner(new_xml_path, true);
285 if (!GeneratePkgInfoXml(app_data, icon_name, app_dir, new_xml_path)) {
286 LOG(ERROR) << "Could not create new XML metadata file '"
287 << new_xml_path.value() << "'.";
291 if (!CreateAppSymbolicLink(app_dir, tizen_app_id))
294 base::FilePath icon =
295 icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
297 CommandLine cmdline(kPkgHelper);
298 cmdline.AppendSwitch("--update");
299 cmdline.AppendArg(app_id);
300 cmdline.AppendArgPath(new_xml_path);
301 cmdline.AppendArgPath(icon);
306 if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
307 LOG(ERROR) << "Could not launch installer helper";
311 if (exit_code != 0) {
312 LOG(ERROR) << "Could not update application: "
313 << output << " (" << exit_code << ")";
317 base::FilePath old_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
318 app_id + std::string(info::kXmlExtension));
319 base::Move(new_xml_path, old_xml_path);
320 app_dir_cleaner.Dismiss();
324 } // namespace application