[Download] module implementation
authorJin-Woo Jeong <jinw.jeong@samsung.com>
Sat, 14 Feb 2015 08:14:23 +0000 (17:14 +0900)
committerJin-Woo Jeong <jinw.jeong@samsung.com>
Sat, 14 Feb 2015 08:16:22 +0000 (17:16 +0900)
Validation: - Own sample app (basic functionality test)
            - tct : 100% (62/62)

Change-Id: Iee69ab930b5a81772b69c5c736b78bae9daa00f3

packaging/webapi-plugins.spec
src/download/download.gyp [new file with mode: 0644]
src/download/download_api.js [new file with mode: 0644]
src/download/download_extension.cc [new file with mode: 0644]
src/download/download_extension.h [new file with mode: 0644]
src/download/download_instance.cc [new file with mode: 0644]
src/download/download_instance.h [new file with mode: 0644]
src/tizen-wrt.gyp

index 1fe9c5cf53796e82559efa752af17428c100ab2d..ea5203a3684817d601d498044c39c02a7dd69550 100644 (file)
@@ -31,7 +31,7 @@ Source0:    %{name}-%{version}.tar.gz
 %define tizen_feature_core_api_support            0
 %define tizen_feature_datacontrol_support         1
 %define tizen_feature_datasync_support            1
-%define tizen_feature_download_support            0
+%define tizen_feature_download_support            1
 %define tizen_feature_exif_support                0
 %define tizen_feature_fm_radio_support            0
 %define tizen_feature_gamepad_support             0
@@ -195,6 +195,10 @@ BuildRequires: pkgconfig(accounts-svc)
 BuildRequires: pkgconfig(capi-data-control)
 %endif
 
+%if 0%{?tizen_feature_download_support}
+BuildRequires: pkgconfig(capi-web-url-download)
+%endif
+
 %if 0%{?tizen_feature_power_support}
 BuildRequires: pkgconfig(capi-system-power)
 %endif
diff --git a/src/download/download.gyp b/src/download/download.gyp
new file mode 100644 (file)
index 0000000..fbc669b
--- /dev/null
@@ -0,0 +1,28 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'tizen_download',
+      'type': 'loadable_module',
+      'sources': [
+        'download_api.js',
+        'download_extension.cc',
+        'download_extension.h',
+        'download_instance.cc',
+        'download_instance.h',
+      ],
+      'conditions': [
+        ['tizen == 1', {
+          'variables': {
+            'packages': [
+               'capi-web-url-download',
+               'capi-system-info'
+            ]
+          },
+        }],
+      ],
+    },
+  ],
+}
diff --git a/src/download/download_api.js b/src/download/download_api.js
new file mode 100644 (file)
index 0000000..ff9869d
--- /dev/null
@@ -0,0 +1,298 @@
+// Download
+
+var validator_ = xwalk.utils.validator;
+var types_ = validator_.Types;
+var check_ = xwalk.utils.type;
+
+
+var callbackId = 0;
+var callbacks = {};
+var requests = {};
+
+
+extension.setMessageListener(function(json) {
+
+  var result = JSON.parse(json);
+  var callback = callbacks[result['callbackId']];
+  //console.log("PostMessage received: " + result.status);
+
+  if (result.status == 'progress') {
+    var receivedSize = result.receivedSize;
+    var totalSize = result.totalSize;
+    callback.onprogress(result.callbackId, receivedSize, totalSize);
+  }
+  else if (result.status == 'paused') {
+    callback.onpaused(result.callbackId);
+  }
+  else if (result.status == 'canceled') {
+    callback.oncanceled(result.callbackId);
+  }
+  else if (result.status == 'completed') {
+    var fullPath = result.fullPath;
+    callback.oncompleted(result.callbackId, fullPath);
+  }
+  else if (result.status == 'error') {
+    callback.onfailed(
+        result.callbackId, new tizen.WebAPIError(result['error'].name, result['error'].message));
+  }
+});
+
+function nextCallbackId() {
+  return callbackId++;
+}
+
+function callNative(cmd, args) {
+  var json = {'cmd': cmd, 'args': args};
+  var argjson = JSON.stringify(json);
+  var resultString = extension.internal.sendSyncMessage(argjson);
+  var result = JSON.parse(resultString);
+
+  if (typeof result !== 'object') {
+    throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+  }
+
+  if (result['status'] == 'success') {
+    if (result['result']) {
+      return result['result'];
+    }
+    return true;
+  } else if (result['status'] == 'error') {
+    var err = result['error'];
+    if (err) {
+      throw new tizen.WebAPIException(err.name, err.message);
+    }
+    return false;
+  }
+}
+
+
+function callNativeWithCallback(cmd, args, callback) {
+  if (callback) {
+    var id = nextCallbackId();
+    args['callbackId'] = id;
+    callbacks[id] = callback;
+  }
+
+  return callNative(cmd, args);
+}
+
+function SetReadOnlyProperty(obj, n, v) {
+  Object.defineProperty(obj, n, {value: v, writable: false});
+}
+
+var DownloadState = {
+  'QUEUED': 'QUEUED',
+  'DOWNLOADING': 'DOWNLOADING',
+  'PAUSED': 'PAUSED',
+  'CANCELED': 'CANCELED',
+  'COMPLETED': 'COMPLETED',
+  'FAILED': 'FAILED'
+};
+
+var DownloadNetworkType = {
+  'CELLULAR': 'CELLULAR',
+  'WIFI': 'WIFI',
+  'ALL': 'ALL'
+};
+
+tizen.DownloadRequest = function(url, destination, fileName, networkType, httpHeader) {
+  validator_.isConstructorCall(this, tizen.DownloadRequest);
+  var args = validator_.validateArgs(arguments, [
+    {'name' : 'url', 'type': types_.STRING, 'nullable': false, 'optional': false},
+    {'name' : 'destination', 'type': types_.STRING, 'nullable': true, 'optional': true},
+    {'name' : 'fileName', 'type': types_.STRING, 'nullable': true, 'optional': true},
+    {'name' : 'networkType', 'type': types_.ENUM, 'values': ['CELLULAR', 'WIFI', 'ALL'],
+    'nullable' : true, 'optional': true},
+    {'name' : 'httpHeader', 'type': types_.Dictionary, 'nullable': true, 'optional': true} 
+  ]);
+
+  var url_ = url;
+  var networkType_;
+  
+  if (networkType === undefined) networkType_ = 'ALL';
+  else if (networkType in DownloadNetworkType) networkType_ = networkType;
+
+  Object.defineProperties(this, {
+    'url': { enumerable: true,
+      get: function() { return url_;},
+      set: function(value) { if (value != null) { url_ = value; }} },
+    'destination': { writable: true, enumerable: true,
+      value: destination === undefined ? '' : destination },
+    'fileName': { writable: true, enumerable: true,
+      value: fileName === undefined ? '' : fileName },
+    'networkType': { enumerable: true,
+      get: function() { return networkType_;},
+      set: function(value) {
+        if (value === null || value in DownloadNetworkType) { networkType_ = value; }} },
+    'httpHeader': { writable: true, enumerable: true,
+      value: httpHeader === undefined ? {} : httpHeader }
+  });
+};
+
+
+function DownloadManager() {
+  // constructor of DownloadManager
+}
+
+DownloadManager.prototype.start = function(downloadRequest) {
+  var args = validator_.validateArgs(arguments, [
+    {'name' : 'downloadRequest', 'type': types_.PLATFORM_OBJECT, 'values': tizen.DownloadRequest},
+    {'name' : 'downloadCallback', 'type': types_.LISTENER,
+      'values' : ['onprogress', 'onpaused', 'oncanceled', 'oncompleted', 'onfailed'],
+      optional: true, nullable: true}
+  ]);
+
+  var nativeParam = {
+    'url': args.downloadRequest.url,
+    'destination': args.downloadRequest.destination,
+    'fileName': args.downloadRequest.fileName,
+    'networkType': args.downloadRequest.networkType,
+    'httpHeader': args.downloadRequest.httpHeader,
+    'callbackId': nextCallbackId()
+  };
+
+  if (args.downloadCallback) {
+    this.setListener(nativeParam.callbackId, args.downloadCallback);
+  }
+
+  try {
+    var syncResult = callNative('DownloadManager_start', nativeParam);
+  } catch (e) {
+    throw e;
+  }
+
+  requests[nativeParam.callbackId] = args.downloadRequest;
+
+  return nativeParam.callbackId;
+};
+
+DownloadManager.prototype.cancel = function(downloadId) {
+  var args = validator_.validateArgs(arguments, [
+    {name: 'downloadId', type: types_.LONG, 'nullable': false, 'optional': false}
+  ]);
+
+  var nativeParam = {
+    'downloadId': args.downloadId
+  };
+
+  if (typeof requests[downloadId] === 'undefined')
+    throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR,
+        'the identifier does not match any download operation in progress');
+
+  try {
+    var syncResult = callNative('DownloadManager_cancel', nativeParam);
+  } catch (e) {
+    throw e;
+  }
+};
+
+DownloadManager.prototype.pause = function(downloadId) {
+  var args = validator_.validateArgs(arguments, [
+    {'name': 'downloadId', 'type': types_.LONG, 'nullable': false, 'optional': false}
+  ]);
+
+  var nativeParam = {
+    'downloadId': args.downloadId
+  };
+
+  if (typeof requests[downloadId] === 'undefined')
+    throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR,
+        'the identifier does not match any download operation in progress');
+
+  try {
+    var syncResult = callNative('DownloadManager_pause', nativeParam);
+  } catch (e) {
+    throw e;
+  }
+};
+
+DownloadManager.prototype.resume = function(downloadId) {
+  var args = validator_.validateArgs(arguments, [
+    {'name' : 'downloadId', 'type': types_.LONG, 'nullable': false, 'optional': false}
+  ]);
+
+  var nativeParam = {
+    'downloadId': args.downloadId
+  };
+
+  if (typeof requests[downloadId] === 'undefined')
+    throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR,
+        'the identifier does not match any download operation in progress');
+
+  try {
+    var syncResult = callNative('DownloadManager_resume', nativeParam);
+  } catch (e) {
+    throw e;
+  }
+};
+
+DownloadManager.prototype.getState = function(downloadId) {
+  var args = validator_.validateArgs(arguments, [
+    {'name' : 'downloadId', 'type': types_.LONG, 'nullable': false, 'optional': false}
+  ]);
+
+  var nativeParam = {
+    'downloadId': args.downloadId
+  };
+
+  if (typeof requests[downloadId] === 'undefined')
+    throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR,
+        'the identifier does not match any download operation in progress');
+
+  try {
+    var syncResult = callNative('DownloadManager_getState', nativeParam);
+  } catch (e) {
+    throw e;
+  }
+
+  return syncResult;
+};
+
+DownloadManager.prototype.getDownloadRequest = function(downloadId) {
+  var args = validator_.validateArgs(arguments, [
+    {'name': 'downloadId', 'type': types_.LONG, 'nullable': false, 'optional': false}
+  ]);
+
+  if (typeof requests[downloadId] === 'undefined')
+    throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR,
+        'the identifier does not match any download operation in progress');
+
+  return requests[args.downloadId];
+};
+
+DownloadManager.prototype.getMIMEType = function(downloadId) {
+  var args = validator_.validateArgs(arguments, [
+    {'name' : 'downloadId', 'type': types_.LONG, 'nullable': false, 'optional': false}
+  ]);
+
+  var nativeParam = {
+    'downloadId': args.downloadId
+  };
+
+  if (typeof requests[downloadId] === 'undefined')
+    throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR,
+        'the identifier does not match any download operation in progress');
+
+  try {
+    var syncResult = callNative('DownloadManager_getMIMEType', nativeParam);
+  } catch (e) {
+    throw e;
+  }
+
+  return syncResult;
+};
+
+DownloadManager.prototype.setListener = function(downloadId, downloadCallback) {
+  var args = validator_.validateArgs(arguments, [
+    {'name' : 'downloadId', 'type': types_.LONG},
+    {'name' : 'downloadCallback', 'type': types_.LISTENER,
+      'values' : ['onprogress', 'onpaused', 'oncanceled', 'oncompleted', 'onfailed']}
+  ]);
+
+  callbacks[args.downloadId] = args.downloadCallback;
+};
+
+
+
+exports = new DownloadManager();
+
diff --git a/src/download/download_extension.cc b/src/download/download_extension.cc
new file mode 100644 (file)
index 0000000..465536d
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "download/download_extension.h"
+
+#include "download/download_instance.h"
+
+// This will be generated from download_api.js
+extern const char kSource_download_api[];
+
+common::Extension* CreateExtension() {
+  return new DownloadExtension;
+}
+
+DownloadExtension::DownloadExtension() {
+  SetExtensionName("tizen.download");
+  SetJavaScriptAPI(kSource_download_api);
+
+  const char* entry_points[] = {
+    "tizen.DownloadRequest",
+    NULL
+  };
+  SetExtraJSEntryPoints(entry_points);
+}
+
+DownloadExtension::~DownloadExtension() {}
+
+common::Instance* DownloadExtension::CreateInstance() {
+  return new extension::download::DownloadInstance;
+}
diff --git a/src/download/download_extension.h b/src/download/download_extension.h
new file mode 100644 (file)
index 0000000..2d5b243
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DOWNLOAD_DOWNLOAD_EXTENSION_H_
+#define DOWNLOAD_DOWNLOAD_EXTENSION_H_
+
+#include "common/extension.h"
+
+class DownloadExtension : public common::Extension {
+ public:
+  DownloadExtension();
+  virtual ~DownloadExtension();
+
+ private:
+  virtual common::Instance* CreateInstance();
+};
+
+#endif  // DOWNLOAD_DOWNLOAD_EXTENSION_H_
diff --git a/src/download/download_instance.cc b/src/download/download_instance.cc
new file mode 100644 (file)
index 0000000..04640c7
--- /dev/null
@@ -0,0 +1,705 @@
+// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "download/download_instance.h"
+#include <functional>
+
+#include "common/picojson.h"
+#include "common/logger.h"
+#include "common/platform_exception.h"
+#include "common/typeutil.h"
+
+
+namespace extension {
+namespace download {
+
+namespace {
+// The privileges that required in Download API
+const std::string kPrivilegeDownload = "";
+
+}  // namespace
+
+using common::NotFoundException;
+using common::UnknownException;
+using common::NetworkException;
+using common::SecurityException;
+using common::QuotaExceededException;
+using common::NotSupportedException;
+using common::InvalidStateException;
+using common::IOException;
+using common::InvalidValuesException;
+using common::ServiceNotAvailableException;
+using common::TypeMismatchException;
+
+DownloadInstance::DownloadInstance() {
+  using std::placeholders::_1;
+  using std::placeholders::_2;
+  #define REGISTER_SYNC(c, x) \
+    RegisterSyncHandler(c, std::bind(&DownloadInstance::x, this, _1, _2));
+  REGISTER_SYNC("DownloadManager_pause", DownloadManagerPause);
+  REGISTER_SYNC
+    ("DownloadManager_getDownloadRequest", DownloadManagerGetdownloadrequest);
+  REGISTER_SYNC("DownloadManager_setListener", DownloadManagerSetlistener);
+  REGISTER_SYNC("DownloadManager_getMIMEType", DownloadManagerGetmimetype);
+  REGISTER_SYNC("DownloadManager_start", DownloadManagerStart);
+  REGISTER_SYNC("DownloadManager_cancel", DownloadManagerCancel);
+  REGISTER_SYNC("DownloadManager_resume", DownloadManagerResume);
+  REGISTER_SYNC("DownloadManager_getState", DownloadManagerGetstate);
+  #undef REGISTER_SYNC
+}
+
+DownloadInstance::~DownloadInstance() {
+  for (DownloadCallbackVector::iterator it = downCbVector.begin();
+    it != downCbVector.end(); it++) {
+    delete (*it);
+  }
+}
+
+#define CHECK_EXIST(args, name, out) \
+    if (!args.contains(name)) {\
+      ReportError(TypeMismatchException(name" is required argument"), out);\
+      return;\
+    }
+
+void DownloadInstance::OnStateChanged(int download_id,
+  download_state_e state, void* user_data) {
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+
+  downCbPtr->state = state;
+  downCbPtr->downloadId = download_id;
+
+  LoggerD("State for callbackId %d changed to %d",
+    downCbPtr->callbackId, static_cast<int>(state));
+
+  switch (state) {
+    case DOWNLOAD_STATE_DOWNLOADING:
+      OnStart(download_id, user_data);
+      break;
+    case DOWNLOAD_STATE_PAUSED:
+      g_idle_add(OnPaused, downCbPtr);
+      break;
+    case DOWNLOAD_STATE_COMPLETED:
+      g_idle_add(OnFinished, downCbPtr);
+      break;
+    case DOWNLOAD_STATE_CANCELED:
+      g_idle_add(OnCanceled, downCbPtr);
+      break;
+    case DOWNLOAD_STATE_FAILED:
+      g_idle_add(OnFailed, downCbPtr);
+      break;
+  }
+}
+
+gboolean DownloadInstance::OnProgressChanged(void* user_data) {
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+  DownloadInfoPtr diPtr = downCbPtr->instance->diMap[downCbPtr->callbackId];
+
+  picojson::value::object out;
+  out["status"] = picojson::value("progress");
+  out["callbackId"] =
+    picojson::value(static_cast<double>(downCbPtr->callbackId));
+  out["receivedSize"] =
+    picojson::value(static_cast<double>(downCbPtr->received));
+  out["totalSize"] = picojson::value(static_cast<double>(diPtr->file_size));
+
+  LoggerD("OnProgressChanged for callbackId %d Called: Received: %ld",
+    downCbPtr->callbackId, downCbPtr->received);
+
+  picojson::value v = picojson::value(out);
+  downCbPtr->instance->PostMessage(v.serialize().c_str());
+
+  return FALSE;
+}
+
+void DownloadInstance::OnStart(int download_id, void* user_data) {
+  unsigned long long totalSize;
+  int ret;
+
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+
+  LoggerD("OnStart for callbackId %d Called", downCbPtr->callbackId);
+
+  DownloadInfoPtr diPtr = downCbPtr->instance->diMap[downCbPtr->callbackId];
+
+  download_get_content_size(download_id, &totalSize);
+
+  diPtr->file_size = totalSize;
+}
+
+gboolean DownloadInstance::OnFinished(void* user_data) {
+  char* fullPath = NULL;
+
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+  DownloadInfoPtr diPtr = downCbPtr->instance->diMap[downCbPtr->callbackId];
+
+  LoggerD("OnFinished for callbackID %d Called", downCbPtr->callbackId);
+
+  download_get_downloaded_file_path(downCbPtr->downloadId, &fullPath);
+
+  download_unset_state_changed_cb(diPtr->download_id);
+  download_unset_progress_cb(diPtr->download_id);
+  download_destroy(diPtr->download_id);
+
+  picojson::value::object out;
+  out["status"] = picojson::value("completed");
+  out["callbackId"] =
+    picojson::value(static_cast<double>(downCbPtr->callbackId));
+  out["fullPath"] = picojson::value(fullPath);
+
+  downCbPtr->instance->PostMessage(picojson::value(out).serialize().c_str());
+  return FALSE;
+}
+
+gboolean DownloadInstance::OnPaused(void* user_data) {
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+  DownloadInfoPtr diPtr = downCbPtr->instance->diMap[downCbPtr->callbackId];
+
+  LoggerD("OnPaused for callbackID %d Called", downCbPtr->callbackId);
+
+  picojson::value::object out;
+  out["status"] = picojson::value("paused");
+  out["callbackId"] =
+    picojson::value(static_cast<double>(downCbPtr->callbackId));
+
+  downCbPtr->instance->PostMessage(picojson::value(out).serialize().c_str());
+  return FALSE;
+}
+
+gboolean DownloadInstance::OnCanceled(void* user_data) {
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+  DownloadInfoPtr diPtr = downCbPtr->instance->diMap[downCbPtr->callbackId];
+
+  LoggerD("OnCanceled for callbackID %d Called", downCbPtr->callbackId);
+
+  download_unset_state_changed_cb(diPtr->download_id);
+  download_unset_progress_cb(diPtr->download_id);
+  download_destroy(diPtr->download_id);
+
+  picojson::value::object out;
+  out["status"] = picojson::value("canceled");
+  out["callbackId"] =
+    picojson::value(static_cast<double>(downCbPtr->callbackId));
+
+  downCbPtr->instance->PostMessage(picojson::value(out).serialize().c_str());
+  return FALSE;
+}
+
+gboolean DownloadInstance::OnFailed(void* user_data) {
+  download_error_e error;
+  picojson::object out;
+
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+  DownloadInstance* instance = downCbPtr->instance;
+
+  download_get_error(downCbPtr->downloadId, &error);
+
+  switch (error) {
+    case DOWNLOAD_ERROR_INVALID_PARAMETER:
+      instance->ReportError(NotFoundException("not found"), out);
+      break;
+    case DOWNLOAD_ERROR_OUT_OF_MEMORY:
+      instance->ReportError(UnknownException("Out of memory"), out);
+      break;
+    case DOWNLOAD_ERROR_NETWORK_UNREACHABLE:
+      instance->ReportError(NetworkException("Network is unreachable"), out);
+      break;
+    case DOWNLOAD_ERROR_CONNECTION_TIMED_OUT:
+      instance->ReportError(NetworkException("HTTP session timeout"), out);
+      break;
+    case DOWNLOAD_ERROR_NO_SPACE:
+      instance->ReportError(QuotaExceededException(
+        "No space left on device"), out);
+      break;
+    case DOWNLOAD_ERROR_PERMISSION_DENIED:
+      instance->ReportError(SecurityException(
+        "The application does not have the privilege to call this method."),
+        out);
+      break;
+    case DOWNLOAD_ERROR_NOT_SUPPORTED:
+      instance->ReportError(NotSupportedException("Not supported"), out);
+      break;
+    case DOWNLOAD_ERROR_INVALID_STATE:
+      instance->ReportError(InvalidStateException("Invalid state"), out);
+      break;
+    case DOWNLOAD_ERROR_CONNECTION_FAILED:
+      instance->ReportError(NetworkException("Connection failed"), out);
+      break;
+    case DOWNLOAD_ERROR_INVALID_URL:
+      instance->ReportError(InvalidValuesException("Invalid URL"), out);
+      break;
+    case DOWNLOAD_ERROR_INVALID_DESTINATION:
+      instance->ReportError(InvalidValuesException(
+        "Invalid destination"), out);
+      break;
+    case DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS:
+      instance->ReportError(QuotaExceededException(
+        "Too many simultaneous downloads"), out);
+      break;
+    case DOWNLOAD_ERROR_QUEUE_FULL:
+      instance->ReportError(QuotaExceededException(
+        "Download server queue is full"), out);
+      break;
+    case DOWNLOAD_ERROR_ALREADY_COMPLETED:
+      instance->ReportError(InvalidStateException(
+        "The download is already completed"), out);
+      break;
+    case DOWNLOAD_ERROR_FILE_ALREADY_EXISTS:
+      instance->ReportError(IOException(
+        "Failed to rename the downloaded file"), out);
+      break;
+    case DOWNLOAD_ERROR_CANNOT_RESUME:
+      instance->ReportError(NotSupportedException("Cannot resume"), out);
+      break;
+    case DOWNLOAD_ERROR_FIELD_NOT_FOUND:
+      instance->ReportError(NotFoundException(
+        "Specified field not found"), out);
+      break;
+    case DOWNLOAD_ERROR_TOO_MANY_REDIRECTS:
+      instance->ReportError(NetworkException(
+        "Too many redirects from HTTP response header"), out);
+      break;
+    case DOWNLOAD_ERROR_UNHANDLED_HTTP_CODE:
+      instance->ReportError(NetworkException(
+        "The download cannot handle the HTTP status value"), out);
+      break;
+    case DOWNLOAD_ERROR_REQUEST_TIMEOUT:
+      instance->ReportError(NetworkException(
+        "No action after client creates a download ID"), out);
+      break;
+    case DOWNLOAD_ERROR_RESPONSE_TIMEOUT:
+      instance->ReportError(NetworkException(
+        "No call to start API for some time although the download is created"),
+        out);
+      break;
+    case DOWNLOAD_ERROR_SYSTEM_DOWN:
+      instance->ReportError(ServiceNotAvailableException(
+        "No response from client after rebooting download daemon"), out);
+      break;
+    case DOWNLOAD_ERROR_ID_NOT_FOUND:
+      instance->ReportError(NotFoundException(
+        "Download ID does not exist in download service module"), out);
+      break;
+    case DOWNLOAD_ERROR_INVALID_NETWORK_TYPE:
+      instance->ReportError(InvalidValuesException(
+        "Network bonding is set but network type is not set as ALL"), out);
+      break;
+    case DOWNLOAD_ERROR_NO_DATA:
+      instance->ReportError(NotFoundException(
+        "No data because the set API is not called"), out);
+      break;
+    case DOWNLOAD_ERROR_IO_ERROR:
+      instance->ReportError(IOException("Internal I/O error"), out);
+      break;
+  }
+
+  out["callbackId"] =
+    picojson::value(static_cast<double>(downCbPtr->callbackId));
+
+  downCbPtr->instance->PostMessage(picojson::value(out).serialize().c_str());
+  return FALSE;
+}
+
+void DownloadInstance::progress_changed_cb
+  (int download_id, long long unsigned received, void* user_data) {
+  DownloadCallback* downCbPtr = static_cast<DownloadCallback*>(user_data);
+  downCbPtr->received = received;
+  downCbPtr->downloadId = download_id;
+
+  g_idle_add(OnProgressChanged, downCbPtr);
+}
+
+void DownloadInstance::DownloadManagerStart
+  (const picojson::value& args, picojson::object& out) {
+  CHECK_EXIST(args, "callbackId", out)
+
+  int ret, downlodId;
+  std::string networkType;
+
+  DownloadInfoPtr diPtr(new DownloadInfo);
+
+  diPtr->callbackId = static_cast<int>(args.get("callbackId").get<double>());
+  diPtr->url = args.get("url").get<std::string>();
+
+  if (!args.get("destination").is<picojson::null>()) {
+    if (args.get("destination").get<std::string>() != "") {
+      diPtr->destination = args.get("destination").get<std::string>();
+      // need to use filesystem API
+    }
+  }
+
+  LoggerD("destination: %s", diPtr->destination.c_str());
+
+  if (!args.get("fileName").is<picojson::null>()) {
+    if (args.get("fileName").get<std::string>() != "") {
+      diPtr->file_name = args.get("fileName").get<std::string>();
+    }
+  }
+
+  if (!args.get("networkType").is<picojson::null>()) {
+    networkType = args.get("networkType").get<std::string>();
+  }
+
+  bool networkSupport;
+
+  if (networkType == "CELLULAR") {
+    system_info_get_platform_bool(
+      "http://tizen.org/feature/network.telephony", &networkSupport);
+    if (!networkSupport) {
+      ReportError(NotSupportedException(
+      "The networkType of the given DownloadRequest" \
+      "is not supported on a device."), out);
+      return;
+    }
+    diPtr->network_type = DOWNLOAD_NETWORK_DATA_NETWORK;
+  } else if (networkType == "WIFI") {
+    system_info_get_platform_bool(
+      "http://tizen.org/feature/network.wifi", &networkSupport);
+    if (!networkSupport) {
+      ReportError(NotSupportedException(
+      "The networkType of the given DownloadRequest" \
+      "is not supported on a device."), out);
+      return;
+    }
+    diPtr->network_type = DOWNLOAD_NETWORK_WIFI;
+  } else if (networkType == "ALL") {
+    diPtr->network_type = DOWNLOAD_NETWORK_ALL;
+  } else {
+    ReportError(
+      InvalidValuesException(
+      "The input parameter contains an invalid network type."), out);
+    return;
+  }
+
+  DownloadCallback* downCbPtr(new DownloadCallback);
+
+  downCbPtr->callbackId = diPtr->callbackId;
+  downCbPtr->instance = this;
+
+  downCbVector.push_back(downCbPtr);
+  LoggerD("Checking callback ID: %d", downCbPtr->callbackId);
+
+  ret = download_create(&diPtr->download_id);
+  ret =
+    download_set_state_changed_cb
+    (diPtr->download_id, OnStateChanged, static_cast<void*>(downCbPtr));
+  ret =
+    download_set_progress_cb
+    (diPtr->download_id, progress_changed_cb, static_cast<void*>(downCbPtr));
+  ret =
+    download_set_url(diPtr->download_id, diPtr->url.c_str());
+
+  const char* dest;
+
+  if (diPtr->destination == "Downloads") {
+    dest = "/opt/usr/media/Downloads";  //  ret = download_set_destination(diPtr->download_id, diPtr->destination.c_str());
+    ret = download_set_destination(diPtr->download_id, dest);
+  }
+
+  if (!diPtr->file_name.empty()) {
+    ret = download_set_file_name(diPtr->download_id, diPtr->file_name.c_str());
+  }
+
+  ret = download_set_network_type(diPtr->download_id, diPtr->network_type);
+
+  if (args.get("httpHeader").is<picojson::object>()) {
+    picojson::object obj = args.get("httpHeader").get<picojson::object>();
+    for (picojson::object::const_iterator it = obj.begin();
+    it != obj.end(); ++it) {
+      download_add_http_header_field
+        (diPtr->download_id, it->first.c_str(), it->second.to_str().c_str());
+    }
+  }
+
+  char* gUrl = NULL;
+  char* gDest = NULL;
+  char* gFilename = NULL;
+  download_network_type_e gNetType;
+  char** fields;
+  char* header_value;
+  int header_length;
+
+  download_get_url(diPtr->download_id, &gUrl);
+  download_get_destination(diPtr->download_id, &gDest);
+  download_get_file_name(diPtr->download_id, &gFilename);
+  download_get_network_type(diPtr->download_id, &gNetType);
+  download_get_http_header_field_list
+    (diPtr->download_id, &fields, &header_length);
+
+  for (int i = 0; i < header_length; i++) {
+    download_get_http_header_field
+      (diPtr->download_id, fields[i], &header_value);
+    LoggerD("HTTP HEADER %d: %s - %s", i, fields[i], header_value);
+  }
+
+  LoggerD("Download Request Received" \
+    "(URL: %s, Destination: %s, File name: %s, Network type: %d ",
+    gUrl, gDest, gFilename, static_cast<int>(gNetType));
+
+  diMap[downCbPtr->callbackId] = diPtr;
+
+  ret = download_start(diPtr->download_id);
+
+  if (ret == DOWNLOAD_ERROR_NONE)
+    ReportSuccess(out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER)
+    ReportError(InvalidValuesException
+    ("The input parameter contains an invalid value."), out);
+  else if (ret == DOWNLOAD_ERROR_OUT_OF_MEMORY)
+    ReportError(UnknownException("Out of memory"), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_STATE)
+    ReportError(InvalidValuesException("Invalid state"), out);
+  else if (ret == DOWNLOAD_ERROR_IO_ERROR)
+    ReportError(UnknownException("Internal I/O error"), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_URL)
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid url."), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_DESTINATION)
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid destination."), out);
+  else if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND)
+    ReportError(InvalidValuesException("No such a download ID found"), out);
+  else if (ret == DOWNLOAD_ERROR_QUEUE_FULL)
+    ReportError(UnknownException("Download server queue is full"), out);
+  else if (ret == DOWNLOAD_ERROR_PERMISSION_DENIED)
+    ReportError(SecurityException(
+    "The application does not have the privilege to call this method."), out);
+  else
+    ReportError(UnknownException("Unknown Error"), out);
+}
+
+void DownloadInstance::DownloadManagerCancel
+  (const picojson::value& args, picojson::object& out) {
+  CHECK_EXIST(args, "downloadId", out)
+  int downloadId, ret;
+
+  int callbackId = static_cast<int>(args.get("downloadId").get<double>());
+
+  if (!GetDownloadID(callbackId, downloadId)) {
+    ReportError(NotFoundException
+      ("The identifier does not match any download operation in progress"),
+      out);
+    return;
+  }
+
+  LoggerD("Download cancel for download ID: %d", downloadId);
+
+  ret = download_cancel(downloadId);
+
+  if (ret == DOWNLOAD_ERROR_NONE)
+    ReportSuccess(out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER)
+    ReportError(InvalidValuesException
+    ("The input parameter contains an invalid value."), out);
+  else if (ret == DOWNLOAD_ERROR_OUT_OF_MEMORY)
+    ReportError(UnknownException("Out of memory"), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_STATE)
+    ReportError(InvalidValuesException("Invalid state"), out);
+  else if (ret == DOWNLOAD_ERROR_IO_ERROR)
+    ReportError(UnknownException("Internal I/O error"), out);
+  else if (ret == DOWNLOAD_ERROR_PERMISSION_DENIED)
+    ReportError(UnknownException("Permission denied"), out);
+  else
+    ReportError(UnknownException("Unknown Error"), out);
+}
+
+void DownloadInstance::DownloadManagerPause
+  (const picojson::value& args, picojson::object& out) {
+  CHECK_EXIST(args, "downloadId", out)
+  int downloadId, ret;
+
+  int callbackId = static_cast<int>(args.get("downloadId").get<double>());
+
+  if (!GetDownloadID(callbackId, downloadId)) {
+    ReportError(NotFoundException(
+      "The identifier does not match any download operation in progress"),
+      out);
+    return;
+  }
+
+  LoggerD("Download pause for download ID: %d", downloadId);
+
+  ret = download_pause(downloadId);
+
+  if (ret == DOWNLOAD_ERROR_NONE)
+    ReportSuccess(out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER)
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid value."), out);
+  else if (ret == DOWNLOAD_ERROR_OUT_OF_MEMORY)
+    ReportError(UnknownException("Out of memory"), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_STATE)
+    ReportError(InvalidValuesException("Invalid state"), out);
+  else if (ret == DOWNLOAD_ERROR_IO_ERROR)
+    ReportError(UnknownException("Internal I/O error"), out);
+  else if (ret == DOWNLOAD_ERROR_PERMISSION_DENIED)
+    ReportError(UnknownException("Permission denied"), out);
+  else
+    ReportError(UnknownException("Unknown Error"), out);
+}
+
+void DownloadInstance::DownloadManagerResume
+  (const picojson::value& args, picojson::object& out) {
+  CHECK_EXIST(args, "downloadId", out)
+  int downloadId, ret;
+
+  int callbackId = static_cast<int>(args.get("downloadId").get<double>());
+
+  if (!GetDownloadID(callbackId, downloadId)) {
+    ReportError(NotFoundException
+      ("The identifier does not match any download operation in progress"),
+      out);
+    return;
+  }
+
+  LoggerD("Download resume for download ID: %d", downloadId);
+
+  ret = download_start(downloadId);
+
+  if (ret == DOWNLOAD_ERROR_NONE)
+    ReportSuccess(out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER)
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid value."), out);
+  else if (ret == DOWNLOAD_ERROR_OUT_OF_MEMORY)
+    ReportError(UnknownException("Out of memory"), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_STATE)
+    ReportError(InvalidValuesException("Invalid state"), out);
+  else if (ret == DOWNLOAD_ERROR_IO_ERROR)
+    ReportError(UnknownException("Internal I/O error"), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_URL)
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid url."), out);
+  else if (ret == DOWNLOAD_ERROR_INVALID_DESTINATION)
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid destination."), out);
+  else if (ret == DOWNLOAD_ERROR_ID_NOT_FOUND)
+    ReportError(InvalidValuesException("No such a download ID found"), out);
+  else if (ret == DOWNLOAD_ERROR_QUEUE_FULL)
+    ReportError(UnknownException("Download server queue is full"), out);
+  else if (ret == DOWNLOAD_ERROR_PERMISSION_DENIED)
+    ReportError(SecurityException(
+    "Application does not have the privilege to call this method."), out);
+  else
+    ReportError(UnknownException("Unknown Error"), out);
+}
+void DownloadInstance::DownloadManagerGetstate
+  (const picojson::value& args, picojson::object& out) {
+  CHECK_EXIST(args, "downloadId", out)
+  int downloadId, ret;
+  std::string stateValue;
+  download_state_e state;
+
+  int callbackId = static_cast<int>(args.get("downloadId").get<double>());
+
+  if (!GetDownloadID(callbackId, downloadId)) {
+    ReportError(NotFoundException
+      ("The identifier does not match any download operation in progress"),
+      out);
+    return;
+  }
+
+  ret = download_get_state(downloadId, &state);
+
+  if (ret == DOWNLOAD_ERROR_NONE) {
+    switch (state) {
+     case DOWNLOAD_STATE_QUEUED:
+       stateValue = "QUEUED";
+       break;
+     case DOWNLOAD_STATE_DOWNLOADING:
+       stateValue = "DOWNLOADING";
+       break;
+     case DOWNLOAD_STATE_PAUSED:
+       stateValue = "PAUSED";
+       break;
+     case DOWNLOAD_STATE_COMPLETED:
+       stateValue = "COMPLETED";
+       break;
+     case DOWNLOAD_STATE_FAILED:
+       stateValue = "FAILED";
+       break;
+     case DOWNLOAD_STATE_CANCELED:
+       stateValue = "CANCELED";
+       break;
+     }
+
+    ReportSuccess(picojson::value(stateValue), out);
+  } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
+      ReportError(InvalidValuesException(
+      "The input parameter contains an invalid value."), out);
+    } else if (ret == DOWNLOAD_ERROR_OUT_OF_MEMORY) {
+      ReportError(UnknownException("Out of memory"), out);
+    } else if (ret == DOWNLOAD_ERROR_INVALID_STATE) {
+      ReportError(InvalidValuesException("Invalid state"), out);
+    } else if (ret == DOWNLOAD_ERROR_IO_ERROR) {
+      ReportError(UnknownException("Internal I/O error"), out);
+    } else if (ret == DOWNLOAD_ERROR_PERMISSION_DENIED) {
+      ReportError(UnknownException("Permission denied"), out);
+    } else {
+      ReportError(UnknownException("Unknown Error"), out);
+    }
+}
+
+void DownloadInstance::DownloadManagerGetmimetype
+  (const picojson::value& args, picojson::object& out) {
+  CHECK_EXIST(args, "downloadId", out)
+
+  int downloadId, ret;
+  char* mimetype = NULL;
+
+  int callbackId = static_cast<int>(args.get("downloadId").get<double>());
+
+  if (!GetDownloadID(callbackId, downloadId)) {
+    ReportError(NotFoundException
+      ("The identifier does not match any download operation in progress"),
+      out);
+    return;
+  }
+
+  ret = download_get_mime_type(downloadId, &mimetype);
+
+  if (ret == DOWNLOAD_ERROR_NONE) {
+    LoggerD("MIMEtype for callbackID %d : %s", callbackId, mimetype);
+    ReportSuccess(picojson::value(mimetype), out);
+  } else if (ret == DOWNLOAD_ERROR_INVALID_PARAMETER) {
+    ReportError(InvalidValuesException(
+    "The input parameter contains an invalid value."), out);
+    } else if (ret == DOWNLOAD_ERROR_OUT_OF_MEMORY) {
+      ReportError(UnknownException("Out of memory"), out);
+    } else if (ret == DOWNLOAD_ERROR_INVALID_STATE) {
+      ReportError(InvalidValuesException("Invalid state"), out);
+    } else if (ret == DOWNLOAD_ERROR_IO_ERROR) {
+      ReportError(UnknownException("Internal I/O error"), out);
+    } else if (ret == DOWNLOAD_ERROR_PERMISSION_DENIED) {
+      ReportError(UnknownException("Permission denied"), out);
+    } else {
+      ReportError(UnknownException("Unknown Error"), out);
+    }
+}
+
+bool DownloadInstance::GetDownloadID
+  (const int callback_id, int& download_id) {
+
+  if (diMap.find(callback_id) != diMap.end()) {
+    download_id = diMap.find(callback_id)->second->download_id;
+  } else {
+    return false;
+  }
+  return true;
+}
+
+void DownloadInstance::DownloadManagerGetdownloadrequest
+  (const picojson::value& args, picojson::object& out) {
+  // Nothing to do
+}
+
+void DownloadInstance::DownloadManagerSetlistener
+  (const picojson::value& args, picojson::object& out) {
+  // Nothing to do
+}
+
+
+#undef CHECK_EXIST
+
+}  // namespace download
+}  // namespace extension
diff --git a/src/download/download_instance.h b/src/download/download_instance.h
new file mode 100644 (file)
index 0000000..a826249
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DOWNLOAD_DOWNLOAD_INSTANCE_H_
+#define DOWNLOAD_DOWNLOAD_INSTANCE_H_
+
+#include <glib.h>
+#include <download.h>
+#include <system_info.h>
+
+#include <memory>
+#include <sstream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "common/extension.h"
+
+template <class T>
+  inline std::string to_string(const T& t) {
+      std::stringstream ss;
+      ss << t;
+      return ss.str();
+  }
+
+namespace extension {
+namespace download {
+
+class DownloadInstance : public common::ParsedInstance {
+ public:
+  DownloadInstance();
+  virtual ~DownloadInstance();
+
+ private:
+  void DownloadManagerStart
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerCancel
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerPause
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerResume
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerGetstate
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerGetdownloadrequest
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerGetmimetype
+    (const picojson::value& args, picojson::object& out);
+  void DownloadManagerSetlistener
+    (const picojson::value& args, picojson::object& out);
+
+  bool GetDownloadID(const int callback_id, int& download_id);
+
+  static void OnStateChanged
+    (int download_id, download_state_e state, void* user_data);
+  static void progress_changed_cb
+    (int download_id, long long unsigned received, void* user_data);
+  static void OnStart(int download_id, void* user_data);
+
+  static gboolean OnProgressChanged(void* user_data);
+  static gboolean OnFinished(void* user_data);
+  static gboolean OnPaused(void* user_data);
+  static gboolean OnCanceled(void* user_data);
+  static gboolean OnFailed(void* user_data);
+
+  struct DownloadInfo {
+    int callbackId;
+    std::string url;
+    std::string destination;
+    std::string file_name;
+    std::string http_header;
+    download_network_type_e network_type;
+
+    int download_id;
+    long long unsigned file_size;
+  };
+
+  struct DownloadCallback {
+    int callbackId;
+    int downloadId;
+    DownloadInstance* instance;
+    unsigned long long received;
+    download_state_e state;
+  };
+
+  typedef std::vector<DownloadCallback*> DownloadCallbackVector;
+  typedef std::shared_ptr<DownloadInfo> DownloadInfoPtr;
+  typedef std::map<int, DownloadInfoPtr> DownloadInfoMap;
+
+  DownloadCallbackVector downCbVector;
+  DownloadInfoMap diMap;
+};
+
+}  // namespace download
+}  // namespace extension
+
+#endif  // DOWNLOAD_DOWNLOAD_INSTANCE_H_
index b8511ce15e14caa2b4b7ccde4f8aeefca55e07b4..3a79c8c691d6fdb43fb1d7743ae59e485ffc6f93 100644 (file)
@@ -31,6 +31,7 @@
               'calendar/calendar.gyp:*',
               'datacontrol/datacontrol.gyp:*',
               'datasync/datasync.gyp:*',
+             'download/download.gyp:*',
               'messaging/messaging.gyp:*',
               'networkbearerselection/networkbearerselection.gyp:*',
               'nfc/nfc.gyp:*',