[Package] Implementation of Package API
authorSudarsana Nagineni <sudarsana.nagineni@intel.com>
Fri, 10 Oct 2014 12:53:31 +0000 (15:53 +0300)
committerSudarsana Nagineni <sudarsana.nagineni@intel.com>
Wed, 15 Oct 2014 09:28:59 +0000 (12:28 +0300)
This patch includes a fully functional implementation of Tizen
Package API. This implemention is dependent on package-manager,
pkgmgr-info and pkgmgr native APIs.

API specification can be found here:
https://developer.tizen.org/dev-guide/2.2.0/org.tizen.web.device.apireference/tizen/package.html

It also includes an example that demonstrates the usage of
Package API.

BUG=XWALK-443

14 files changed:
examples/index.html
examples/package.html [new file with mode: 0644]
package/package.gyp [new file with mode: 0644]
package/package_api.js [new file with mode: 0644]
package/package_extension.cc [new file with mode: 0644]
package/package_extension.h [new file with mode: 0644]
package/package_extension_utils.h [new file with mode: 0644]
package/package_info.cc [new file with mode: 0644]
package/package_info.h [new file with mode: 0644]
package/package_instance.cc [new file with mode: 0644]
package/package_instance.h [new file with mode: 0644]
package/package_manager.cc [new file with mode: 0644]
package/package_manager.h [new file with mode: 0644]
tizen-wrt.gyp

index c8cea61..3a78732 100644 (file)
@@ -38,5 +38,6 @@ div.block {
 <a href="system_setting.html"><div class="block">system setting</div></a>
 <a href="time.html"><div class="block">time</div></a>
 <a href="tizen.html"><div class="block">tizen top namespace</div></a>
+<a href="package.html"><div class="block">package</div></a>
 </body>
 </html>
diff --git a/examples/package.html b/examples/package.html
new file mode 100644 (file)
index 0000000..a8cc111
--- /dev/null
@@ -0,0 +1,169 @@
+<html>
+<h1>Tizen Package API</h1>
+<body>
+Input package path to Install: <input type="text" id="pkg_install" value="/home/app/content/Downloads/HangOnMan_0.0.2_2013-10-11_182519.wgt"></input>
+<input type="button" value="Install" onclick="handleInstall()"></input>
+<br>
+<br>Package Installation Status:</br>
+<textarea id="install_output" rows="5" cols="64"></textarea>
+
+<br>
+<br>
+Input package ID to Uninstall: <input type="text" id="pkg_remove" value="nrT4AQuzWO"></input>
+<input type="button" value="Uninstall" onclick="handleUninstall()"></input>
+<br>
+<br>Package Uninstallation Status:</br>
+<textarea id="remove_output" rows="5" cols="64"></textarea>
+
+<br>
+<br>
+Input package id: <input type="text" id="pkg_id" value="nrT4AQuzWO"></input>
+<input type="button" value="GePackageInfo" onclick="handleGetPkgInfo()"></input>
+<br>
+<br>Package Information:</br>
+<textarea id="pkg_info" rows="10" cols="64"></textarea>
+
+<br>
+<br>
+<input type="button" value="GetPackagesInfo" onclick="handleGetPkgsInfo()"></input>
+<br>
+<br>Packages Information:</br>
+<textarea id="pkgs_id" rows="5" cols="64"></textarea>
+
+<br>
+<br>
+<input type="button" value="AddPackageInfoEvent" onclick="handleAddPkgInfoEvent()"></input>
+<input type="button" value="RemovePackageInfoEvent" onclick="handleRemovePkgInfoEvent()"></input>
+<br>
+<br>Notifications:</br>
+<textarea id="pkg_event" rows="6" cols="64"></textarea>
+
+<pre id="console"></pre>
+<script src="js/js-test-pre.js"></script>
+<script>
+
+function handleInstall() {
+  try {
+    var pkg_install = document.getElementById("pkg_install");
+    var output = document.getElementById("install_output");
+    var onInstallation = {
+      onprogress: function(packageId, percentage) {
+        output.value += "On installation(" + packageId + ") : progress(" + percentage + ")" + "\n";
+      },
+      oncomplete: function(packageId) {
+        output.value += "Installation(" + packageId + ") Complete" + "\n";
+      }
+    };
+    var onError = function(error) {
+      output.value += "Error occurred on installation : " + error.name + "\n";
+    };
+    // FIXME(babu): https://crosswalk-project.org/jira/browse/XWALK-2564
+    /*tizen.filesystem.resolve(pkg_install.value,
+     function (file) {
+       console.log("file path : " + file.path);
+       //tizen.package.install(file.toURI(), onInstallation, onError);
+     },
+     function (err) {
+       output.value += "Error occurred on resolve : " + err.name + "\n";
+     },
+     "r");*/
+     tizen.package.install(pkg_install.value, onInstallation, onError);
+  } catch (err) {
+    debug(err.name);
+  }
+}
+
+function handleUninstall() {
+  try {
+    var pkg_remove = document.getElementById("pkg_remove");
+    var output = document.getElementById("remove_output");
+    var onUninstallation = {
+      onprogress: function(packageId, percentage) {
+        output.value += "On Uninstallation(" + packageId + ") : progress(" + percentage + ")" + "\n";
+      },
+      oncomplete: function(packageId) {
+        output.value += "Uninstallation(" + packageId + ") Complete" + "\n";
+      }
+    };
+    var onerror = function(error) {
+      output.value += "Error occurred on Uninstallation : " + error.name + "\n";
+    };
+
+    tizen.package.uninstall(pkg_remove.value, onUninstallation, onerror);
+  } catch (err) {
+    debug(err.name);
+  }
+}
+
+function handleGetPkgInfo() {
+  try {
+    var pkg_id = document.getElementById("pkg_id");
+    var info = (pkg_id.value === "current app") ?
+        tizen.package.getPackageInfo() :
+        tizen.package.getPackageInfo(pkg_id.value);
+
+    var output = document.getElementById("pkg_info");
+    output.value += "Current Package ID: " + info.id + "\n";
+    output.value += "name: " + info.name + "\n";
+    output.value += "iconPath: " + info.iconPath + "\n";
+    output.value += "version: " + info.version + "\n";
+    output.value += "totalSize: " + info.totalSize + "\n";
+    output.value += "dataSize: " + info.dataSize + "\n";
+    output.value += "lastModified: " + info.lastModified+ "\n";
+    output.value += "author: " + info.author + "\n";
+    output.value += "description: " + info.description + "\n";
+    output.value += "appIds: " + info.appIds + "\n";
+  } catch (err) {
+    debug(err.name);
+  }
+}
+
+function handleGetPkgsInfo() {
+  try {
+    var output = document.getElementById("pkgs_id");
+    var onsuccess = function(pkgs) {
+      for (var i = 0; i < pkgs.length; i++) {
+        output.value += "Package id["+i+"]: " + pkgs[i].id + "\n";
+      }
+    };
+
+    var onerror = function(error) {
+      output.value += "Fail to get packages info: " + error.name;
+    };
+
+    tizen.package.getPackagesInfo(onsuccess, onerror);
+  } catch (err) {
+    debug(err.name);
+  }
+}
+
+function handleAddPkgInfoEvent() {
+  try {
+    var output = document.getElementById("pkg_event");
+    tizen.package.setPackageInfoEventListener({
+      oninstalled: function(pkginfo) {
+        output.value += "install package name:" + pkginfo.name + "\n";
+      },
+      onupdated: function(pkginfo) {
+        output.value += "update package name:" + pkginfo.name + "\n";
+      },
+      onuninstalled: function(pkgid) {
+        output.value += "uninstalled package id:" + pkgid + '\n';
+      }
+    });
+  } catch(err) {
+    debug(err.name);
+  }
+}
+
+function handleRemovePkgInfoEvent() {
+  try {
+    tizen.package.unsetPackageInfoEventListener();
+  } catch (err) {
+    debug(err.name);
+  }
+}
+
+</script>
+</body>
+</html>
diff --git a/package/package.gyp b/package/package.gyp
new file mode 100644 (file)
index 0000000..b8355c8
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'tizen_package',
+      'type': 'loadable_module',
+      'sources': [
+        'package_api.js',
+        'package_extension.cc',
+        'package_extension.h',
+        'package_extension_utils.h',
+        'package_info.cc',
+        'package_info.h',
+        'package_instance.cc',
+        'package_instance.h',
+        'package_manager.cc',
+        'package_manager.h',
+      ],
+      'conditions': [
+        ['tizen == 1', {
+          'includes': [
+            '../common/pkg-config.gypi',
+          ],
+          'variables': {
+            'packages': [
+              'capi-appfw-package-manager',
+              'pkgmgr',
+              'pkgmgr-info',
+            ]
+          },
+        }],
+      ],
+    }
+  ]
+}
diff --git a/package/package_api.js b/package/package_api.js
new file mode 100644 (file)
index 0000000..ba63a2c
--- /dev/null
@@ -0,0 +1,181 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+var PackageEventState = {
+  'STARTED': 0,
+  'PROCESSING': 1,
+  'COMPLETED': 2,
+  'FAILED': 3
+};
+
+var PackageEventType = {
+  'INSTALL': 0,
+  'UNINSTALL': 1,
+  'UPDATE': 2
+};
+
+var asyncCallbacks = {
+  _next_id: 0,
+  _callbacks: {},
+  key: '_callback',
+  infoEvent: '_pkgInfoEventCallback',
+
+  setup: function(callback) {
+    var id = ++this._next_id;
+    this._callbacks[id] = callback;
+    return id;
+  },
+
+  dispatch: function(m) {
+    var id = m[this.key];
+    var callback = this._callbacks[id];
+    callback.call(null, m);
+    if (m.error != null || m.cmd == PackageEventState.COMPLETED ||
+        m.cmd == PackageEventState.FAILED)
+      delete this._callbacks[id];
+  }
+};
+
+extension.setMessageListener(function(msg) {
+  var m = JSON.parse(msg);
+  if (typeof m[asyncCallbacks.key] === 'number') {
+    asyncCallbacks.dispatch(m);
+  } else if (m[asyncCallbacks.key] === asyncCallbacks.infoEvent &&
+             exports.changeListener != null) {
+    if (m.eventType == PackageEventType.INSTALL) {
+      var pkgInfo = new PackageInformation(m.data);
+      exports.changeListener.oninstalled(pkgInfo);
+    }
+    if (m.eventType == PackageEventType.UPDATE) {
+      var pkgInfo = new PackageInformation(m.data);
+      exports.changeListener.onupdated(m.data);
+    }
+    if (m.eventType == PackageEventType.UNINSTALL) {
+      exports.changeListener.onuninstalled(m.data);
+    }
+  } else {
+    console.error('unexpected message received' + msg);
+  }
+});
+
+function postMessage(msg, callbackId) {
+  msg[asyncCallbacks.key] = callbackId;
+  extension.postMessage(JSON.stringify(msg));
+}
+
+function sendSyncMessage(msg) {
+  return JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg)));
+}
+
+function PackageManager() {
+  this.changeListener = null;
+}
+
+function defineReadOnlyProperty(object, key, value) {
+  Object.defineProperty(object, key, {
+    enumerable: true,
+    writable: false,
+    value: value
+  });
+}
+
+// PackageInformation interface.
+function PackageInformation(json) {
+  for (var field in json) {
+    var val = json[field];
+    if (field === 'installDate')
+      val = new Date(val * 1000);
+    defineReadOnlyProperty(this, field, val);
+  }
+}
+
+PackageManager.prototype.install = function(path, onsuccess, onerror) {
+  if (typeof path !== 'string' ||
+      onsuccess === undefined ||
+      onerror && typeof onerror !== 'function')
+    throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+  var callbackId = asyncCallbacks.setup(function(result) {
+    if (result.error != null && typeof onerror === 'function') {
+      onerror(new tizen.WebAPIError(result.errorCode));
+    } else if (result.cmd == PackageEventState.COMPLETED &&
+               typeof onsuccess.oncomplete !== 'undefined') {
+      onsuccess.oncomplete(result.id);
+    } else if (typeof onsuccess.onprogress !== 'undefined') {
+      onsuccess.onprogress(result.id, result.progress);
+    }
+  });
+
+  var msg = { cmd: 'PackageManager.install', id: path };
+  postMessage(msg, callbackId);
+};
+
+PackageManager.prototype.uninstall = function(id, onsuccess, onerror) {
+  if (typeof id !== 'string' ||
+      onsuccess === undefined ||
+      onerror && typeof onerror !== 'function')
+    throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+  var callbackId = asyncCallbacks.setup(function(result) {
+    if (result.error != null && typeof onerror === 'function') {
+      return onerror(new tizen.WebAPIError(result.error));
+    } else if (result.cmd == PackageEventState.COMPLETED &&
+               typeof onsuccess.oncomplete !== 'undefined') {
+      onsuccess.oncomplete(result.id);
+    } else if (typeof onsuccess.onprogress !== 'undefined') {
+      onsuccess.onprogress(result.id, result.progress);
+    }
+  });
+
+  var msg = { cmd: 'PackageManager.uninstall', id: id };
+  postMessage(msg, callbackId);
+};
+
+PackageManager.prototype.getPackageInfo = function(pkgId) {
+  if (typeof pkgId !== 'string' && pkgId != undefined)
+    throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+  var result = sendSyncMessage({ cmd: 'PackageManager.getPackageInfo', id: pkgId });
+  if (result.error != null)
+    throw new tizen.WebAPIException(result.error);
+
+  return new PackageInformation(result.data);
+};
+
+PackageManager.prototype.getPackagesInfo = function(onsuccess, onerror) {
+  if (typeof onsuccess !== 'function' ||
+      onerror && typeof onerror !== 'function')
+    throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+  var callbackId = asyncCallbacks.setup(function(result) {
+    if (result.error != null && typeof onerror === 'function') {
+      return onerror(new tizen.WebAPIError(result.error));
+    }
+
+    var pkgsInfo = [];
+    for (var i = 0, len = result.data.length; i < len; ++i)
+      pkgsInfo.push(new PackageInformation(result.data[i]));
+    return onsuccess(pkgsInfo);
+  });
+
+  var msg = { cmd: 'PackageManager.getPackagesInfo' };
+  postMessage(msg, callbackId);
+};
+
+PackageManager.prototype.setPackageInfoEventListener = function(listener) {
+  if (typeof listener !== 'object' ||
+      typeof listener.oninstalled !== 'function' ||
+      typeof listener.onupdated !== 'function' ||
+      typeof listener.onuninstalled !== 'function')
+    throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+  this.changeListener = listener;
+  sendSyncMessage({cmd: 'PackageManager.setPackageInfoEventListener'});
+};
+
+PackageManager.prototype.unsetPackageInfoEventListener = function() {
+  this.changeListener = null;
+  sendSyncMessage({cmd: 'PackageManager.unsetPackageInfoEventListener'});
+};
+
+exports = new PackageManager();
diff --git a/package/package_extension.cc b/package/package_extension.cc
new file mode 100644 (file)
index 0000000..1827baa
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <iostream>
+#include <sstream>
+
+#include "common/picojson.h"
+#include "package/package_extension.h"
+#include "package/package_instance.h"
+
+common::Extension* CreateExtension() {
+  std::string id_str = common::Extension::GetRuntimeVariable("app_id", 64);
+  picojson::value id_val;
+  std::istringstream buf(id_str);
+  std::string error = picojson::parse(id_val, buf);
+  if (!error.empty()) {
+    std::cerr << "Got invalid application ID." << std::endl;
+    return NULL;
+  }
+
+  std::string app_id = id_val.get<std::string>();
+  if (app_id.empty()) {
+    std::cerr << "Package extension will not be created without "
+              << "context." << std::endl;
+    return NULL;
+  }
+
+  return new PackageExtension(app_id);
+}
+
+// JS source code for the API
+extern const char kSource_package_api[];
+
+PackageExtension::PackageExtension(const std::string& app_id)
+    : app_id_(app_id) {
+  SetExtensionName("tizen.package");
+  SetJavaScriptAPI(kSource_package_api);
+}
+
+PackageExtension::~PackageExtension() {}
+
+common::Instance* PackageExtension::CreateInstance() {
+  return new PackageInstance(this);
+}
diff --git a/package/package_extension.h b/package/package_extension.h
new file mode 100644 (file)
index 0000000..8a5a9e6
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PACKAGE_PACKAGE_EXTENSION_H_
+#define PACKAGE_PACKAGE_EXTENSION_H_
+
+#include <string>
+
+#include "common/extension.h"
+
+class PackageExtension : public common::Extension {
+ public:
+  explicit PackageExtension(const std::string& app_id);
+  virtual ~PackageExtension();
+
+  const std::string& current_app_id() { return app_id_; }
+
+ private:
+  virtual common::Instance* CreateInstance();
+  std::string app_id_;
+};
+
+#endif  // PACKAGE_PACKAGE_EXTENSION_H_
diff --git a/package/package_extension_utils.h b/package/package_extension_utils.h
new file mode 100644 (file)
index 0000000..ab90693
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PACKAGE_PACKAGE_EXTENSION_UTILS_H_
+#define PACKAGE_PACKAGE_EXTENSION_UTILS_H_
+
+#include "common/picojson.h"
+#include "tizen/tizen.h"
+
+inline picojson::value* CreateResultMessage() {
+  return new picojson::value(picojson::object());
+}
+
+inline picojson::value* CreateResultMessage(WebApiAPIErrors error) {
+  picojson::object obj;
+  obj["error"] = picojson::value(static_cast<double>(error));
+  return new picojson::value(obj);
+}
+
+inline picojson::value* CreateResultMessage(const picojson::array& data) {
+  picojson::object obj;
+  obj["data"] = picojson::value(data);
+  return new picojson::value(obj);
+}
+
+inline picojson::value* CreateResultMessage(const picojson::value& data) {
+  picojson::object obj;
+  obj["data"] = data;
+  return new picojson::value(obj);
+}
+
+#endif  // PACKAGE_PACKAGE_EXTENSION_UTILS_H_
diff --git a/package/package_info.cc b/package/package_info.cc
new file mode 100644 (file)
index 0000000..edf929b
--- /dev/null
@@ -0,0 +1,335 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "package/package_info.h"
+
+#include <package-manager.h>
+#include <pkgmgr-dbinfo.h>
+#include <pkgmgr-info.h>
+
+#include <unistd.h>
+#include <vector>
+
+#include "common/utils.h"
+#include "package/package_extension_utils.h"
+#include "tizen/tizen.h"
+
+namespace {
+
+void SetErrorMessage(picojson::object& error,
+                     const std::string& property_name) {
+  std::string error_message = "Fail to get " + property_name;
+  if (property_name == "pkginfo" || property_name == "pkgid")
+    error["code"] = picojson::value(
+        static_cast<double>(WebApiAPIErrors::NOT_FOUND_ERR));
+  else
+    error["code"] = picojson::value(
+        static_cast<double>(WebApiAPIErrors::UNKNOWN_ERR));
+  error["message"] = picojson::value(error_message);
+  std::cerr << error_message << std::endl;
+}
+
+class PkgMgrHandle {
+ public:
+  static PkgMgrHandle* Create(const std::string& id,
+                              bool is_app_id,
+                              picojson::object& error) {
+    pkgmgr_pkginfo_h pkginfo_handle;
+    int ret = PMINFO_R_OK;
+    char* pkg_id = NULL;
+    uid_t uid = getuid();
+    // Get the package ID of the current application.
+    if (is_app_id) {
+      pkgmgrinfo_appinfo_h handle;
+      if (uid != GLOBAL_USER)
+        ret = pkgmgrinfo_appinfo_get_usr_appinfo(id.c_str(), uid, &handle);
+      else
+        ret = pkgmgrinfo_appinfo_get_appinfo(id.c_str(), &handle);
+
+      if (ret != PMINFO_R_OK) {
+        SetErrorMessage(error, "pkginfo");
+        return NULL;
+      }
+
+      ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkg_id);
+      pkgmgrinfo_appinfo_destroy_appinfo(handle);
+      if (ret != PMINFO_R_OK) {
+        SetErrorMessage(error, "pkginfo");
+        return NULL;
+      }
+    } else {
+      pkg_id = const_cast<char*>(id.c_str());
+    }
+
+    if (uid != GLOBAL_USER)
+      ret = pkgmgr_pkginfo_get_usr_pkginfo(pkg_id, uid, &pkginfo_handle);
+    else
+      ret = pkgmgr_pkginfo_get_pkginfo(pkg_id, &pkginfo_handle);
+
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "pkginfo");
+      return NULL;
+    }
+
+    return new PkgMgrHandle(pkg_id, pkginfo_handle, true);
+  }
+
+  static PkgMgrHandle* Create(pkgmgrinfo_pkginfo_h& pkginfo_handle,
+                              picojson::object& error) {
+    char* pkg_id = NULL;
+    int ret = pkgmgrinfo_pkginfo_get_pkgid(pkginfo_handle, &pkg_id);
+    if (ret != PMINFO_R_OK || !pkg_id) {
+      SetErrorMessage(error, "pkgid");
+      return NULL;
+    }
+
+    return new PkgMgrHandle(pkg_id, pkginfo_handle, false);
+  }
+
+  ~PkgMgrHandle() {
+    if (owns_pkginfo_handle_)
+      pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle_);
+  }
+
+  const std::string& pkg_id() const { return pkg_id_; }
+
+  bool GetName(char** name, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_pkgname(pkginfo_handle_, name);
+    if (ret != PMINFO_R_OK || *name == NULL) {
+      SetErrorMessage(error, "name");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetIcon(char** icon_path, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_icon(pkginfo_handle_, icon_path);
+    if ((ret != PMINFO_R_OK) || (*icon_path == NULL)) {
+      SetErrorMessage(error, "iconPath");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetVersion(char** version, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_version(pkginfo_handle_, version);
+    if ((ret != PMINFO_R_OK) || (*version == NULL)) {
+      SetErrorMessage(error, "version");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetTotalSize(int* total_size, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_total_size(pkginfo_handle_, total_size);
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "totalSize");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetDataSize(int* data_size, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_data_size(pkginfo_handle_, data_size);
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "dataSize");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetModifiedDate(int* date, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_installed_time(pkginfo_handle_, date);
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "lastModified");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetAuthor(char** author, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_author_name(pkginfo_handle_, author);
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "author");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetDescription(char** description, picojson::object& error) {
+    int ret = pkgmgrinfo_pkginfo_get_description(pkginfo_handle_, description);
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "description");
+      return false;
+    }
+    return true;
+  }
+
+  bool GetAppIds(std::vector<picojson::value>* appIds,
+                 picojson::object& error) {
+    int ret = pkgmgrinfo_appinfo_get_list(pkginfo_handle_, PMINFO_ALL_APP,
+        &PkgMgrHandle::SaveAppIdsCallback, &appIds);
+    if (ret != PMINFO_R_OK) {
+      SetErrorMessage(error, "appIds");
+      return false;
+    }
+    return true;
+  }
+
+ private:
+  static int SaveAppIdsCallback(const pkgmgrinfo_appinfo_h handle, void* data) {
+    char* app_id = NULL;
+    int ret = pkgmgrinfo_appinfo_get_appid(handle, &app_id);
+    if (ret != PMINFO_R_OK)
+      return -1;
+
+    std::vector<picojson::value>* appIds =
+        static_cast<std::vector<picojson::value>*>(data);
+    appIds->push_back(picojson::value(app_id));
+
+    return 1;
+  }
+
+  PkgMgrHandle(const std::string& pkg_id,
+               pkgmgrinfo_pkginfo_h pkginfo_handle,
+               bool owns_pkginfo_handle)
+    : pkg_id_(pkg_id),
+      pkginfo_handle_(pkginfo_handle),
+      owns_pkginfo_handle_(owns_pkginfo_handle) {
+  }
+
+  std::string pkg_id_;
+  pkgmgrinfo_pkginfo_h pkginfo_handle_;
+  bool owns_pkginfo_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(PkgMgrHandle);
+};
+
+void RetrieveAppInfo(PkgMgrHandle& handle,
+                     picojson::object& info,
+                     picojson::object& error) {
+  char* name = NULL;
+  char* icon_path = NULL;
+  char* version = NULL;
+  int total_size = 0;
+  int data_size = 0;
+  int modified_date = 0;
+  char* author = NULL;
+  char* description = NULL;
+  std::vector<picojson::value> appIds;
+
+  if (!handle.GetName(&name, error) ||
+      !handle.GetIcon(&icon_path, error) ||
+      !handle.GetVersion(&version, error) ||
+      !handle.GetTotalSize(&total_size, error) ||
+      !handle.GetDataSize(&data_size, error) ||
+      !handle.GetModifiedDate(&modified_date, error) ||
+      !handle.GetAuthor(&author, error) ||
+      !handle.GetDescription(&description, error) ||
+      !handle.GetAppIds(&appIds, error)) {
+    return;
+  }
+
+  info["id"] = picojson::value(handle.pkg_id());
+  info["name"] = picojson::value(name);
+  info["iconPath"] = picojson::value(icon_path);
+  info["version"] = picojson::value(version);
+  info["totalSize"] = picojson::value(static_cast<double>(total_size));
+  info["dataSize"] = picojson::value(static_cast<double>(data_size));
+  info["lastModified"] = picojson::value(static_cast<double>(modified_date));
+  info["author"] = picojson::value(author);
+  info["description"] = picojson::value(description);
+  info["appIds"] = picojson::value(appIds);
+}
+
+int GetPackagesInfoCallback(pkgmgrinfo_pkginfo_h pkginfo_handle,
+                            void* user_data) {
+  picojson::array* data = static_cast<picojson::array*>(user_data);
+  picojson::object info;
+  picojson::object error;
+
+  std::unique_ptr<PkgMgrHandle> handle(
+      PkgMgrHandle::Create(pkginfo_handle, error));
+  if (!handle) {
+    data->clear();
+    return -1;
+  }
+
+  RetrieveAppInfo(*handle, info, error);
+  if (!error.empty()) {
+    data->clear();
+    return -1;
+  }
+
+  data->push_back(picojson::value(info));
+  return 0;
+}
+
+void RetrieveAllInstalledPackagesInfo(picojson::array& data,
+                                      picojson::object& error) {
+  int ret = PMINFO_R_OK;
+  uid_t uid = getuid();
+
+  if (uid != GLOBAL_USER)
+    ret = pkgmgrinfo_pkginfo_get_usr_list(GetPackagesInfoCallback, &data, uid);
+  else
+    ret = pkgmgrinfo_pkginfo_get_list(GetPackagesInfoCallback, &data);
+
+  if (ret != PMINFO_R_OK) {
+    SetErrorMessage(error, "get_all");
+    return;
+  }
+
+  if (data.empty())
+    SetErrorMessage(error, "get_all");
+}
+
+}  // namespace
+
+picojson::value* PackageInformation::GetAllInstalled() {
+  picojson::array data;
+  picojson::object error;
+
+  RetrieveAllInstalledPackagesInfo(data, error);
+  if (!error.empty())
+    return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR);
+  else
+    return CreateResultMessage(data);
+}
+
+PackageInformation::PackageInformation(
+    const std::string& id,
+    bool is_app_id) {
+  std::unique_ptr<PkgMgrHandle> handle(PkgMgrHandle::Create(
+      id, is_app_id, error_));
+  if (handle && IsValid())
+    RetrieveAppInfo(*handle, data_, error_);
+}
+
+PackageInformation::~PackageInformation() {}
+
+const picojson::value& PackageInformation::Value() {
+  if (value_.is<picojson::null>() && IsValid())
+    value_ = picojson::value(data_);
+
+  return value_;
+}
+
+const std::string PackageInformation::Serialize() {
+  std::unique_ptr<picojson::value> result;
+  if (!IsValid()) {
+    picojson::object::const_iterator it = error_.find("code");
+    assert(it != error_.end());
+    result.reset(CreateResultMessage(
+        static_cast<WebApiAPIErrors>(it->second.get<double>())));
+  } else {
+    result.reset(CreateResultMessage(Value()));
+  }
+
+  return result->serialize();
+}
+
+bool PackageInformation::IsValid() const {
+  return error_.empty();
+}
diff --git a/package/package_info.h b/package/package_info.h
new file mode 100644 (file)
index 0000000..b262e51
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PACKAGE_PACKAGE_INFO_H_
+#define PACKAGE_PACKAGE_INFO_H_
+
+#include <memory>
+#include <string>
+
+#include "common/picojson.h"
+
+class PackageInformation {
+ public:
+  static picojson::value* GetAllInstalled();
+
+  PackageInformation(const std::string& id, bool is_app_id);
+  ~PackageInformation();
+
+  const picojson::value& Value();
+  const std::string Serialize();
+  bool IsValid() const;
+
+ private:
+  picojson::object data_;
+  picojson::object error_;
+  picojson::value value_;
+};
+
+#endif  // PACKAGE_PACKAGE_INFO_H_
diff --git a/package/package_instance.cc b/package/package_instance.cc
new file mode 100644 (file)
index 0000000..ae74db4
--- /dev/null
@@ -0,0 +1,136 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "package/package_instance.h"
+
+#include "package/package_extension.h"
+#include "package/package_info.h"
+#include "package/package_manager.h"
+
+namespace {
+
+const char kJSCallbackKey[] = "_callback";
+const char kPkgInfoEventCallback[] = "_pkgInfoEventCallback";
+
+}  // namespace
+
+PackageInstance::PackageInstance(PackageExtension* extension)
+    : extension_(extension) {
+  package_manager_.reset(new PackageManager(this));
+}
+
+PackageInstance::~PackageInstance() {
+  package_manager_->PackageManagerDestroy();
+}
+
+void PackageInstance::HandleMessage(const char* message) {
+  picojson::value v;
+  picojson::value::object o;
+
+  std::string err;
+  picojson::parse(v, message, message + strlen(message), &err);
+  if (!err.empty()) {
+    std::cerr << "Ignoring message. \n";
+    return;
+  }
+
+  std::string cmd = v.get("cmd").to_str();
+  if (cmd == "PackageManager.install") {
+    HandleInstallRequest(v);
+  } else if (cmd == "PackageManager.uninstall") {
+    HandleUninstallRequest(v);
+  } else if (cmd == "PackageManager.getPackagesInfo") {
+    HandleGetPackagesInfoRequest(v);
+  } else {
+    std::cerr << "Message " + cmd + " is not supported.\n";
+  }
+}
+
+void PackageInstance::HandleSyncMessage(const char* message) {
+  picojson::value v;
+  picojson::value::object o;
+
+  std::string err;
+  picojson::parse(v, message, message + strlen(message), &err);
+  if (!err.empty()) {
+    std::cerr << "Ignoring message.\n";
+    return;
+  }
+
+  std::string cmd = v.get("cmd").to_str();
+  if (cmd == "PackageManager.setPackageInfoEventListener") {
+    HandleRegisterPackageInfoEvent();
+  } else if (cmd == "PackageManager.unsetPackageInfoEventListener") {
+    HandleUnregisterPackageInfoEvent();
+  } else if (cmd == "PackageManager.getPackageInfo") {
+    HandleGetPackageInfoRequest(v);
+  } else {
+    std::cerr << "Message " + cmd + " is not supported.\n";
+  }
+}
+
+void PackageInstance::HandleInstallRequest(const picojson::value& msg) {
+  const char* pkg_path = msg.get("id").to_str().c_str();
+  double callback_id = msg.get(kJSCallbackKey).get<double>();
+  std::unique_ptr<picojson::value> result(
+      package_manager()->InstallPackage(pkg_path, callback_id));
+  if (result)
+    ReturnMessageAsync(callback_id, *result);
+}
+
+void PackageInstance::HandleUninstallRequest(const picojson::value& msg) {
+  const char* pkg_id = msg.get("id").to_str().c_str();
+  double callback_id = msg.get(kJSCallbackKey).get<double>();
+  std::unique_ptr<picojson::value> result(
+      package_manager()->UnInstallPackage(pkg_id, callback_id));
+  if (result)
+    ReturnMessageAsync(callback_id, *result);
+}
+
+void PackageInstance::HandleGetPackageInfoRequest(const picojson::value& msg) {
+  std::string id = (msg.contains("id") && msg.get("id").is<std::string>()) ?
+      msg.get("id").to_str() :
+      extension_->current_app_id();
+
+  PackageInformation pkg_info(id, false);
+  SendSyncReply(pkg_info.Serialize().c_str());
+}
+
+void PackageInstance::HandleGetPackagesInfoRequest(const picojson::value& msg) {
+  std::unique_ptr<picojson::value> result(
+      PackageInformation::GetAllInstalled());
+  double callback_id = msg.get(kJSCallbackKey).get<double>();
+  ReturnMessageAsync(callback_id, *result);
+}
+
+void PackageInstance::HandleRegisterPackageInfoEvent() {
+  std::unique_ptr<picojson::value> result(
+      package_manager()->RegisterPackageInfoEvent());
+  SendSyncReply(result->serialize().c_str());
+}
+
+void PackageInstance::HandleUnregisterPackageInfoEvent() {
+  std::unique_ptr<picojson::value> result(
+      package_manager()->UnregisterPackageInfoEvent());
+  SendSyncReply(result->serialize().c_str());
+}
+
+void PackageInstance::PostPackageInfoEventMessage(picojson::object& events) {
+  events[kJSCallbackKey] = picojson::value(kPkgInfoEventCallback);
+  PostMessage(picojson::value(events).serialize().c_str());
+}
+
+void PackageInstance::PostPackageRequestMessage(
+    picojson::object& reply,
+    double callback_id) {
+  reply[kJSCallbackKey] = picojson::value(callback_id);
+  PostMessage(picojson::value(reply).serialize().c_str());
+}
+
+void PackageInstance::ReturnMessageAsync(
+    double callback_id,
+    picojson::value& value) {
+  value.get<picojson::object>()[kJSCallbackKey] = picojson::value(callback_id);
+  PostMessage(value.serialize().c_str());
+}
diff --git a/package/package_instance.h b/package/package_instance.h
new file mode 100644 (file)
index 0000000..6bbf1cd
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PACKAGE_PACKAGE_INSTANCE_H_
+#define PACKAGE_PACKAGE_INSTANCE_H_
+
+#include <memory>
+#include <string>
+
+#include "common/extension.h"
+#include "common/picojson.h"
+#include "tizen/tizen.h"
+
+namespace picojson {
+
+class value;
+
+}  // namespace picojson
+
+class PackageManager;
+class PackageExtension;
+
+class PackageInstance : public common::Instance {
+ public:
+  explicit PackageInstance(PackageExtension* extension);
+  virtual ~PackageInstance();
+
+  void PostPackageInfoEventMessage(picojson::object& events);
+  void PostPackageRequestMessage(picojson::object& events, double callback_id);
+
+  PackageManager* package_manager() { return package_manager_.get(); }
+
+ private:
+  // common::Instance implementation
+  virtual void HandleMessage(const char* msg);
+  virtual void HandleSyncMessage(const char* msg);
+
+  // Synchronous message handlers.
+  void HandleGetPackageInfoRequest(const picojson::value& json);
+  void HandleRegisterPackageInfoEvent();
+  void HandleUnregisterPackageInfoEvent();
+
+  // Asynchronous message handlers.
+  void HandleInstallRequest(const picojson::value& json);
+  void HandleUninstallRequest(const picojson::value& json);
+  void HandleGetPackagesInfoRequest(const picojson::value& json);
+
+  void ReturnMessageAsync(double callback_id, picojson::value& value);
+
+  std::unique_ptr<PackageManager> package_manager_;
+  PackageExtension* extension_;
+};
+
+#endif  // PACKAGE_PACKAGE_INSTANCE_H_
diff --git a/package/package_manager.cc b/package/package_manager.cc
new file mode 100644 (file)
index 0000000..d885e7a
--- /dev/null
@@ -0,0 +1,172 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "package/package_manager.h"
+
+#include <package-manager.h>
+#include <pkgmgr-dbinfo.h>
+#include <unistd.h>
+
+#include "common/picojson.h"
+#include "package/package_extension_utils.h"
+#include "package/package_info.h"
+#include "tizen/tizen.h"
+
+namespace {
+
+void PackageRequestsCallback(int id, const char *type, const char *package,
+                             package_manager_event_type_e event_type,
+                             package_manager_event_state_e event_state,
+                             int progress, package_manager_error_e error,
+                             void *user_data) {
+  std::string pkg_id(package);
+  PackageManager* manager = static_cast<PackageManager*> (user_data);
+  manager->OnPackageRequestCallback(id, pkg_id, event_type,
+                                    event_state, progress);
+}
+
+void PackageEventsCallback(const char *type, const char *package,
+                           package_manager_event_type_e event_type,
+                           package_manager_event_state_e event_state,
+                           int progress, package_manager_error_e error,
+                           void *user_data) {
+  if (event_state != PACKAGE_MANAGER_EVENT_STATE_COMPLETED)
+    return;
+
+  std::string pkg_id(package);
+  PackageManager* manager = static_cast<PackageManager*> (user_data);
+  manager->OnPackageInfoEvent(pkg_id, event_type);
+}
+
+}  // namespace
+
+PackageManager::PackageManager(PackageInstance* instace)
+    : instance_(instace) {
+  int ret = package_manager_create(&manager_);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE)
+    std::cerr << "package manager creation failed." << std::endl;
+
+  ret = package_manager_request_create(&request_handle_);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+    std::cerr << "package manager fail to create request handle." << std::endl;
+  } else {
+    // Install/Uninstall works only in quiet mode for now.
+    package_manager_request_set_mode(request_handle_,
+                                     PACKAGE_MANAGER_REQUEST_MODE_QUIET);
+    package_manager_request_set_event_cb(request_handle_,
+                                         PackageRequestsCallback,
+                                         this);
+  }
+}
+
+PackageManager::~PackageManager() {}
+
+void PackageManager::PackageManagerDestroy() {
+  if (request_handle_) {
+    package_manager_request_unset_event_cb(request_handle_);
+    package_manager_request_destroy(request_handle_);
+  }
+
+  if (manager_)
+    package_manager_destroy(manager_);
+}
+
+picojson::value* PackageManager::InstallPackage(
+    const char* pkg_path,
+    double callback_id) {
+  int request_id;
+  int ret = package_manager_request_install(
+      request_handle_,
+      pkg_path,
+      &request_id);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE)
+    return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR);
+
+  callbacks_id_map_[request_id] = callback_id;
+  return NULL;
+}
+
+picojson::value* PackageManager::UnInstallPackage(
+    const char* pkg_id,
+    double callback_id) {
+  // FIXME(babu): Carshes on calling package_manager_request_uninstall()
+  // if package is not installed. API must be fixed before we remove this.
+  pkgmgr_pkginfo_h handle;
+  uid_t uid = getuid();
+  int ret = (uid != GLOBAL_USER) ?
+      pkgmgr_pkginfo_get_usr_pkginfo(pkg_id, uid, &handle) :
+      pkgmgr_pkginfo_get_pkginfo(pkg_id, &handle);
+
+  if (ret != PKGMGR_R_OK)
+    return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR);
+
+  pkgmgr_pkginfo_destroy_pkginfo(handle);
+
+  int request_id;
+  ret = package_manager_request_uninstall(request_handle_,
+                                          pkg_id,
+                                          &request_id);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE)
+    return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR);
+
+  callbacks_id_map_[request_id] = callback_id;
+  return NULL;
+}
+
+picojson::value* PackageManager::RegisterPackageInfoEvent() {
+  int ret = package_manager_set_event_cb(manager_,
+                                         PackageEventsCallback,
+                                         this);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE)
+    return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR);
+
+  return CreateResultMessage();
+}
+
+picojson::value* PackageManager::UnregisterPackageInfoEvent() {
+  if (package_manager_unset_event_cb(manager_) != PACKAGE_MANAGER_ERROR_NONE)
+    return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR);
+
+  return CreateResultMessage();
+}
+
+void PackageManager::OnPackageRequestCallback(
+    int id,
+    const std::string& pkg_id,
+    int event_type, int event_state,
+    int progress) {
+  picojson::object result;
+  if (event_state != PACKAGE_MANAGER_EVENT_STATE_FAILED) {
+    result["id"] = picojson::value(pkg_id);
+    result["cmd"] = picojson::value(static_cast<double>(event_state));
+    if (event_state != PACKAGE_MANAGER_EVENT_STATE_PROCESSING)
+      result["progress"] = picojson::value(static_cast<double>(progress));
+  } else {
+    result["error"] =
+      picojson::value(static_cast<double>(WebApiAPIErrors::UNKNOWN_ERR));
+  }
+
+  double callback_id = 0;
+  std::map<int, double>::const_iterator iter = callbacks_id_map_.find(id);
+  if (iter != callbacks_id_map_.end())
+    callback_id = iter->second;
+  instance_->PostPackageRequestMessage(result, callback_id);
+}
+
+void PackageManager::OnPackageInfoEvent(
+    const std::string& pkg_id,
+    int event_type) {
+  picojson::object result;
+  if (event_type == PACAKGE_MANAGER_EVENT_TYPE_INSTALL ||
+      event_type == PACKAGE_MANAGER_EVENT_TYPE_UPDATE) {
+    PackageInformation pkg_info(pkg_id, false);
+    if (pkg_info.IsValid())
+      result["data"] = pkg_info.Value();
+  } else if (event_type == PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL) {
+    result["data"] = picojson::value(pkg_id);
+  }
+
+  result["eventType"] = picojson::value(static_cast<double>(event_type));
+  instance_->PostPackageInfoEventMessage(result);
+}
diff --git a/package/package_manager.h b/package/package_manager.h
new file mode 100644 (file)
index 0000000..7514aba
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PACKAGE_PACKAGE_MANAGER_H_
+#define PACKAGE_PACKAGE_MANAGER_H_
+
+#include <package_manager.h>
+
+#include <map>
+#include <string>
+
+#include "package/package_instance.h"
+
+class PackageManager {
+ public:
+  explicit PackageManager(PackageInstance* instance);
+  ~PackageManager();
+  void PackageManagerDestroy();
+
+  picojson::value* InstallPackage(const char* pkg_path, double request_id);
+  picojson::value* UnInstallPackage(const char* pkg_id, double request_id);
+  picojson::value* RegisterPackageInfoEvent();
+  picojson::value* UnregisterPackageInfoEvent();
+
+  void OnPackageInfoEvent(const std::string& pkg_id, int event_type);
+  void OnPackageRequestCallback(
+      int id, const std::string& pkg_id,
+      int event_type, int event_state,
+      int progress);
+
+ private:
+  PackageInstance* instance_;
+  package_manager_h manager_;
+  package_manager_request_h request_handle_;
+  std::map<int, double> callbacks_id_map_;
+};
+
+#endif  // PACKAGE_PACKAGE_MANAGER_H_
index 1d96fe5..4ba0aaa 100644 (file)
@@ -35,6 +35,7 @@
             'filesystem/filesystem.gyp:*',
             'messageport/messageport.gyp:*',
             'nfc/nfc.gyp:*',
+            'package/package.gyp:*',
           ],
         }],
         [ 'extension_host_os == "mobile"', {