diff options
142 files changed, 7282 insertions, 2545 deletions
diff --git a/packaging/webapi-plugins.spec b/packaging/webapi-plugins.spec index 75c40e0e..b555e027 100644 --- a/packaging/webapi-plugins.spec +++ b/packaging/webapi-plugins.spec @@ -8,7 +8,7 @@ %define crosswalk_extensions_path %{_libdir}/%{crosswalk_extensions} Name: webapi-plugins -Version: 2.34 +Version: 2.39 Release: 0 License: Apache-2.0 and BSD-3-Clause and MIT Group: Development/Libraries @@ -30,6 +30,7 @@ Source0: %{name}-%{version}.tar.gz %define tizen_common_feature_bluetooth_support 1 %define tizen_common_feature_bookmark_support 0 %define tizen_common_feature_calendar_support 0 +%define tizen_common_feature_callhistory_support 0 %define tizen_common_feature_contact_support 0 %define tizen_common_feature_content_support 1 %define tizen_common_feature_datacontrol_support 0 @@ -40,19 +41,22 @@ Source0: %{name}-%{version}.tar.gz %define tizen_common_feature_filesystem_support 1 %define tizen_common_feature_fm_radio_support 0 %define tizen_common_feature_ham_support 0 +%define tizen_common_feature_inputdevice_support 0 %define tizen_common_feature_iotcon_support 0 -%define tizen_common_feature_location_batch 0 %define tizen_common_feature_key_manager_support 0 +%define tizen_common_feature_location_batch 0 %define tizen_common_feature_media_controller_support 0 %define tizen_common_feature_media_key_support 0 %define tizen_common_feature_message_port_support 1 %define tizen_common_feature_messaging_support 0 -%define tizen_common_feature_nfc_emulation_support 0 +%define tizen_common_feature_nbs_support 0 %define tizen_common_feature_nfc_support 0 +%define tizen_common_feature_nfc_emulation_support 0 %define tizen_common_feature_notification_support 0 %define tizen_common_feature_package_support 1 %define tizen_common_feature_player_util_support 0 %define tizen_common_feature_power_support 0 +%define tizen_common_feature_ppm_support 0 %define tizen_common_feature_preference_support 0 %define tizen_common_feature_push_support 0 %define tizen_common_feature_se_support 0 @@ -62,16 +66,11 @@ Source0: %{name}-%{version}.tar.gz %define tizen_common_feature_system_setting_support 0 %define tizen_common_feature_telephony_support 0 %define tizen_common_feature_time_support 1 -%define tizen_common_feature_web_setting_support 0 -%define tizen_common_feature_widget_service_support 0 -%define tizen_common_feature_wi_fi_support 1 -%define tizen_common_feature_inputdevice_support 0 -%define tizen_common_feature_callhistory_support 0 -%define tizen_common_feature_nbs_support 0 %define tizen_common_feature_tvinputdevice_support 0 %define tizen_common_feature_voicecontrol_support 0 -%define tizen_common_feature_ppm_support 0 - +%define tizen_common_feature_web_setting_support 0 +%define tizen_common_feature_wi_fi_support 1 +%define tizen_common_feature_widget_service_support 0 #################################################################### # Mobile Profile : TM1(32bit), Redwood(SM-Z910F), KIRAN(Z130H) # @@ -105,24 +104,21 @@ Source0: %{name}-%{version}.tar.gz %endif %define tizen_mobile_feature_ham_support 0 - +%define tizen_mobile_feature_inputdevice_support 1 %define tizen_mobile_feature_iotcon_support 1 -%define tizen_mobile_feature_location_batch 0 %define tizen_mobile_feature_key_manager_support 1 +%define tizen_mobile_feature_location_batch 0 %define tizen_mobile_feature_media_controller_support 1 - %define tizen_mobile_feature_media_key_support 1 - %define tizen_mobile_feature_message_port_support 1 %define tizen_mobile_feature_messaging_support 1 - -%define tizen_mobile_feature_nfc_emulation_support 0 %define tizen_mobile_feature_nfc_support 0 - +%define tizen_mobile_feature_nfc_emulation_support 0 %define tizen_mobile_feature_notification_support 1 %define tizen_mobile_feature_package_support 1 %define tizen_mobile_feature_player_util_support 1 %define tizen_mobile_feature_power_support 1 +%define tizen_mobile_feature_ppm_support 1 %define tizen_mobile_feature_preference_support 1 %define tizen_mobile_feature_push_support 1 @@ -150,17 +146,33 @@ Source0: %{name}-%{version}.tar.gz %endif %define tizen_mobile_feature_time_support 1 +%define tizen_mobile_feature_tvinputdevice_support 0 +%define tizen_mobile_feature_voicecontrol_support 1 %define tizen_mobile_feature_web_setting_support 1 +%define tizen_mobile_feature_wi_fi_support 1 %define tizen_mobile_feature_widget_service_support 1 -%define tizen_mobile_feature_wi_fi_support 1 +## Mobile emulator -%define tizen_mobile_feature_inputdevice_support 1 +%define tizen_mobile_emulator_feature_bluetooth_support 0 -%define tizen_mobile_feature_tvinputdevice_support 0 +# FM radio feature +%define tizen_mobile_emulator_feature_fm_radio_support 1 -%define tizen_mobile_feature_voicecontrol_support 1 -%define tizen_mobile_feature_ppm_support 1 +%define tizen_mobile_emulator_feature_ham_support 1 +%define tizen_mobile_emulator_feature_media_key_support 0 +%define tizen_mobile_emulator_feature_nfc_support 1 +%define tizen_mobile_emulator_feature_nfc_emulation_support 0 + +# secure element feature +%define tizen_mobile_emulator_feature_se_support 0 + +# telephony feature +%define tizen_mobile_emulator_feature_telephony_support 1 +%define tizen_mobile_emulator_feature_callhistory_support 1 +%define tizen_mobile_emulator_feature_nbs_support 1 + +%define tizen_mobile_emulator_feature_wi_fi_support 0 #################################################################### # Wearable Profile : B2 / TW2 # @@ -198,22 +210,24 @@ Source0: %{name}-%{version}.tar.gz %define tizen_wearable_feature_filesystem_support 1 %define tizen_wearable_feature_fm_radio_support 0 %define tizen_wearable_feature_ham_support 1 +%define tizen_wearable_feature_inputdevice_support 1 %define tizen_wearable_feature_iotcon_support 1 +%define tizen_wearable_feature_key_manager_support 1 %define tizen_wearable_feature_location_batch 0 %define tizen_wearable_feature_media_controller_support 1 # MediayKey API is optional in Tizen Wearable Profile. # tizen.org/feature/network.bluetooth.audio.media is required for MediayKey API %define tizen_wearable_feature_media_key_support 1 -%define tizen_wearable_feature_key_manager_support 1 %define tizen_wearable_feature_message_port_support 1 %define tizen_wearable_feature_messaging_support 0 -%define tizen_wearable_feature_nfc_emulation_support 0 %define tizen_wearable_feature_nfc_support 1 +%define tizen_wearable_feature_nfc_emulation_support 0 %define tizen_wearable_feature_notification_support 1 %define tizen_wearable_feature_package_support 1 %define tizen_wearable_feature_player_util_support 1 %define tizen_wearable_feature_power_support 1 +%define tizen_wearable_feature_ppm_support 1 %define tizen_wearable_feature_preference_support 1 %define tizen_wearable_feature_push_support 1 %define tizen_wearable_feature_se_support 1 @@ -230,15 +244,29 @@ Source0: %{name}-%{version}.tar.gz %define tizen_wearable_feature_nbs_support 0 %define tizen_wearable_feature_time_support 1 +%define tizen_wearable_feature_tvinputdevice_support 0 +%define tizen_wearable_feature_voicecontrol_support 1 %define tizen_wearable_feature_web_setting_support 0 -%define tizen_wearable_feature_widget_service_support 1 %define tizen_wearable_feature_wi_fi_support 1 -%define tizen_wearable_feature_inputdevice_support 1 -%define tizen_wearable_feature_tvinputdevice_support 0 +%define tizen_wearable_feature_widget_service_support 1 -%define tizen_wearable_feature_voicecontrol_support 1 -%define tizen_wearable_feature_ppm_support 1 +## Wearable emulator + +%define tizen_wearable_emulator_feature_bluetooth_support 0 + +# MediayKey API is optional in Tizen Wearable Profile. +# tizen.org/feature/network.bluetooth.audio.media is required for MediayKey API +%define tizen_wearable_emulator_feature_media_key_support 0 + +#- telephony related APIs +# CallHistory API is optional in Tizen Wearable Profile. +# NetworkBearerSelection API is optional in Tizen Wearable Profile. +%define tizen_wearable_emulator_feature_telephony_support 1 +%define tizen_wearable_emulator_feature_callhistory_support 1 +%define tizen_wearable_emulator_feature_nbs_support 1 +%define tizen_wearable_emulator_feature_se_support 0 +%define tizen_wearable_emulator_feature_sensor_support 1 #################################################################### # TV Profile # @@ -246,7 +274,7 @@ Source0: %{name}-%{version}.tar.gz %define tizen_tv_privilege_engine CYNARA -%define tizen_tv_feature_account_support 0 +%define tizen_tv_feature_account_support 1 %define tizen_tv_feature_alarm_support 1 %define tizen_tv_feature_app_control_settings_support 0 %define tizen_tv_feature_application_support 1 @@ -266,19 +294,21 @@ Source0: %{name}-%{version}.tar.gz %define tizen_tv_feature_filesystem_support 1 %define tizen_tv_feature_fm_radio_support 0 %define tizen_tv_feature_ham_support 0 +%define tizen_tv_feature_inputdevice_support 0 %define tizen_tv_feature_iotcon_support 1 %define tizen_tv_feature_key_manager_support 1 -%define tizen_tv_feature_media_controller_support 0 +%define tizen_tv_feature_media_controller_support 1 %define tizen_tv_feature_media_key_support 0 %define tizen_tv_feature_message_port_support 1 %define tizen_tv_feature_messaging_support 0 %define tizen_tv_feature_nbs_support 0 -%define tizen_tv_feature_nfc_emulation_support 0 %define tizen_tv_feature_nfc_support 0 +%define tizen_tv_feature_nfc_emulation_support 0 %define tizen_tv_feature_notification_support 0 %define tizen_tv_feature_package_support 1 %define tizen_tv_feature_player_util_support 0 %define tizen_tv_feature_power_support 0 +%define tizen_tv_feature_ppm_support 0 %define tizen_tv_feature_preference_support 0 %define tizen_tv_feature_push_support 1 %define tizen_tv_feature_se_support 0 @@ -288,13 +318,11 @@ Source0: %{name}-%{version}.tar.gz %define tizen_tv_feature_system_setting_support 0 %define tizen_tv_feature_telephony_support 0 %define tizen_tv_feature_time_support 1 -%define tizen_tv_feature_web_setting_support 1 -%define tizen_tv_feature_widget_service_support 0 -%define tizen_tv_feature_wi_fi_support 1 -%define tizen_tv_feature_inputdevice_support 0 %define tizen_tv_feature_tvinputdevice_support 1 %define tizen_tv_feature_voicecontrol_support 1 -%define tizen_tv_feature_ppm_support 0 +%define tizen_tv_feature_web_setting_support 1 +%define tizen_tv_feature_wi_fi_support 1 +%define tizen_tv_feature_widget_service_support 0 # common, or "unified (undefined)" %define unified_build 1 @@ -453,7 +481,7 @@ BuildRequires: pkgconfig(capi-data-control) BuildRequires: pkgconfig(capi-web-url-download) %endif -%if "%{?tizen_feature_ham_support}" == "1" || "%{?unified_build}" == "1" +%if "%{?tizen_feature_ham_support}" == "1" || "%{?unified_build}" == "1" || "%{?tizen_mobile_emulator_feature_ham_support}" == "1" BuildRequires: pkgconfig(motion) BuildRequires: pkgconfig(capi-system-sensor) BuildRequires: pkgconfig(capi-location-manager) @@ -490,7 +518,6 @@ BuildRequires: pkgconfig(ecore-file) BuildRequires: pkgconfig(email-service) BuildRequires: pkgconfig(msg-service) BuildRequires: pkgconfig(db-util) -BuildRequires: pkgconfig(dbus-glib-1) %endif %if "%{?tizen_feature_badge_support}" == "1" || "%{?unified_build}" == "1" @@ -505,7 +532,7 @@ BuildRequires: pkgconfig(calendar-service2) BuildRequires: pkgconfig(contacts-service2) %endif -%if "%{?tizen_feature_callhistory_support}" == "1" || "%{?unified_build}" == "1" +%if "%{?tizen_feature_callhistory_support}" == "1" || "%{?unified_build}" == "1" || "%{?tizen_mobile_emulator_feature_callhistory_support}" == "1" || "%{?tizen_wearable_emulator_feature_callhistory_support}" == "1" BuildRequires: pkgconfig(contacts-service2) %endif @@ -513,12 +540,12 @@ BuildRequires: pkgconfig(contacts-service2) BuildRequires: pkgconfig(libexif) %endif -%if "%{?tizen_feature_nfc_support}" == "1" || "%{?unified_build}" == "1" +%if "%{?tizen_feature_nfc_support}" == "1" || "%{?unified_build}" == "1" || "%{?tizen_mobile_emulator_feature_nfc_support}" == "1" BuildRequires: pkgconfig(capi-network-nfc) BuildRequires: pkgconfig(capi-appfw-app-control) %endif -%if "%{?tizen_feature_fm_radio_support}" == "1" || "%{?unified_build}" == "1" +%if "%{?tizen_feature_fm_radio_support}" == "1" || "%{?unified_build}" == "1" || "%{?tizen_mobile_emulator_feature_fm_radio_support}" == "1" BuildRequires: pkgconfig(capi-media-radio) %endif @@ -547,7 +574,7 @@ BuildRequires: pkgconfig(capi-appfw-preference) BuildRequires: pkgconfig(capi-media-sound-manager) %endif -%if "%{?tizen_feature_sensor_support}" == "1" || "%{?unified_build}" == "1" +%if "%{?tizen_feature_sensor_support}" == "1" || "%{?unified_build}" == "1" || "%{?tizen_wearable_emulator_feature_sensor_support}" == "1" BuildRequires: pkgconfig(capi-system-sensor) %endif @@ -720,33 +747,30 @@ GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_ppm_support=%{?tizen_mobile_feature_pp ninja -C out/Default %{?_smp_mflags} pushd out mv Default bin_mobile -%if "%{?profile}" == "mobile" -ln -sf bin_mobile Default -%endif popd # mobile-extension-emulator %ifarch %{ix86} x86_64 -%define tizen_mobile_feature_bluetooth_support 0 +%define tizen_mobile_feature_bluetooth_support %{?tizen_mobile_emulator_feature_bluetooth_support} # FM radio feature -%define tizen_mobile_feature_fm_radio_support 1 +%define tizen_mobile_feature_fm_radio_support %{?tizen_mobile_emulator_feature_fm_radio_support} -%define tizen_mobile_feature_ham_support 1 -%define tizen_mobile_feature_media_key_support 0 -%define tizen_mobile_feature_nfc_emulation_support 0 -%define tizen_mobile_feature_nfc_support 1 +%define tizen_mobile_feature_ham_support %{?tizen_mobile_emulator_feature_ham_support} +%define tizen_mobile_feature_media_key_support %{?tizen_mobile_emulator_feature_media_key_support} +%define tizen_mobile_feature_nfc_emulation_support %{?tizen_mobile_emulator_feature_nfc_emulation_support} +%define tizen_mobile_feature_nfc_support %{?tizen_mobile_emulator_feature_nfc_support} # secure element feature -%define tizen_mobile_feature_se_support 0 +%define tizen_mobile_feature_se_support %{?tizen_mobile_emulator_feature_se_support} # telephony feature -%define tizen_mobile_feature_telephony_support 1 -%define tizen_mobile_feature_callhistory_support 1 -%define tizen_mobile_feature_nbs_support 1 +%define tizen_mobile_feature_telephony_support %{?tizen_mobile_emulator_feature_telephony_support} +%define tizen_mobile_feature_callhistory_support %{?tizen_mobile_emulator_feature_callhistory_support} +%define tizen_mobile_feature_nbs_support %{?tizen_mobile_emulator_feature_nbs_support} -%define tizen_mobile_feature_wi_fi_support 0 +%define tizen_mobile_feature_wi_fi_support %{?tizen_mobile_emulator_feature_wi_fi_support} GYP_OPTIONS="--depth=. -Dtizen=1 -Dextension_build_type=Debug -Dextension_host_os=mobile -Dprivilege_engine=%{tizen_mobile_privilege_engine}" GYP_OPTIONS="$GYP_OPTIONS -Ddisplay_type=%{display_type}" @@ -810,11 +834,15 @@ GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_ppm_support=%{?tizen_mobile_feature_pp ninja -C out/Default %{?_smp_mflags} pushd out mv Default bin_mobile_emulator +popd +%endif # mobile-extension-emulator + +pushd out %if "%{?profile}" == "mobile" -ln -sf bin_mobile_emulator Default +ln -sf bin_mobile Default %endif popd -%endif # mobile-extension-emulator + %endif # MOBILE %if "%{?unified_build}" == "1" || "%{?profile}" == "wearable" @@ -881,28 +909,25 @@ GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_ppm_support=%{?tizen_wearable_feature_ ninja -C out/Default %{?_smp_mflags} pushd out mv Default bin_wearable -%if "%{?profile}" == "wearable" -ln -sf bin_wearable Default -%endif popd # wearable-extension-emulator %ifarch %{ix86} x86_64 -%define tizen_wearable_feature_bluetooth_support 0 +%define tizen_wearable_feature_bluetooth_support %{?tizen_wearable_emulator_feature_bluetooth_support} # MediayKey API is optional in Tizen Wearable Profile. # tizen.org/feature/network.bluetooth.audio.media is required for MediayKey API -%define tizen_wearable_feature_media_key_support 0 +%define tizen_wearable_feature_media_key_support %{?tizen_wearable_emulator_feature_media_key_support} #- telephony related APIs # CallHistory API is optional in Tizen Wearable Profile. # NetworkBearerSelection API is optional in Tizen Wearable Profile. -%define tizen_wearable_feature_se_support 0 -%define tizen_wearable_feature_telephony_support 1 -%define tizen_wearable_feature_callhistory_support 1 -%define tizen_wearable_feature_nbs_support 1 -%define tizen_wearable_feature_sensor_support 1 +%define tizen_wearable_feature_se_support %{?tizen_wearable_emulator_feature_se_support} +%define tizen_wearable_feature_telephony_support %{?tizen_wearable_emulator_feature_telephony_support} +%define tizen_wearable_feature_callhistory_support %{?tizen_wearable_emulator_feature_callhistory_support} +%define tizen_wearable_feature_nbs_support %{?tizen_wearable_emulator_feature_nbs_support} +%define tizen_wearable_feature_sensor_support %{?tizen_wearable_emulator_feature_sensor_support} GYP_OPTIONS="--depth=. -Dtizen=1 -Dextension_build_type=Debug -Dextension_host_os=wearable -Dprivilege_engine=%{tizen_wearable_privilege_engine}" GYP_OPTIONS="$GYP_OPTIONS -Ddisplay_type=%{display_type}" @@ -966,11 +991,15 @@ GYP_OPTIONS="$GYP_OPTIONS -Dtizen_feature_ppm_support=%{?tizen_wearable_feature_ ninja -C out/Default %{?_smp_mflags} pushd out mv Default bin_wearable_emulator +popd +%endif # wearable-extension-emulator + +pushd out %if "%{?profile}" == "wearable" -ln -sf bin_wearable_emulator Default +ln -sf bin_wearable Default %endif popd -%endif # wearable-extension-emulator + %endif # WEARABLE %if "%{?unified_build}" == "1" || "%{?profile}" == "tv" diff --git a/src/account/account.gyp b/src/account/account.gyp index 272a0435..3fb0e3ed 100644 --- a/src/account/account.gyp +++ b/src/account/account.gyp @@ -22,7 +22,6 @@ ['tizen == 1', { 'variables': { 'packages': [ - 'capi-appfw-package-manager', 'accounts-svc', ] }, diff --git a/src/account/account_api.js b/src/account/account_api.js index 5c7c8832..db4eb6f3 100755 --- a/src/account/account_api.js +++ b/src/account/account_api.js @@ -39,26 +39,10 @@ function AccountProvider(data) { Object.freeze(internal_); Object.defineProperties(this, { - applicationId: { - enumerable: true, - writable: false, - value: data.applicationId - }, - displayName: { - enumerable: true, - writable: false, - value: data.displayName - }, - iconUri: { - enumerable: true, - writable: false, - value: data.iconUri - }, - smallIconUri: { - enumerable: true, - writable: false, - value: data.smallIconUri - }, + applicationId: { enumerable: true, writable: false, value: data.applicationId }, + displayName: { enumerable: true, writable: false, value: data.displayName }, + iconUri: { enumerable: true, writable: false, value: data.iconUri }, + smallIconUri: { enumerable: true, writable: false, value: data.smallIconUri }, capabilities: { enumerable: true, set: function() {}, @@ -77,11 +61,7 @@ function AccountProvider(data) { function Account() { validator_.isConstructorCall(this, tizen.Account); var args = validator_.validateArgs(arguments, [ - { - name: 'provider', - type: types_.PLATFORM_OBJECT, - values: AccountProvider - }, + { name: 'provider', type: types_.PLATFORM_OBJECT, values: AccountProvider }, { name: 'accountInitDict', type: types_.DICTIONARY, @@ -291,18 +271,8 @@ AccountManager.prototype.getAccounts = function() { optional: false, nullable: false }, - { - name: 'errorCallback', - type: types_.FUNCTION, - optional: true, - nullable: true - }, - { - name: 'applicationId', - type: types_.STRING, - optional: true, - nullable: true - } + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'applicationId', type: types_.STRING, optional: true, nullable: true } ]); var result = native_.call( @@ -361,18 +331,8 @@ AccountManager.prototype.getProviders = function() { optional: false, nullable: false }, - { - name: 'errorCallback', - type: types_.FUNCTION, - optional: true, - nullable: true - }, - { - name: 'capability', - type: types_.STRING, - optional: true, - nullable: true - } + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'capability', type: types_.STRING, optional: true, nullable: true } ]); var result = native_.call( diff --git a/src/alarm/alarm.gyp b/src/alarm/alarm.gyp index 6a5739e9..df129e03 100644 --- a/src/alarm/alarm.gyp +++ b/src/alarm/alarm.gyp @@ -26,7 +26,9 @@ 'packages': [ 'capi-appfw-alarm', 'capi-appfw-app-control', + 'alarm-service', 'capi-appfw-application', + 'notification', ] }, }], diff --git a/src/alarm/alarm_api.js b/src/alarm/alarm_api.js index eb668868..98b4c7be 100755 --- a/src/alarm/alarm_api.js +++ b/src/alarm/alarm_api.js @@ -446,8 +446,8 @@ tizen.AlarmAbsolute = function(date, second, internal) { if (_warningLogs.enableLog && isAlarmAbsolutePeriodDeprecated) { privUtils_.warn( 'This Constructor is deprecated since Tizen 4.0.' + - ' Please consider using other constructors or ' + - 'other type of an alarm.' + ' Please consider using other constructors or other ' + + 'type of an alarm.' ); } } @@ -476,8 +476,8 @@ tizen.AlarmAbsolute = function(date, second, internal) { if (_warningLogs.enableLog && isAlarmAbsolutePeriodDeprecated) { privUtils_.warn( 'Since Tizen 4.0 constructor AlarmAbsolute(Date date, ' + - 'long period) is deprecated, thus period attribute should ' + - 'not be used.' + 'long period) is deprecated, thus period attribute ' + + 'should not be used.' ); } return m_period; @@ -486,8 +486,8 @@ tizen.AlarmAbsolute = function(date, second, internal) { if (_warningLogs.enableLog && isAlarmAbsolutePeriodDeprecated) { privUtils_.warn( 'Since Tizen 4.0 constructor AlarmAbsolute(Date date, ' + - 'long period) is deprecated, thus period attribute should ' + - 'not be used.' + 'long period) is deprecated, thus period attribute ' + + 'should not be used.' ); } diff --git a/src/alarm/alarm_manager.cc b/src/alarm/alarm_manager.cc index 7a27dfcc..bcc74e04 100644 --- a/src/alarm/alarm_manager.cc +++ b/src/alarm/alarm_manager.cc @@ -143,7 +143,8 @@ void AlarmManager::Add(const picojson::value& args, picojson::object& out) { } std::string delay_str = std::to_string(delay); - int ret_app = app_control_add_extra_data(app_control, kAlarmRelativeDelayKey, delay_str.c_str()); + int ret_app = + app_control_add_extra_data(app_control, kAlarmRelativeDelayKey, delay_str.c_str()); if (APP_CONTROL_ERROR_NONE != ret_app) { LogAndReportError( PlatformResult(ErrorCode::UNKNOWN_ERR, "Fail to add data from app_control."), &out, @@ -631,8 +632,9 @@ PlatformResult AlarmManager::GetAlarm(int id, picojson::object& obj) { int ret_app = app_control_get_extra_data(app_control, kAlarmRelativeDelayKey, &delay_string); if (APP_CONTROL_ERROR_NONE != ret_app) { - return LogAndCreateResult(ErrorCode::NOT_FOUND_ERR, "Failed to get data.", - ("Failed to get data: %d (%s)", ret_app, get_error_message(ret_app))); + return LogAndCreateResult( + ErrorCode::NOT_FOUND_ERR, "Failed to get data.", + ("Failed to get data: %d (%s)", ret_app, get_error_message(ret_app))); } obj.insert(std::make_pair("type", picojson::value(kAlarmRelative))); diff --git a/src/application/application_api.js b/src/application/application_api.js index 70586eef..8855b06b 100755 --- a/src/application/application_api.js +++ b/src/application/application_api.js @@ -753,8 +753,8 @@ var applicationEventListener = new ListenerManager(native, APPLICATION_EVENT_LIS ApplicationManager.prototype.addAppInfoEventListener = function() { privUtils_.warn( - 'DEPRECATION WARNING: addAppInfoEventListener() is deprecated ' + - 'and will be removed from next release. ' + + 'DEPRECATION WARNING: addAppInfoEventListener() is deprecated and will ' + + 'be removed from next release. ' + 'Use tizen.package.setPackageInfoEventListener() instead.' ); @@ -771,8 +771,8 @@ ApplicationManager.prototype.addAppInfoEventListener = function() { ApplicationManager.prototype.removeAppInfoEventListener = function() { privUtils_.warn( - 'DEPRECATION WARNING: removeAppInfoEventListener() is deprecated ' + - 'and will be removed from next release. ' + + 'DEPRECATION WARNING: removeAppInfoEventListener() is deprecated and will ' + + 'be removed from next release. ' + 'Use tizen.package.unsetPackageInfoEventListener() instead.' ); @@ -1003,8 +1003,8 @@ Application.prototype.addEventListener = function(event, callback) { event_listeners_[eventName][id](eventInfo, msg.data); } else { delete msg.name; - //TODO: type should come from native site msg.type = parsedName[2]; + //TODO: type should come from native site eventInfo.name = parsedName[2].toUpperCase(); event_listeners_[eventName][id](eventInfo, msg); } diff --git a/src/application/application_manager.cc b/src/application/application_manager.cc index 69e2a559..840faafe 100644 --- a/src/application/application_manager.cc +++ b/src/application/application_manager.cc @@ -16,6 +16,7 @@ #include "application_manager.h" +#include <glib.h> #include <sys/stat.h> #include <unistd.h> #include <type_traits> @@ -213,7 +214,7 @@ class TerminateHandler { return; \ } -void ApplicationManager::AsyncResponse(PlatformResult& result, +void ApplicationManager::AsyncResponse(const PlatformResult& result, std::shared_ptr<picojson::value>* response) { ScopeLogger(); LogAndReportError(result, &(*response)->get<picojson::object>()); @@ -375,82 +376,52 @@ void ApplicationManager::Kill(const picojson::value& args) { kill(); } -void ApplicationManager::Launch(const picojson::value& args) { +namespace { + +PlatformResult PrepareAppControlForLaunchAppControl(const picojson::value& args, + app_control_h* app_control) { ScopeLogger(); - int callback_id = -1; - const auto& callback = args.get(kCallbackId); - if (callback.is<double>()) { - callback_id = static_cast<int>(callback.get<double>()); + const auto& control = args.get("appControl"); + if (!control.is<picojson::object>()) { + return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); } + const picojson::object& app_control_obj = control.get<picojson::object>(); - std::shared_ptr<picojson::value> response(new picojson::value(picojson::object())); - picojson::object& obj = response->get<picojson::object>(); - obj.insert(std::make_pair(kCallbackId, picojson::value(static_cast<double>(callback_id)))); + app_control_h tmp_app_control = nullptr; + auto result = ApplicationUtils::ApplicationControlToService(app_control_obj, &tmp_app_control); + std::unique_ptr<std::remove_pointer<app_control_h>::type, decltype(&app_control_destroy)> + app_control_ptr(tmp_app_control, &app_control_destroy); - const auto& app_id = args.get("id"); - if (!app_id.is<std::string>()) { - PlatformResult ret = - LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); - AsyncResponse(ret, &response); - return; + if (result.IsError()) { + LoggerE("Application control to service failed."); + return result; } - const std::string& id = app_id.get<std::string>(); - auto launch = [id](const std::shared_ptr<picojson::value>& response) -> void { - ScopeLogger("launch"); - PlatformResult result = PlatformResult(ErrorCode::NO_ERROR); - const char* app_id = id.c_str(); - const int retry_count = 3; + std::string app_id; + const auto& id = args.get("id"); + if (id.is<std::string>()) { + app_id = id.get<std::string>(); + } - int retry = 0; - int ret = 0; + if (!app_id.empty()) { + LoggerD("app_id: %s", app_id.c_str()); - while (retry < retry_count) { - ret = aul_open_app(app_id); + int ret = app_control_set_app_id(app_control_ptr.get(), app_id.c_str()); - if (ret >= 0) { - break; - } - - // delay 300ms for each retry - struct timespec sleep_time = {0, 300L * 1000L * 1000L}; - nanosleep(&sleep_time, nullptr); - ++retry; - - LoggerD("Retry launch request: %d", retry); + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Failed to set app id: %d (%s)", ret, get_error_message(ret)); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); } + } - if (ret < 0) { - result = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error has occurred."); - - LoggerD("Aul open return: %d (%s)", ret, get_error_message(ret)); - switch (ret) { - case AUL_R_EINVAL: - case AUL_R_ERROR: - case AUL_R_ENOAPP: - result = - LogAndCreateResult(ErrorCode::NOT_FOUND_ERR, "Launchpad returns not found error.", - ("aul_open_app returns Not Found error")); - break; - - case AUL_R_ECOMM: - result = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Internal IPC error has occurred.", - ("aul_open_app returns internal IPC error")); - break; - } - - LogAndReportError(result, &response->get<picojson::object>()); - } else { - LoggerD("Launch request success"); - ReportSuccess(response->get<picojson::object>()); - } - }; + *app_control = app_control_ptr.release(); - launch(response); - Instance::PostMessage(&this->instance_, response->serialize().c_str()); + return PlatformResult(ErrorCode::NO_ERROR); } +} // namespace + void ApplicationManager::LaunchAppControl(const picojson::value& args) { ScopeLogger(); @@ -465,180 +436,226 @@ void ApplicationManager::LaunchAppControl(const picojson::value& args) { response_obj.insert( std::make_pair(kCallbackId, picojson::value(static_cast<double>(callback_id)))); - PlatformResult result = PlatformResult(ErrorCode::NO_ERROR); - const auto& control = args.get("appControl"); - if (!control.is<picojson::object>()) { - result = LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); - AsyncResponse(result, &response); - return; - } - const picojson::object& app_control_obj = control.get<picojson::object>(); - - std::string launch_mode_str; - const auto& launch_mode = control.get("launchMode"); - if (launch_mode.is<std::string>()) { - launch_mode_str = launch_mode.get<std::string>(); - } - app_control_h app_control = nullptr; - result = ApplicationUtils::ApplicationControlToService(app_control_obj, &app_control); - std::shared_ptr<std::remove_pointer<app_control_h>::type> app_control_ptr( - app_control, &app_control_destroy); // automatically release the memory - - if (result.IsError()) { - LoggerE("Application control to service failed."); - AsyncResponse(result, &response); + auto prepare_app_control_result = PrepareAppControlForLaunchAppControl(args, &app_control); + if (prepare_app_control_result.IsError()) { + AsyncResponse(LogAndCreateResult(prepare_app_control_result), &response); return; } - std::string app_id; - const auto& id = args.get("id"); - if (id.is<std::string>()) { - app_id = id.get<std::string>(); - } + std::unique_ptr<std::remove_pointer<app_control_h>::type, decltype(&app_control_destroy)> + app_control_ptr(app_control, &app_control_destroy); - std::string reply_callback; + std::string reply_callback_id; const auto& reply = args.get("replyCallback"); if (reply.is<std::string>()) { - reply_callback = reply.get<std::string>(); + reply_callback_id = reply.get<std::string>(); } - auto launch = [this, app_control_ptr, app_id, launch_mode_str, - reply_callback](const std::shared_ptr<picojson::value>& response) -> void { - ScopeLogger("Entered into asynchronous function, launch"); + struct LaunchAppControlCallbackData { + ApplicationInstance* instance; + std::shared_ptr<picojson::value> response; + std::string reply_callback_id; + }* launch_app_user_data = new (std::nothrow) + LaunchAppControlCallbackData{&this->instance_, response, reply_callback_id}; - if (!app_id.empty()) { - LoggerD("app_id: %s", app_id.c_str()); + if (!launch_app_user_data) { + LoggerE("Memory allocation fail!"); + AsyncResponse(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."), &response); + return; + } - int ret = app_control_set_app_id(app_control_ptr.get(), app_id.c_str()); + app_control_reply_cb reply_callback = nullptr; + if (!reply_callback_id.empty()) { + launch_app_user_data->reply_callback_id = reply_callback_id; - if (APP_CONTROL_ERROR_NONE != ret) { - LogAndReportError( - PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), - &response->get<picojson::object>(), - ("Failed to set app id: %d (%s)", ret, get_error_message(ret))); - return; - } - } + reply_callback = [](app_control_h request, app_control_h reply, app_control_result_e result, + void* user_data) { + ScopeLogger("reply_callback"); - if (!launch_mode_str.empty()) { - app_control_launch_mode_e launch_mode; - - if ("SINGLE" == launch_mode_str) { - launch_mode = APP_CONTROL_LAUNCH_MODE_SINGLE; - } else if ("GROUP" == launch_mode_str) { - launch_mode = APP_CONTROL_LAUNCH_MODE_GROUP; - } else { - ReportError(PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), - &response->get<picojson::object>()); + LaunchAppControlCallbackData* callback_data = + static_cast<LaunchAppControlCallbackData*>(user_data); + if (!callback_data) { + LoggerD("reply_callback failed: user_data is nullptr"); return; } - int ret = app_control_set_launch_mode(app_control_ptr.get(), launch_mode); - if (APP_CONTROL_ERROR_NONE != ret) { - LogAndReportError(PlatformResult(ErrorCode::NOT_FOUND_ERR, "Setting launch mode failed."), - &response->get<picojson::object>(), - ("Setting launch mode failed: %d (%s)", ret, get_error_message(ret))); - return; + picojson::value return_value = picojson::value(picojson::object()); + picojson::object& return_value_obj = return_value.get<picojson::object>(); + return_value_obj.insert( + std::make_pair(kListenerId, picojson::value(callback_data->reply_callback_id))); + + if (APP_CONTROL_RESULT_SUCCEEDED == result) { + LoggerD("App started"); + return_value_obj.insert(std::make_pair("data", picojson::value(picojson::array()))); + if (!ApplicationUtils::ServiceToApplicationControlDataArray( + reply, &return_value_obj.find("data")->second.get<picojson::array>())) { + return_value_obj.erase("data"); + } + ReportSuccess(return_value_obj); + } else { + ReportError(return_value_obj); } - } - app_control_reply_cb callback = nullptr; - struct ReplayCallbackData { - ApplicationInstance* app_instance; - std::string reply_callback; + Instance::PostMessage(callback_data->instance, return_value.serialize().c_str()); + delete callback_data; }; + } - ReplayCallbackData* user_data = nullptr; + app_control_result_cb result_callback = [](app_control_h launch_request, + app_control_error_e launch_result, void* user_data) { + ScopeLogger("LaunchAppControl result_callback"); - if (!reply_callback.empty()) { - user_data = new ReplayCallbackData(); - user_data->app_instance = &this->instance_; - user_data->reply_callback = reply_callback; + LaunchAppControlCallbackData* callback_data = + static_cast<LaunchAppControlCallbackData*>(user_data); - callback = [](app_control_h request, app_control_h reply, app_control_result_e result, - void* user_data) { - LoggerD("send_launch_request callback"); + auto result = ApplicationUtils::TranslateAppControlError(launch_result); - picojson::value return_value = picojson::value(picojson::object()); - picojson::object& return_value_obj = return_value.get<picojson::object>(); - ReplayCallbackData* reply_callback = static_cast<ReplayCallbackData*>(user_data); + if (result.IsError()) { + LogAndReportError(result, &(callback_data->response->get<picojson::object>())); + } else { + ReportSuccess(callback_data->response->get<picojson::object>()); + } - if (APP_CONTROL_RESULT_SUCCEEDED == result) { - const std::string data = "data"; - return_value_obj.insert(std::make_pair(data, picojson::value(picojson::array()))); - if (!ApplicationUtils::ServiceToApplicationControlDataArray( - reply, &return_value_obj.find(data)->second.get<picojson::array>())) { - return_value_obj.erase(data); - } - ReportSuccess(return_value_obj); - } else { - ReportError(return_value_obj); - } + Instance::PostMessage(callback_data->instance, callback_data->response->serialize().c_str()); - return_value_obj.insert( - std::make_pair("listenerId", picojson::value(reply_callback->reply_callback))); - Instance::PostMessage(reply_callback->app_instance, return_value.serialize().c_str()); - delete reply_callback; - }; + if (result.IsError() || (callback_data->reply_callback_id).empty()) { + delete callback_data; } + }; - const int retry_count = 3; + /* + * TODO: Observe how often app_control_send_launch_request_async tries to launch the application. + * Previous implementation, using synchronous app_control_send_launch_request, + * tries to launch the application 3 times, before reporting an error. + * New implementation, using app_control_send_launch_request_async makes only one attempt. + * If problems, such as failed application start occur, multiple attempts may solve the problem. + */ + auto launch_result = + ApplicationUtils::TranslateAppControlError(static_cast<app_control_error_e>(app_control_send_launch_request_async( + app_control_ptr.get(), result_callback, reply_callback, launch_app_user_data))); + + if (launch_result.IsError()) { + delete launch_app_user_data; + AsyncResponse(launch_result, &response); + } else { + LoggerD("App launched"); + } +} - int retry = 0; - int ret = 0; +namespace { - while (retry < retry_count) { - LoggerD("Calling launch request. Attempt number: %d", retry); +PlatformResult TranslateLaunchError(app_control_error_e return_code) { + ScopeLogger(); - ret = app_control_send_launch_request(app_control_ptr.get(), callback, user_data); - LoggerD("App control launch request returned: %d, %s", ret, get_error_message(ret)); - if (APP_CONTROL_ERROR_NONE == ret) { - break; - } + auto result = ApplicationUtils::TranslateAppControlError(return_code); + if (ErrorCode::SECURITY_ERR == result.error_code()) { + result = PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."); + } - // delay 300ms for each retry - struct timespec sleep_time = {0, 300L * 1000L * 1000L}; - nanosleep(&sleep_time, nullptr); - ++retry; - } + return result; +} + +PlatformResult PrepareAppControlForLaunch(const picojson::value& args, app_control_h* app_control) { + ScopeLogger(); + + const auto& app_id = args.get("id"); + if (!app_id.is<std::string>()) { + return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); + } + const auto app_id_str = app_id.get<std::string>(); + + app_control_h tmp_app_control = nullptr; + int result = app_control_create(&tmp_app_control); + if (APP_CONTROL_ERROR_NONE != result) { + LoggerD("app_control_create() failed: %d (%s)", result, get_error_message(result)); + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occurred."); + } + + std::unique_ptr<std::remove_pointer<app_control_h>::type, decltype(&app_control_destroy)> + app_control_ptr(tmp_app_control, &app_control_destroy); + + if (!app_id_str.empty()) { + LoggerD("app_id: %s", app_id_str.c_str()); + + int ret = app_control_set_app_id(app_control_ptr.get(), app_id_str.c_str()); if (APP_CONTROL_ERROR_NONE != ret) { - delete user_data; - - switch (ret) { - case APP_CONTROL_ERROR_INVALID_PARAMETER: - LogAndReportError( - PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter returned."), - &response->get<picojson::object>(), - ("app_control_send_launch_request returns APP_CONTROL_ERROR_INVALID_PARAMETER")); - return; - case APP_CONTROL_ERROR_OUT_OF_MEMORY: - LogAndReportError( - PlatformResult(ErrorCode::UNKNOWN_ERR, "Out of memory."), - &response->get<picojson::object>(), - ("app_control_send_launch_request returns APP_CONTROL_ERROR_OUT_OF_MEMORY")); - return; - case APP_CONTROL_ERROR_LAUNCH_REJECTED: - case APP_CONTROL_ERROR_APP_NOT_FOUND: - LogAndReportError( - PlatformResult(ErrorCode::NOT_FOUND_ERR, "No matched application found."), - &response->get<picojson::object>(), - ("app_control_send_launch_request returns APP_CONTROL_ERROR_APP_NOT_FOUND")); - return; - default: - LogAndReportError( - PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."), - &response->get<picojson::object>(), - ("app_control_send_launch_request returns: %d (%s)", ret, get_error_message(ret))); - return; - } + LoggerE("Failed to set app id: %d (%s)", ret, get_error_message(ret)); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); } - ReportSuccess(response->get<picojson::object>()); + } + + *app_control = app_control_ptr.release(); + + return PlatformResult(ErrorCode::NO_ERROR); +} + +} // namespace +void ApplicationManager::Launch(const picojson::value& args) { + ScopeLogger(); + + int callback_id = -1; + const auto& callback = args.get(kCallbackId); + if (callback.is<double>()) { + callback_id = static_cast<int>(callback.get<double>()); + } + + std::shared_ptr<picojson::value> response(new picojson::value(picojson::object())); + picojson::object& response_obj = response->get<picojson::object>(); + response_obj.insert( + std::make_pair(kCallbackId, picojson::value(static_cast<double>(callback_id)))); + + app_control_h app_control = nullptr; + auto prepare_app_control_result = PrepareAppControlForLaunch(args, &app_control); + if (prepare_app_control_result.IsError()) { + AsyncResponse(LogAndCreateResult(prepare_app_control_result), &response); + return; + } + + std::unique_ptr<std::remove_pointer<app_control_h>::type, decltype(&app_control_destroy)> + app_control_ptr(app_control, &app_control_destroy); + + struct LaunchCallbackData { + ApplicationInstance* instance; + std::shared_ptr<picojson::value> response; + }* launch_user_data = new (std::nothrow) LaunchCallbackData{&this->instance_, response}; + + app_control_result_cb result_callback = [](app_control_h launch_request, + app_control_error_e launch_result, void* user_data) { + ScopeLogger("Launch result_callback"); + + LaunchCallbackData* callback_data = static_cast<LaunchCallbackData*>(user_data); + + auto result = TranslateLaunchError(launch_result); + + if (result.IsError()) { + LogAndReportError(result, &(callback_data->response->get<picojson::object>())); + } else { + ReportSuccess(callback_data->response->get<picojson::object>()); + } + + Instance::PostMessage(callback_data->instance, callback_data->response->serialize().c_str()); + + delete callback_data; }; - launch(response); - Instance::PostMessage(&this->instance_, response->serialize().c_str()); + /* + * TODO: Observe how often app_control_send_launch_request_async tries to launch the application. + * Previous implementation, using synchronous app_control_send_launch_request, + * tries to launch the application 3 times, before reporting an error. + * New implementation, using app_control_send_launch_request_async makes only one attempt. + * If problems, such as failed application start occur, multiple attempts may solve the problem. + */ + auto launch_result = TranslateLaunchError(static_cast<app_control_error_e>(app_control_send_launch_request_async( + app_control_ptr.get(), result_callback, nullptr, launch_user_data))); + + if (launch_result.IsError()) { + delete launch_user_data; + AsyncResponse(launch_result, &response); + } else { + LoggerD("App launched"); + } } // internal impl of app_control_foreach_app_matched() for handling APP_CONTROL_ERROR_APP_NOT_FOUND @@ -1368,8 +1385,6 @@ void ApplicationManager::GetBatteryUsageInfo(const picojson::value& args, picojs TaskQueue::GetInstance().Queue<picojson::value>(get_battery_usage, get_battery_usage_response, data); #else - // 20170510 Context API is supported only for mobile profile, other ones would result with - // NotSupportedError LogAndReportError(PlatformResult(ErrorCode::NOT_SUPPORTED_ERR, "This feature is not supported on this profile."), out, ("NOT_SUPPORTED_ERR: This feature is not supported on this profile")); @@ -1404,8 +1419,6 @@ void ApplicationManager::GetAppsUsageInfo(const picojson::value& args, picojson: TaskQueue::GetInstance().Queue<picojson::value>(get_apps_usage, get_apps_usage_response, data); #else - // Context API is supported only for mobile profile, other ones would result with - // NotSupportedError LogAndReportError(PlatformResult(ErrorCode::NOT_SUPPORTED_ERR, "This feature is not supported on this profile."), out, ("NOT_SUPPORTED_ERR: This feature is not supported on this profile")); diff --git a/src/application/application_manager.h b/src/application/application_manager.h index c1fa8ad2..be0ebbf6 100644 --- a/src/application/application_manager.h +++ b/src/application/application_manager.h @@ -64,7 +64,8 @@ class ApplicationManager { void StartAppInfoEventListener(picojson::object* out); void StopAppInfoEventListener(); void GetApplicationInformationSize(const picojson::value& args, picojson::object* out); - void AsyncResponse(common::PlatformResult& result, std::shared_ptr<picojson::value>* response); + void AsyncResponse(const common::PlatformResult& result, + std::shared_ptr<picojson::value>* response); void BroadcastEventHelper(const picojson::value& args, picojson::object& out, bool trusted); common::PlatformResult StartEventListener(const std::string& event_name, diff --git a/src/application/application_utils.cc b/src/application/application_utils.cc index 596d292a..3fbeeba5 100644 --- a/src/application/application_utils.cc +++ b/src/application/application_utils.cc @@ -183,64 +183,182 @@ void ApplicationUtils::CreateApplicationMetaData(const char* key, const char* va app_meta_data->insert(std::make_pair("value", picojson::value(value))); } +namespace { + +const std::string kOperationAppControlField = "operation"; +const std::string kURIAppControlField = "uri"; +const std::string kMIMEAppControlField = "mime"; +const std::string kCategoryAppControlField = "category"; +const std::string kLaunchModeAppControlField = "launchMode"; +const std::string kDataAppControlField = "data"; + +const std::string kGroupLaunchMode = "GROUP"; +const std::string kSingleLaunchMode = "SINGLE"; + +using AppControlTextFieldSetter = std::function<int(app_control_h, const char*)>; +// clang-format off +const std::map<std::string, AppControlTextFieldSetter> AppControlTextFieldSetters = { + { kOperationAppControlField, app_control_set_operation }, + { kURIAppControlField, app_control_set_uri }, + { kMIMEAppControlField, app_control_set_mime }, + { kCategoryAppControlField, app_control_set_category } +}; // clang-format on + +PlatformResult SetAppControlTextField(app_control_h app_control, const std::string& field_name, + const std::string& value) { + ScopeLogger("Field: %s, value: %s", field_name.c_str(), value.c_str()); + + auto setter_it = AppControlTextFieldSetters.find(field_name); + if (AppControlTextFieldSetters.end() == setter_it) { + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occurred."); + } + auto setter = setter_it->second; + + auto result = setter(app_control, value.c_str()); + auto result_translated = + ApplicationUtils::TranslateAppControlError(static_cast<app_control_error_e>(result)); + + if (result_translated.IsError()) { + LoggerD("Setting app_control's %s field failed: %s", field_name.c_str(), + result_translated.message().c_str()); + } + + return result_translated; +} + +app_control_launch_mode_e LaunchModeStringToEnum(const std::string& launch_mode) { + ScopeLogger(); + + return launch_mode == kGroupLaunchMode ? APP_CONTROL_LAUNCH_MODE_GROUP + : APP_CONTROL_LAUNCH_MODE_SINGLE; +} + +bool LaunchModeIsInvalid(const std::string& launch_mode) { + ScopeLogger(); + + auto is_valid = launch_mode == kSingleLaunchMode || launch_mode == kGroupLaunchMode; + LoggerD("Launch mode: %s (%s)", launch_mode.c_str(), is_valid ? "valid" : "invalid"); + + return !is_valid; +} + +PlatformResult SetAppControlLaunchModeField(app_control_h app_control, + const std::string& launch_mode_str) { + ScopeLogger(); + + if (LaunchModeIsInvalid(launch_mode_str)) { + LoggerD("Invalid launchMode value: %s", launch_mode_str.c_str()); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, + "Invalid launchMode value: " + launch_mode_str); + } + + auto result = app_control_set_launch_mode(app_control, LaunchModeStringToEnum(launch_mode_str)); + return ApplicationUtils::TranslateAppControlError(static_cast<app_control_error_e>(result)); +} + +PlatformResult SetAppControlDataField(app_control_h app_control, const picojson::array& data) { + ScopeLogger(); + + for (auto iter = data.begin(); iter != data.end(); ++iter) { + if (iter->is<picojson::object>()) { + PlatformResult ret = ApplicationUtils::ApplicationControlDataToServiceExtraData( + iter->get<picojson::object>(), app_control); + if (ret.IsError()) { + return ret; + } + } else { + LoggerD("Invalid data value: not an object"); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid data type: not an object"); + } + } + + return PlatformResult{ErrorCode::NO_ERROR}; +} + +PlatformResult SetAppControlFieldIfValueSpecified( + app_control_h app_control, const std::string& field_name, + const picojson::object::const_iterator& iterator) { + ScopeLogger(); + + if (iterator->second.is<std::string>()) { + if (field_name != kLaunchModeAppControlField) { + return SetAppControlTextField(app_control, field_name, iterator->second.get<std::string>()); + } + return SetAppControlLaunchModeField(app_control, iterator->second.get<std::string>()); + } else if (iterator->second.is<picojson::array>() && kDataAppControlField == field_name) { + return SetAppControlDataField(app_control, iterator->second.get<picojson::array>()); + } + + return PlatformResult{ErrorCode::NO_ERROR}; +} + +} // namespace + PlatformResult ApplicationUtils::ApplicationControlToService( const picojson::object& app_control_obj, app_control_h* app_control) { ScopeLogger(); - const auto it_operation = app_control_obj.find("operation"); - const auto it_uri = app_control_obj.find("uri"); - const auto it_mime = app_control_obj.find("mime"); - const auto it_category = app_control_obj.find("category"); - const auto it_data = app_control_obj.find("data"); + + const auto it_operation = app_control_obj.find(kOperationAppControlField); + const auto it_uri = app_control_obj.find(kURIAppControlField); + const auto it_mime = app_control_obj.find(kMIMEAppControlField); + const auto it_category = app_control_obj.find(kCategoryAppControlField); + const auto it_data = app_control_obj.find(kDataAppControlField); + const auto it_launch_mode = app_control_obj.find(kLaunchModeAppControlField); const auto it_app_control_end = app_control_obj.end(); if (it_operation == it_app_control_end || it_uri == it_app_control_end || it_mime == it_app_control_end || it_category == it_app_control_end || it_data == it_app_control_end || !it_operation->second.is<std::string>() || - !it_data->second.is<picojson::array>()) { + !it_data->second.is<picojson::array>() || it_launch_mode == it_app_control_end) { return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter was passed."); } app_control_h app_control_tmp = nullptr; int result = app_control_create(&app_control_tmp); - if (APP_CONTROL_ERROR_NONE != result) { - return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Creation AppControl failed.", - ("Problem with create handle.")); + LoggerD("app_control_create() failed: %d (%s)", result, get_error_message(result)); + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occurred."); } std::unique_ptr<std::remove_pointer<app_control_h>::type, int (*)(app_control_h)> app_control_ptr( app_control_tmp, &app_control_destroy); - // operation - app_control_set_operation(app_control_tmp, it_operation->second.get<std::string>().c_str()); + auto set_field_result = PlatformResult(ErrorCode::UNKNOWN_ERR); + + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kOperationAppControlField, it_operation); + if (set_field_result.IsError()) { + return set_field_result; + } - // uri - if (it_uri->second.is<std::string>()) { - app_control_set_uri(app_control_tmp, it_uri->second.get<std::string>().c_str()); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kURIAppControlField, it_uri); + if (set_field_result.IsError()) { + return set_field_result; } - // mime - if (it_mime->second.is<std::string>()) { - app_control_set_mime(app_control_tmp, it_mime->second.get<std::string>().c_str()); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kMIMEAppControlField, it_mime); + if (set_field_result.IsError()) { + return set_field_result; } - // category - if (it_category->second.is<std::string>()) { - app_control_set_category(app_control_tmp, it_category->second.get<std::string>().c_str()); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kCategoryAppControlField, it_category); + if (set_field_result.IsError()) { + return set_field_result; } - // ApplicationControlData - const picojson::array& data = it_data->second.get<picojson::array>(); + set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kLaunchModeAppControlField, + it_launch_mode); + if (set_field_result.IsError()) { + return set_field_result; + } - for (auto iter = data.begin(); iter != data.end(); ++iter) { - if (iter->is<picojson::object>()) { - PlatformResult ret = - ApplicationControlDataToServiceExtraData(iter->get<picojson::object>(), app_control_tmp); - if (ret.IsError()) { - LoggerE("Failed ApplicationControlDataToServiceExtraData()"); - return ret; - } - } + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kDataAppControlField, it_data); + if (set_field_result.IsError()) { + return set_field_result; } *app_control = app_control_ptr.release(); @@ -259,27 +377,43 @@ PlatformResult ApplicationUtils::ApplicationControlDataToServiceExtraData( if (it_key == it_app_control_data_end || it_value == it_app_control_data_end || !it_key->second.is<std::string>() || !it_value->second.is<picojson::array>()) { return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter was passed.", - ("Problem with key or value.")); + ("Invalid key or value.")); } const std::string& key = it_key->second.get<std::string>(); const picojson::array& value = it_value->second.get<picojson::array>(); - const size_t size = value.size(); - const char** arr = new const char*[size]; - size_t i = 0; + std::vector<const char*> value_data; - for (auto iter = value.begin(); iter != value.end(); ++iter, ++i) { - arr[i] = iter->get<std::string>().c_str(); + for (auto& v : value) { + value_data.push_back(v.get<std::string>().c_str()); } - if (1 == size) { - app_control_add_extra_data(app_control, key.c_str(), arr[0]); + int result = APP_CONTROL_ERROR_NONE; + /* + * Native applications handle single extra data objects and arrays in a different ways, + * hence they have to be packed with different native API functions. + */ + if (1 == value_data.size()) { + result = app_control_add_extra_data(app_control, key.c_str(), value_data[0]); } else { - app_control_add_extra_data_array(app_control, key.c_str(), arr, size); + result = app_control_add_extra_data_array(app_control, key.c_str(), value_data.data(), value_data.size()); + } + + if (APP_CONTROL_ERROR_INVALID_PARAMETER == result) { + if (0 == key.length()) { + LoggerD("app_control_add_extra_data_array failed: zero-length key"); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, + "Invalid AppControlData key: key length is 0."); + } + LoggerD("app_control_add_extra_data_array failed: invalid parameter passed"); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, + "Invalid AppControlData value, associated with key: " + key); + } else if (APP_CONTROL_ERROR_KEY_REJECTED == result) { + LoggerD("app_control_add_extra_data_array failed: key rejected"); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid AppControlData's key: " + key); } - delete[] arr; return PlatformResult(ErrorCode::NO_ERROR); } @@ -295,42 +429,68 @@ void ApplicationUtils::ServiceToApplicationControl(app_control_h app_control, }; ret = app_control_get_operation(app_control, &tmp_str); - if ((APP_CONTROL_ERROR_NONE == ret) && (nullptr != tmp_str)) { + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Get operation failed: %d (%s)", ret, get_error_message(ret)); + } else if (tmp_str) { LoggerD("operation: %s", tmp_str); - app_control_obj->insert(std::make_pair("operation", picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kOperationAppControlField, picojson::value(std::string(tmp_str)))); } else { - LoggerE("Get operation failed: %d (%s)", ret, get_error_message(ret)); + LoggerD("operation field is empty"); } clear(tmp_str); ret = app_control_get_uri(app_control, &tmp_str); - if ((APP_CONTROL_ERROR_NONE == ret) && (nullptr != tmp_str)) { + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Get URI failed: %d (%s)", ret, get_error_message(ret)); + } else if (tmp_str) { LoggerD("URI: %s", tmp_str); - app_control_obj->insert(std::make_pair("uri", picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kURIAppControlField, picojson::value(std::string(tmp_str)))); + } else { + LoggerD("URI field is empty"); } clear(tmp_str); ret = app_control_get_mime(app_control, &tmp_str); - if ((APP_CONTROL_ERROR_NONE == ret) && (nullptr != tmp_str)) { + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Get MIME failed: %d (%s)", ret, get_error_message(ret)); + } else if (tmp_str) { LoggerD("MIME: %s", tmp_str); - app_control_obj->insert(std::make_pair("mime", picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kMIMEAppControlField, picojson::value(std::string(tmp_str)))); } else { - LoggerE("Get mime failed: %d (%s)", ret, get_error_message(ret)); + LoggerD("MIME field is empty"); } clear(tmp_str); ret = app_control_get_category(app_control, &tmp_str); - if ((APP_CONTROL_ERROR_NONE == ret) && (nullptr != tmp_str)) { + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Get category failed: %d (%s)", ret, get_error_message(ret)); + } else if (tmp_str) { LoggerD("category: %s", tmp_str); - app_control_obj->insert(std::make_pair("category", picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kCategoryAppControlField, picojson::value(std::string(tmp_str)))); } else { - LoggerE("Get category failed: %d (%s)", ret, get_error_message(ret)); + LoggerD("category field is empty"); } clear(tmp_str); - app_control_obj->insert(std::make_pair("data", picojson::value(picojson::array()))); + app_control_launch_mode_e launch_mode = APP_CONTROL_LAUNCH_MODE_SINGLE; + ret = app_control_get_launch_mode(app_control, &launch_mode); + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Get launch mode failed: %d (%s)", ret, get_error_message(ret)); + } else { + std::string launch_mode_str = + launch_mode == APP_CONTROL_LAUNCH_MODE_SINGLE ? kSingleLaunchMode : kGroupLaunchMode; + LoggerD("launch mode: %s", launch_mode_str.c_str()); + app_control_obj->insert( + std::make_pair(kLaunchModeAppControlField, picojson::value(launch_mode_str))); + } + + app_control_obj->insert(std::make_pair(kDataAppControlField, picojson::value(picojson::array()))); ServiceToApplicationControlDataArray( - app_control, &app_control_obj->find("data")->second.get<picojson::array>()); + app_control, &app_control_obj->find(kDataAppControlField)->second.get<picojson::array>()); } void ApplicationUtils::ServiceExtraDataToApplicationControlData( @@ -391,6 +551,36 @@ bool ApplicationUtils::ServiceToApplicationControlDataArray(app_control_h app_co return APP_CONTROL_ERROR_NONE == ret; } +PlatformResult ApplicationUtils::TranslateAppControlError(app_control_error_e return_code) { + ScopeLogger("Return code: %d (%s)", static_cast<int>(return_code), + get_error_message(static_cast<int>(return_code))); + + switch (return_code) { + case APP_CONTROL_ERROR_NONE: + return PlatformResult(ErrorCode::NO_ERROR); + + case APP_CONTROL_ERROR_INVALID_PARAMETER: + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); + + case APP_CONTROL_ERROR_APP_NOT_FOUND: + return PlatformResult(ErrorCode::NOT_FOUND_ERR, "No matched application found."); + + case APP_CONTROL_ERROR_PERMISSION_DENIED: + return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied."); + + case APP_CONTROL_ERROR_OUT_OF_MEMORY: + case APP_CONTROL_ERROR_KEY_NOT_FOUND: + case APP_CONTROL_ERROR_KEY_REJECTED: + case APP_CONTROL_ERROR_INVALID_DATA_TYPE: + case APP_CONTROL_ERROR_LAUNCH_REJECTED: + case APP_CONTROL_ERROR_LAUNCH_FAILED: + case APP_CONTROL_ERROR_TIMED_OUT: + case APP_CONTROL_ERROR_IO_ERROR: + default: + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."); + } +} + bool ApplicationUtils::ServiceExtraDataCallback(app_control_h app_control, const char* key, void* user_data) { ScopeLogger(); diff --git a/src/application/application_utils.h b/src/application/application_utils.h index 455f1977..308827be 100644 --- a/src/application/application_utils.h +++ b/src/application/application_utils.h @@ -59,6 +59,8 @@ class ApplicationUtils { static bool ServiceToApplicationControlDataArray(app_control_h app_control, picojson::array* data); + static common::PlatformResult TranslateAppControlError(app_control_error_e error_code); + private: static bool ServiceExtraDataCallback(app_control_h app_control, const char* key, void* user_data); }; diff --git a/src/archive/archive_api.js b/src/archive/archive_api.js index 750eceff..61c2b993 100755 --- a/src/archive/archive_api.js +++ b/src/archive/archive_api.js @@ -22,23 +22,46 @@ var privUtils_ = xwalk.utils; function CommonFS() {} CommonFS.cacheVirtualToReal = {}; +CommonFS.isCacheReady = false; +CommonFS.listenerRegistered = false; + +function clearCache() { + CommonFS.cacheVirtualToReal = {}; + CommonFS.isCacheReady = false; +} function _initializeCache() { - try { - var result = native_.callSync('Archive_fetchVirtualRoots', {}); + if (CommonFS.isCacheReady) { + return; + } + var result = native_.callSync('Archive_fetchStorages', {}); - if (native_.isFailure(result)) { - throw native_.getErrorObject(result); - } + if (native_.isFailure(result)) { + privUtils_.log( + 'Exception while getting widget paths was thrown: ' + + native_.getErrorObject(result).message + ); + return; + } - result = native_.getResultObject(result); - for (var i = 0; i < result.length; ++i) { - CommonFS.cacheVirtualToReal[result[i].name] = { - path: result[i].path - }; + result = native_.getResultObject(result); + for (var i = 0; i < result.length; ++i) { + CommonFS.cacheVirtualToReal[result[i].name] = { + path: result[i].path + }; + } + CommonFS.isCacheReady = true; + if (!CommonFS.listenerRegistered) { + try { + tizen.filesystem.addStorageStateChangeListener(clearCache); + CommonFS.listenerRegistered = true; + } catch (e) { + privUtils_.log( + 'Failed to register storage change listener, ' + + 'storage information may be corrupted: ' + + e.message + ); } - } catch (e) { - privUtils_.log('Exception while getting widget paths was thrown: ' + e); } } @@ -52,6 +75,7 @@ CommonFS.toRealPath = function(aPath) { _fileRealPath = aPath.substr(_uriPrefix.length); } else if (aPath[0] != '/') { // virtual path$ + _initializeCache(); var _pathTokens = aPath.split('/'); if ( this.cacheVirtualToReal[_pathTokens[0]] && @@ -77,7 +101,7 @@ CommonFS.toRealPath = function(aPath) { CommonFS.isVirtualPath = function(aPath) { var root = aPath.split('/')[0]; - + _initializeCache(); return this.cacheVirtualToReal[root] != undefined; }; @@ -178,8 +202,8 @@ var ArchiveFileProgressCallback = function(msg) { native_.addListener(ARCHIVE_ONPROGRESS_CALLBACK, ArchiveFileProgressCallback); /** - * The ArchiveFileEntry interface provides access to ArchiveFile member information and - * file data. + * The ArchiveFileEntry interface provides access to ArchiveFile member information + * and file data. * This constructor is for internal use only. * It should be prohibited to call this constructor by user. */ @@ -216,10 +240,7 @@ function ArchiveFileEntry(data, priv) { */ this.extract = function() { var args = validator_.validateArgs(arguments, [ - { - name: 'destinationDirectory', - type: types_.FILE_REFERENCE - }, + { name: 'destinationDirectory', type: types_.FILE_REFERENCE }, { name: 'onsuccess', type: types_.FUNCTION, @@ -330,10 +351,7 @@ function ArchiveFile(data) { */ this.add = function() { var args = validator_.validateArgs(arguments, [ - { - name: 'sourceFile', - type: types_.FILE_REFERENCE - }, + { name: 'sourceFile', type: types_.FILE_REFERENCE }, { name: 'onsuccess', type: types_.FUNCTION, @@ -416,10 +434,7 @@ function ArchiveFile(data) { */ this.extractAll = function() { var args = validator_.validateArgs(arguments, [ - { - name: 'destinationDirectory', - type: types_.FILE_REFERENCE - }, + { name: 'destinationDirectory', type: types_.FILE_REFERENCE }, { name: 'onsuccess', type: types_.FUNCTION, @@ -523,8 +538,8 @@ function ArchiveFile(data) { }; /** - * Retrieves information about ArchiveFileEntry with the specified name - * in ArchiveFile. + * Retrieves information about ArchiveFileEntry with the specified name in + * ArchiveFile. */ this.getEntryByName = function() { var args = validator_.validateArgs(arguments, [ @@ -579,8 +594,8 @@ function ArchiveFile(data) { var ArchiveManager = function() {}; /** - * Opens the archive file. - * After this operation, it is possible to add or get files to and from the archive. + * Opens the archive file. After this operation, it is possible to add or get files + * to and from the archive. */ ArchiveManager.prototype.open = function() { var args = validator_.validateArgs(arguments, [ diff --git a/src/archive/archive_file.cc b/src/archive/archive_file.cc index 0f911990..94ec9dbf 100644 --- a/src/archive/archive_file.cc +++ b/src/archive/archive_file.cc @@ -76,7 +76,7 @@ ArchiveFile::~ArchiveFile() { ScopeLogger(); if (m_entry_map) { - LoggerD("Unlinking old m_entry_map: %d ArchiveFileEntries", m_entry_map->size()); + LoggerD("Unlinking old m_entry_map: %zu ArchiveFileEntries", m_entry_map->size()); for (auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) { if (it->second) { it->second->setArchiveFileNonProtectPtr(NULL); @@ -537,7 +537,7 @@ void ArchiveFile::setEntryMap(ArchiveFileEntryPtrMapPtr entries) { ScopeLogger(); if (m_entry_map) { - LoggerD("Unlinking old m_entry_map: %d ArchiveFileEntries", m_entry_map->size()); + LoggerD("Unlinking old m_entry_map: %zu ArchiveFileEntries", m_entry_map->size()); for (auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) { if (it->second) { it->second->setArchiveFileNonProtectPtr(NULL); @@ -547,7 +547,7 @@ void ArchiveFile::setEntryMap(ArchiveFileEntryPtrMapPtr entries) { m_entry_map = entries; - LoggerD("Linking new m_entry_map ArchiveFileEntries (%d) with ArchiveFile object", + LoggerD("Linking new m_entry_map ArchiveFileEntries (%zu) with ArchiveFile object", m_entry_map->size()); for (auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) { if (it->second) { diff --git a/src/archive/archive_instance.cc b/src/archive/archive_instance.cc index ae2745cb..cf7f9c8c 100644 --- a/src/archive/archive_instance.cc +++ b/src/archive/archive_instance.cc @@ -72,7 +72,7 @@ ArchiveInstance::ArchiveInstance() { REGISTER_ASYNC("ArchiveFileEntry_extract", Extract); - REGISTER_SYNC("Archive_fetchVirtualRoots", FetchVirtualRoots); + REGISTER_SYNC("Archive_fetchStorages", FetchStorages); #undef REGISTER_ASYNC #undef REGISTER_SYNC @@ -101,9 +101,10 @@ void ArchiveInstance::Open(const picojson::value& args, picojson::object& out) { ScopeLogger("%s", args.serialize().c_str()); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); - picojson::object data = args.get<picojson::object>(); picojson::value v_file = data.at(PARAM_FILE); + CHECK_STORAGE_ACCESS(v_file.get<std::string>(), &out); + picojson::value v_mode = data.at(PARAM_MODE); picojson::value v_op_id = data.at(PARAM_OPERATION_ID); picojson::object options = data.at(PARAM_OPTIONS).get<picojson::object>(); @@ -146,7 +147,7 @@ void ArchiveInstance::Open(const picojson::value& args, picojson::object& out) { } file_ptr = FilePtr(new File(node, File::PermissionList())); - LoggerD("open: %s mode: 0x%x overwrite: %d", location_full_path.c_str(), fm, overwrite); + LoggerD("open: %s mode: 0x%d overwrite: %d", location_full_path.c_str(), fm, overwrite); if (FileMode::WRITE == fm || FileMode::READ_WRITE == fm) { if (overwrite) { LoggerD("Deleting existing file: %s", location_full_path.c_str()); @@ -260,9 +261,10 @@ void ArchiveInstance::Add(const picojson::value& args, picojson::object& out) { ScopeLogger("%s", args.serialize().c_str()); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); - picojson::object data = args.get<picojson::object>(); picojson::value v_source = data.at(PARAM_SOURCE_FILE); + CHECK_STORAGE_ACCESS(v_source.get<std::string>(), &out); + picojson::value v_options = data.at(PARAM_OPTIONS); picojson::value v_op_id = data.at(PARAM_OPERATION_ID); picojson::value v_handle = data.at(ARCHIVE_FILE_HANDLE); @@ -347,9 +349,10 @@ void ArchiveInstance::ExtractAll(const picojson::value& args, picojson::object& ScopeLogger("%s", args.serialize().c_str()); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); - picojson::object data = args.get<picojson::object>(); picojson::value v_dest_dir = data.at(PARAM_DESTINATION_DIR); + CHECK_STORAGE_ACCESS(v_dest_dir.get<std::string>(), &out); + picojson::value v_overwrite = data.at(PARAM_OVERWRITE); picojson::value v_op_id = data.at(PARAM_OPERATION_ID); picojson::value v_handle = data.at(ARCHIVE_FILE_HANDLE); @@ -520,9 +523,10 @@ void ArchiveInstance::Extract(const picojson::value& args, picojson::object& out ScopeLogger("%s", args.serialize().c_str()); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); - picojson::object data = args.get<picojson::object>(); picojson::value v_dest_dir = data.at(PARAM_DESTINATION_DIR); + CHECK_STORAGE_ACCESS(v_dest_dir.get<std::string>(), &out); + picojson::value v_strip_name = data.at(PARAM_STRIP_NAME); picojson::value v_overwrite = data.at(PARAM_OVERWRITE); picojson::value v_op_id = data.at(PARAM_OPERATION_ID); @@ -596,14 +600,14 @@ void ArchiveInstance::Extract(const picojson::value& args, picojson::object& out } } -void ArchiveInstance::FetchVirtualRoots(const picojson::value& args, picojson::object& out) { +void ArchiveInstance::FetchStorages(const picojson::value& args, picojson::object& out) { ScopeLogger(); - picojson::array roots; - for (const auto& root : common::FilesystemProvider::Create().GetVirtualPaths()) { - roots.push_back(root.ToJson()); + picojson::array storages; + for (const auto& storage : common::FilesystemProvider::Create().GetAllStorages()) { + storages.push_back(storage->ToJson()); } - ReportSuccess(picojson::value(roots), out); + ReportSuccess(picojson::value(storages), out); } } // namespace archive diff --git a/src/archive/archive_instance.h b/src/archive/archive_instance.h index 8b55ebf2..c9e418b3 100644 --- a/src/archive/archive_instance.h +++ b/src/archive/archive_instance.h @@ -48,7 +48,7 @@ class ArchiveInstance : public common::ParsedInstance { void Extract(const picojson::value& args, picojson::object& out); /* Filesystem related method */ - void FetchVirtualRoots(const picojson::value& args, picojson::object& out); + void FetchStorages(const picojson::value& args, picojson::object& out); void PostError(const common::PlatformException& e, double callback_id); void PostError(const common::PlatformResult& e, double callback_id); diff --git a/src/archive/filesystem_node.cc b/src/archive/filesystem_node.cc index 13d23f7e..1d8048c2 100644 --- a/src/archive/filesystem_node.cc +++ b/src/archive/filesystem_node.cc @@ -115,9 +115,10 @@ PlatformResult Node::resolve(const PathPtr& path, NodePtr* node) { struct stat syminfo; if (lstat(path->getFullPath().c_str(), &info) != 0) { - LoggerE("File:[%s] error no:%d", path->getFullPath().c_str(), errno); + int tmp_errno = errno; + LoggerE("File: [%s] error no: %d", path->getFullPath().c_str(), tmp_errno); - switch (errno) { + switch (tmp_errno) { case EACCES: SLoggerE("File: [%s]", path->getFullPath().c_str()); return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Node access denied"); @@ -147,7 +148,7 @@ PlatformResult Node::resolve(const PathPtr& path, NodePtr* node) { } type = S_ISDIR(syminfo.st_mode) ? NT_DIRECTORY : NT_FILE; - LoggerD("%x", type); + LoggerD("%d", type); } *node = std::shared_ptr<Node>(new Node(path, type)); diff --git a/src/archive/un_zip.cc b/src/archive/un_zip.cc index 9689622f..b381bdd4 100644 --- a/src/archive/un_zip.cc +++ b/src/archive/un_zip.cc @@ -51,7 +51,7 @@ UnZip::UnZip(const std::string& filename) UnZip::~UnZip() { ScopeLogger(); for (auto& x : path_access_map) { - LoggerD("Setting permission for path: %s [%d] ", x.first.c_str(), x.second); + LoggerD("Setting permission for path: %s [%u] ", x.first.c_str(), x.second); if (chmod(x.first.c_str(), x.second) == -1) { LoggerE("Couldn't set permissions for: [%s] errno: %s", x.first.c_str(), GetErrorString(errno).c_str()); diff --git a/src/archive/un_zip_extract_request.cc b/src/archive/un_zip_extract_request.cc index 3cb09566..1fdd4907 100644 --- a/src/archive/un_zip_extract_request.cc +++ b/src/archive/un_zip_extract_request.cc @@ -297,7 +297,7 @@ PlatformResult UnZipExtractRequest::handleDirectoryEntry() { } } - LoggerD("Set dir: [%s] access and modify to: %4d-%2d-%2d %2d:%2d:%2d", m_new_dir_path.c_str(), + LoggerD("Set dir: [%s] access and modify to: %4u-%2u-%2u %2u:%2u:%2u", m_new_dir_path.c_str(), m_file_info.tmu_date.tm_year, m_file_info.tmu_date.tm_mon, m_file_info.tmu_date.tm_mday, m_file_info.tmu_date.tm_hour, m_file_info.tmu_date.tm_min, m_file_info.tmu_date.tm_sec); diff --git a/src/archive/zip_add_request.cc b/src/archive/zip_add_request.cc index bbf823ca..78840c6d 100644 --- a/src/archive/zip_add_request.cc +++ b/src/archive/zip_add_request.cc @@ -370,7 +370,7 @@ PlatformResult ZipAddRequest::addToZipArchive(filesystem::NodePtr src_file_node) return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Reading input file failed", ("fseek failed with error! [%d]", res)); } - LoggerD("Source file: [%s] size: %d - %s", src_file_path.c_str(), in_file_size, + LoggerD("Source file: [%s] size: %zu - %s", src_file_path.c_str(), in_file_size, bytesToReadableString(in_file_size).c_str()); cur_afentry->setSize(in_file_size); @@ -392,7 +392,7 @@ PlatformResult ZipAddRequest::addToZipArchive(filesystem::NodePtr src_file_node) return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "New file addition failed"); } - LoggerD("Read: %d bytes from input file:[%s]", size_read, src_file_path.c_str()); + LoggerD("Read: %zu bytes from input file:[%s]", size_read, src_file_path.c_str()); total_bytes_read += size_read; m_bytes_compressed += size_read; @@ -411,7 +411,7 @@ PlatformResult ZipAddRequest::addToZipArchive(filesystem::NodePtr src_file_node) LoggerD( "Callculatting overall progress: %llu/%llu bytes; " - "%lu/%lu files; current file: [%s] progress: %d/%d bytes; ", + "%lu/%lu files; current file: [%s] progress: %zu/%zu bytes; ", m_bytes_compressed, m_bytes_to_compress, m_files_compressed, m_files_to_compress, src_file_path.c_str(), total_bytes_read, in_file_size); @@ -432,7 +432,7 @@ PlatformResult ZipAddRequest::addToZipArchive(filesystem::NodePtr src_file_node) if (in_file_size != total_bytes_read) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not add file to archive", - ("in_file_size(%d) != total_bytes_read(%d)", in_file_size, total_bytes_read)); + ("in_file_size(%zu) != total_bytes_read(%zu)", in_file_size, total_bytes_read)); } fclose(m_input_file); diff --git a/src/badge/badge_manager.cc b/src/badge/badge_manager.cc index fab178e6..b41f9cc2 100644 --- a/src/badge/badge_manager.cc +++ b/src/badge/badge_manager.cc @@ -88,7 +88,7 @@ PlatformResult BadgeManager::SetBadgeCount(const std::string &app_id, unsigned i } ret = badge_set_count(app_id_str, count); - LoggerD("badge_set_count() ret : %d, %s, count : %d ", ret, get_error_message(ret), count); + LoggerD("badge_set_count() ret : %d, %s, count : %u ", ret, get_error_message(ret), count); if (ret == BADGE_ERROR_PERMISSION_DENIED) { return LogAndCreateResult(ErrorCode::SECURITY_ERR, "Security error"); @@ -136,7 +136,7 @@ PlatformResult BadgeManager::GetBadgeCount(const std::string &app_id, unsigned i ret = badge_get_count(app_id.c_str(), count); - LoggerD("badge_get_count() ret : %d count : %d", ret, *count); + LoggerD("badge_get_count() ret : %d count : %u", ret, *count); switch (ret) { case BADGE_ERROR_NONE: diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js index 7a6540a8..c229e6cc 100755 --- a/src/bluetooth/bluetooth_api.js +++ b/src/bluetooth/bluetooth_api.js @@ -150,7 +150,7 @@ var BluetoothClassDeviceService = function() { }); }; -//class tizen.BluetoothLEServiceData ////////////////////// +//class tizen.BluetoothLEServiceData ////////////////////////// tizen.BluetoothLEServiceData = function(d) { AV.isConstructorCall(this, tizen.BluetoothLEServiceData); var uuid_ = ''; @@ -191,7 +191,7 @@ tizen.BluetoothLEServiceData = function(d) { } }; -//class BluetoothLEAdvertiseData ////////////////////// +//class BluetoothLEAdvertiseData ////////////////////////// tizen.BluetoothLEAdvertiseData = function(dict) { AV.isConstructorCall(this, tizen.BluetoothLEAdvertiseData); var includeName_ = false; @@ -370,7 +370,7 @@ tizen.BluetoothLEAdvertiseData = function(dict) { } }; -//class tizen.BluetoothLEManufacturerData ////////////////////// +//class tizen.BluetoothLEManufacturerData ////////////////////////// tizen.BluetoothLEManufacturerData = function(d) { AV.isConstructorCall(this, tizen.BluetoothLEManufacturerData); var id_ = ''; @@ -411,7 +411,7 @@ tizen.BluetoothLEManufacturerData = function(d) { } }; -// class BluetoothClass ////////////////////// +// class BluetoothClass ////////////////////////// var BluetoothClass = function(data) { var services = []; if (data) { @@ -459,7 +459,7 @@ BluetoothClass.prototype.hasService = function() { return BluetoothClass_hasService.apply(this, arguments); }; -// class BluetoothSocket ////////////////////// +// class BluetoothSocket ////////////////////////// var _BLUETOOTH_SOCKET_STATE_CLOSED = 'CLOSED'; function BluetoothSocketListeners() { @@ -590,7 +590,7 @@ BluetoothSocket.prototype.close = function() { } }; -//class BluetoothLEDevice ////////////////////// +//class BluetoothLEDevice ////////////////////////// var BluetoothLEDevice = function(data) { var address = '', name = null, @@ -803,7 +803,7 @@ BluetoothLEDevice.prototype.removeConnectStateChangeListener = function() { _bleConnectChangeListener.removeListener(args.watchID); }; -// class BluetoothDevice ////////////////////// +// class BluetoothDevice ////////////////////////// var BluetoothDevice = function(data) { var self = this; function _getter(field) { @@ -913,7 +913,7 @@ BluetoothDevice.prototype.connectToServiceByUUID = function() { } }; -// class BluetoothServiceHandler ////////////////////// +// class BluetoothServiceHandler ////////////////////////// function BluetoothServiceListeners() { var that = this; this.serviceCallback = function(data) { @@ -1016,7 +1016,7 @@ BluetoothServiceHandler.prototype.unregister = function() { _bluetoothServiceListeners.removeListener(this.uuid); }; -// class BluetoothHealthApplication ////////////////////// +// class BluetoothHealthApplication ////////////////////////// function BluetoothHealthApplicationListeners() { var that = this; this.appCallback = function(data) { @@ -1110,7 +1110,7 @@ BluetoothHealthApplication.prototype.unregister = function() { _bluetoothHealthApplicationListeners.removeListener(this._id); }; -// class BluetoothProfileHandler ////////////////////// +// class BluetoothProfileHandler ////////////////////////// var _BluetoothProfileType = { HEALTH: 'HEALTH' }; @@ -1123,7 +1123,7 @@ var BluetoothProfileHandler = function(data) { } }; -// class BluetoothHealthProfileHandler ////////////////////// +// class BluetoothHealthProfileHandler ////////////////////////// var BluetoothHealthProfileHandler = function(data) { BluetoothProfileHandler.call(this, data); }; @@ -1233,7 +1233,7 @@ BluetoothHealthProfileHandler.prototype.connectToSource = function() { } }; -// class BluetoothHealthChannel ////////////////////// +// class BluetoothHealthChannel ////////////////////////// var BluetoothHealthChannel = function(data) { Object.defineProperties(this, { peer: { value: data.peer, writable: false, enumerable: true }, @@ -1379,8 +1379,8 @@ BluetoothHealthChannel.prototype.unsetListener = function() { * @param {string} name - name of the listener this manager handles * @param {function} callback - function to be invoked when event specified by the name * fires. - * This function should return false if the callback doesn't - * want to handle the event anymore, true otherwise. + * This function should return false if the callback + * doesn't want to handle the event anymore, true otherwise. * This function should have following signature: * bool callback(event, successCallback, errorCallback); * @@ -1484,7 +1484,7 @@ var _bleAdvertiseListener = _singleListenerBuilder( } ); -//class BluetoothLEAdapter ////////////////////// +//class BluetoothLEAdapter ////////////////////////// var BluetoothLEAdapter = function() {}; BluetoothLEAdapter.prototype.startScan = function() { @@ -1598,7 +1598,7 @@ BluetoothLEAdapter.prototype.stopAdvertise = function() { } }; -//class BluetoothGATTService ////////////////////// +//class BluetoothGATTService ////////////////////////// var BluetoothGATTService = function(data, address) { var handle_ = data.handle; var uuid_ = data.uuid; @@ -1653,7 +1653,7 @@ var toByteArray = function(array) { return d; }; -//class BluetoothGATTCharacteristic ////////////////////// +//class BluetoothGATTCharacteristic ////////////////////////// var BluetoothGATTCharacteristic = function(data, address) { var handle_ = data.handle; var descriptors_ = []; @@ -1988,7 +1988,7 @@ var _bleConnectChangeListener = _multipleListenerBuilder( 'BluetoothLEDevice_removeConnectStateChangeListener' ); -//class BluetoothGATTDescriptor ////////////////////// +//class BluetoothGATTDescriptor ////////////////////////// var BluetoothGATTDescriptor = function(data, address) { var handle_ = data.handle; //address_ is needed to control if device is still connected @@ -2071,7 +2071,7 @@ var BluetoothGATTDescriptor = function(data, address) { }; }; -// class BluetoothAdapter ////////////////////// +// class BluetoothAdapter ////////////////////////// var BluetoothAdapter = function() { function nameGetter() { var result = native.callSync('BluetoothAdapter_getName', {}); @@ -2179,9 +2179,9 @@ BluetoothAdapter.prototype.setName = function() { BluetoothAdapter.prototype.setPowered = function() { privUtils_.log('Entered BluetoothAdapter.setPowered()'); privUtils_.warn( - 'DEPRECATION WARNING: setPowered() is deprecated ' + - 'and will be removed from next release. ' + - 'Let the user turn on/off Bluetooth through the Settings application instead.' + 'DEPRECATION WARNING: setPowered() is deprecated and will be removed from ' + + 'next release. Let the user turn on/off Bluetooth through the Settings ' + + 'application instead.' ); var args = AV.validateMethod(arguments, [ @@ -2225,10 +2225,9 @@ BluetoothAdapter.prototype.setPowered = function() { BluetoothAdapter.prototype.setVisible = function() { privUtils_.log('Entered BluetoothAdapter.setVisible()'); privUtils_.warn( - 'DEPRECATION WARNING: setVisible() is deprecated ' + - 'and will be removed from next release. ' + - 'Let the user change the Bluetooth visibility through ' + - 'the Settings application instead.' + 'DEPRECATION WARNING: setVisible() is deprecated and will be removed from ' + + 'next release. Let the user change the Bluetooth visibility through the ' + + 'Settings application instead.' ); var args = AV.validateMethod(arguments, [ @@ -2698,7 +2697,7 @@ BluetoothAdapter.prototype.getBluetoothProfileHandler = function() { } }; -// class BluetoothManager ////////////////////// +// class BluetoothManager ////////////////////////// var BluetoothManager = function() { Object.defineProperties(this, { deviceMajor: { @@ -2748,5 +2747,5 @@ BluetoothManager.prototype.getLEAdapter = function() { privUtils_.log('Entered BluetoothManager.getLEAdapter()'); return BluetoothManager_getLEAdapter(); }; -// exports ///////////////////////////////////// +// exports ///////////////////////////////////////// exports = new BluetoothManager(); diff --git a/src/calendar/calendar_item.cc b/src/calendar/calendar_item.cc index 99109bc3..b3bc59d5 100644 --- a/src/calendar/calendar_item.cc +++ b/src/calendar/calendar_item.cc @@ -123,14 +123,14 @@ const PlatformEnumMap CalendarItem::platform_enum_map_ = { {{kDefaultEnumKey, CALENDAR_EVENT_BUSY_STATUS_BUSY}, {"FREE", CALENDAR_EVENT_BUSY_STATUS_FREE}, {"BUSY", CALENDAR_EVENT_BUSY_STATUS_BUSY}, - {"BUSY-UNAVAILABLE", CALENDAR_EVENT_BUSY_STATUS_UNAVAILABLE}, - {"BUSY-TENTATIVE", CALENDAR_EVENT_BUSY_STATUS_TENTATIVE}}}, + {"BUSY_UNAVAILABLE", CALENDAR_EVENT_BUSY_STATUS_UNAVAILABLE}, + {"BUSY_TENTATIVE", CALENDAR_EVENT_BUSY_STATUS_TENTATIVE}}}, {kEventAvailability, {{kDefaultEnumKey, CALENDAR_EVENT_BUSY_STATUS_BUSY}, {"FREE", CALENDAR_EVENT_BUSY_STATUS_FREE}, {"BUSY", CALENDAR_EVENT_BUSY_STATUS_BUSY}, - {"BUSY-UNAVAILABLE", CALENDAR_EVENT_BUSY_STATUS_UNAVAILABLE}, - {"BUSY-TENTATIVE", CALENDAR_EVENT_BUSY_STATUS_TENTATIVE}}}, + {"BUSY_UNAVAILABLE", CALENDAR_EVENT_BUSY_STATUS_UNAVAILABLE}, + {"BUSY_TENTATIVE", CALENDAR_EVENT_BUSY_STATUS_TENTATIVE}}}, {kEventPriority, {{kDefaultEnumKey, CALENDAR_EVENT_PRIORITY_NONE}, {"NONE", CALENDAR_EVENT_PRIORITY_NONE}, @@ -829,7 +829,7 @@ PlatformResult CalendarItem::AttendeesToJson(int type, calendar_record_h rec, calendar_record_h attendee; for (unsigned int i = 0; i < count; ++i) { - LoggerD("Processing the attendee %d", i); + LoggerD("Processing the attendee %u", i); if (GetChildRecordAt(rec, property, &attendee, i).IsError()) { LoggerW("Can't get attendee record"); @@ -1049,7 +1049,7 @@ PlatformResult CalendarItem::AlarmsToJson(int type, calendar_record_h rec, picoj int tick, tick_unit; calendar_record_h alarm; for (unsigned int i = 0; i < count; ++i) { - LoggerD("Processing the alarm %d", i); + LoggerD("Processing the alarm %u", i); if (GetChildRecordAt(rec, property, &alarm, i).IsError()) { LoggerW("Can't get alarm record"); diff --git a/src/calendar/js/calendar.js b/src/calendar/js/calendar.js index 6269df69..0aede5ee 100755 --- a/src/calendar/js/calendar.js +++ b/src/calendar/js/calendar.js @@ -720,8 +720,8 @@ var Calendar_removeChangeListener = function() { if (type_.isEmptyObject(_listeners)) { var result; - // @todo consider listener unregister when we are not listening - // on this.type of calendar + // @todo consider listener unregister when we are not listening on + // this.type of calendar var fail = false; for (var listenerId in _nativeListeners) { if (_nativeListeners.hasOwnProperty(listenerId)) { diff --git a/src/calendar/js/calendar_item.js b/src/calendar/js/calendar_item.js index 38d1142d..8f2ad6ea 100644 --- a/src/calendar/js/calendar_item.js +++ b/src/calendar/js/calendar_item.js @@ -46,7 +46,9 @@ var CalendarItemStatus = { var EventAvailability = { BUSY: 'BUSY', //default for CalendarEvent - FREE: 'FREE' + FREE: 'FREE', + BUSY_UNAVAILABLE: 'BUSY_UNAVAILABLE', + BUSY_TENTATIVE: 'BUSY_TENTATIVE' }; var CalendarEventId = function(uid, rid) { @@ -236,16 +238,15 @@ var CalendarItem = function(data) { : this.dueDate; } _duration = v instanceof tizen.TimeDuration ? v : null; - /* - @todo Fix UTC, UTC expect duration value but according to documentation: - ... the implementation may not save the duration itself, - rather convert it to the corresponding endDate/dueDate attribute and - save it. For example, if you set the startDate and the duration attributes - and save the item, you may see that the duration is null while - endDate/dueDate is non-null after retrieving it because the implementation - has calculated the endDate/dueDate based on the duration and the startDate - then saved it, not the duration. - */ + //@todo Fix UTC, UTC expect duration value but according to + // documentation: + // ... the implementation may not save the duration itself, + // rather convert it to the corresponding endDate/dueDate attribute and + // save it. For example, if you set the startDate and the duration + // attributes and save the item, you may see that the duration is null + // while endDate/dueDate is non-null after retrieving it because the + // implementation has calculated the endDate/dueDate based on the + // duration and the startDate then saved it, not the duration. }, enumerable: true }, diff --git a/src/callhistory/callhistory.cc b/src/callhistory/callhistory.cc index 1108c80d..9b610577 100644 --- a/src/callhistory/callhistory.cc +++ b/src/callhistory/callhistory.cc @@ -291,7 +291,7 @@ void CallHistory::LoadPhoneNumbers(const picojson::object& args, CallHistory* ca LoggerD("wait..."); fut.wait(); n = fut.get(); - LoggerD("Phone number [%d] : %s", modem_num, n.c_str()); + LoggerD("Phone number [%u] : %s", modem_num, n.c_str()); } while (false); phone_numbers.push_back(n); @@ -305,11 +305,11 @@ void CallHistory::LoadPhoneNumbers(const picojson::object& args, CallHistory* ca void CallHistory::find(const picojson::object& args) { ScopeLogger(); - std::thread([args, this]() { - ScopeLogger("Entered into asynchronus function, std::thread's argument"); + common::TaskQueue::GetInstance().Async([args, this]() { + ScopeLogger("Entered into asynchronus function"); LoadPhoneNumbers(args, this); FindThread(args, this); - }).detach(); + }); } PlatformResult CallHistory::remove(const picojson::object& args) { diff --git a/src/common/GDBus/proxy.cpp b/src/common/GDBus/proxy.cpp index 86d4d6fa..4b330fd1 100644 --- a/src/common/GDBus/proxy.cpp +++ b/src/common/GDBus/proxy.cpp @@ -90,7 +90,7 @@ void Proxy::signalSubscribe() { m_signal_name.c_str(), m_signal_path.c_str(), NULL, G_DBUS_SIGNAL_FLAGS_NONE, signalCallbackProxy, static_cast<gpointer>(this), NULL); - LoggerD("g_dbus_connection_signal_subscribe returned id: %d", m_sub_id); + LoggerD("g_dbus_connection_signal_subscribe returned id: %u", m_sub_id); m_dbus_signal_subscribed = true; } diff --git a/src/common/common.gyp b/src/common/common.gyp index ff428eed..c3b685cd 100644 --- a/src/common/common.gyp +++ b/src/common/common.gyp @@ -34,9 +34,12 @@ 'task-queue.h', 'tools.cc', 'tools.h', + 'worker.cc', + 'worker.h', 'optional.h', 'platform_result.cc', 'platform_result.h', + 'platform_enum.h', 'assert.h', 'GDBus/connection.cpp', 'GDBus/connection.h', @@ -117,6 +120,7 @@ ], }, }, + 'defines': ['PICOJSON_USE_RVALUE_REFERENCE'], }, ], } diff --git a/src/common/common.gypi b/src/common/common.gypi index cf4fed83..4609a66f 100644 --- a/src/common/common.gypi +++ b/src/common/common.gypi @@ -82,6 +82,7 @@ '-fvisibility=hidden', '-Wall', '-Werror', + '-Wformat-signedness', ], 'cflags_c': [ '-std=c11', diff --git a/src/common/extension.cc b/src/common/extension.cc index 53477601..9fc1547b 100644 --- a/src/common/extension.cc +++ b/src/common/extension.cc @@ -218,26 +218,36 @@ int32_t Extension::XW_Initialize(XW_Extension extension, XW_GetInterface get_int return XW_OK; } +std::mutex Instance::instances_mutex_; std::unordered_set<Instance*> Instance::all_instances_; Instance::Instance() : xw_instance_(0) { ScopeLogger(); - { all_instances_.insert(this); } + { + std::lock_guard<std::mutex> lock{instances_mutex_}; + all_instances_.insert(this); + } } Instance::~Instance() { ScopeLogger(); - { all_instances_.erase(this); } + { + std::lock_guard<std::mutex> lock{instances_mutex_}; + all_instances_.erase(this); + } Assert(xw_instance_ == 0); } void Instance::PostMessage(Instance* that, const char* msg) { ScopeLogger(); - if (that && all_instances_.end() != all_instances_.find(that)) { - that->PostMessage(msg); - } else { - LoggerE("Trying to post message to non-existing instance: [%p], ignoring", that); + if (nullptr != that) { + std::lock_guard<std::mutex> lock{instances_mutex_}; + if (all_instances_.end() != all_instances_.find(that)) { + that->PostMessage(msg); + return; + } } + LoggerE("Trying to post message to non-existing instance: [%p], ignoring", that); } void Instance::PostMessage(const char* msg) { @@ -335,6 +345,7 @@ void ParsedInstance::HandleMessage(const char* msg, bool is_sync) { auto it = handler_map_.find(cmd); if (handler_map_.end() == it) { + LoggerE("Unknown command: %s", cmd.c_str()); throw UnknownException("Unknown command."); } diff --git a/src/common/extension.h b/src/common/extension.h index 907d1df3..9bb16f40 100644 --- a/src/common/extension.h +++ b/src/common/extension.h @@ -21,6 +21,7 @@ #include <functional> #include <map> +#include <mutex> #include <string> #include <unordered_set> @@ -109,6 +110,7 @@ class Instance { private: friend class Extension; + static std::mutex instances_mutex_; static std::unordered_set<Instance*> all_instances_; XW_Instance xw_instance_; diff --git a/src/common/logger.h b/src/common/logger.h index 3410dd85..228c871b 100644 --- a/src/common/logger.h +++ b/src/common/logger.h @@ -7,6 +7,32 @@ #include <dlog.h> +#ifdef TIZEN_DEBUG_ENABLE +// Using static inline function with no operation inside to cause compiler types check. +// Using empty do {} while in WEBAPI_CHECK_PRINTF_FORMAT_ARGS macro will cause that when +// TIZEN_DEBUG_ENABLE flag is turned off, then no types checking will be performed. This could cause +// problems when developing code with this flag off and then enabling it. +static inline int _printf_format_checker(const char* fmt, ...) + __attribute__((format(printf, 1, 2))); + +int _printf_format_checker(const char* fmt, ...) { + return 0; +} + +#define WEBAPI_CHECK_PRINTF_FORMAT_ARGS(...) \ + ({ \ + do { \ + _printf_format_checker(__VA_ARGS__); \ + } while (0); \ + }) + +#else // TIZEN_DEBUG_ENABLE + +#define WEBAPI_NOOP() \ + do { \ + } while (0); +#endif // TIZEN_DEBUG_ENABLE + // Tizen 3.0 uses different debug flag (DLOG_DEBUG_ENABLE) which is always // enabled, following code allows to disable logs with DLOG_DEBUG priority if // TIZEN_DEBUG_ENABLE is not set. @@ -17,6 +43,7 @@ #define LOG_(id, prio, tag, fmt, arg...) \ ({ \ do { \ + WEBAPI_CHECK_PRINTF_FORMAT_ARGS(fmt, ##arg); \ __dlog_print(id, prio, tag, "%s: %s(%d) > " fmt, __MODULE__, __func__, __LINE__, ##arg); \ } while (0); \ }) @@ -36,12 +63,13 @@ #define SECURE_LOG_(id, prio, tag, fmt, arg...) \ ({ \ do { \ + WEBAPI_CHECK_PRINTF_FORMAT_ARGS(fmt, ##arg); \ __dlog_print(id, prio, tag, "%s: %s(%d) > [SECURE_LOG] " fmt, __MODULE__, __func__, \ __LINE__, ##arg); \ } while (0); \ }) #else // TIZEN_DEBUG_ENABLE -#define SECURE_LOG_(id, prio, tag, fmt, arg...) NOP(fmt, ##arg) +#define SECURE_LOG_(id, prio, tag, fmt, arg...) WEBAPI_NOOP() #endif // TIZEN_DEBUG_ENABLE #include <cstring> @@ -247,13 +275,14 @@ class ScopeLogger { #ifdef TIZEN_DEBUG_ENABLE #define ScopeLogger(EX, args...) \ + WEBAPI_CHECK_PRINTF_FORMAT_ARGS("noop" EX, ##args); \ __dlog_print(LOG_ID_MAIN, DLOG_DEBUG, LOGGER_TAG, \ "logger.h: ScopeLogger > %s: %s(%d) > Enter " EX, __MODULE__, __func__, __LINE__, \ ##args); \ const common::ScopeLogger __sl__{__MODULE__, __func__}; #else -#define ScopeLogger(EX, args...) +#define ScopeLogger(EX, args...) WEBAPI_NOOP() #endif #endif // COMMON_LOGGER_H_ diff --git a/src/common/picojson.h b/src/common/picojson.h index 44ae5431..c6b9aded 100644 --- a/src/common/picojson.h +++ b/src/common/picojson.h @@ -1,60 +1,116 @@ /* * Copyright 2009-2010 Cybozu Labs, Inc. - * Copyright 2011 Kazuho Oku + * Copyright 2011-2014 Kazuho Oku + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. + * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY CYBOZU LABS, INC. ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL CYBOZU LABS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are - * those of the authors and should not be interpreted as representing official - * policies, either expressed or implied, of Cybozu Labs, Inc. - * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef picojson_h #define picojson_h #include <algorithm> -#include <cmath> +#include <cstddef> #include <cstdio> #include <cstdlib> #include <cstring> #include <iomanip> #include <iostream> #include <iterator> +#include <limits> #include <map> +#include <memory> #include <sstream> +#include <stdexcept> #include <string> +#include <utility> #include <vector> #include "common/assert.h" +// for isnan/isinf +#if __cplusplus >= 201103L +#include <cmath> +#else +extern "C" { +#ifdef _MSC_VER +#include <float.h> +#elif defined(__INTEL_COMPILER) +#include <mathimf.h> +#else +#include <math.h> +#endif +} +#endif + +#ifndef PICOJSON_USE_RVALUE_REFERENCE +#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) +#define PICOJSON_USE_RVALUE_REFERENCE 1 +#else +#define PICOJSON_USE_RVALUE_REFERENCE 0 +#endif +#endif // PICOJSON_USE_RVALUE_REFERENCE + +#ifndef PICOJSON_NOEXCEPT +#if PICOJSON_USE_RVALUE_REFERENCE +#define PICOJSON_NOEXCEPT noexcept +#else +#define PICOJSON_NOEXCEPT throw() +#endif +#endif + +// experimental support for int64_t (see README.mkdn for detail) +#ifdef PICOJSON_USE_INT64 +#define __STDC_FORMAT_MACROS +#include <errno.h> +#include <inttypes.h> +#endif + #ifdef _MSC_VER #define SNPRINTF _snprintf_s #pragma warning(push) #pragma warning(disable : 4244) // conversion from int to char +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4702) // unreachable code #else #define SNPRINTF snprintf #endif namespace picojson { -enum { null_type, boolean_type, number_type, string_type, array_type, object_type }; +enum { + null_type, + boolean_type, + number_type, + string_type, + array_type, + object_type +#ifdef PICOJSON_USE_INT64 + , + int64_type +#endif +}; + +enum { INDENT_WIDTH = 2 }; struct null {}; @@ -65,9 +121,12 @@ class value { union _storage { bool boolean_; double number_; - std::string* string_; - array* array_; - object* object_; +#ifdef PICOJSON_USE_INT64 + int64_t int64_; +#endif + std::string *string_; + array *array_; + object *object_; }; protected: @@ -78,44 +137,71 @@ class value { value(); value(int type, bool); explicit value(bool b); +#ifdef PICOJSON_USE_INT64 + explicit value(int64_t i); +#endif explicit value(double n); - explicit value(const std::string& s); - explicit value(const array& a); - explicit value(const object& o); - explicit value(const char* s); - value(const char* s, size_t len); + explicit value(const std::string &s); + explicit value(const array &a); + explicit value(const object &o); +#if PICOJSON_USE_RVALUE_REFERENCE + explicit value(std::string &&s); + explicit value(array &&a); + explicit value(object &&o); +#endif + explicit value(const char *s); + value(const char *s, size_t len); ~value(); - value(const value& x); - value& operator=(const value& x); - void swap(value& x); + value(const value &x); + value &operator=(const value &x); +#if PICOJSON_USE_RVALUE_REFERENCE + value(value &&x) PICOJSON_NOEXCEPT; + value &operator=(value &&x) PICOJSON_NOEXCEPT; +#endif + void swap(value &x) PICOJSON_NOEXCEPT; template <typename T> bool is() const; template <typename T> - const T& get() const; + const T &get() const; + template <typename T> + T &get(); template <typename T> - T& get(); + void set(const T &); +#if PICOJSON_USE_RVALUE_REFERENCE + template <typename T> + void set(T &&); +#endif bool evaluate_as_boolean() const; - const value& get(size_t idx) const; - const value& get(const std::string& key) const; - bool contains(size_t idx) const; - bool contains(const std::string& key) const; + const value &get(const size_t idx) const; + const value &get(const std::string &key) const; + value &get(const size_t idx); + value &get(const std::string &key); + + bool contains(const size_t idx) const; + bool contains(const std::string &key) const; std::string to_str() const; template <typename Iter> - void serialize(Iter os) const; - std::string serialize() const; + void serialize(Iter os, bool prettify = false) const; + std::string serialize(bool prettify = false) const; private: template <typename T> - value(const T*); // intentionally defined to block implicit conversion of pointer to bool + value(const T *); // intentionally defined to block implicit conversion of pointer to bool + template <typename Iter> + static void _indent(Iter os, int indent); + template <typename Iter> + void _serialize(Iter os, int indent) const; + std::string _serialize(int indent) const; + void clear(); }; typedef value::array array; typedef value::object object; -inline value::value() : type_(null_type) { +inline value::value() : type_(null_type), u_() { } -inline value::value(int type, bool) : type_(type) { +inline value::value(int type, bool) : type_(type), u_() { switch (type) { #define INIT(p, v) \ case p##type: \ @@ -123,6 +209,9 @@ inline value::value(int type, bool) : type_(type) { break INIT(boolean_, false); INIT(number_, 0.0); +#ifdef PICOJSON_USE_INT64 + INIT(int64_, 0); +#endif INIT(string_, new std::string()); INIT(array_, new array()); INIT(object_, new object()); @@ -132,35 +221,66 @@ inline value::value(int type, bool) : type_(type) { } } -inline value::value(bool b) : type_(boolean_type) { +inline value::value(bool b) : type_(boolean_type), u_() { u_.boolean_ = b; } -inline value::value(double n) : type_(number_type) { +#ifdef PICOJSON_USE_INT64 +inline value::value(int64_t i) : type_(int64_type), u_() { + u_.int64_ = i; +} +#endif + +inline value::value(double n) : type_(number_type), u_() { + if ( +#ifdef _MSC_VER + !_finite(n) +#elif __cplusplus >= 201103L + std::isnan(n) || std::isinf(n) +#else + isnan(n) || isinf(n) +#endif + ) { + throw std::overflow_error(""); + } u_.number_ = n; } -inline value::value(const std::string& s) : type_(string_type) { +inline value::value(const std::string &s) : type_(string_type), u_() { u_.string_ = new std::string(s); } -inline value::value(const array& a) : type_(array_type) { +inline value::value(const array &a) : type_(array_type), u_() { u_.array_ = new array(a); } -inline value::value(const object& o) : type_(object_type) { +inline value::value(const object &o) : type_(object_type), u_() { u_.object_ = new object(o); } -inline value::value(const char* s) : type_(string_type) { +#if PICOJSON_USE_RVALUE_REFERENCE +inline value::value(std::string &&s) : type_(string_type), u_() { + u_.string_ = new std::string(std::move(s)); +} + +inline value::value(array &&a) : type_(array_type), u_() { + u_.array_ = new array(std::move(a)); +} + +inline value::value(object &&o) : type_(object_type), u_() { + u_.object_ = new object(std::move(o)); +} +#endif + +inline value::value(const char *s) : type_(string_type), u_() { u_.string_ = new std::string(s); } -inline value::value(const char* s, size_t len) : type_(string_type) { +inline value::value(const char *s, size_t len) : type_(string_type), u_() { u_.string_ = new std::string(s, len); } -inline value::~value() { +inline void value::clear() { switch (type_) { #define DEINIT(p) \ case p##type: \ @@ -175,7 +295,11 @@ inline value::~value() { } } -inline value::value(const value& x) : type_(x.type_) { +inline value::~value() { + clear(); +} + +inline value::value(const value &x) : type_(x.type_), u_() { switch (type_) { #define INIT(p, v) \ case p##type: \ @@ -191,15 +315,24 @@ inline value::value(const value& x) : type_(x.type_) { } } -inline value& value::operator=(const value& x) { +inline value &value::operator=(const value &x) { if (this != &x) { - this->~value(); - new (this) value(x); + value t(x); + swap(t); } return *this; } -inline void value::swap(value& x) { +#if PICOJSON_USE_RVALUE_REFERENCE +inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() { + swap(x); +} +inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT { + swap(x); + return *this; +} +#endif +inline void value::swap(value &x) PICOJSON_NOEXCEPT { std::swap(type_, x.type_); std::swap(u_, x.u_); } @@ -211,31 +344,78 @@ inline void value::swap(value& x) { } IS(null, null) IS(bool, boolean) -IS(int, number) -IS(double, number) +#ifdef PICOJSON_USE_INT64 +IS(int64_t, int64) +#endif IS(std::string, string) IS(array, array) IS(object, object) #undef IS +template <> +inline bool value::is<double>() const { + return type_ == number_type +#ifdef PICOJSON_USE_INT64 + || type_ == int64_type +#endif + ; +} -#define GET(ctype, var) \ - template <> \ - inline const ctype& value::get<ctype>() const { \ - Assert("type mismatch! call vis<type>() before get<type>()" && is<ctype>()); \ - return var; \ - } \ - template <> \ - inline ctype& value::get<ctype>() { \ - Assert("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \ - return var; \ +#define GET(ctype, var) \ + template <> \ + inline const ctype &value::get<ctype>() const { \ + Assert("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \ + return var; \ + } \ + template <> \ + inline ctype &value::get<ctype>() { \ + Assert("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \ + return var; \ } GET(bool, u_.boolean_) -GET(double, u_.number_) GET(std::string, *u_.string_) GET(array, *u_.array_) GET(object, *u_.object_) +#ifdef PICOJSON_USE_INT64 +GET(double, (type_ == int64_type && (const_cast<value *>(this)->type_ = number_type, + const_cast<value *>(this)->u_.number_ = u_.int64_), + u_.number_)) +GET(int64_t, u_.int64_) +#else +GET(double, u_.number_) +#endif #undef GET +#define SET(ctype, jtype, setter) \ + template <> \ + inline void value::set<ctype>(const ctype &_val) { \ + clear(); \ + type_ = jtype##_type; \ + setter \ + } +SET(bool, boolean, u_.boolean_ = _val;) +SET(std::string, string, u_.string_ = new std::string(_val);) +SET(array, array, u_.array_ = new array(_val);) +SET(object, object, u_.object_ = new object(_val);) +SET(double, number, u_.number_ = _val;) +#ifdef PICOJSON_USE_INT64 +SET(int64_t, int64, u_.int64_ = _val;) +#endif +#undef SET + +#if PICOJSON_USE_RVALUE_REFERENCE +#define MOVESET(ctype, jtype, setter) \ + template <> \ + inline void value::set<ctype>(ctype && _val) { \ + clear(); \ + type_ = jtype##_type; \ + setter \ + } +MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));) +MOVESET(array, array, u_.array_ = new array(std::move(_val));) +MOVESET(object, object, u_.object_ = new object(std::move(_val));) +#undef MOVESET +#endif + inline bool value::evaluate_as_boolean() const { switch (type_) { case null_type: @@ -244,6 +424,10 @@ inline bool value::evaluate_as_boolean() const { return u_.boolean_; case number_type: return u_.number_ != 0; +#ifdef PICOJSON_USE_INT64 + case int64_type: + return u_.int64_ != 0; +#endif case string_type: return !u_.string_->empty(); default: @@ -251,25 +435,38 @@ inline bool value::evaluate_as_boolean() const { } } -inline const value& value::get(size_t idx) const { +inline const value &value::get(const size_t idx) const { + static value s_null; + Assert(is<array>()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; +} + +inline value &value::get(const size_t idx) { static value s_null; Assert(is<array>()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } -inline const value& value::get(const std::string& key) const { +inline const value &value::get(const std::string &key) const { static value s_null; Assert(is<object>()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } -inline bool value::contains(size_t idx) const { +inline value &value::get(const std::string &key) { + static value s_null; + Assert(is<object>()); + object::iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; +} + +inline bool value::contains(const size_t idx) const { Assert(is<array>()); return idx < u_.array_->size(); } -inline bool value::contains(const std::string& key) const { +inline bool value::contains(const std::string &key) const { Assert(is<object>()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end(); @@ -281,6 +478,13 @@ inline std::string value::to_str() const { return "null"; case boolean_type: return u_.boolean_ ? "true" : "false"; +#ifdef PICOJSON_USE_INT64 + case int64_type: { + char buf[sizeof("-9223372036854775808")]; + SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); + return buf; + } +#endif case number_type: { std::stringstream num_str; num_str.imbue(std::locale::classic()); @@ -303,24 +507,24 @@ inline std::string value::to_str() const { } template <typename Iter> -void copy(const std::string& s, Iter oi) { +void copy(const std::string &s, Iter oi) { std::copy(s.begin(), s.end(), oi); } template <typename Iter> -void serialize_str(const std::string& s, Iter oi) { - // C0 control characters, 00-1F - static const char* u_map[] = { - "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", - "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", - "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", - "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; - // To be sure we could rewrite C1 control characters also (first decode UTF-8, check, then map to - // \u sequence), but for now chromium allows C1 in JSON.parse - - *oi++ = '"'; - for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { - switch (*i) { +struct serialize_str_char { + Iter oi; + void operator()(char c) { + // C0 control characters, 00-1F + static const char *u_map[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + // To be sure we could rewrite C1 control characters also (first decode UTF-8, check, then map + // to + // \u sequence), but for now chromium allows C1 in JSON.parse + switch (c) { case '"': copy("\\\"", oi); break; @@ -331,46 +535,97 @@ void serialize_str(const std::string& s, Iter oi) { copy("\\u007f", oi); break; default: - if ((unsigned char)*i < 0x20) { - const char* u = u_map[(unsigned char)*i]; + if ((unsigned char)c < 0x20) { + const char *u = u_map[(unsigned char)c]; while (*u) { *oi++ = *u++; } } else { - *oi++ = *i; + *oi++ = c; } break; } } +}; + +template <typename Iter> +void serialize_str(const std::string &s, Iter oi) { + *oi++ = '"'; + serialize_str_char<Iter> process_char = {oi}; + std::for_each(s.begin(), s.end(), process_char); *oi++ = '"'; } template <typename Iter> -void value::serialize(Iter oi) const { +void value::serialize(Iter oi, bool prettify) const { + return _serialize(oi, prettify ? 0 : -1); +} + +inline std::string value::serialize(bool prettify) const { + return _serialize(prettify ? 0 : -1); +} + +template <typename Iter> +void value::_indent(Iter oi, int indent) { + *oi++ = '\n'; + for (int i = 0; i < indent * INDENT_WIDTH; ++i) { + *oi++ = ' '; + } +} + +template <typename Iter> +void value::_serialize(Iter oi, int indent) const { switch (type_) { case string_type: serialize_str(*u_.string_, oi); break; case array_type: { *oi++ = '['; + if (indent != -1) { + ++indent; + } for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { if (i != u_.array_->begin()) { *oi++ = ','; } - i->serialize(oi); + if (indent != -1) { + _indent(oi, indent); + } + i->_serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (!u_.array_->empty()) { + _indent(oi, indent); + } } *oi++ = ']'; break; } case object_type: { *oi++ = '{'; + if (indent != -1) { + ++indent; + } for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { if (i != u_.object_->begin()) { *oi++ = ','; } + if (indent != -1) { + _indent(oi, indent); + } serialize_str(i->first, oi); *oi++ = ':'; - i->second.serialize(oi); + if (indent != -1) { + *oi++ = ' '; + } + i->second._serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (!u_.object_->empty()) { + _indent(oi, indent); + } } *oi++ = '}'; break; @@ -379,11 +634,14 @@ void value::serialize(Iter oi) const { copy(to_str(), oi); break; } + if (indent == 0) { + *oi++ = '\n'; + } } -inline std::string value::serialize() const { +inline std::string value::_serialize(int indent) const { std::string s; - serialize(std::back_inserter(s)); + _serialize(std::back_inserter(s), indent); return s; } @@ -391,36 +649,35 @@ template <typename Iter> class input { protected: Iter cur_, end_; - int last_ch_; - bool ungot_; + bool consumed_; int line_; public: - input(const Iter& first, const Iter& last) - : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) { + input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) { } int getc() { - if (ungot_) { - ungot_ = false; - return last_ch_; + if (consumed_) { + if (*cur_ == '\n') { + ++line_; + } + ++cur_; } if (cur_ == end_) { - last_ch_ = -1; + consumed_ = false; return -1; } - if (last_ch_ == '\n') { - line_++; - } - last_ch_ = *cur_++ & 0xff; - return last_ch_; + consumed_ = true; + return *cur_ & 0xff; } void ungetc() { - if (last_ch_ != -1) { - Assert(!ungot_); - ungot_ = true; - } + consumed_ = false; } Iter cur() const { + if (consumed_) { + input<Iter> *self = const_cast<input<Iter> *>(this); + self->consumed_ = false; + ++self->cur_; + } return cur_; } int line() const { @@ -435,15 +692,15 @@ class input { } } } - bool expect(int expect) { + bool expect(const int expected) { skip_ws(); - if (getc() != expect) { + if (getc() != expected) { ungetc(); return false; } return true; } - bool match(const std::string& pattern) { + bool match(const std::string &pattern) { for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { if (getc() != *pi) { ungetc(); @@ -455,7 +712,7 @@ class input { }; template <typename Iter> -inline int _parse_quadhex(input<Iter>& in) { +inline int _parse_quadhex(input<Iter> &in) { int uni_ch = 0, hex; for (int i = 0; i < 4; i++) { if ((hex = in.getc()) == -1) { @@ -477,7 +734,7 @@ inline int _parse_quadhex(input<Iter>& in) { } template <typename String, typename Iter> -inline bool _parse_codepoint(String& out, input<Iter>& in) { +inline bool _parse_codepoint(String &out, input<Iter> &in) { int uni_ch; if ((uni_ch = _parse_quadhex(in)) == -1) { return false; @@ -500,26 +757,26 @@ inline bool _parse_codepoint(String& out, input<Iter>& in) { uni_ch += 0x10000; } if (uni_ch < 0x80) { - out.push_back(uni_ch); + out.push_back(static_cast<char>(uni_ch)); } else { if (uni_ch < 0x800) { - out.push_back(0xc0 | (uni_ch >> 6)); + out.push_back(static_cast<char>(0xc0 | (uni_ch >> 6))); } else { if (uni_ch < 0x10000) { - out.push_back(0xe0 | (uni_ch >> 12)); + out.push_back(static_cast<char>(0xe0 | (uni_ch >> 12))); } else { - out.push_back(0xf0 | (uni_ch >> 18)); - out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); + out.push_back(static_cast<char>(0xf0 | (uni_ch >> 18))); + out.push_back(static_cast<char>(0x80 | ((uni_ch >> 12) & 0x3f))); } - out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); + out.push_back(static_cast<char>(0x80 | ((uni_ch >> 6) & 0x3f))); } - out.push_back(0x80 | (uni_ch & 0x3f)); + out.push_back(static_cast<char>(0x80 | (uni_ch & 0x3f))); } return true; } template <typename String, typename Iter> -inline bool _parse_string(String& out, input<Iter>& in) { +inline bool _parse_string(String &out, input<Iter> &in) { while (1) { int ch = in.getc(); if (ch < ' ') { @@ -554,32 +811,32 @@ inline bool _parse_string(String& out, input<Iter>& in) { return false; } } else { - out.push_back(ch); + out.push_back(static_cast<char>(ch)); } } return false; } template <typename Context, typename Iter> -inline bool _parse_array(Context& ctx, input<Iter>& in) { +inline bool _parse_array(Context &ctx, input<Iter> &in) { if (!ctx.parse_array_start()) { return false; } + size_t idx = 0; if (in.expect(']')) { - return true; + return ctx.parse_array_stop(idx); } - size_t idx = 0; do { if (!ctx.parse_array_item(in, idx)) { return false; } idx++; } while (in.expect(',')); - return in.expect(']'); + return in.expect(']') && ctx.parse_array_stop(idx); } template <typename Context, typename Iter> -inline bool _parse_object(Context& ctx, input<Iter>& in) { +inline bool _parse_object(Context &ctx, input<Iter> &in) { if (!ctx.parse_object_start()) { return false; } @@ -599,13 +856,13 @@ inline bool _parse_object(Context& ctx, input<Iter>& in) { } template <typename Iter> -inline bool _parse_number(double& out, input<Iter>& in) { +inline std::string _parse_number(input<Iter> &in) { std::stringstream num_str; num_str.imbue(std::locale::classic()); while (true) { int ch = in.getc(); - if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == '.' || ch == 'e' || ch == 'E') { + if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E' || ch == '.') { num_str.put(ch); } else { in.ungetc(); @@ -613,13 +870,11 @@ inline bool _parse_number(double& out, input<Iter>& in) { } } - num_str >> out; - - return num_str && num_str.eof(); + return num_str.str(); } template <typename Context, typename Iter> -inline bool _parse(Context& ctx, input<Iter>& in) { +inline bool _parse(Context &ctx, input<Iter> &in) { in.skip_ws(); int ch = in.getc(); switch (ch) { @@ -642,14 +897,31 @@ inline bool _parse(Context& ctx, input<Iter>& in) { return _parse_object(ctx, in); default: if (('0' <= ch && ch <= '9') || ch == '-') { - in.ungetc(); double f; - if (_parse_number(f, in)) { + char *endp; + in.ungetc(); + std::string num_str(_parse_number(in)); + if (num_str.empty()) { + return false; + } +#ifdef PICOJSON_USE_INT64 + { + errno = 0; + intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); + if (errno == 0 && std::numeric_limits<int64_t>::min() <= ival && + ival <= std::numeric_limits<int64_t>::max() && + endp == num_str.c_str() + num_str.size()) { + ctx.set_int64(ival); + return true; + } + } +#endif + f = strtod(num_str.c_str(), &endp); + if (endp == num_str.c_str() + num_str.size()) { ctx.set_number(f); return true; - } else { - return false; } + return false; } break; } @@ -665,35 +937,43 @@ class deny_parse_context { bool set_bool(bool) { return false; } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { + return false; + } +#endif bool set_number(double) { return false; } template <typename Iter> - bool parse_string(input<Iter>&) { + bool parse_string(input<Iter> &) { return false; } bool parse_array_start() { return false; } template <typename Iter> - bool parse_array_item(input<Iter>&, size_t) { + bool parse_array_item(input<Iter> &, size_t) { + return false; + } + bool parse_array_stop(size_t) { return false; } bool parse_object_start() { return false; } template <typename Iter> - bool parse_object_item(input<Iter>&, const std::string&) { + bool parse_object_item(input<Iter> &, const std::string &) { return false; } }; class default_parse_context { protected: - value* out_; + value *out_; public: - default_parse_context(value* out) : out_(out) { + default_parse_context(value *out) : out_(out) { } bool set_null() { *out_ = value(); @@ -703,12 +983,18 @@ class default_parse_context { *out_ = value(b); return true; } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t i) { + *out_ = value(i); + return true; + } +#endif bool set_number(double f) { *out_ = value(f); return true; } template <typename Iter> - bool parse_string(input<Iter>& in) { + bool parse_string(input<Iter> &in) { *out_ = value(string_type, false); return _parse_string(out_->get<std::string>(), in); } @@ -717,26 +1003,29 @@ class default_parse_context { return true; } template <typename Iter> - bool parse_array_item(input<Iter>& in, size_t) { - array& a = out_->get<array>(); + bool parse_array_item(input<Iter> &in, size_t) { + array &a = out_->get<array>(); a.push_back(value()); default_parse_context ctx(&a.back()); return _parse(ctx, in); } + bool parse_array_stop(size_t) { + return true; + } bool parse_object_start() { *out_ = value(object_type, false); return true; } template <typename Iter> - bool parse_object_item(input<Iter>& in, const std::string& key) { - object& o = out_->get<object>(); + bool parse_object_item(input<Iter> &in, const std::string &key) { + object &o = out_->get<object>(); default_parse_context ctx(&o[key]); return _parse(ctx, in); } private: - default_parse_context(const default_parse_context&); - default_parse_context& operator=(const default_parse_context&); + default_parse_context(const default_parse_context &); + default_parse_context &operator=(const default_parse_context &); }; class null_parse_context { @@ -755,11 +1044,16 @@ class null_parse_context { bool set_bool(bool) { return true; } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { + return true; + } +#endif bool set_number(double) { return true; } template <typename Iter> - bool parse_string(input<Iter>& in) { + bool parse_string(input<Iter> &in) { dummy_str s; return _parse_string(s, in); } @@ -767,32 +1061,35 @@ class null_parse_context { return true; } template <typename Iter> - bool parse_array_item(input<Iter>& in, size_t) { + bool parse_array_item(input<Iter> &in, size_t) { return _parse(*this, in); } + bool parse_array_stop(size_t) { + return true; + } bool parse_object_start() { return true; } template <typename Iter> - bool parse_object_item(input<Iter>& in, const std::string&) { + bool parse_object_item(input<Iter> &in, const std::string &) { return _parse(*this, in); } private: - null_parse_context(const null_parse_context&); - null_parse_context& operator=(const null_parse_context&); + null_parse_context(const null_parse_context &); + null_parse_context &operator=(const null_parse_context &); }; // obsolete, use the version below template <typename Iter> -inline std::string parse(value& out, Iter& pos, const Iter& last) { +inline std::string parse(value &out, Iter &pos, const Iter &last) { std::string err; pos = parse(out, pos, last, &err); return err; } template <typename Context, typename Iter> -inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { +inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) { input<Iter> in(first, last); if (!_parse(ctx, in) && err != NULL) { char buf[64]; @@ -803,7 +1100,7 @@ inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::strin if (ch == -1 || ch == '\n') { break; } else if (ch >= ' ') { - err->push_back(ch); + err->push_back(static_cast<char>(ch)); } } } @@ -811,12 +1108,18 @@ inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::strin } template <typename Iter> -inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { +inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) { default_parse_context ctx(&out); return _parse(ctx, first, last, err); } -inline std::string parse(value& out, std::istream& is) { +inline std::string parse(value &out, const std::string &s) { + std::string err; + parse(out, s.begin(), s.end(), &err); + return err; +} + +inline std::string parse(value &out, std::istream &is) { std::string err; parse(out, std::istreambuf_iterator<char>(is.rdbuf()), std::istreambuf_iterator<char>(), &err); return err; @@ -829,15 +1132,15 @@ struct last_error_t { template <typename T> std::string last_error_t<T>::s; -inline void set_last_error(const std::string& s) { +inline void set_last_error(const std::string &s) { last_error_t<bool>::s = s; } -inline const std::string& get_last_error() { +inline const std::string &get_last_error() { return last_error_t<bool>::s; } -inline bool operator==(const value& x, const value& y) { +inline bool operator==(const value &x, const value &y) { if (x.is<null>()) return y.is<null>(); #define PICOJSON_CMP(type) \ if (x.is<type>()) return y.is<type>() && x.get<type>() == y.get<type>() @@ -854,21 +1157,23 @@ inline bool operator==(const value& x, const value& y) { return false; } -inline bool operator!=(const value& x, const value& y) { +inline bool operator!=(const value &x, const value &y) { return !(x == y); } } +#if !PICOJSON_USE_RVALUE_REFERENCE namespace std { template <> -inline void swap(picojson::value& x, picojson::value& y) { +inline void swap(picojson::value &x, picojson::value &y) { x.swap(y); } } +#endif -inline std::istream& operator>>(std::istream& is, picojson::value& x) { +inline std::istream &operator>>(std::istream &is, picojson::value &x) { picojson::set_last_error(std::string()); - std::string err = picojson::parse(x, is); + const std::string err(picojson::parse(x, is)); if (!err.empty()) { picojson::set_last_error(err); is.setstate(std::ios::failbit); @@ -876,7 +1181,7 @@ inline std::istream& operator>>(std::istream& is, picojson::value& x) { return is; } -inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) { +inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) { x.serialize(std::ostream_iterator<char>(os)); return os; } @@ -885,235 +1190,3 @@ inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) { #endif #endif -#ifdef TEST_PICOJSON -#ifdef _MSC_VER -#pragma warning(disable : 4127) // conditional expression is constant -#endif - -using namespace std; - -static void plan(int num) { - printf("1..%d\n", num); -} - -static bool success = true; - -static void ok(bool b, const char* name = "") { - static int n = 1; - if (!b) success = false; - printf("%s %d - %s\n", b ? "ok" : "ng", n++, name); -} - -template <typename T> -void is(const T& x, const T& y, const char* name = "") { - if (x == y) { - ok(true, name); - } else { - ok(false, name); - } -} - -#include <float.h> -#include <limits.h> -#include <algorithm> -#include <sstream> - -int main(void) { - plan(85); - -// constructors -#define TEST(expr, expected) \ - is(picojson::value expr.serialize(), string(expected), "picojson::value" #expr) - - TEST((true), "true"); - TEST((false), "false"); - TEST((42.0), "42"); - TEST((string("hello")), "\"hello\""); - TEST(("hello"), "\"hello\""); - TEST(("hello", 4), "\"hell\""); - - { - double a = 1; - for (int i = 0; i < 1024; i++) { - picojson::value vi(a); - std::stringstream ss; - ss << vi; - picojson::value vo; - ss >> vo; - double b = vo.get<double>(); - if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) { - printf("ng i=%d a=%.18e b=%.18e\n", i, a, b); - } - a *= 2; - } - } - -#undef TEST - -#define TEST(in, type, cmp, serialize_test) \ - { \ - picojson::value v; \ - const char* s = in; \ - string err = picojson::parse(v, s, s + strlen(s)); \ - ok(err.empty(), in " no error"); \ - ok(v.is<type>(), in " check type"); \ - is<type>(v.get<type>(), cmp, in " correct output"); \ - is(*s, '\0', in " read to eof"); \ - if (serialize_test) { \ - is(v.serialize(), string(in), in " serialize"); \ - } \ - } - TEST("false", bool, false, true); - TEST("true", bool, true, true); - TEST("90.5", double, 90.5, false); - TEST("1.7976931348623157e+308", double, DBL_MAX, false); - TEST("\"hello\"", string, string("hello"), true); - TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"), true); - TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string, string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), - false); - TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false); -#undef TEST - -#define TEST(type, expr) \ - { \ - picojson::value v; \ - const char* s = expr; \ - string err = picojson::parse(v, s, s + strlen(s)); \ - ok(err.empty(), "empty " #type " no error"); \ - ok(v.is<picojson::type>(), "empty " #type " check type"); \ - ok(v.get<picojson::type>().empty(), "check " #type " array size"); \ - } - TEST(array, "[]"); - TEST(object, "{}"); -#undef TEST - - { - picojson::value v; - const char* s = "[1,true,\"hello\"]"; - string err = picojson::parse(v, s, s + strlen(s)); - ok(err.empty(), "array no error"); - ok(v.is<picojson::array>(), "array check type"); - is(v.get<picojson::array>().size(), size_t(3), "check array size"); - ok(v.contains(0), "check contains array[0]"); - ok(v.get(0).is<double>(), "check array[0] type"); - is(v.get(0).get<double>(), 1.0, "check array[0] value"); - ok(v.contains(1), "check contains array[1]"); - ok(v.get(1).is<bool>(), "check array[1] type"); - ok(v.get(1).get<bool>(), "check array[1] value"); - ok(v.contains(2), "check contains array[2]"); - ok(v.get(2).is<string>(), "check array[2] type"); - is(v.get(2).get<string>(), string("hello"), "check array[2] value"); - ok(!v.contains(3), "check not contains array[3]"); - } - - { - picojson::value v; - const char* s = "{ \"a\": true }"; - string err = picojson::parse(v, s, s + strlen(s)); - ok(err.empty(), "object no error"); - ok(v.is<picojson::object>(), "object check type"); - is(v.get<picojson::object>().size(), size_t(1), "check object size"); - ok(v.contains("a"), "check contains property"); - ok(v.get("a").is<bool>(), "check bool property exists"); - is(v.get("a").get<bool>(), true, "check bool property value"); - is(v.serialize(), string("{\"a\":true}"), "serialize object"); - ok(!v.contains("z"), "check not contains property"); - } - -#define TEST(json, msg) \ - do { \ - picojson::value v; \ - const char* s = json; \ - string err = picojson::parse(v, s, s + strlen(s)); \ - is(err, string("syntax error at line " msg), msg); \ - } while (0) - TEST("falsoa", "1 near: oa"); - TEST("{]", "1 near: ]"); - TEST("\n\bbell", "2 near: bell"); - TEST("\"abc\nd\"", "1 near: "); -#undef TEST - - { - picojson::value v1, v2; - const char* s; - string err; - s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; - err = picojson::parse(v1, s, s + strlen(s)); - s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }"; - err = picojson::parse(v2, s, s + strlen(s)); - ok((v1 == v2), "check == operator in deep comparison"); - } - - { - picojson::value v1, v2; - const char* s; - string err; - s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; - err = picojson::parse(v1, s, s + strlen(s)); - s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }"; - err = picojson::parse(v2, s, s + strlen(s)); - ok((v1 != v2), "check != operator for array in deep comparison"); - } - - { - picojson::value v1, v2; - const char* s; - string err; - s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; - err = picojson::parse(v1, s, s + strlen(s)); - s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }"; - err = picojson::parse(v2, s, s + strlen(s)); - ok((v1 != v2), "check != operator for object in deep comparison"); - } - - { - picojson::value v1, v2; - const char* s; - string err; - s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; - err = picojson::parse(v1, s, s + strlen(s)); - picojson::object& o = v1.get<picojson::object>(); - o.erase("b"); - picojson::array& a = o["a"].get<picojson::array>(); - picojson::array::iterator i; - i = std::remove(a.begin(), a.end(), picojson::value(std::string("three"))); - a.erase(i, a.end()); - s = "{ \"a\": [1,2], \"d\": 2 }"; - err = picojson::parse(v2, s, s + strlen(s)); - ok((v1 == v2), "check erase()"); - } - - ok(picojson::value(3.0).serialize() == "3", "integral number should be serialized as a integer"); - - { - const char* s = "{ \"a\": [1,2], \"d\": 2 }"; - picojson::null_parse_context ctx; - string err; - picojson::_parse(ctx, s, s + strlen(s), &err); - ok(err.empty(), "null_parse_context"); - } - - { - picojson::value v1, v2; - v1 = picojson::value(true); - swap(v1, v2); - ok(v1.is<picojson::null>(), "swap (null)"); - ok(v2.get<bool>() == true, "swap (bool)"); - - v1 = picojson::value("a"); - v2 = picojson::value(1.0); - swap(v1, v2); - ok(v1.get<double>() == 1.0, "swap (dobule)"); - ok(v2.get<string>() == "a", "swap (string)"); - - v1 = picojson::value(picojson::object()); - v2 = picojson::value(picojson::array()); - swap(v1, v2); - ok(v1.is<picojson::array>(), "swap (array)"); - ok(v2.is<picojson::object>(), "swap (object)"); - } - - return success ? 0 : 1; -} - -#endif diff --git a/src/common/platform_enum.h b/src/common/platform_enum.h new file mode 100644 index 00000000..6c626954 --- /dev/null +++ b/src/common/platform_enum.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMON_PLATFORM_ENUM_H_ +#define COMMON_PLATFORM_ENUM_H_ + +#include "platform_result.h" + +#include <string> +#include <map> +#include <vector> +#include <initializer_list> + + +namespace common { + + +template<typename NativeEnumType> +class PlatformEnum +{ +using MapType = std::map<std::string, NativeEnumType>; +using RevMapType = std::map<NativeEnumType, std::string>; + +using MapTypeConstIter = typename MapType::const_iterator; + +public: + PlatformEnum(std::initializer_list<std::pair<const std::string, NativeEnumType>> m) : mapping_(m) { + for (auto it: mapping_) { + rev_mapping_[it.second] = it.first; + } + } + + PlatformResult getName(NativeEnumType value, std::string* name) const { + auto it = rev_mapping_.find(value); + if (it != rev_mapping_.end()) { + *name = it->second; + return PlatformResult(ErrorCode::NO_ERROR); + } + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "enum value not registered"); + } + + PlatformResult getValue(std::string name, NativeEnumType* value) const { + auto it = mapping_.find(name); + if (it != mapping_.end()) { + *value = it->second; + return PlatformResult(ErrorCode::NO_ERROR); + } + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "enum name not registered"); + } + + MapTypeConstIter begin() const { + return mapping_.cbegin(); + } + + MapTypeConstIter end() const { + return mapping_.cend(); + } + +private: + MapType mapping_; + RevMapType rev_mapping_; +}; + + +} // common + + +#endif // COMMON_PLATFORM_ENUM_H_ diff --git a/src/common/task-queue.cpp b/src/common/task-queue.cpp index 9bafdae4..fe551a5e 100644 --- a/src/common/task-queue.cpp +++ b/src/common/task-queue.cpp @@ -24,44 +24,54 @@ TaskQueue& TaskQueue::GetInstance() { } template <> -gboolean TaskQueue::AfterWorkCallback<void>(gpointer data) { +gboolean TaskQueue::WorkCallback<void>(gpointer data) { QueueData<void>* d = static_cast<QueueData<void>*>(data); if (nullptr != d) { - d->after_work_callback_(); + d->work_callback_(); delete d; } return FALSE; } -template <> -void* TaskQueue::WorkCallback<void>(void* data) { - QueueData<void>* d = static_cast<QueueData<void>*>(data); - if (nullptr != d) { - d->work_callback_(); - if (d->after_work_callback_) { - g_idle_add(AfterWorkCallback<void>, d); - } - } - return nullptr; +void TaskQueue::Queue(const std::function<void()>& work, const std::function<void()>& after_work) { + auto do_work = [work, after_work]() { + work(); + after_work(); + }; + this->queue_worker_.add_job(do_work); } -void TaskQueue::Queue(const std::function<void()>& work, const std::function<void()>& after_work) { - QueueData<void>* d = new QueueData<void>(); - d->work_callback_ = work; - d->after_work_callback_ = after_work; +void TaskQueue::Async(const std::function<void()>& work) { + this->async_worker_.add_job(work); +} - if (pthread_create(&d->thread_, nullptr, WorkCallback<void>, d) != 0) { - LoggerE("Failed to create a background thread."); - delete d; - } else { - pthread_detach(d->thread_); +void TaskQueue::Async(const std::function<void(const common::AsyncToken& token)>& work, + const common::AsyncToken& token) { + auto do_work = [work, token]() { work(token); }; + Async(do_work); +} + +void TaskQueue::DeleteJobs() { + { + std::lock_guard<std::mutex> lck{this->queue_worker_.jobs_mtx}; + this->queue_worker_.jobs.clear(); + } + { + std::lock_guard<std::mutex> lck{this->async_worker_.jobs_mtx}; + this->async_worker_.jobs.clear(); } } -void TaskQueue::Async(const std::function<void()>& work) { +void TaskQueue::Stop() { + LoggerI("Stopping TaskQueue workers"); + queue_worker_.stop(); + async_worker_.stop(); +} + +void TaskQueue::ScheduleWorkInMainThread(const std::function<void()>& work) { QueueData<void>* d = new QueueData<void>(); - d->after_work_callback_ = work; - g_idle_add(AfterWorkCallback<void>, d); + d->work_callback_ = work; + g_idle_add(WorkCallback<void>, d); } } // namespace common diff --git a/src/common/task-queue.h b/src/common/task-queue.h index fb3300a3..c44cc09c 100644 --- a/src/common/task-queue.h +++ b/src/common/task-queue.h @@ -22,23 +22,55 @@ #include <functional> #include <memory> -#include "logger.h" +#include "tizen_instance.h" +#include "worker.h" namespace common { +/** + * TaskQueue is a class, which aggregates two instances of Worker class. + * The class contains two workers to prevent from blocking jobs, which should execute and return + * results after a few seconds. + */ class TaskQueue { - public: - TaskQueue(const TaskQueue&) = delete; - TaskQueue& operator=(const TaskQueue&) = delete; + private: + TaskQueue() { + } + /** + * Worker for asynchronous 'quick' jobs. + * This queue is supposed to hold jobs, which are expected to take 'a little' time. + */ + Worker async_worker_; + /** + * Worker for asynchronous 'long' jobs. + * This queue is supposed to hold jobs, which are expected to take 'long' time. + */ + Worker queue_worker_; + + public: static TaskQueue& GetInstance(); + ~TaskQueue() { + } + + /** + * Method used to clear all delegated jobs during lifetime of UtilsInstance class. + * The UtilsInstance class is the first constructed and the last destructed instance of Instance + * class, which determines the lifetime of frame in the page. + */ + void DeleteJobs(); + + /** + * Stops the workers. This method should be called only once, before shutting down the process. + * After that time, no more messages will be sent to Crosswalk. + */ + void Stop(); /** - * @brief Schedules work to be executed in a separate thread, after_work is going - * to be called in main glib loop. + * @brief Schedule a 'long' job * - * @param[in] work - callback is going to be called in a separate thread - * @param[in] after_work - callback is going to be called in main glib loop + * @param[in] work - callback is going to be called in a worker's thread + * @param[in] after_work - callback is going to be called after work * @param[in] data - data passed to both callbacks */ template <class T> @@ -47,19 +79,17 @@ class TaskQueue { const std::shared_ptr<T>& data); /** - * @brief Schedules work to be executed in a separate thread, after_work is going - * to be called in main glib loop. + * @brief Schedule a 'long' job * - * @param[in] work - callback is going to be called in a separate thread - * @param[in] after_work - callback is going to be called in main glib loop + * @param[in] work - callback is going to be called in a worker's thread + * @param[in] after_work - callback is going to be called after work function */ - void Queue(const std::function<void()>& work, - const std::function<void()>& after_work = std::function<void()>()); + void Queue(const std::function<void()>& work, const std::function<void()>& after_work = [] {}); /** - * @brief Schedules work to be executed in main glib loop. + * @brief Schedule a 'quick' job * - * @param[in] work - callback is going to be called in main glib loop + * @param[in] work - callback is going to be called in a worker's thread * @param[in] data - data passed to callback */ template <class T> @@ -67,82 +97,98 @@ class TaskQueue { const std::shared_ptr<T>& data); /** - * @brief Schedules work to be executed in main glib loop. + * @brief Schedule a 'quick' job * - * @param[in] work - callback is going to be called in main glib loop + * @param[in] work - callback is going to be called in a worker's thread */ void Async(const std::function<void()>& work); - private: - TaskQueue() { - } + /** + * @brief Schedule a 'quick' job requiring AsyncToken + * + * @param[in] work - callback is going to be called in a worker's thread + * @param[in] token - token passed to the work function + */ + void Async(const std::function<void(const common::AsyncToken& token)>& work, + const common::AsyncToken& token); + + /** + * @brief Schedules work to be executed in main glib loop + * + * This method should be used only for jobs, which HAVE to be executed in main loop of the + * process. + * + * @param[in] work - callback is going to be called in main glib loop + * @param[in] data - data passed to callback + */ + template <class T> + void ScheduleWorkInMainThread(const std::function<void(const std::shared_ptr<T>&)>& work, + const std::shared_ptr<T>& data); + /** + * @brief Schedules work to be executed in main glib loop + * + * This method should be used only for jobs, which HAVE to be executed in main loop of the + * process. + * + * @param[in] work - callback is going to be called in main glib loop + */ + void ScheduleWorkInMainThread(const std::function<void()>& work); template <class T> struct QueueData { - pthread_t thread_; std::function<void(const std::shared_ptr<T>&)> work_callback_; - std::function<void(const std::shared_ptr<T>&)> after_work_callback_; std::shared_ptr<T> data_; }; template <class T> - static void* WorkCallback(void* data); + static gboolean WorkCallback(gpointer data); - template <class T> - static gboolean AfterWorkCallback(gpointer data); + TaskQueue(const TaskQueue&) = delete; + TaskQueue& operator=(const TaskQueue&) = delete; + TaskQueue(TaskQueue&&) = delete; + TaskQueue& operator=(TaskQueue&&) = delete; }; template <> struct TaskQueue::QueueData<void> { - pthread_t thread_; std::function<void()> work_callback_; - std::function<void()> after_work_callback_; }; template <class T> -gboolean TaskQueue::AfterWorkCallback(gpointer data) { +gboolean TaskQueue::WorkCallback(gpointer data) { QueueData<T>* d = static_cast<QueueData<T>*>(data); if (nullptr != d) { - d->after_work_callback_(d->data_); + d->work_callback_(d->data_); delete d; } return FALSE; } template <class T> -void* TaskQueue::WorkCallback(void* data) { - QueueData<T>* d = static_cast<QueueData<T>*>(data); - if (nullptr != d) { - d->work_callback_(d->data_); - g_idle_add(AfterWorkCallback<T>, d); - } - return nullptr; -} - -template <class T> void TaskQueue::Queue(const std::function<void(const std::shared_ptr<T>&)>& work, const std::function<void(const std::shared_ptr<T>&)>& after_work, const std::shared_ptr<T>& data) { - QueueData<T>* d = new QueueData<T>(); - d->work_callback_ = work; - d->after_work_callback_ = after_work; - d->data_ = data; - - if (pthread_create(&d->thread_, nullptr, WorkCallback<T>, d) != 0) { - LoggerE("Failed to create a background thread."); - delete d; - } else { - pthread_detach(d->thread_); - } + auto do_work = [data, work, after_work]() { + work(data); + after_work(data); + }; + this->queue_worker_.add_job(do_work); } template <class T> void TaskQueue::Async(const std::function<void(const std::shared_ptr<T>&)>& work, const std::shared_ptr<T>& data) { + auto do_work = [data, work]() { work(data); }; + this->async_worker_.add_job(do_work); +} + +template <class T> +void TaskQueue::ScheduleWorkInMainThread(const std::function<void(const std::shared_ptr<T>&)>& work, + const std::shared_ptr<T>& data) { QueueData<T>* d = new QueueData<T>(); - d->after_work_callback_ = work; + d->work_callback_ = work; d->data_ = data; - g_idle_add(AfterWorkCallback<T>, d); + g_idle_add(WorkCallback<T>, d); } } // namespace common diff --git a/src/common/tools.cc b/src/common/tools.cc index c1d49984..fd4a2f30 100644 --- a/src/common/tools.cc +++ b/src/common/tools.cc @@ -446,6 +446,23 @@ bool IsAppVersionEarlierThan(const std::string& ver) { return false; } +/* + * RequestStoragePrivilegeChecker structure is used to check whether the file stating is needed. + * File stating is done to ensure that the application has given access to files on + * internal/external memory. The checking is done since Tizen 5.0 for applications, which + * require at least 5.0 version of Tizen. + * */ +struct RequestStoragePrivilegeChecker { + bool isStorageAccessCheckNeeded; + RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) { + } +}; + +bool IsStoragePrivilegeCheckNeeded() { + static RequestStoragePrivilegeChecker checker{}; + return checker.isStorageAccessCheckNeeded; +} + std::string GetErrorString(int error_code) { static const size_t kSize = 1024; char msg[kSize] = {0}; @@ -491,6 +508,37 @@ bool IsPathValid(const std::string& path) { path.length() - 3 != path.rfind("/.."); } +PlatformResult CheckStorageAccess(const std::string& path) { + ScopeLogger("path: [%s]", path.c_str()); + struct stat buf {}; + char* real_path = realpath(path.c_str(), nullptr); + SCOPE_EXIT { + free(real_path); + }; + if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) { + LoggerE("The application does not have necessary privilege to access given file: [%s]", + path.c_str()); + return PlatformResult(ErrorCode::SECURITY_ERR, + "Permission denied: accessing files requires proper storage privilege"); + } + return PlatformResult(ErrorCode::NO_ERROR); +} + +TizenResult CheckStorageAccessAndReturn(const std::string& path) { + ScopeLogger("path: [%s]", path.c_str()); + struct stat buf {}; + char* real_path = realpath(path.c_str(), nullptr); + SCOPE_EXIT { + free(real_path); + }; + if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) { + LoggerE("The application does not have necessary privilege to access given file: [%s]", + path.c_str()); + return SecurityError("Permission denied: accessing files requires proper storage privilege"); + } + return TizenSuccess(); +} + PlatformResult CheckFileStatus(const std::string& path) { ScopeLogger(); diff --git a/src/common/tools.h b/src/common/tools.h index d72a09b8..88136c48 100644 --- a/src/common/tools.h +++ b/src/common/tools.h @@ -23,6 +23,7 @@ #include "common/picojson.h" #include "common/platform_exception.h" #include "common/platform_result.h" +#include "common/tizen_result.h" namespace common { namespace tools { @@ -35,8 +36,10 @@ void ReportError(const PlatformResult& error, picojson::object* out); common::PlatformResult CheckAccess(const std::string& privilege); common::PlatformResult CheckAccess(const std::vector<std::string>& privileges); +common::PlatformResult CheckStorageAccess(const std::string& path); +common::TizenResult CheckStorageAccessAndReturn(const std::string& path); common::PlatformResult GetPkgApiVersion(std::string* api_version); - +bool IsStoragePrivilegeCheckNeeded(); bool IsAppVersionEarlierThan(const std::string& ver); // it is used for modules which return TizenResult objects to JS layer @@ -57,6 +60,29 @@ bool IsAppVersionEarlierThan(const std::string& ver); } \ } while (0) +// The below macro is designed to check, whether the application has necessary privilege in order +// to access resource on internal or external memory. +#define CHECK_STORAGE_ACCESS(path, out) \ + do { \ + if (common::tools::IsStoragePrivilegeCheckNeeded()) { \ + auto ret = common::tools::CheckStorageAccess(path); \ + if (!ret) { \ + common::tools::ReportError(ret, out); \ + return; \ + } \ + } \ + } while (0) + +#define CHECK_STORAGE_ACCESS_AND_RETURN(path) \ + do { \ + if (common::tools::IsStoragePrivilegeCheckNeeded()) { \ + auto ret = common::tools::CheckStorageAccessAndReturn(path); \ + if (!ret) { \ + return ret; \ + } \ + } \ + } while (0) + #define CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(current_priv, prev_priv, out) \ do { \ auto ret = common::tools::CheckAccess(current_priv); \ diff --git a/src/common/worker.cc b/src/common/worker.cc new file mode 100644 index 00000000..4442c992 --- /dev/null +++ b/src/common/worker.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worker.h" + +namespace common { + +void Worker::main() { + std::unique_lock<std::mutex> lck{jobs_mtx}; + while (true) { + jobs_cond.wait(lck, [this] { return !jobs.empty() || exit; }); + if (exit) { + return; + } + + while (!jobs.empty()) { + auto job = jobs.front(); + jobs.pop_front(); + //////////// end of critical section + lck.unlock(); + + try { + job.func(); + } catch (...) { + // should never happen + LoggerE("Func should never throw"); + } + + try { + job.finally(); + } catch (...) { + // should never happen + LoggerE("Finally should never throw"); + } + + lck.lock(); + //////////// start of critical section + if (exit) { + return; + } + } + } +} + +void Worker::add_job(const std::function<void()>& func, const std::function<void()>& finally) { + { + std::lock_guard<std::mutex> lck{jobs_mtx}; + jobs.push_back({func, finally}); + } + jobs_cond.notify_one(); +} + +Worker::Worker() : exit(false), thread(std::bind(&Worker::main, this)) { +} + +Worker::~Worker() { + if (!exit && thread.joinable()) { + stop(); + } +} + +void Worker::stop() { + { + // use memory barrier for exit flag (could be std::atomic_flag, but we use lock instead) + std::lock_guard<std::mutex> lck{jobs_mtx}; + exit = true; + } + jobs_cond.notify_one(); + + try { + thread.join(); + } catch (std::exception& e) { + LoggerE("Failed to join thread: %s", e.what()); + } + + // finalize jobs left in queue + for (auto job : jobs) { + try { + job.finally(); + } catch (...) { + // should never happen + LoggerE("Finally should never throw"); + } + } +} + +} // namespace common
\ No newline at end of file diff --git a/src/common/worker.h b/src/common/worker.h new file mode 100644 index 00000000..85aea8b9 --- /dev/null +++ b/src/common/worker.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WEBAPI_PLUGINS_COMMON_WORKER_H_ +#define WEBAPI_PLUGINS_COMMON_WORKER_H_ + +#include <condition_variable> +#include <deque> +#include <functional> +#include <memory> +#include <mutex> +#include <thread> +#include "common/logger.h" + +namespace common { + +/** + * @brief Implements single worker executing in new thread + * + * Jobs are done in FIFO order. If this worker is destroyed all pending jobs are cancelled, + * and all remaining 'finally' functions are called. + */ +class Worker { + private: + friend class TaskQueue; + bool exit; + struct Job { + std::function<void()> func; + std::function<void()> finally; + }; + std::mutex jobs_mtx; + std::condition_variable jobs_cond; + std::deque<Job> jobs; + std::thread thread; + void main(void); + + public: + Worker(); + virtual ~Worker(); + void stop(); + + /** + * @brief Schedule a job + * Parameters will be copied (no reference is held) + * + * @param job function called as a job (should not throw) + * @param finally function called after completion or canceling. (should not throw) + */ + virtual void add_job(const std::function<void()>& job, const std::function<void()>& finally); + + /** + * @brief Schedule a job. Same as above, but with empty finally function. + * Parameter will be copied (no reference is held) + * + * @param job function called as a job (should not throw) + */ + virtual void add_job(const std::function<void()>& job) { + add_job(job, [] {}); + } + + Worker(const Worker&) = delete; + Worker& operator=(const Worker&) = delete; +}; + +} // namespace common + +#endif // WEBAPI_PLUGINS_COMMON_WORKER_H_
\ No newline at end of file diff --git a/src/contact/contact_instance.cc b/src/contact/contact_instance.cc index 0b58bd8e..939aaf07 100644 --- a/src/contact/contact_instance.cc +++ b/src/contact/contact_instance.cc @@ -17,6 +17,7 @@ #include "contact/contact_instance.h" #include "common/converter.h" +#include "common/filesystem/filesystem_provider.h" #include "common/logger.h" #include "common/platform_exception.h" #include "common/task-queue.h" @@ -32,7 +33,31 @@ namespace contact { namespace { const std::string kPrivilegeContactRead = "http://tizen.org/privilege/contact.read"; const std::string kPrivilegeContactWrite = "http://tizen.org/privilege/contact.write"; -} +const std::vector<const char*> kContactURIs = {"photoURI", "ringtoneURI", "vibrationURI", + "messageAlertURI"}; +const std::vector<const char*> kPersonGroupURIs = {"photoURI", "ringtoneURI"}; +const std::vector<const char*> kOrganizationURIs = {"logoURI"}; +} + +#define CHECK_CONTACT_ATTRIBUTES_STORAGE(in, to_check, out) \ + do { \ + for (auto& attr : to_check) { \ + if (!IsNull(in, attr)) { \ + const std::string& real_path = \ + common::FilesystemProvider::Create().GetRealPath(FromJson<std::string>(in, attr)); \ + CHECK_STORAGE_ACCESS(real_path, out); \ + } \ + } \ + } while (0); + +#define CHECK_CONTACT_ATTRIBUTES_ARRAY(in, attribute, to_check, out) \ + do { \ + JsonArray array = FromJson<JsonArray>(in, attribute); \ + for (auto& el : array) { \ + JsonObject element = common::JsonCast<JsonObject>(el); \ + CHECK_CONTACT_ATTRIBUTES_STORAGE(element, to_check, out); \ + } \ + } while (0); using namespace common; @@ -110,6 +135,11 @@ void ContactInstance::AddressBookGet(const JsonValue& args, JsonObject& out) { void ContactInstance::AddressBookAdd(const JsonValue& args, JsonObject& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + + const auto& contact = args.get("contact").get<JsonObject>(); + CHECK_CONTACT_ATTRIBUTES_STORAGE(contact, kContactURIs, &out); + CHECK_CONTACT_ATTRIBUTES_ARRAY(contact, "organizations", kOrganizationURIs, &out); + JsonValue val{JsonObject{}}; PlatformResult status = AddressBook::AddressBookAdd(common::JsonCast<JsonObject>(args), val.get<JsonObject>()); @@ -123,6 +153,13 @@ void ContactInstance::AddressBookAddBatch(const JsonValue& args, JsonObject& out ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + const auto& batch_args = args.get("batchArgs").get<JsonArray>(); + for (auto& item : batch_args) { + JsonObject contact = common::JsonCast<JsonObject>(item); + CHECK_CONTACT_ATTRIBUTES_STORAGE(contact, kContactURIs, &out); + CHECK_CONTACT_ATTRIBUTES_ARRAY(contact, "organizations", kOrganizationURIs, &out); + } + const double callback_id = args.get("callbackId").get<double>(); auto get = [=](const std::shared_ptr<JsonValue>& response) -> void { @@ -180,6 +217,13 @@ void ContactInstance::AddressBookUpdateBatch(const JsonValue& args, JsonObject& ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + const auto& batch_args = args.get("batchArgs").get<JsonArray>(); + for (auto& item : batch_args) { + JsonObject contact = common::JsonCast<JsonObject>(item); + CHECK_CONTACT_ATTRIBUTES_STORAGE(contact, kContactURIs, &out); + CHECK_CONTACT_ATTRIBUTES_ARRAY(contact, "organizations", kOrganizationURIs, &out); + } + const double callback_id = args.get("callbackId").get<double>(); auto get = [=](const std::shared_ptr<JsonValue>& response) -> void { @@ -208,6 +252,11 @@ void ContactInstance::AddressBookUpdateBatch(const JsonValue& args, JsonObject& void ContactInstance::AddressBookUpdate(const JsonValue& args, JsonObject& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + + const auto& contact = args.get("contact").get<JsonObject>(); + CHECK_CONTACT_ATTRIBUTES_STORAGE(contact, kContactURIs, &out); + CHECK_CONTACT_ATTRIBUTES_ARRAY(contact, "organizations", kOrganizationURIs, &out); + JsonValue val{JsonObject{}}; PlatformResult status = AddressBook::AddressBookUpdate(common::JsonCast<JsonObject>(args), val.get<JsonObject>()); @@ -260,6 +309,10 @@ void ContactInstance::AddressBookFind(const JsonValue& args, JsonObject& out) { void ContactInstance::AddressBookAddGroup(const JsonValue& args, JsonObject& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + + const auto& group = args.get("group").get<JsonObject>(); + CHECK_CONTACT_ATTRIBUTES_STORAGE(group, kPersonGroupURIs, &out); + JsonValue val{JsonObject{}}; PlatformResult status = AddressBook::AddressBookAddGroup(common::JsonCast<JsonObject>(args), val.get<JsonObject>()); @@ -283,6 +336,10 @@ void ContactInstance::AddressBookGetGroup(const JsonValue& args, JsonObject& out void ContactInstance::AddressBookUpdateGroup(const JsonValue& args, JsonObject& out) { ScopeLogger(); + + const auto& group = args.get("group").get<JsonObject>(); + CHECK_CONTACT_ATTRIBUTES_STORAGE(group, kPersonGroupURIs, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); JsonValue val{JsonObject{}}; PlatformResult status = AddressBook::AddressBookUpdateGroup(common::JsonCast<JsonObject>(args), @@ -421,6 +478,10 @@ void ContactInstance::ContactManagerGet(const JsonValue& args, JsonObject& out) void ContactInstance::ContactManagerUpdate(const JsonValue& args, JsonObject& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + + const auto& group = args.get("person").get<JsonObject>(); + CHECK_CONTACT_ATTRIBUTES_STORAGE(group, kPersonGroupURIs, &out); + JsonValue val{JsonObject{}}; PlatformResult status = ContactManager::ContactManagerUpdate(common::JsonCast<JsonObject>(args), val.get<JsonObject>()); @@ -434,6 +495,12 @@ void ContactInstance::ContactManagerUpdateBatch(const JsonValue& args, JsonObjec ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContactWrite, &out); + const auto& batch_args = args.get("batchArgs").get<JsonArray>(); + for (auto& item : batch_args) { + JsonObject contact = common::JsonCast<JsonObject>(item); + CHECK_CONTACT_ATTRIBUTES_STORAGE(contact, kPersonGroupURIs, &out); + } + const double callback_id = args.get("callbackId").get<double>(); auto get = [=](const std::shared_ptr<JsonValue>& response) -> void { @@ -640,6 +707,8 @@ void ContactInstance::PersonResetUsageCount(const JsonValue& args, JsonObject& o } else { LogAndReportError(status, &out); } +#undef CHECK_CONTACT_ATTRIBUTES_STORAGE +#undef CHECK_CONTACT_ATTRIBUTES_ARRAY } } // namespace contact diff --git a/src/contact/contact_util.cc b/src/contact/contact_util.cc index eeefc421..f1078433 100644 --- a/src/contact/contact_util.cc +++ b/src/contact/contact_util.cc @@ -1731,7 +1731,7 @@ PlatformResult ImportContactInstantMessengerFromContactsRecord(contacts_record_h int err = contacts_record_get_child_record_at_p(contacts_record, _contacts_contact.messenger, index, &child_record); if (CONTACTS_ERROR_NONE != err && CONTACTS_ERROR_NO_DATA != err) { - LoggerW("Skipping message with index %i. error code: %i", index, err); + LoggerW("Skipping message with index %u. error code: %i", index, err); return PlatformResult(ErrorCode::NO_ERROR); } @@ -1744,7 +1744,7 @@ PlatformResult ImportContactInstantMessengerFromContactsRecord(contacts_record_h } if (!im_address) { - LoggerW("Skipping message with index %i. missing im address", index); + LoggerW("Skipping message with index %u. missing im address", index); return PlatformResult(ErrorCode::NO_ERROR); } @@ -2201,7 +2201,7 @@ PlatformResult ImportGroupIdsFromContactsRecord(contacts_record_h contacts_recor index, &record); if (CONTACTS_ERROR_NONE != err && CONTACTS_ERROR_NO_DATA != err) { // ignoring this record - LoggerW("Skipping record with index %i. error code: %i", index, err); + LoggerW("Skipping record with index %u. error code: %i", index, err); return PlatformResult(ErrorCode::NO_ERROR); } @@ -2914,18 +2914,6 @@ PlatformResult ExportPersonToContactsRecord(contacts_record_h record, const Json return status; } - if (!IsNull(args, "photoURI") && !FromJson<JsonString>(args, "photoURI").empty()) { - PlatformResult status = ContactUtil::SetStrInRecord( - record, _contacts_person.image_thumbnail_path, - ConvertUriToPath(FromJson<JsonString>(args, "photoURI")).c_str()); - if (status.IsError()) { - LoggerE("Try updating read only attribute photoURI"); - return status; - } - } else { - // TO DO: fix when photoURI attribute changed from read only to write mode - } - if (!IsNull(args, "ringtoneURI")) { PlatformResult status = ContactUtil::SetStrInRecord( record, _contacts_person.ringtone_path, diff --git a/src/contact/js/contact.js b/src/contact/js/contact.js index cf5098a1..dcdd7def 100755 --- a/src/contact/js/contact.js +++ b/src/contact/js/contact.js @@ -32,8 +32,8 @@ var Contact = function(data) { data.addressBookId = null; data.lastUpdate = null; - // Force edit mode so that anonymous objects can be promoted - // to their correct types. + // Force edit mode so that anonymous objects can be promoted to their + // correct types. _forceEditMode = true; } else if (type_.isObject(data) || type_.isFunction(data)) { // It's a dictionary diff --git a/src/contact/js/person.js b/src/contact/js/person.js index 16072c14..edcf84ba 100755 --- a/src/contact/js/person.js +++ b/src/contact/js/person.js @@ -118,7 +118,7 @@ var Person = function(data) { }, photoURI: { value: data.hasOwnProperty('photoURI') ? data.photoURI : null, - writable: true, + writable: false, enumerable: true }, ringtoneURI: { diff --git a/src/content/content_instance.cc b/src/content/content_instance.cc index 8a3de66c..bc75361b 100644 --- a/src/content/content_instance.cc +++ b/src/content/content_instance.cc @@ -242,67 +242,11 @@ static void ScanDirectoryCallback(media_content_error_e error, void* user_data) common::Instance::PostMessage(cbData->instance, picojson::value(out).serialize().c_str()); } -// DEPRECATED CALLBACK. contentChangeCallback() is currently in use -static void changedContentV1Callback(media_content_error_e error, int pid, - media_content_db_update_item_type_e update_item, - media_content_db_update_type_e update_type, - media_content_type_e media_type, char* uuid, char* path, - char* mime_type, void* user_data) { - ScopeLogger("File change callback"); - - if (error != MEDIA_CONTENT_ERROR_NONE) { - LoggerE("Media content changed callback error: %d", (int)error); - return; - } - - if (update_item == MEDIA_ITEM_FILE) { - if (!uuid) { - LoggerE("Provided uuid is NULL, ignoring"); - return; - } - - ReplyCallbackData* cbData = static_cast<ReplyCallbackData*>(user_data); - - int ret; - picojson::value result = picojson::value(picojson::object()); - picojson::object& obj = result.get<picojson::object>(); - - if (update_type == MEDIA_CONTENT_INSERT || update_type == MEDIA_CONTENT_UPDATE) { - media_info_h media = NULL; - ret = media_info_get_media_from_db(uuid, &media); - if (ret == MEDIA_CONTENT_ERROR_NONE && media != NULL) { - picojson::object o; - - ContentToJson(media, o); - ReportSuccess(picojson::value(o), obj); - - if (update_type == MEDIA_CONTENT_INSERT) { - obj["state"] = picojson::value("oncontentadded"); - } else { - obj["state"] = picojson::value("oncontentupdated"); - } - - media_info_destroy(media); - } - } else { - ReportSuccess(picojson::value(std::string(uuid)), obj); - obj["state"] = picojson::value("oncontentremoved"); - } - - obj["listenerId"] = cbData->args.get("listenerId"); - common::Instance::PostMessage(cbData->instance, result.serialize().c_str()); - } else { - LoggerD("Media item is not a file, skipping."); - return; - } -} - -// DEPRECATED CALLBACK. contentChangeCallback() is currently in use -static void changedContentV2Callback(media_content_error_e error, int pid, - media_content_db_update_item_type_e update_item, - media_content_db_update_type_e update_type, - media_content_type_e media_type, char* uuid, char* path, - char* mime_type, void* user_data) { +static void changedContentCallback(media_content_error_e error, int pid, + media_content_db_update_item_type_e update_item, + media_content_db_update_type_e update_type, + media_content_type_e media_type, char* uuid, char* path, + char* mime_type, void* user_data) { ScopeLogger("Directory change callback"); if (error != MEDIA_CONTENT_ERROR_NONE) { @@ -566,6 +510,11 @@ void ContentInstance::ContentManagerScanfile(const picojson::value& args, picojs CHECK_EXIST(args, "callbackId", out) CHECK_EXIST(args, "contentURI", out) + const std::string& contentURI = args.get("contentURI").get<std::string>(); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(contentURI); + + CHECK_STORAGE_ACCESS(real_path, &out); + double callbackId = args.get("callbackId").get<double>(); auto cbData = std::shared_ptr<ReplyCallbackData>(new ReplyCallbackData); cbData->callbackId = callbackId; @@ -588,6 +537,11 @@ void ContentInstance::ContentManagerScanDirectory(const picojson::value& args, CHECK_EXIST(args, "contentDirURI", out) CHECK_EXIST(args, "recursive", out) + const std::string& contentURI = args.get("contentDirURI").get<std::string>(); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(contentURI); + + CHECK_STORAGE_ACCESS(real_path, &out); + ReplyCallbackData* cbData = new ReplyCallbackData; cbData->callbackId = args.get("callbackId").get<double>(); cbData->instance = this; @@ -678,17 +632,9 @@ void ContentInstance::ContentManagerSetchangelistener(const picojson::value& arg listener_data_->cbType = ContentManagerErrorCallback; } - if (ContentManager::getInstance() - ->setChangeListener(changedContentV1Callback, static_cast<void*>(listener_data_)) - .IsError()) { - LogAndReportError(common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, - "The callback did not register properly"), - &out); - } - if (nullptr == noti_handle_) { // To remain consistency with the previous implementation if (ContentManager::getInstance() - ->addChangeListener(¬i_handle_, changedContentV2Callback, + ->addChangeListener(¬i_handle_, changedContentCallback, static_cast<void*>(listener_data_)) .IsError()) { LogAndReportError(common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, @@ -708,9 +654,6 @@ void ContentInstance::ContentManagerUnsetchangelistener(const picojson::value& a // CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out); CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out); - if (ContentManager::getInstance()->unSetChangeListener().IsError()) { - LoggerD("unsuccesfull deregistering of callback"); - } if (ContentManager::getInstance()->removeChangeListener(noti_handle_).IsError()) { LoggerD("unsuccesfull deregistering of callback"); } else { @@ -791,17 +734,37 @@ void ContentInstance::ContentManagerCreateThumbnail(const picojson::value& args, picojson::object& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out); - common::PlatformResult result = common::PlatformResult(common::ErrorCode::NO_ERROR); - - if (ContentManager::getInstance()->isConnected()) { - result = ContentManager::getInstance()->createThumbnail(args); - } else { - result = LogAndCreateResult(common::ErrorCode::UNKNOWN_ERR, "DB Connection is failed."); - } - if (!result) { - LogAndReportError(result, &out, ("Failed to create a thumbnail")); - common::Instance::PostMessage(this, picojson::value(out).serialize().c_str()); + double callback_id = args.get("callbackId").get<double>(); + + if (not ContentManager::getInstance()->isConnected()) { + TaskQueue::GetInstance().Async([this, callback_id]() { + PlatformResult result = + LogAndCreateResult(common::ErrorCode::ABORT_ERR, "DB Connection is not established."); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj.emplace("callbackId", picojson::value(callback_id)); + LogAndReportError(result, &obj, ("DB Connection is not established.")); + Instance::PostMessage(this, response.serialize().c_str()); + }); + return; } + + const std::string& id = args.get("id").get<std::string>(); + auto work = [this, id, callback_id]() { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj.emplace("callbackId", picojson::value(callback_id)); + auto result = ContentManager::getInstance()->createThumbnail(id, &obj); + if (result) { + ReportSuccess(obj); + } else { + LogAndReportError(result, &obj); + } + Instance::PostMessage(this, response.serialize().c_str()); + }; + TaskQueue::GetInstance().Async(work); + ReportSuccess(out); } void ContentInstance::ContentManagerPlaylistAdd(const picojson::value& args, @@ -945,6 +908,11 @@ void ContentInstance::ContentManagerAudioGetLyrics(const picojson::value& args, picojson::object& out) { ScopeLogger(); + const std::string& contentURI = args.get("contentURI").get<std::string>(); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(contentURI); + + CHECK_STORAGE_ACCESS(real_path, &out); + picojson::object lyrics; if (ContentManager::getInstance()->isConnected()) { int ret = ContentManager::getInstance()->getLyrics(args, lyrics); @@ -1013,6 +981,10 @@ void ContentInstance::PlaylistSetThumbnailUri(const picojson::value& args, picoj CHECK_EXIST(args, "uri", out) int id = static_cast<int>(args.get("id").get<double>()); std::string uri = args.get("uri").get<std::string>(); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(uri); + + CHECK_STORAGE_ACCESS(real_path, &out); + ret = ContentManager::getInstance()->setThumbnailUri(id, uri); if (ret != MEDIA_CONTENT_ERROR_NONE) { LogAndReportError(ContentManager::getInstance()->convertError(ret), &out); diff --git a/src/content/content_manager.cc b/src/content/content_manager.cc index 9c069ed1..86e2655d 100644 --- a/src/content/content_manager.cc +++ b/src/content/content_manager.cc @@ -644,38 +644,6 @@ static bool playlist_content_member_cb(int playlist_member_id, media_info_h medi return true; } -void CreateThumbnailCallback(media_content_error_e err, const char* path, void* user_data) { - ScopeLogger(); - - std::unique_ptr<CreateThumbnailCallbackData> callback_data_ptr( - (CreateThumbnailCallbackData*)user_data); - if (0 == callback_data_ptr->callback_id) { - LoggerD("Callback id is 0"); - return; - } - - if (!(ContentManager::getInstance()->getContentInstance())) { - // There is not instance already - LoggerD("There is not instance now"); - return; - } - - picojson::object out; - - out["callbackId"] = picojson::value(callback_data_ptr->callback_id); - - if (MEDIA_CONTENT_ERROR_NONE == err) { - out["result"] = picojson::value(std::string(path)); - ReportSuccess(out); - } else { - PlatformResult result = ContentManager::getInstance()->convertError(err); - LogAndReportError(result, &out, ("Failed to create a thumbnail")); - } - - common::Instance::PostMessage(ContentManager::getInstance()->getContentInstance(), - picojson::value(out).serialize().c_str()); -} - ContentManager::ContentManager() { ScopeLogger("ContentManager called"); if (media_content_connect() == MEDIA_CONTENT_ERROR_NONE) { @@ -910,31 +878,6 @@ PlatformResult ContentManager::removeChangeListener(media_content_noti_h noti_ha } } -PlatformResult ContentManager::setChangeListener(media_content_db_update_cb callback, - void* user_data) { - ScopeLogger(); - - int ret = media_content_set_db_updated_cb(callback, user_data); - if (ret != MEDIA_CONTENT_ERROR_NONE) { - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "registering the listener is failed.", - ("Failed: registering the listener is failed %d (%s)", ret, get_error_message(ret))); - } - return PlatformResult(ErrorCode::NO_ERROR); -} - -PlatformResult ContentManager::unSetChangeListener() { - ScopeLogger(); - - int ret = media_content_unset_db_updated_cb(); - if (ret != MEDIA_CONTENT_ERROR_NONE) { - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "unregistering the listener is failed.", - ("Failed: unregistering the listener is failed: %d (%s)", ret, get_error_message(ret))); - } - return PlatformResult(ErrorCode::NO_ERROR); -} - void ContentManager::createPlaylist(std::string name, const std::shared_ptr<ReplyCallbackData>& user_data) { ScopeLogger(); @@ -1602,27 +1545,35 @@ int ContentManager::getNumberOfTracks(int id, int* result) { return MEDIA_CONTENT_ERROR_NONE; } -common::PlatformResult ContentManager::createThumbnail(const picojson::value& args) { +common::PlatformResult ContentManager::createThumbnail(const std::string& id, + picojson::object* obj) { ScopeLogger(); - std::unique_ptr<CreateThumbnailCallbackData> callback_data_ptr{new CreateThumbnailCallbackData}; - callback_data_ptr->callback_id = args.get("callbackId").get<double>(); - std::string id = args.get("id").get<std::string>(); - - int ret = media_info_get_media_from_db(id.c_str(), &callback_data_ptr->media); - if (MEDIA_CONTENT_ERROR_NONE != ret && nullptr == callback_data_ptr->media) { - return LogAndCreateResult(ErrorCode::ABORT_ERR, "Getting media is failed.", - ("Getting media is failed: %d (%s)", ret, get_error_message(ret))); + media_info_h media_h = nullptr; + int ret = media_info_get_media_from_db(id.c_str(), &media_h); + if (MEDIA_CONTENT_ERROR_NONE != ret || nullptr == media_h) { + return LogAndCreateResult(ErrorCode::ABORT_ERR, "Getting media failed.", + ("Getting media failed: %d (%s)", ret, get_error_message(ret))); } + SCOPE_EXIT { + media_info_destroy(media_h); + }; - ret = media_info_create_thumbnail(callback_data_ptr->media, CreateThumbnailCallback, - static_cast<void*>(callback_data_ptr.get())); - + ret = media_info_generate_thumbnail(media_h); if (MEDIA_CONTENT_ERROR_NONE != ret) { return LogAndCreateResult(ErrorCode::ABORT_ERR, "Creating thumbnail failed.", ("Creating thumbnail failed: %d (%s)", ret, get_error_message(ret))); } - callback_data_ptr.release(); + + char* path = nullptr; + ret = media_info_get_thumbnail_path(media_h, &path); + if (MEDIA_CONTENT_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::ABORT_ERR, "Creating thumbnail succeeded, but failed to get thumbnail path.", + ("Getting thumbnail path failed: %d (%s)", ret, get_error_message(ret))); + } + obj->emplace("result", picojson::value(path)); + std::unique_ptr<char[], decltype(&free)>(path, free); return PlatformResult(ErrorCode::NO_ERROR); } diff --git a/src/content/content_manager.h b/src/content/content_manager.h index 1801ec41..af8efa76 100644 --- a/src/content/content_manager.h +++ b/src/content/content_manager.h @@ -38,14 +38,6 @@ typedef std::unique_ptr<std::remove_pointer<media_playlist_h>::type, void (*)(me void ContentToJson(media_info_h info, picojson::object& o); void ContentDirToJson(media_folder_h folder, picojson::object& o); -struct CreateThumbnailCallbackData { - double callback_id; - media_info_h media; - ~CreateThumbnailCallbackData() { - media_info_destroy(media); - } -}; - class ContentManager { public: virtual ~ContentManager(); @@ -65,8 +57,6 @@ class ContentManager { common::PlatformResult addChangeListener(media_content_noti_h* noti_handle, media_content_db_update_cb callback, void* user_data); common::PlatformResult removeChangeListener(media_content_noti_h noti_handle); - common::PlatformResult setChangeListener(media_content_db_update_cb callback, void* user_data); - common::PlatformResult unSetChangeListener(); // Lyrics int getLyrics(const picojson::value& args, picojson::object& result); @@ -93,7 +83,7 @@ class ContentManager { // thumbnail int getThumbnailUri(int id, std::string* result); int setThumbnailUri(int id, const std::string& thb_uri); - common::PlatformResult createThumbnail(const picojson::value& args); + common::PlatformResult createThumbnail(const std::string& id, picojson::object* obj); private: // int setContent(media_info_h media, picojson::value content); diff --git a/src/content/js/datatypes.js b/src/content/js/datatypes.js index 1f8ec435..9ddfd0dd 100755 --- a/src/content/js/datatypes.js +++ b/src/content/js/datatypes.js @@ -483,6 +483,10 @@ function AudioContent(data) { if (native_.isFailure(result)) { utils_.log('Getting lyrics failed for ' + data.contentURI); + var error_object = native_.getErrorObject(result); + if (WebAPIException.SECURITY_ERR == error_object.code) { + throw error_object; + } return; } diff --git a/src/content/js/manager.js b/src/content/js/manager.js index 26f2964d..3c585458 100755 --- a/src/content/js/manager.js +++ b/src/content/js/manager.js @@ -406,9 +406,8 @@ ContentManager.prototype.removeChangeListener = function() { ContentManager.prototype.setChangeListener = function(changeCallback) { privUtils_.warn( - 'DEPRECATION WARNING: setChangeListener() is deprecated ' + - 'and will be removed from next release. ' + - 'Use addChangeListener() instead.' + 'DEPRECATION WARNING: setChangeListener() is deprecated and will be removed ' + + 'from next release. Use addChangeListener() instead.' ); var args = validator_.validateArgs(arguments, [ @@ -455,9 +454,8 @@ ContentManager.prototype.setChangeListener = function(changeCallback) { ContentManager.prototype.unsetChangeListener = function() { privUtils_.warn( - 'DEPRECATION WARNING: unsetChangeListener() ' + - 'is deprecated and will be removed from next release. ' + - 'Use removeChangeListener() instead.' + 'DEPRECATION WARNING: unsetChangeListener() is deprecated and will be removed ' + + 'from next release. Use removeChangeListener() instead.' ); var data = {}; diff --git a/src/content/js/playlist.js b/src/content/js/playlist.js index 3b8ab05a..14ff1fab 100755 --- a/src/content/js/playlist.js +++ b/src/content/js/playlist.js @@ -75,19 +75,19 @@ function Playlist(data) { throw native_.getErrorObject(result); } var res = native_.getResultObject(result); - //CoreAPI not support empty thumbnail, - //so one space must be used instead null thumbnail + //CoreAPI not support empty thumbnail, so one space must be used instead + //null thumbnail return res === ' ' ? null : res; }, set: function(v) { var thumbnailURI = converter_.toString(v, true); if (type_.isNullOrUndefined(thumbnailURI)) { - //CoreAPI not support empty thumbnail, - //so one space must be used instead null thumbnail + //CoreAPI not support empty thumbnail, so one space must be used + //instead null thumbnail thumbnailURI = ' '; } - //TODO probably thumbnailURI should be converted here - //to absolute uri in case of virtual + //TODO probably thumbnailURI should be converted here to absolute uri + //in case of virtual var result = native_.callSync('ContentPlaylist_setThumbnailUri', { id: Number(id), uri: thumbnailURI diff --git a/src/datacontrol/datacontrol_api.js b/src/datacontrol/datacontrol_api.js index 94bf83b9..18ff77fc 100755 --- a/src/datacontrol/datacontrol_api.js +++ b/src/datacontrol/datacontrol_api.js @@ -249,12 +249,10 @@ var getDataControlConsumer = function(providerId, dataId, type) { returnObject = new MappedDataControlConsumer(); } - // read only property - SetReadOnlyProperty(returnObject, 'type', args.type); - // read only property - SetReadOnlyProperty(returnObject, 'providerId', args.providerId); - // read only property - SetReadOnlyProperty(returnObject, 'dataId', args.dataId); + SetReadOnlyProperty(returnObject, 'type', args.type); // read only property + SetReadOnlyProperty(returnObject, 'providerId', args.providerId); // read only + // property + SetReadOnlyProperty(returnObject, 'dataId', args.dataId); // read only property return returnObject; }; diff --git a/src/download/download_instance.cc b/src/download/download_instance.cc index f33102c5..0ebd945a 100644 --- a/src/download/download_instance.cc +++ b/src/download/download_instance.cc @@ -571,6 +571,11 @@ void DownloadInstance::DownloadManagerStart(const picojson::value& args, picojso } } + CHECK_STORAGE_ACCESS(di_ptr->destination.empty() + ? common::FilesystemProvider::Create().GetRealPath("downloads") + : di_ptr->destination, + &out); + if (!args.get("fileName").is<picojson::null>()) { if (args.get("fileName").is<std::string>() && args.get("fileName").get<std::string>() != "") { di_ptr->file_name = args.get("fileName").get<std::string>(); diff --git a/src/exif/exif_api.js b/src/exif/exif_api.js index bd732e18..841ad213 100644 --- a/src/exif/exif_api.js +++ b/src/exif/exif_api.js @@ -231,7 +231,12 @@ ExifManager.prototype.getExifInfo = function() { } }; - native_.call('ExifManager_getExifInfo', { uri: args.uri }, callback); + var result = native_.call('ExifManager_getExifInfo', { uri: args.uri }, callback); + + if (native_.isFailure(result)) { + // since tizen 5.0 the only possible error type here is SecurityError + throw native_.getErrorObject(result); + } }; ExifManager.prototype.saveExifInfo = function() { @@ -257,7 +262,7 @@ ExifManager.prototype.saveExifInfo = function() { } ]); - if (!_isValidAbsoluteURI(args.exifInfo.uri)) { + if (!args.exifInfo.uri || !_isValidAbsoluteURI(args.exifInfo.uri)) { setTimeout(function() { native_.callIfPossible( args.errorCallback, @@ -277,7 +282,12 @@ ExifManager.prototype.saveExifInfo = function() { } }; - native_.call('ExifManager_saveExifInfo', json, callback); + var result = native_.call('ExifManager_saveExifInfo', json, callback); + + if (native_.isFailure(result)) { + // since tizen 5.0 the only possible error type here is SecurityError + throw native_.getErrorObject(result); + } }; ExifManager.prototype.getThumbnail = function() { @@ -321,22 +331,18 @@ ExifManager.prototype.getThumbnail = function() { } }; - native_.call('ExifManager_getThumbnail', { uri: args.uri }, _callback); + var result = native_.call('ExifManager_getThumbnail', { uri: args.uri }, _callback); + + if (native_.isFailure(result)) { + // since tizen 5.0 the only possible error type here is SecurityError + throw native_.getErrorObject(result); + } }; -tizen.ExifInformation = function() { +tizen.ExifInformation = function(exifInitDict) { validator_.isConstructorCall(this, tizen.ExifInformation); - var args = validator_.validateArgs(arguments, [ - { - name: 'ExifInitDict', - type: validator_.Types.DICTIONARY, - optional: true, - nullable: false - } - ]); - - var uri_ = null, + var uri_ = '', width_ = null, height_ = null, deviceMaker_ = null, @@ -356,16 +362,6 @@ tizen.ExifInformation = function() { gpsTime_ = null, userComment_ = null; - var exifInitDict = args.ExifInitDict; - if (exifInitDict) { - if (exifInitDict.uri === null) { - throw new WebAPIException( - WebAPIException.INVALID_VALUES_ERR, - 'Parameter "uri" is required.' - ); - } - } - Object.defineProperties(this, { uri: { get: function() { @@ -526,9 +522,8 @@ tizen.ExifInformation = function() { }, set: function(v) { if (!type_.isUndefined(v)) { - if (v === null || v instanceof tizen.SimpleCoordinates) { + if (v === null || v instanceof tizen.SimpleCoordinates) gpsLocation_ = v; - } } }, enumerable: true @@ -562,9 +557,8 @@ tizen.ExifInformation = function() { }, set: function(v) { if (!type_.isUndefined(v)) { - if (v === null || v instanceof Date || v instanceof tizen.TZDate) { + if (v === null || v instanceof Date || v instanceof tizen.TZDate) gpsTime_ = v; - } } } }, diff --git a/src/exif/exif_information.cc b/src/exif/exif_information.cc index 9a07286b..6ed1bcc5 100644 --- a/src/exif/exif_information.cc +++ b/src/exif/exif_information.cc @@ -333,7 +333,7 @@ void ExifInformation::setGpsProcessingMethod(const std::string& type, const std::string& processing_method) { if (type != EXIF_UNDEFINED_TYPE_ASCII && type != EXIF_UNDEFINED_TYPE_JIS && type != EXIF_UNDEFINED_TYPE_UNICODE && type != EXIF_UNDEFINED_TYPE_UNDEFINED) { - LoggerW("Trying to set invalid GPSProcessingMethod type: [%s] len:%d", type.c_str(), + LoggerW("Trying to set invalid GPSProcessingMethod type: [%s] len: %zu", type.c_str(), type.length()); return; } @@ -368,7 +368,7 @@ const std::string& ExifInformation::getUserCommentType() { void ExifInformation::setUserComment(const std::string& type, const std::string& user_comment) { if (type != EXIF_UNDEFINED_TYPE_ASCII && type != EXIF_UNDEFINED_TYPE_JIS && type != EXIF_UNDEFINED_TYPE_UNICODE && type != EXIF_UNDEFINED_TYPE_UNDEFINED) { - LoggerW("Trying to set invalid user comment type: [%s] len:%d", type.c_str(), type.length()); + LoggerW("Trying to set invalid user comment type: [%s] len: %zu", type.c_str(), type.length()); return; } @@ -689,7 +689,7 @@ PlatformResult ExifInformation::updateAttributesInExifData(ExifData* exif_data) } if (isSet(EXIF_INFORMATION_ATTRIBUTE_ISO_SPEED_RATINGS)) { std::vector<long long int> iso_ratings = getIsoSpeedRatings(); - LoggerD("Saving iso speed ratings count:%d", iso_ratings.size()); + LoggerD("Saving iso speed ratings count: %zu", iso_ratings.size()); ret = ExifTagSaver::saveToExif(iso_ratings, EXIF_FORMAT_SHORT, EXIF_TAG_ISO_SPEED_RATINGS, exif_data); if (!ret) { diff --git a/src/exif/exif_instance.cc b/src/exif/exif_instance.cc index 0687d700..28ae74c1 100644 --- a/src/exif/exif_instance.cc +++ b/src/exif/exif_instance.cc @@ -20,9 +20,11 @@ #include <libexif/exif-loader.h> #include <libexif/exif-utils.h> +#include <glib.h> #include <sstream> #include <string> +#include "common/filesystem/filesystem_provider.h" #include "common/logger.h" #include "common/platform_result.h" #include "common/task-queue.h" @@ -59,15 +61,18 @@ void ExifInstance::ExifManagerGetExifInfo(const picojson::value& args, picojson: ScopeLogger(); const std::string& uri = args.get("uri").get<std::string>(); + const std::string& file_path = ExifUtil::convertUriToPath(uri); const double callback_id = args.get("callbackId").get<double>(); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(file_path); + + CHECK_STORAGE_ACCESS(real_path, &out); + auto get = [=](const std::shared_ptr<JsonValue>& response) -> void { ScopeLogger("Entered into asynchronous function, get"); JsonValue result = JsonValue(JsonObject()); PlatformResult status(ErrorCode::NO_ERROR); - const std::string& file_path = ExifUtil::convertUriToPath(uri); - PlatformResult fileAvailability(common::tools::CheckFileAvailability(file_path)); if (!fileAvailability) { LogAndReportError(fileAvailability, &response->get<picojson::object>()); @@ -99,15 +104,20 @@ void ExifInstance::ExifManagerSaveExifInfo(const picojson::value& args, picojson ScopeLogger(); const double callback_id = args.get("callbackId").get<double>(); + + const std::string& file_uri = args.get("uri").get<std::string>(); + const std::string& file_path = ExifUtil::convertUriToPath(file_uri); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(file_path); + + CHECK_STORAGE_ACCESS(real_path, &out); + auto get = [=](const std::shared_ptr<JsonValue>& response) -> void { ScopeLogger("Entered into asynchronous function, get"); JsonValue result = JsonValue(JsonObject()); PlatformResult status(ErrorCode::NO_ERROR); ExifInformationPtr exifInfo(new ExifInformation(args)); - const std::string& uri = exifInfo->getUri(); - const std::string& path = ExifUtil::convertUriToPath(uri); - status = exifInfo->saveToFile(path); + status = exifInfo->saveToFile(file_path); if (status) ReportSuccess(result, response->get<picojson::object>()); @@ -131,12 +141,16 @@ void ExifInstance::ExifManagerGetThumbnail(const picojson::value& args, picojson ScopeLogger(); const std::string& uri = args.get("uri").get<std::string>(); + const std::string& file_path = ExifUtil::ExifUtil::convertUriToPath(uri); + const std::string& real_path = common::FilesystemProvider::Create().GetRealPath(file_path); + + CHECK_STORAGE_ACCESS(real_path, &out); + const double callback_id = args.get("callbackId").get<double>(); auto get = [=](const std::shared_ptr<JsonValue>& response) -> void { ScopeLogger("Entered into asynchronous function, get"); PlatformResult status(ErrorCode::NO_ERROR); - const std::string& file_path = ExifUtil::convertUriToPath(uri); JsonValue result = JsonValue(JsonObject()); JsonObject& result_obj = result.get<JsonObject>(); diff --git a/src/exif/exif_tag_saver.cc b/src/exif/exif_tag_saver.cc index 0d917669..74b8092f 100644 --- a/src/exif/exif_tag_saver.cc +++ b/src/exif/exif_tag_saver.cc @@ -31,7 +31,7 @@ void ExifTagSaver::removeExifEntryWithTag(const ExifTag tag, ExifData* exif_data ScopeLogger("tag: %d (%p)", tag, exif_data); ExifEntry* exif_entry = exif_data_get_entry(exif_data, tag); if (!exif_entry) { - LoggerE("Exif entry with tag: %d (0x%x) is not present", tag, tag); + LoggerE("Exif entry with tag: %d (0x%x) is not present", tag, (unsigned)tag); return; } diff --git a/src/exif/get_exif_info.cc b/src/exif/get_exif_info.cc index a5e4e81c..4cd21c48 100644 --- a/src/exif/get_exif_info.cc +++ b/src/exif/get_exif_info.cc @@ -85,7 +85,7 @@ bool DecomposeExifUndefined(ExifEntry* entry, std::string& type, std::string& va } if (entry->size < EXIF_UNDEFINED_TYPE_LENGTH) { - LoggerW("entry size is invalid %d < EXIF_UNDEFINED_TYPE_LENGTH", entry->size); + LoggerW("entry size is invalid %u < EXIF_UNDEFINED_TYPE_LENGTH", entry->size); return false; } @@ -313,7 +313,7 @@ PlatformResult GetExifInfo::ProcessEntry(ExifEntry* entry, ExifData* exif_data, result_obj->insert(pair); LoggerD("Setting ExifInformation gps longitude REF to: WEST"); } else { - LoggerW("Unknown longitude ref: %c (0x%x)", ref, static_cast<int>(ref)); + LoggerW("Unknown longitude ref: %c (0x%x)", ref, static_cast<unsigned>(ref)); } break; } @@ -357,7 +357,7 @@ PlatformResult GetExifInfo::ProcessEntry(ExifEntry* entry, ExifData* exif_data, result_obj->insert(pair); LoggerD("Setting ExifInformation gps latitude REF to: SOUTH"); } else { - LoggerW("Unknown latitude ref: %c (0x%x)", ref, static_cast<int>(ref)); + LoggerW("Unknown latitude ref: %c (0x%x)", ref, static_cast<unsigned>(ref)); } break; } @@ -390,7 +390,7 @@ PlatformResult GetExifInfo::ProcessEntry(ExifEntry* entry, ExifData* exif_data, // UNDEFINED - Any std::string type, value; if (DecomposeExifUndefined(entry, type, value)) { - LoggerD("Extracted GPSProcessingMethod: [%s], len:%d, type:%s", value.c_str(), + LoggerD("Extracted GPSProcessingMethod: [%s], len: %zu, type: %s", value.c_str(), value.length(), type.c_str()); pair = std::make_pair("gpsProcessingMethod", JsonValue(value)); result_obj->insert(pair); @@ -425,7 +425,7 @@ PlatformResult GetExifInfo::ProcessEntry(ExifEntry* entry, ExifData* exif_data, // UNDEFINED - Any std::string type, value; if (DecomposeExifUndefined(entry, type, value)) { - LoggerD("Extracted UserComment: [%s], len:%d, type:%s", value.c_str(), value.length(), + LoggerD("Extracted UserComment: [%s], len: %zu, type: %s", value.c_str(), value.length(), type.c_str()); pair = std::make_pair("userComment", JsonValue(value)); diff --git a/src/exif/jpeg_file.cc b/src/exif/jpeg_file.cc index 5fe598dc..8c5e4746 100644 --- a/src/exif/jpeg_file.cc +++ b/src/exif/jpeg_file.cc @@ -167,7 +167,7 @@ PlatformResult JpegFile::load(const std::string& path) { ("fseek failed with error! [%d]", res)); } - LoggerD("JPEG file: [%s] size:%d", path.c_str(), in_file_size); + LoggerD("JPEG file: [%s] size: %zu", path.c_str(), in_file_size); if (0 == in_file_size) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "JPEG file is invalid", ("Input file [%s] is empty!", path.c_str())); @@ -176,7 +176,7 @@ PlatformResult JpegFile::load(const std::string& path) { m_in_data = new (std::nothrow) unsigned char[in_file_size]; if (!m_in_data) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Memory allocation failed", - ("Couldn't allocate buffer with size: %d", in_file_size)); + ("Couldn't allocate buffer with size: %zu", in_file_size)); } m_in_data_size = in_file_size; @@ -185,7 +185,7 @@ PlatformResult JpegFile::load(const std::string& path) { if (read_bytes != m_in_data_size) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not read JPEG file", - ("Couldn't read all: %d bytes. Read only: %u bytes!", m_in_data_size, read_bytes)); + ("Couldn't read all: %zu bytes. Read only: %zu bytes!", m_in_data_size, read_bytes)); } if (fclose(m_in_file) == EOF) { @@ -241,7 +241,7 @@ common::PlatformResult JpegFile::generateListOfSections() { m_padding_data_size = 0; for (size_t offset = 0, iterration = 0; offset < m_in_data_size; ++iterration) { - LoggerD("offset:%u | Starting iteration: %u", offset, iterration); + LoggerD("offset: %zu | Starting iteration: %zu", offset, iterration); const std::size_t search_len = 10; std::size_t search_offset = 0; for (search_offset = 0; search_offset < search_len; ++search_offset) { @@ -254,7 +254,7 @@ common::PlatformResult JpegFile::generateListOfSections() { if (search_len == search_offset) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "JPEG file is invalid", - ("offset:%u | Couldn't find marker! RAW DATA:{%s}", offset, + ("offset: %zu | Couldn't find marker! RAW DATA:{%s}", offset, getPartOfFile(offset, 0, 10).c_str())); } @@ -262,16 +262,16 @@ common::PlatformResult JpegFile::generateListOfSections() { unsigned char* section_begin = m_in_data + section_offset; offset = section_offset; // Move to section begin - LoggerD("offset:%u | Moved to section begin", offset); + LoggerD("offset: %zu | Moved to section begin", offset); if (!isJpegMarker(section_begin[1])) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "JPEG file is invalid", - ("offset:%u | Is not valid marker: 0x%x RAW DATA:{%s}", offset, + ("offset: %zu | Is not valid marker: 0x%x RAW DATA:{%s}", offset, section_begin[1], getPartOfFile(section_offset, 0, 4).c_str())); } const JpegMarker cur_marker = castToJpegMarker(section_begin[1]); - LoggerD("offset:%u | Found valid marker: 0x%x RAW DATA:{%s}", offset, cur_marker, + LoggerD("offset: %zu | Found valid marker: 0x%x RAW DATA:{%s}", offset, (unsigned)cur_marker, getPartOfFile(section_offset, 0, 4).c_str()); offset += 2; // Read 0xffxx marker tag - 2 bytes @@ -290,7 +290,7 @@ common::PlatformResult JpegFile::generateListOfSections() { section->type = cur_marker; m_sections.push_back(section); if (cur_marker == JPEG_MARKER_SOI || cur_marker == JPEG_MARKER_EOI) { - LoggerD("offset:%u | Found: %s marker, moving to next marker at:%u", section_offset, + LoggerD("offset: %zu | Found: %s marker, moving to next marker at: %zu", section_offset, ((cur_marker == JPEG_MARKER_SOI) ? "SOI" : "EOI"), offset); if (cur_marker == JPEG_MARKER_EOI && m_padding_data != NULL) { @@ -317,23 +317,23 @@ common::PlatformResult JpegFile::generateListOfSections() { // size 2 bytes const long section_data_len = total_section_len - 2; - LoggerD("offset:%u tag:0x%x | Read total_section_len:%ld (data len:%ld)", section_offset, - cur_marker, total_section_len, section_data_len); + LoggerD("offset: %zu tag:0x%x | Read total_section_len: %ld (data len: %ld)", section_offset, + (unsigned)cur_marker, total_section_len, section_data_len); offset += 2; // Read data size - 2 bytes if (total_section_len < 0) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "JPEG file is invalid", - ("offset:%u tag:0x%x | Error: total_section_len is: %ld < 0", - offset, cur_marker, total_section_len)); + ("offset: %zu tag:0x%x | Error: total_section_len is: %ld < 0", + offset, (unsigned)cur_marker, total_section_len)); } if (section_offset + 2 + total_section_len > m_in_data_size) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "JPEG file is invalid", - ("offset:%u tag:0x%x | Error: current section offset:%u" - " + 2 + total_section_len:%ld = %lu is greater then file size:%u", - offset, cur_marker, section_offset, total_section_len, + ("offset: %zu tag:0x%x | Error: current section offset: %zu" + " + 2 + total_section_len: %ld = %lu is greater then file size: %zu", + offset, (unsigned)cur_marker, section_offset, total_section_len, section_offset + total_section_len, m_in_data_size)); } @@ -342,12 +342,12 @@ common::PlatformResult JpegFile::generateListOfSections() { section->exif_data = exif_data_new_from_data(section_begin, exif_data_size); LoggerD( - "offset:%u tag:0x%x | Loading exif from offset:%u" - " len:%u exif_data_new_from_data returned: %p", - offset, cur_marker, section_offset, exif_data_size, section->exif_data); + "offset: %zu tag:0x%x | Loading exif from offset: %zu" + " len: %u exif_data_new_from_data returned: %p", + offset, (unsigned)cur_marker, section_offset, exif_data_size, section->exif_data); if (!section->exif_data) { - LoggerW("offset:%d tag:0x%x | Couldn't load Exif!", offset, cur_marker); + LoggerW("offset: %zu tag:0x%x | Couldn't load Exif!", offset, (unsigned)cur_marker); } } @@ -367,9 +367,9 @@ common::PlatformResult JpegFile::generateListOfSections() { // -2 (exclude ending EOI marker (2 bytes) std::size_t image_size = m_in_data_size - image_data_offset - 2; LoggerW( - "offset:%d tag:0x%x" - " | Image data offset:%u Estimated image size:%u", - offset, cur_marker, image_data_offset, image_size); + "offset: %zu tag:0x%x" + " | Image data offset: %zu Estimated image size: %zu", + offset, (unsigned)cur_marker, image_data_offset, image_size); m_image_data = m_in_data + image_data_offset; @@ -384,31 +384,32 @@ common::PlatformResult JpegFile::generateListOfSections() { "JPEG file contains image data stream: image_size+= 2"); image_size += 2; // Skip expected EOI tag which is not present } else { - LoggerD("EOI tag found at offset: %d from SOS data", eoi_tag_index); + LoggerD("EOI tag found at offset: %zu from SOS data", eoi_tag_index); if (eoi_tag_index != image_size) { LoggerW( - "Estimated image size:%d doesn't match EOI tag index:%d" - " delta:%d", + "Estimated image size: %zu doesn't match EOI tag index: %zu" + " delta: %zu", image_size, eoi_tag_index, image_size - eoi_tag_index); - LoggerW("Setting image_size to EOI tag: %u", eoi_tag_index); + LoggerW("Setting image_size to EOI tag: %zu", eoi_tag_index); image_size = eoi_tag_index; m_padding_data = m_image_data + image_size + 2; // (skip EOI tag) m_padding_data_size = (m_in_data + m_in_data_size) - m_padding_data; - LoggerW("Saving padding data from offset:%d with size:%d", m_padding_data - m_in_data, - m_padding_data_size); + LoggerW("Saving padding data from offset: %td with size: %zu", + m_padding_data - m_in_data, m_padding_data_size); } } m_image_size = image_size; offset = image_data_offset + image_size; - LoggerD("offset:%u tag:0x%x | SOS Offset moved to next marker", offset, cur_marker); + LoggerD("offset: %zu tag:0x%x | SOS Offset moved to next marker", offset, + (unsigned)cur_marker); } else { offset += section_data_len; - LoggerD("offset:%u tag:0x%x | Offset moved to next marker", offset, cur_marker); + LoggerD("offset: %zu tag:0x%x | Offset moved to next marker", offset, (unsigned)cur_marker); } } } @@ -419,7 +420,7 @@ common::PlatformResult JpegFile::generateListOfSections() { bool JpegFile::searchForTagInBuffer(const unsigned char* buffer_start, const unsigned char* buffer_end, const JpegMarker marker, std::size_t& out_index) { - ScopeLogger("start:%p end:%p marker:0x%x", buffer_start, buffer_end, marker); + ScopeLogger("start: %p end: %p marker:0x%x", buffer_start, buffer_end, (unsigned)marker); if (!buffer_start) { LoggerE("buffer_start is NULL"); @@ -436,7 +437,7 @@ bool JpegFile::searchForTagInBuffer(const unsigned char* buffer_start, return false; } - LoggerD("Bytes to scan: %d", static_cast<size_t>(buffer_end - buffer_start)); + LoggerD("Bytes to scan: %zu", static_cast<size_t>(buffer_end - buffer_start)); const unsigned char marker_uchar = static_cast<unsigned char>(marker); for (const unsigned char* ptr = buffer_start; ptr < buffer_end; ++ptr) { @@ -520,7 +521,7 @@ ExifData* JpegFile::getExifData() { } PlatformResult JpegFile::saveToFile(const std::string& out_path) { - ScopeLogger("out_path:%s", out_path.c_str()); + ScopeLogger("out_path: %s", out_path.c_str()); PlatformResult status = saveToFilePriv(out_path); if (status) return status; @@ -549,7 +550,7 @@ PlatformResult JpegFile::saveToFile(const std::string& out_path) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Couldn't restore whole file", ("Couldn't restore whole JPEG! " - "Only %d of %d bytes have been wrote!", + "Only %zu of %zu bytes have been wrote!", bytes_wrote, m_in_data_size)); } if (EOF == fclose(outf)) { @@ -563,7 +564,7 @@ PlatformResult JpegFile::saveToFile(const std::string& out_path) { } PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { - ScopeLogger("out_path:%s", out_path.c_str()); + ScopeLogger("out_path: %s", out_path.c_str()); m_out_file = fopen(out_path.c_str(), "wb"); if (!m_out_file) { @@ -580,7 +581,7 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { JpegFileSectionPtr cur = *it; const JpegMarker cur_marker = cur->type; - LoggerD("offset:%d | Section: %d marker 0x%x", offset, section_index, cur_marker); + LoggerD("offset: %zu | Section: %d marker 0x%x", offset, section_index, (unsigned)cur_marker); std::size_t bytes_to_write = 0; std::size_t bytes_wrote = 0; @@ -604,15 +605,15 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Could not save Exif in JPEG file"); } - LoggerD("offset:%d | Generated Exif RAW Data length:%d", offset, exif_output_size); + LoggerD("offset: %zu | Generated Exif RAW Data length: %u", offset, exif_output_size); exif_output_data.reset(tmp); if (exif_output_size > MAX_AVAILABLE_JPEG_SECTION_DATA_SIZE) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Exif data is to big to be saved in JPEG file", - ("exif_output_size:%d is greater then maximum JPEG section" - "data block size: %d", + ("exif_output_size: %u is greater then maximum JPEG section" + "data block size: %u", exif_output_size, MAX_AVAILABLE_JPEG_SECTION_DATA_SIZE)); } section_size += exif_output_size; @@ -627,9 +628,9 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { } LoggerD( - "offset:%d | Writing section:" - " marker:0x%x size:%d", - offset, cur_marker, cur->size); + "offset: %zu | Writing section:" + " marker:0x%x size: %d", + offset, (unsigned)cur_marker, cur->size); bytes_wrote = fwrite(tmp_buf, 1, bytes_to_write, m_out_file); offset += bytes_wrote; @@ -637,11 +638,11 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { if (bytes_wrote != bytes_to_write) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not write JPEG file", - ("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write, bytes_wrote)); + ("Couldn't wrote %zu bytes! Only %zu bytes wrote", bytes_to_write, bytes_wrote)); } if (write_section_data && cur->size > 0) { - LoggerD("offset:%d | Writing data with length:%d", offset, cur->size); + LoggerD("offset: %zu | Writing data with length: %d", offset, cur->size); bytes_to_write = cur->size; bytes_wrote = fwrite(cur->data_ptr, 1, bytes_to_write, m_out_file); @@ -650,12 +651,12 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { if (bytes_wrote != bytes_to_write) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not write JPEG file", - ("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write, bytes_wrote)); + ("Couldn't wrote %zu bytes! Only %zu bytes wrote", bytes_to_write, bytes_wrote)); } } if (write_exif_data && exif_output_data && exif_output_size > 0) { - LoggerD("offset:%d | Writing new exif data with length:%d", offset, exif_output_size); + LoggerD("offset: %zu | Writing new exif data with length: %u", offset, exif_output_size); bytes_to_write = exif_output_size; bytes_wrote = fwrite(exif_output_data.get(), 1, bytes_to_write, m_out_file); @@ -664,12 +665,12 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { if (bytes_wrote != bytes_to_write) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not write JPEG file", - ("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write, bytes_wrote)); + ("Couldn't wrote %zu bytes! Only %zu bytes wrote", bytes_to_write, bytes_wrote)); } } if (JPEG_MARKER_SOS == cur_marker) { - LoggerD("offset:%d | Writing image data stream with lenght:%d", offset, m_image_size); + LoggerD("offset: %zu | Writing image data stream with lenght: %zu", offset, m_image_size); bytes_to_write = m_image_size; bytes_wrote = fwrite(m_image_data, 1, bytes_to_write, m_out_file); @@ -678,19 +679,19 @@ PlatformResult JpegFile::saveToFilePriv(const std::string& out_path) { if (bytes_wrote != bytes_to_write) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not write JPEG file", - ("Couldn't wrote %d bytes! Only %d bytes wrote", bytes_to_write, bytes_wrote)); + ("Couldn't wrote %zu bytes! Only %zu bytes wrote", bytes_to_write, bytes_wrote)); } } } if (m_padding_data && m_padding_data_size > 0) { - LoggerD("Padding data exists and contains:%d bytes saving to JPEG file", m_padding_data_size); + LoggerD("Padding data exists and contains: %zu bytes saving to JPEG file", m_padding_data_size); const std::size_t bytes_wrote = fwrite(m_image_data, 1, m_padding_data_size, m_out_file); if (bytes_wrote != m_padding_data_size) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Could not write JPEG file", - ("Couldn't wrote %d bytes! Only %d bytes wrote", m_padding_data_size, bytes_wrote)); + ("Couldn't wrote %zu bytes! Only %zu bytes wrote", m_padding_data_size, bytes_wrote)); } } @@ -724,7 +725,7 @@ JpegFileSectionPtr JpegFile::getExifSection() { first_exif_section = cur; } else { LoggerW( - "Warning: found %d APP1/Exif sections -" + "Warning: found %zu APP1/Exif sections -" " only first is currently supported!", num_exif_sections); } diff --git a/src/feedback/feedback_api.js b/src/feedback/feedback_api.js index 8863c317..378d91ca 100755 --- a/src/feedback/feedback_api.js +++ b/src/feedback/feedback_api.js @@ -20,57 +20,9 @@ var types_ = validator_.Types; var type_ = xwalk.utils.type; var native_ = new xwalk.utils.NativeManager(extension); -var ExceptionMap = { - UnknownError: WebAPIException.UNKNOWN_ERR, - TypeMismatchError: WebAPIException.TYPE_MISMATCH_ERR, - InvalidValuesError: WebAPIException.INVALID_VALUES_ERR, - IOError: WebAPIException.IO_ERR, - ServiceNotAvailableError: WebAPIException.SERVICE_NOT_AVAILABLE_ERR, - SecurityError: WebAPIException.SECURITY_ERR, - NetworkError: WebAPIException.NETWORK_ERR, - NotSupportedError: WebAPIException.NOT_SUPPORTED_ERR, - NotFoundError: WebAPIException.NOT_FOUND_ERR, - InvalidAccessError: WebAPIException.INVALID_ACCESS_ERR, - AbortError: WebAPIException.ABORT_ERR, - QuotaExceededError: WebAPIException.QUOTA_EXCEEDED_ERR -}; - -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 WebAPIException(WebAPIException.UNKNOWN_ERR); - } - - if (result['status'] == 'success') { - if (result.hasOwnProperty('result')) { - return result['result']; - } - return true; - } else if (result['status'] == 'error') { - var err = result['error']; - if (err) { - if (ExceptionMap[err.name]) { - throw new WebAPIException(ExceptionMap[err.name], err.message); - } else { - throw new WebAPIException(WebAPIException.UNKNOWN_ERR, err.message); - } - } - return false; - } -} - -function SetReadOnlyProperty(obj, n, v) { - Object.defineProperty(obj, n, { value: v, writable: false }); -} - var FeedbackType = { TYPE_SOUND: 'TYPE_SOUND', - TYPE_VIBRATION: 'TYPE_VIBRATION', - NONE: 'NONE' + TYPE_VIBRATION: 'TYPE_VIBRATION' }; var FeedbackPattern = { @@ -124,7 +76,12 @@ FeedbackManager.prototype.isPatternSupported = function(pattern, type) { { name: 'pattern', type: types_.ENUM, values: Object.keys(FeedbackPattern) }, { name: 'type', type: types_.ENUM, values: Object.keys(FeedbackType) } ]); - return callNative('FeedbackManager_isPatternSupported', args); + + var result = native_.callSync('FeedbackManager_isPatternSupported', args); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); }; FeedbackManager.prototype.play = function(pattern, type) { @@ -148,15 +105,19 @@ FeedbackManager.prototype.play = function(pattern, type) { type: args.type ? args.type : 'any' }; - callNative('FeedbackManager_play', nativeParam); - return; + var result = native_.callSync('FeedbackManager_play', nativeParam); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } }; FeedbackManager.prototype.stop = function() { var args = validator_.validateArgs(arguments, []); - callNative('FeedbackManager_stop', args); - return; + var result = native_.callSync('FeedbackManager_stop', args); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } }; exports = new FeedbackManager(); diff --git a/src/feedback/feedback_manager.cc b/src/feedback/feedback_manager.cc index 3aba534a..07e03710 100644 --- a/src/feedback/feedback_manager.cc +++ b/src/feedback/feedback_manager.cc @@ -137,9 +137,11 @@ common::PlatformResult FeedbackManager::isPatternSupported(const std::string &pa } int ret = feedback_is_supported_pattern(type_e, pattern_e, patternStatus); - if (ret != FEEDBACK_ERROR_NONE) { + if (FEEDBACK_ERROR_NOT_SUPPORTED == ret) { + *patternStatus = false; + } else if (FEEDBACK_ERROR_NONE != ret) { LoggerE("isPatternSupported failed: %d", ret); - return CodeToResult(FEEDBACK_ERROR_NOT_SUPPORTED, "Pattern not supported"); + return CodeToResult(ret, "Checking pattern support failed"); } m_feedbackMapsPtr->setPatternSupport(pattern_e, type_e, *patternStatus); diff --git a/src/filesystem/filesystem_api.js b/src/filesystem/filesystem_api.js index cd2442ce..38a9b269 100755 --- a/src/filesystem/filesystem_api.js +++ b/src/filesystem/filesystem_api.js @@ -18,4 +18,5 @@ //= require('common.js'); //= require('file_stream.js'); //= require('file.js'); +//= require('file_handle.js'); //= require('file_system_manager.js'); diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 5df0cbbe..06d8095d 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -16,11 +16,13 @@ #include "filesystem/filesystem_instance.h" +#include <linux/limits.h> #include <cstdint> #include <functional> #include <stdexcept> #include <sys/stat.h> +#include <system_error> #include "common/logger.h" #include "common/picojson.h" #include "common/platform_exception.h" @@ -33,20 +35,76 @@ namespace extension { namespace filesystem { namespace { + +using common::tools::GetErrorString; + // The privileges that required in Filesystem API const std::string kPrivilegeFilesystemRead = "http://tizen.org/privilege/filesystem.read"; const std::string kPrivilegeFilesystemWrite = "http://tizen.org/privilege/filesystem.write"; + +const std::string kISOEncoding = "ISO-8859-1"; +const std::string kUTF8Encoding = "UTF-8"; + +std::string GetFopenMode(const picojson::value& args) { + ScopeLogger(); + const std::string& open_mode = args.get("openMode").get<std::string>(); + + if ("rw" == open_mode) { + return "r+"; + } else if ("rwo" == open_mode) { + return "w+"; + } + + return open_mode; +} + +bool WriteAccessRequested(const picojson::value& args) { + ScopeLogger(); + const std::string& open_mode = args.get("openMode").get<std::string>(); + + return "a" == open_mode || "rw" == open_mode || "rwo" == open_mode || "w" == open_mode; +} + +bool ReadAccessRequested(const picojson::value& args) { + ScopeLogger(); + const std::string& open_mode = args.get("openMode").get<std::string>(); + + return "r" == open_mode || "rw" == open_mode || "rwo" == open_mode; +} + +bool ShouldMakeParents(const picojson::value& args) { + ScopeLogger(); + const std::string& path = args.get("path").get<std::string>(); + struct stat buf {}; + if (FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(path), &buf)) { + return false; + } + bool make_parents = args.get("makeParents").get<bool>(); + const std::string& open_mode = args.get("openMode").get<std::string>(); + return make_parents && ("w" == open_mode || "a" == open_mode || "rwo" == open_mode); +} } using namespace common; using namespace extension::filesystem; +using namespace std::string_literals; + +FileHandle::~FileHandle() { + ScopeLogger(); + + if (file_handle && std::fclose(file_handle)) { + LoggerE("close file failed, error message: %s", GetErrorString(errno).c_str()); + } +} FilesystemInstance::FilesystemInstance() { ScopeLogger(); + using std::placeholders::_1; using std::placeholders::_2; #define REGISTER_SYNC(c, x) RegisterSyncHandler(c, std::bind(&FilesystemInstance::x, this, _1, _2)); + REGISTER_SYNC("File_stat", FileStat); REGISTER_SYNC("File_statSync", FileStatSync); REGISTER_SYNC("File_createSync", FileCreateSync); @@ -67,12 +125,38 @@ FilesystemInstance::FilesystemInstance() { REGISTER_SYNC("File_removeDirectory", RemoveDirectory); REGISTER_SYNC("File_copyTo", CopyTo); REGISTER_SYNC("FileSystemManager_getCanonicalPath", FileSystemManagerGetCanonicalPath); + + REGISTER_SYNC("FileSystemManager_openFile", FileSystemManagerOpenFile); + REGISTER_SYNC("FileSystemManager_createDirectory", FileSystemManagerCreateDirectory); + REGISTER_SYNC("FileSystemManager_deleteFile", FileSystemManagerDeleteFile); + REGISTER_SYNC("FileSystemManager_deleteDirectory", FileSystemManagerDeleteDirectory); + REGISTER_SYNC("FileSystemManager_copyFile", FileSystemManagerCopyFile); + REGISTER_SYNC("FileSystemManager_copyDirectory", FileSystemManagerCopyDirectory); + REGISTER_SYNC("FileSystemManager_moveFile", FileSystemManagerMoveFile); + REGISTER_SYNC("FileSystemManager_moveDirectory", FileSystemManagerMoveDirectory); + REGISTER_SYNC("FileSystemManager_rename", FileSystemManagerRename); + REGISTER_SYNC("FileSystemManager_listDirectory", FileSystemManagerListDirectory); + REGISTER_SYNC("FileSystemManager_isFile", FileSystemManagerIsFile); + REGISTER_SYNC("FileSystemManager_isDirectory", FileSystemManagerIsDirectory); + REGISTER_SYNC("FileSystemManager_pathExists", FileSystemManagerPathExists); + REGISTER_SYNC("FileSystemManager_getLimits", FileSystemManagerGetLimits); + + REGISTER_SYNC("FileHandle_seek", FileHandleSeek); + REGISTER_SYNC("FileHandle_readString", FileHandleReadString); + REGISTER_SYNC("FileHandle_writeString", FileHandleWriteString); + REGISTER_SYNC("FileHandle_readData", FileHandleReadData); + REGISTER_SYNC("FileHandle_writeData", FileHandleWriteData); + REGISTER_SYNC("FileHandle_flush", FileHandleFlush); + REGISTER_SYNC("FileHandle_sync", FileHandleSync); + REGISTER_SYNC("FileHandle_close", FileHandleClose); + #undef REGISTER_SYNC FilesystemManager::GetInstance().AddListener(this); } FilesystemInstance::~FilesystemInstance() { ScopeLogger(); + worker.stop(); FilesystemManager::GetInstance().StopListening(); FilesystemManager::GetInstance().RemoveListener(); } @@ -85,10 +169,14 @@ FilesystemInstance::~FilesystemInstance() { void FilesystemInstance::FileCreateSync(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.createFile() is deprecated since Tizen 5.0. Use " + "FileSystemManager.openFile() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) - const std::string& location = args.get("location").get<std::string>(); + CHECK_STORAGE_ACCESS(location, &out); auto onSuccess = [&](const FilesystemStat& data) { ScopeLogger("Entered into asynchronous function, onSuccess"); @@ -105,14 +193,21 @@ void FilesystemInstance::FileCreateSync(const picojson::value& args, picojson::o void FilesystemInstance::FileRename(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.moveTo() is deprecated since Tizen 5.0. Use " + "FileSystemManager.moveFile() or FileSystemManager.moveDirectory() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "callbackId", out) + CHECK_EXIST(args, "oldPath", out) + const std::string& oldPath = args.get("oldPath").get<std::string>(); + CHECK_STORAGE_ACCESS(oldPath, &out); CHECK_EXIST(args, "newPath", out) + const std::string& newPath = args.get("newPath").get<std::string>(); + CHECK_STORAGE_ACCESS(newPath, &out); double callback_id = args.get("callbackId").get<double>(); - const std::string& oldPath = args.get("oldPath").get<std::string>(); - const std::string& newPath = args.get("newPath").get<std::string>(); auto onSuccess = [this, callback_id](const FilesystemStat& data) { ScopeLogger("Entered into asynchronous function, onSuccess"); @@ -133,7 +228,7 @@ void FilesystemInstance::FileRename(const picojson::value& args, picojson::objec }; FilesystemManager& fsm = FilesystemManager::GetInstance(); - common::TaskQueue::GetInstance().Async( + common::TaskQueue::GetInstance().Queue( std::bind(&FilesystemManager::Rename, &fsm, oldPath, newPath, onSuccess, onError)); } @@ -182,17 +277,54 @@ static auto from_utf8 = &decode_binary_from_string; } static constexpr std::size_t NPOS = (std::size_t)(-1); + +/** + * On failure throws std::system_error + */ +static std::size_t file_size(FILE* file) { + ScopeLogger(); + + struct ::stat buf; + int status = ::fstat(::fileno(file), &buf); + if (status != 0) { + throw std::system_error{errno, std::generic_category(), "failed to get file size"}; + } + + return buf.st_size; +} + +/** + * Returns the amount of bytes to the EOF starting from current file-position indicator. + * + * On failure throws std::system_error + */ +static std::size_t file_bytes_to_eof(FILE* file) { + ScopeLogger(); + + std::size_t total_fize_size = file_size(file); + long file_position = ftell(file); + if (-1 == file_position) { + throw std::system_error{errno, std::generic_category(), + "Failed to get file position"s + GetErrorString(errno)}; + } + return total_fize_size - static_cast<size_t>(file_position); +} + +static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length = NPOS, + std::size_t* read_bytes = nullptr); + /** * Returns a buffer. If length is NPOS, then it reads whole file, up to the end. * On failure throws std::runtime_error */ -static std::vector<std::uint8_t> read_file(std::string path, std::size_t offset, +static std::vector<std::uint8_t> read_file(std::string path, long offset = 0, std::size_t length = NPOS) { ScopeLogger(); FILE* file = std::fopen(path.c_str(), "r"); if (!file) { - throw std::runtime_error("cannot open file to read"); + std::string err_msg = std::string("Cannot open file to read. ") + GetErrorString(errno); + throw std::system_error{errno, std::generic_category(), err_msg}; } SCOPE_EXIT { @@ -202,27 +334,45 @@ static std::vector<std::uint8_t> read_file(std::string path, std::size_t offset, } }; - if (std::fseek(file, offset, SEEK_SET) != 0) { - throw std::runtime_error("cannot perform seek"); + if (0 != offset && 0 != std::fseek(file, offset, SEEK_SET)) { + std::string err_msg = std::string("Cannot perform seek. ") + GetErrorString(errno); + throw std::system_error{errno, std::generic_category(), err_msg}; + } + + if (NPOS == length) { + length = file_size(file) - offset; } + return read_file(file, length); +} + +/** + * Returns a buffer. If length is NPOS, then it reads whole file, up to the end. + * On failure throws std::runtime_error + */ +static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length /*= NPOS*/, + std::size_t* read_bytes /* = nullptr*/) { + ScopeLogger(); + // By default reads whole file. Get the file size. - if (length == NPOS) { - struct ::stat buf; - if (::fstat(::fileno(file), &buf) != 0) { - throw std::runtime_error("cannot fstat"); - } - length = buf.st_size - offset; + if (NPOS == length) { + length = file_size(file); } - std::vector<std::uint8_t> out_buf(length); + std::vector<std::uint8_t> out_buf; + try { + out_buf.resize(length); + } catch (std::bad_alloc& err) { + throw std::runtime_error{"Could not allocate memory"}; + } std::uint8_t* data_p = out_buf.data(); std::uint8_t* end_p = data_p + length; while (data_p != end_p) { data_p += std::fread(data_p, 1, end_p - data_p, file); if (std::ferror(file)) { - throw std::runtime_error("error during file read"); + std::string err_msg = std::string("Error during file read. ") + GetErrorString(errno); + throw std::runtime_error(err_msg); } if (std::feof(file)) { @@ -230,20 +380,52 @@ static std::vector<std::uint8_t> read_file(std::string path, std::size_t offset, break; } } + // read_file function is used in API since version 1.0. + // read_bytes was added in Tizen 5.0, with default value equal to nullptr, the behaviour is not + // changed. + // It is used to return the actual number of read bytes, because requested length might be bigger + // than possible bytes to be read. + if (nullptr != read_bytes) { + *read_bytes = std::distance(out_buf.data(), data_p); + } return out_buf; } /** * On failure throws std::runtime_error */ -void write_file(const std::uint8_t* data, std::size_t len, std::string path, std::size_t offset, - bool rewrite) { +void write_file(const std::uint8_t* data, std::size_t len, FILE* file) { ScopeLogger(); - FILE* file = fopen(path.c_str(), rewrite ? "w" : "r+"); + const std::uint8_t* data_p = data; + const std::uint8_t* end_p = data + len; + while (data_p != end_p) { + data_p += fwrite(data_p, 1, end_p - data_p, file); + + if (std::ferror(file)) { + std::string err_msg = std::string("Error during file write. ") + GetErrorString(errno); + throw std::runtime_error(err_msg); + } + } + + if (std::fflush(file)) { + std::string err_msg = std::string("Error during file write. ") + GetErrorString(errno); + throw std::runtime_error(err_msg); + } +} + +/** + * On failure throws std::runtime_error + */ +void write_file(const std::uint8_t* data, std::size_t len, std::string path, long offset, + const char* mode) { + ScopeLogger(); + + FILE* file = std::fopen(path.c_str(), mode); if (!file) { - throw std::runtime_error("cannot open file to write"); + std::string err_msg = std::string("Cannot open file to write. ") + GetErrorString(errno); + throw std::runtime_error(err_msg); } SCOPE_EXIT { @@ -253,23 +435,167 @@ void write_file(const std::uint8_t* data, std::size_t len, std::string path, std } }; - if (std::fseek(file, offset, SEEK_SET) != 0) { - throw std::runtime_error("cannot perform seek"); + if (offset != 0 && std::fseek(file, offset, SEEK_SET) != 0) { + std::string err_msg = std::string("Cannot perform seek. ") + GetErrorString(errno); + throw std::system_error{errno, std::generic_category(), err_msg}; } - const std::uint8_t* data_p = data; - const std::uint8_t* end_p = data + len; - while (data_p != end_p) { - data_p += fwrite(data_p, 1, end_p - data_p, file); + write_file(data, len, file); +} - if (std::ferror(file)) { - throw std::runtime_error("error during file write"); +#define FIRST_BIT_MASK 0x80 +#define SECOND_BIT_MASK 0x40 +#define THIRD_BIT_MASK 0x20 +#define FOURTH_BIT_MASK 0x10 +#define FIFTH_BIT_MASK 0x08 + +enum class ByteOfUTF8Classification { + NOT_VALID, + ONE_BYTE_CHAR, + FIRST_OF_2_BYTES, + FIRST_OF_3_BYTES, + FIRST_OF_4_BYTES, + EXTENSION_BYTE +}; + +ByteOfUTF8Classification is_utf8_byte(uint8_t byte) { + if (FIRST_BIT_MASK & byte) { + if (SECOND_BIT_MASK & byte) { + if (THIRD_BIT_MASK & byte) { + if (FOURTH_BIT_MASK & byte) { + if (FIFTH_BIT_MASK & byte) { + return ByteOfUTF8Classification::NOT_VALID; + } else { + return ByteOfUTF8Classification::FIRST_OF_4_BYTES; + } + } else { + return ByteOfUTF8Classification::FIRST_OF_3_BYTES; + } + } else { + return ByteOfUTF8Classification::FIRST_OF_2_BYTES; + } + } else { + return ByteOfUTF8Classification::EXTENSION_BYTE; + } + } else { + return ByteOfUTF8Classification::ONE_BYTE_CHAR; + } +} + +void skip_partial_character(std::vector<std::uint8_t>& buf) { + auto buf_position = buf.begin(); + + while (buf.end() != buf_position && + (ByteOfUTF8Classification::EXTENSION_BYTE == is_utf8_byte(*buf_position))) { + buf_position = buf.erase(buf_position); + LoggerD("Removed UTF-8 Extension Byte from begining of read buffer"); + } +} + +bool validate_and_check_character_count(std::vector<std::uint8_t>& buf, unsigned long& char_count, + short& no_of_extensions_expected) { + ScopeLogger(); + char_count = 0; + no_of_extensions_expected = 0; + auto buf_position = buf.begin(); + + skip_partial_character(buf); + + while (buf.end() != buf_position) { + switch (is_utf8_byte(*buf_position)) { + case ByteOfUTF8Classification::EXTENSION_BYTE: + no_of_extensions_expected--; + if (0 > no_of_extensions_expected) { + return false; + } else if (0 == no_of_extensions_expected) { + char_count++; + } + break; + case ByteOfUTF8Classification::ONE_BYTE_CHAR: + if (0 != no_of_extensions_expected) { + return false; + } + char_count++; + break; + case ByteOfUTF8Classification::FIRST_OF_2_BYTES: + if (0 != no_of_extensions_expected) { + return false; + } + no_of_extensions_expected = 1; + break; + case ByteOfUTF8Classification::FIRST_OF_3_BYTES: + if (0 != no_of_extensions_expected) { + return false; + } + no_of_extensions_expected = 2; + break; + case ByteOfUTF8Classification::FIRST_OF_4_BYTES: + if (0 != no_of_extensions_expected) { + return false; + } + no_of_extensions_expected = 3; + break; + case ByteOfUTF8Classification::NOT_VALID: + return false; + default: + LoggerE("Abnormal value returned from is_utf8_byte function"); } + buf_position++; } + return true; +} - if (std::fflush(file)) { - throw std::runtime_error("error during file write"); +/* + * add_utf8_chars_to_buffer + * Method returns false if byte read is not a valid utf8 char + */ +bool add_utf8_chars_to_buffer(FILE* file, std::vector<std::uint8_t>& buf, int chars_to_read, + short& extension_bytes_expected) { + int character; + LoggerD("chars_to_read: %i", chars_to_read); + LoggerD("extension_bytes_expected: %i", extension_bytes_expected); + while (chars_to_read) { + if (extension_bytes_expected) { + character = getc(file); + if (EOF == character) { + return false; + } + buf.push_back(character); + if (!--extension_bytes_expected) { + chars_to_read--; + } + continue; + } + character = getc(file); + if (EOF == character) { + return false; + } + buf.push_back(character); + switch (is_utf8_byte(character)) { + case ByteOfUTF8Classification::ONE_BYTE_CHAR: + chars_to_read--; + break; + case ByteOfUTF8Classification::FIRST_OF_2_BYTES: + extension_bytes_expected = 1; + break; + case ByteOfUTF8Classification::FIRST_OF_3_BYTES: + extension_bytes_expected = 2; + break; + case ByteOfUTF8Classification::FIRST_OF_4_BYTES: + extension_bytes_expected = 3; + break; + case ByteOfUTF8Classification::EXTENSION_BYTE: + LoggerD("unexpected EXTENSION_BYTE"); + return false; + case ByteOfUTF8Classification::NOT_VALID: + LoggerE("unexpected NOT_VALID byte while reading utf-8"); + return false; + default: + LoggerE("Abnormal. Last read char: %c: ", character); + return false; + } } + return true; } namespace base64 { @@ -339,6 +665,10 @@ static std::vector<std::uint8_t> decode(const char* str, std::size_t len) { void FilesystemInstance::FileReadString(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: This method is deprecated since Tizen 5.0.Use FileHandle.readString() " + "or FileHandle.readStringNonBlocking() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "offset", out) @@ -369,6 +699,10 @@ void FilesystemInstance::FileReadString(const picojson::value& args, picojson::o void FilesystemInstance::FileReadBytes(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: This method is deprecated since Tizen 5.0. Use FileHandle.readData() " + "or FileHandle.readDataNonBlocking() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "offset", out) @@ -392,16 +726,21 @@ void FilesystemInstance::FileReadBytes(const picojson::value& args, picojson::ob void FilesystemInstance::FileWriteString(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: FileStream.write() is deprecated since Tizen 5.0. Use " + "FileHandle.writeString() or FileHandle.writeStringNonBlocking() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) - CHECK_EXIST(args, "rewrite", out) + CHECK_EXIST(args, "truncate", out) const std::string& location = args.get("location").get<std::string>(); const std::string& str = args.get("data").get<std::string>(); size_t offset = static_cast<size_t>(args.get("offset").get<double>()); - bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>()); + bool truncate = static_cast<bool>(args.get("truncate").get<bool>()); + const char* mode = truncate ? "w" : "r+"; const std::string& encoding = args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8"; @@ -409,11 +748,11 @@ void FilesystemInstance::FileWriteString(const picojson::value& args, picojson:: if (encoding == "iso-8859-1") { std::vector<std::uint8_t> data; latin1::from_utf8(str, data); - write_file(data.data(), data.size(), location, offset, rewrite); + write_file(data.data(), data.size(), location, offset, mode); } else { // default: UTF-8 const std::uint8_t* buf = (const std::uint8_t*)str.c_str(); std::size_t len = str.length(); - write_file(buf, len, location, offset, rewrite); + write_file(buf, len, location, offset, mode); } } catch (std::runtime_error& e) { LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what()); @@ -426,21 +765,26 @@ void FilesystemInstance::FileWriteString(const picojson::value& args, picojson:: void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: FileStream.writeBytes() is deprecated since Tizen 5.0. To read and Use " + "FileHandle.writeData() or FileHandle.writeDataNonBlocking() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) - CHECK_EXIST(args, "rewrite", out) + CHECK_EXIST(args, "truncate", out) const std::string& location = args.get("location").get<std::string>(); const std::string& str = args.get("data").get<std::string>(); size_t offset = static_cast<size_t>(args.get("offset").get<double>()); - bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>()); + bool truncate = static_cast<bool>(args.get("truncate").get<bool>()); + const char* mode = truncate ? "w" : "r+"; try { std::vector<std::uint8_t> data; decode_binary_from_string(str, data); - write_file(data.data(), data.size(), location, offset, rewrite); + write_file(data.data(), data.size(), location, offset, mode); } catch (std::runtime_error& e) { LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); PrepareError(FilesystemError::Other, out); @@ -452,16 +796,22 @@ void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::o void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: FileStream.writeBase64() is deprecated since Tizen 5.0. Use " + "FileHandle.writeData() or FileHandle.writeDataNonBlocking() in combination with atob() and " + "btoa() functions instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) - CHECK_EXIST(args, "rewrite", out) + CHECK_EXIST(args, "truncate", out) const std::string& location = args.get("location").get<std::string>(); const std::string& str = args.get("data").get<std::string>(); size_t offset = static_cast<size_t>(args.get("offset").get<double>()); - bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>()); + bool truncate = static_cast<bool>(args.get("truncate").get<bool>()); + const char* mode = truncate ? "w" : "r+"; std::vector<std::uint8_t> data; try { @@ -473,7 +823,7 @@ void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson:: } try { - write_file(data.data(), data.size(), location, offset, rewrite); + write_file(data.data(), data.size(), location, offset, mode); ReportSuccess(picojson::value{(double)data.size()}, out); } catch (std::runtime_error& e) { LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); @@ -484,11 +834,12 @@ void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson:: void FilesystemInstance::FileStat(const picojson::value& args, picojson::object& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); - CHECK_EXIST(args, "callbackId", out) CHECK_EXIST(args, "location", out) + const std::string& location = args.get("location").get<std::string>(); + CHECK_STORAGE_ACCESS(location, &out); + CHECK_EXIST(args, "callbackId", out) double callback_id = args.get("callbackId").get<double>(); - const std::string& location = args.get("location").get<std::string>(); auto onSuccess = [this, callback_id](const FilesystemStat& data) { ScopeLogger("Entered into asynchronous function, onSuccess"); @@ -517,8 +868,8 @@ void FilesystemInstance::FileStatSync(const picojson::value& args, picojson::obj ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); CHECK_EXIST(args, "location", out) - const std::string& location = args.get("location").get<std::string>(); + CHECK_STORAGE_ACCESS(location, &out); auto onSuccess = [&](const FilesystemStat& data) { ScopeLogger("Entered into asynchronous function, onSuccess"); @@ -643,10 +994,14 @@ void FilesystemInstance::FileSystemManagerMakeDirectory(const picojson::value& a void FilesystemInstance::FileSystemManagerMakeDirectorySync(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.createDirectory() is deprecated since Tizen 5.0. Use " + "FileSystemManager.createDirectory() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) - const std::string& location = args.get("location").get<std::string>(); + CHECK_STORAGE_ACCESS(location, &out); auto onResult = [&](FilesystemError e) { ScopeLogger("Entered into asynchronous function, onResult"); @@ -661,6 +1016,10 @@ void FilesystemInstance::FileSystemManagerMakeDirectorySync(const picojson::valu void FilesystemInstance::ReadDir(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.listFiles() is deprecated since Tizen 5.0. Use " + "FileSystemManager.listDirectory() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); CHECK_EXIST(args, "pathToDir", out) CHECK_EXIST(args, "callbackId", out) @@ -706,11 +1065,15 @@ void FilesystemInstance::ReadDir(const picojson::value& args, picojson::object& void FilesystemInstance::UnlinkFile(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.deleteFile() is deprecated since Tizen 5.0. Use " + "FileSystemManager.deleteFile() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "pathToFile", out) - double callback_id = args.get("callbackId").get<double>(); const std::string& pathToFile = args.get("pathToFile").get<std::string>(); + CHECK_STORAGE_ACCESS(pathToFile, &out); auto onSuccess = [this, callback_id]() { ScopeLogger("Entered into asynchronous function, onSuccess"); @@ -738,11 +1101,15 @@ void FilesystemInstance::UnlinkFile(const picojson::value& args, picojson::objec void FilesystemInstance::RemoveDirectory(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.deleteDirectory() is deprecated since Tizen 5.0. Use " + "FileSystemManager.deleteDirectory() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "pathToDelete", out) - double callback_id = args.get("callbackId").get<double>(); const std::string& pathToDelete = args.get("pathToDelete").get<std::string>(); + CHECK_STORAGE_ACCESS(pathToDelete, &out); auto onSuccess = [this, callback_id]() { ScopeLogger("Entered into asynchronous function, onSuccess"); @@ -764,21 +1131,29 @@ void FilesystemInstance::RemoveDirectory(const picojson::value& args, picojson:: }; FilesystemManager& fm = FilesystemManager::GetInstance(); - common::TaskQueue::GetInstance().Async( + common::TaskQueue::GetInstance().Queue( std::bind(&FilesystemManager::RemoveDirectory, &fm, pathToDelete, onSuccess, onError)); } void FilesystemInstance::CopyTo(const picojson::value& args, picojson::object& out) { ScopeLogger(); + LoggerW( + "DEPRECATION WARNING: File.copyTo() is deprecated since Tizen 5.0. Use " + "FileSystemManager.CopyFile() or FileSystemManager.CopyDirectory() instead."); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "callbackId", out) + CHECK_EXIST(args, "originFilePath", out) + const std::string& originPath = args.get("originFilePath").get<std::string>(); + CHECK_STORAGE_ACCESS(originPath, &out); CHECK_EXIST(args, "destinationFilePath", out) + const std::string& destinationPath = args.get("destinationFilePath").get<std::string>(); + CHECK_STORAGE_ACCESS(destinationPath, &out); + CHECK_EXIST(args, "overwrite", out) double callback_id = args.get("callbackId").get<double>(); - const std::string& originPath = args.get("originFilePath").get<std::string>(); - const std::string& destinationPath = args.get("destinationFilePath").get<std::string>(); const bool& overwrite = args.get("overwrite").get<bool>(); auto onSuccess = [this, callback_id]() { @@ -801,7 +1176,7 @@ void FilesystemInstance::CopyTo(const picojson::value& args, picojson::object& o }; FilesystemManager& fm = FilesystemManager::GetInstance(); - common::TaskQueue::GetInstance().Async(std::bind(&FilesystemManager::CopyTo, &fm, originPath, + common::TaskQueue::GetInstance().Queue(std::bind(&FilesystemManager::CopyTo, &fm, originPath, destinationPath, overwrite, onSuccess, onError)); } @@ -865,6 +1240,1117 @@ void FilesystemInstance::FileSystemManagerGetCanonicalPath(const picojson::value FilesystemManager::GetInstance().GetCanonicalPath(path, onSuccess, onError); } +namespace { + +FILE* OpenFile(const std::string& path, const std::string& fopen_mode) { + FILE* file = std::fopen(path.c_str(), fopen_mode.c_str()); + if (!file) { + throw std::system_error{errno, std::generic_category(), "Could not open file"}; + } + + return file; +} + +FILE* MakeParentsAndOpenFile(const std::string& path, const std::string& fopen_mode) { + /* + * If fopen fails, created parent directories have to be removed. + * Save the path to the first nonexistent parent directory in the file path, + * to know where to start recursive removal + */ + std::string first_nonexistent_parent = FilesystemUtils::Dirname(path); + struct ::stat buf; + + while ( + !FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(first_nonexistent_parent), &buf)) { + first_nonexistent_parent = FilesystemUtils::Dirname(first_nonexistent_parent); + } + + FilesystemUtils::Mkdir(FilesystemUtils::Dirname(path), true); + + FILE* file = std::fopen(path.c_str(), fopen_mode.c_str()); + if (file) { + return file; + } + + std::system_error fopen_error = + std::system_error(errno, std::generic_category(), "Could not open file"); + + try { + FilesystemUtils::RemoveDirectoryRecursively(first_nonexistent_parent); + } catch (const std::system_error& error) { + LoggerD("Failed to remove created parent directories: %s", error.what()); + } + + throw fopen_error; +} + +} // namespace + +void FilesystemInstance::FileSystemManagerOpenFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + const int unique_id = static_cast<int>(args.get("id").get<double>()); + if (opened_files.find(unique_id) != opened_files.end()) { + LogAndReportError(IOException("Internal error (id is already in use)"), out); + return; + } + + bool access_checked = false; + if (WriteAccessRequested(args)) { + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + access_checked = true; + } + + if (ReadAccessRequested(args)) { + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + access_checked = true; + } + + // File open mode received from JS layer can be different than expected by + // WriteAccessRequested and ReadAccessRequested functions. In case like that + // privilege would not be checked and user could gain unauthorized access to file. + // To prevent this situation we only accept specific open modes. + if (false == access_checked) { + const std::string& open_mode = args.get("openMode").get<std::string>(); + LogAndReportError(TypeMismatchException("Invalid open mode: " + open_mode), out); + return; + } + + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + const std::string open_mode = GetFopenMode(args); + FILE* file = nullptr; + try { + if (ShouldMakeParents(args)) { + file = MakeParentsAndOpenFile(path, open_mode); + } else { + file = OpenFile(path, open_mode); + } + } catch (const std::system_error& error) { + FilesystemUtils::TranslateException(error, out); + return; + } + + opened_files.emplace(std::make_pair(unique_id, std::make_shared<FileHandle>(file))); + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + double callback_id = args.get("callbackId").get<double>(); + const std::string& path = args.get("path").get<std::string>(); + bool make_parents = args.get("makeParents").get<bool>(); + CHECK_STORAGE_ACCESS(path, &out); + + common::TaskQueue::GetInstance().Async([this, callback_id, path, make_parents] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + + try { + FilesystemUtils::Mkdir(path, make_parents); + ReportSuccess(picojson::value(path), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerDeleteFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + double callback_id = args.get("callbackId").get<double>(); + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + common::TaskQueue::GetInstance().Async([this, callback_id, path] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) { + LogAndReportError(NotFoundException("Given path does not point to file."), obj); + return; + } + FilesystemUtils::Unlink(path); + ReportSuccess(picojson::value(FilesystemUtils::Dirname(path)), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerDeleteDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + double callback_id = args.get("callbackId").get<double>(); + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + bool recursive = args.get("recursive").get<bool>(); + + common::TaskQueue::GetInstance().Queue([this, callback_id, path, recursive] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + if (recursive) { + FilesystemUtils::RemoveDirectoryRecursively(path); + } else { + FilesystemUtils::RemoveDirectory(path); + } + ReportSuccess(picojson::value(FilesystemUtils::Dirname(path)), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerCopyFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + + double callback_id = args.get("callbackId").get<double>(); + const std::string& path = args.get("path").get<std::string>(); + CHECK_STORAGE_ACCESS(path, &out); + const std::string& destination_path = args.get("destinationPath").get<std::string>(); + CHECK_STORAGE_ACCESS(destination_path, &out); + bool overwrite = args.get("overwrite").get<bool>(); + + common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) { + LogAndReportError(NotFoundException("Given path does not point to file."), obj); + return; + } + buf = {}; + if (FilesystemUtils::CheckIfExists(destination_path, &buf) && !overwrite) { + LogAndReportError( + IOException("Given path points to an existing resource, overwriting is not allowed."), + obj); + return; + } + FilesystemUtils::CopyFile(path, destination_path, overwrite); + ReportSuccess(picojson::value(destination_path), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerCopyDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + const std::string& path = args.get("path").get<std::string>(); + CHECK_STORAGE_ACCESS(path, &out); + const std::string& destination_path = args.get("destinationPath").get<std::string>(); + CHECK_STORAGE_ACCESS(destination_path, &out); + double callback_id = args.get("callbackId").get<double>(); + bool overwrite = args.get("overwrite").get<bool>(); + + common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + buf = {}; + bool exists = FilesystemUtils::CheckIfExists(destination_path, &buf); + if (exists && !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(IOException("File with conflicting name already exists."), obj); + return; + } else if (!exists) { + FilesystemUtils::Mkdir(destination_path); + } + FilesystemUtils::CopyDirectory(path, destination_path, overwrite); + ReportSuccess(picojson::value(destination_path), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerMoveFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + + const std::string& path = args.get("path").get<std::string>(); + CHECK_STORAGE_ACCESS(path, &out); + const std::string& destination_path = args.get("destinationPath").get<std::string>(); + CHECK_STORAGE_ACCESS(destination_path, &out); + double callback_id = args.get("callbackId").get<double>(); + bool overwrite = args.get("overwrite").get<bool>(); + + common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) { + LogAndReportError(NotFoundException("Given path does not point to file."), obj); + return; + } + buf = {}; + if (!FilesystemUtils::CheckIfExists(destination_path, &buf) || + !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + + buf = {}; + std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path); + if (!overwrite && FilesystemUtils::CheckIfExists(new_path, &buf)) { + LogAndReportError(IOException("File or directory with conflicting name already exists."), + obj); + return; + } + FilesystemUtils::MoveFile(path, new_path, overwrite); + ReportSuccess(picojson::value(new_path), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerMoveDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + double callback_id = args.get("callbackId").get<double>(); + const std::string& path = args.get("path").get<std::string>(); + CHECK_STORAGE_ACCESS(path, &out); + const std::string& destination_path = args.get("destinationPath").get<std::string>(); + CHECK_STORAGE_ACCESS(destination_path, &out); + bool overwrite = args.get("overwrite").get<bool>(); + + common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + buf = {}; + if (!FilesystemUtils::CheckIfExists(destination_path, &buf) || + !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(NotFoundException("Given path does not point to directory."), obj); + return; + } + buf = {}; + std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path); + if (FilesystemUtils::CheckIfExists(new_path, &buf) && !FilesystemUtils::CheckIfDir(buf)) { + LogAndReportError(IOException("File or directory with conflicting name already exists."), + obj); + return; + } + FilesystemUtils::MoveDirectory(path, destination_path, overwrite); + ReportSuccess(picojson::value(new_path), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerRename(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + double callback_id = args.get("callbackId").get<double>(); + const std::string& new_name = args.get("newName").get<std::string>(); + + common::TaskQueue::GetInstance().Async([this, callback_id, new_name, path] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + SCOPE_EXIT { + this->PostMessage(response.serialize().c_str()); + }; + + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + LogAndReportError(NotFoundException("Given path does not point to file or directory."), + obj); + return; + } + std::string new_path{FilesystemUtils::Dirname(path) + "/" + new_name}; + buf = {}; + exists = FilesystemUtils::CheckIfExists(new_path, &buf); + if (exists) { + LogAndReportError(IOException("File or directory with conflicting name already exists"), + obj); + return; + } + FilesystemUtils::Rename(path, new_path); + ReportSuccess(picojson::value(new_path), obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + }); + + ReportSuccess(out); +} + +void FilterResult(std::vector<const char*>& names, std::vector<unsigned char>& types, bool is_type, + unsigned char type) { + int i = (int)names.size() - 1; + + while (i >= 0) { + if (is_type ? type != types[i] : type == types[i]) { + names.erase(names.begin() + i); + } + i--; + } +} + +void FilesystemInstance::FileSystemManagerListDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + + double callback_id = args.get("callbackId").get<double>(); + const std::string& path = args.get("path").get<std::string>(); + const picojson::object& filter = args.get("filter").get<picojson::object>(); + CHECK_STORAGE_ACCESS(path, &out); + + common::TaskQueue::GetInstance().Async([this, callback_id, path, filter] { + ScopeLogger(); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get<picojson::object>(); + obj["callbackId"] = picojson::value(callback_id); + + try { + std::vector<const char*> names; + { + std::vector<unsigned char> types; + FilesystemUtils::ListDirectory(path, [&](const char* name, unsigned char type) { + names.push_back(name); + types.push_back(type); + }); + + auto it = filter.find("isFile"); + if (filter.end() != it) { + FilterResult(names, types, it->second.get<bool>(), DT_REG); + } + + it = filter.find("isDirectory"); + if (filter.end() != it) { + FilterResult(names, types, it->second.get<bool>(), DT_DIR); + } + } + + auto start_modified_it = filter.find("startModified"), + end_modified_it = filter.find("endModified"), + start_created_it = filter.find("startCreated"), + end_created_it = filter.find("endCreated"); + if (filter.end() != start_modified_it || filter.end() != end_modified_it || + filter.end() != start_created_it || filter.end() != end_created_it) { + auto name_iterator = names.begin(); + while (name_iterator != names.end()) { + struct ::stat buf; + std::string path_with_name = path + std::string("/") + std::string(*name_iterator); + int status = ::stat(path_with_name.c_str(), &buf); + if (status != 0) { + throw std::system_error{errno, std::generic_category(), + "Failed to get last modification date of a file"}; + } + if (filter.end() != start_modified_it && + (buf.st_mtime < start_modified_it->second.get<double>())) { + name_iterator = names.erase(name_iterator); + continue; + } + if (filter.end() != end_modified_it && + (buf.st_mtime > end_modified_it->second.get<double>())) { + name_iterator = names.erase(name_iterator); + continue; + } + if (filter.end() != start_created_it && + (buf.st_ctime < start_created_it->second.get<double>())) { + name_iterator = names.erase(name_iterator); + continue; + } + if (filter.end() != end_created_it && + (buf.st_ctime > end_created_it->second.get<double>())) { + name_iterator = names.erase(name_iterator); + continue; + } + name_iterator++; + } + } + + picojson::value value = picojson::value(picojson::object()); + picojson::object& object = value.get<picojson::object>(); + + object["names"] = picojson::value{picojson::array_type, true}; + object["path"] = picojson::value(path); + + picojson::array& names_array = object["names"].get<picojson::array>(); + names_array.reserve(names.size()); + for (unsigned int i = 0; i < names.size(); ++i) { + names_array.push_back(picojson::value(names[i])); + } + + ReportSuccess(value, obj); + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, obj); + } + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void FilesystemInstance::FileSystemManagerIsFile(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + picojson::value is_file{}; + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + LogAndReportError(NotFoundException("Given path does not point to file."), out); + return; + } + is_file = picojson::value{FilesystemUtils::CheckIfFile(buf)}; + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, out); + } + ReportSuccess(is_file, out); +} + +void FilesystemInstance::FileSystemManagerIsDirectory(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + picojson::value is_directory{}; + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + LogAndReportError(NotFoundException("Given path does not point to directory."), out); + return; + } + is_directory = picojson::value{FilesystemUtils::CheckIfDir(buf)}; + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, out); + } + ReportSuccess(is_directory, out); +} + +void FilesystemInstance::FileSystemManagerPathExists(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out); + const std::string& path = args.get("path").get<std::string>(); + + CHECK_STORAGE_ACCESS(path, &out); + picojson::value does_file_exist = picojson::value{true}; + try { + struct stat buf {}; + bool exists = FilesystemUtils::CheckIfExists(path, &buf); + if (!exists) { + does_file_exist = picojson::value{false}; + } + } catch (const std::system_error& e) { + FilesystemUtils::TranslateException(e, out); + } + ReportSuccess(does_file_exist, out); +} + +void FilesystemInstance::FileSystemManagerGetLimits(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + picojson::value response = + picojson::value{picojson::array{picojson::value{static_cast<double>(NAME_MAX)}, + picojson::value{static_cast<double>(PATH_MAX)}}}; + ReportSuccess(response, out); +} + +void FilesystemInstance::FileHandleSeek(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast<int>(args.get("id").get<double>()); + const long offset = static_cast<long>(args.get("offset").get<double>()); + const std::string& _whence = args.get("whence").get<std::string>(); + + auto fh = opened_files.find(fh_id); + + int whence = SEEK_SET; + + if ("CURRENT" == _whence) { + whence = SEEK_CUR; + LoggerD("SEEK_CUR selected"); + } else if ("END" == _whence) { + whence = SEEK_END; + LoggerD("SEEK_END selected"); + } + + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle, whence, offset](decltype(out) out) { + long ret = fseek(handle->file_handle, offset, whence); + if (0 != ret) { + LoggerE("fseek returned failed"); + std::string error_message = + std::string("seek failed, fileHandle may be corrupted, error message: ") + + GetErrorString(errno); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + + ret = ftell(handle->file_handle); + if (-1L == ret) { + LoggerE("ftell returned failed"); + std::string error_message = + std::string("seek failed, fileHandle may be corrupted, error message: ") + + GetErrorString(errno); + LogAndReportError(IOException(error_message.c_str()), out); + return; + } + + ReportSuccess(picojson::value((double)ret), out); + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleReadString(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + const int fh_id = static_cast<int>(args.get("id").get<double>()); + const std::string& encoding = + args.contains("encoding") ? args.get("encoding").get<std::string>() : "UTF-8"; + if (encoding != kISOEncoding && encoding != kUTF8Encoding) { + LogAndReportError(NotSupportedException("Given encoding is not supported."), out); + return; + } + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + size_t count; + bool whole_file = false; + if (args.contains("count")) { + // If user passed 'count' parameter, we need to read at most 'count' characters. + double count_double = args.get("count").get<double>(); + if (std::string::npos <= static_cast<unsigned long long>(count_double)) { + LogAndReportError(InvalidValuesException("Invalid count was given"), out); + return; + } + count = static_cast<size_t>(count_double); + } else { + try { + count = file_size(fh->second->file_handle); + whole_file = true; + } catch (const std::system_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + } + LoggerD("count: %zu", count); + + auto handle = fh->second; + + auto logic = [handle, count, encoding, whole_file](decltype(out) out) { + try { + size_t read_bytes = 0; + std::vector<std::uint8_t> buf = read_file(handle->file_handle, count, &read_bytes); + buf.resize(read_bytes); // this protects from reporting too big arrays to JS + if (encoding == kISOEncoding) { // for iso-8859-1 1 byte is equal to 1 character + out["result"] = picojson::value(picojson::string_type, true); + latin1::to_utf8(buf, out["result"].get<std::string>()); + ReportSuccess(out); + } else { // UTF-8 + unsigned long char_count; + short expected_extension_bytes; + if (!validate_and_check_character_count(buf, char_count, expected_extension_bytes)) { + LogAndReportError( + IOException("File doesn't contain UTF-8 encoded string with given length"), out); + return; + } + LoggerD("char_count: %lu", char_count); + LoggerD("ftell: %ld", ftell(handle->file_handle)); + if (!(std::feof( + handle->file_handle))) { // read number of characters if not whole file read + LoggerD("count parameter given: %zu", count); + if (!whole_file && + !add_utf8_chars_to_buffer(handle->file_handle, buf, count - char_count, + expected_extension_bytes)) { + LogAndReportError( + IOException("File doesn't contain UTF-8 encoded string with given length"), out); + } + } + const char* str = (const char*)buf.data(); + ReportSuccess(picojson::value{str, buf.size()}, out); + } + } catch (std::runtime_error& e) { + LoggerE("Cannot read, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleWriteString(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + CHECK_EXIST(args, "string", out) + const int fh_id = static_cast<int>(args.get("id").get<double>()); + const std::string& str = args.get("string").get<std::string>(); + const std::string& encoding = + args.contains("encoding") ? args.get("encoding").get<std::string>() : "UTF-8"; + if (encoding != kISOEncoding && encoding != kUTF8Encoding) { + LogAndReportError(NotSupportedException("Given encoding is not supported."), out); + return; + } + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [str, handle, encoding](decltype(out) out) { + try { + std::vector<std::uint8_t> data; + data.resize(str.size()); + + if (encoding == kISOEncoding) { + latin1::from_utf8(str, data); + } else { // UTF-8 + LoggerD("copying string memory to vector"); + std::memcpy(data.data(), str.data(), str.size()); + } + write_file(data.data(), data.size(), handle->file_handle); + ReportSuccess(picojson::value{(double)data.size()}, out); + } catch (std::runtime_error& e) { + LoggerE("Cannot write, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleReadData(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + const int fh_id = static_cast<int>(args.get("id").get<double>()); + auto fh = opened_files.find(fh_id); + + if (opened_files.end() == fh) { + LoggerE("FileHandle with id: %d not found", fh_id); + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + size_t size; + // We need to check how many bytes is it possible to read until the EOF. + try { + // We need to read from file exactly the minimum value of 'size' given by user and the + // 'size ' to avoid returning array with redundant data (which would be equal to 0). + size = file_bytes_to_eof(fh->second->file_handle); + if (args.contains("size")) { + // If user passed 'size' parameter, we need to read at most 'size' bytes. + double size_double = args.get("size").get<double>(); + if (std::string::npos <= static_cast<unsigned long long>(size_double)) { + LogAndReportError(InvalidValuesException("Invalid size was given"), out); + return; + } + size = std::min(static_cast<size_t>(size_double), size); + } + } catch (const std::system_error& e) { + LogAndReportError(IOException(e.what()), out); + return; + } + + LoggerD("size: %zu", size); + + auto handle = fh->second; + + auto logic = [handle, size](decltype(out) out) { + try { + std::vector<std::uint8_t> data = read_file(handle->file_handle, size); + out["result"] = picojson::value(picojson::string_type, true); + encode_binary_in_string(data, out["result"].get<std::string>()); + } catch (std::runtime_error& e) { + LoggerE("Cannot read, cause: %s", e.what()); + LogAndReportError(IOException(e.what()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleWriteData(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_EXIST(args, "id", out) + CHECK_EXIST(args, "data", out) + const auto& str = args.get("data").get<std::string>(); + const int fh_id = static_cast<int>(args.get("id").get<double>()); + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LoggerE("FileHandle with id: %d not found", fh_id); + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [str, handle](decltype(out) out) { + try { + std::vector<std::uint8_t> bytes; + decode_binary_from_string(str, bytes); + write_file(bytes.data(), bytes.size(), handle->file_handle); + } catch (std::runtime_error& e) { + LogAndReportError(IOException(e.what()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleFlush(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast<int>(args.get("id").get<double>()); + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle](decltype(out) out) { + int ret = fflush(handle->file_handle); + if (ret) { + std::string error_message = + std::string("flush failed, error message: ") + GetErrorString(errno); + LogAndReportError(IOException(error_message.c_str()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleSync(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast<int>(args.get("id").get<double>()); + + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + auto handle = fh->second; + + auto logic = [handle](decltype(out) out) { + int ret = fsync(fileno(handle->file_handle)); + if (ret) { + std::string error_message = + std::string("sync failed, error message: ") + GetErrorString(errno); + LogAndReportError(IOException(error_message.c_str()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + logic(out); + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + +void FilesystemInstance::FileHandleClose(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + const int fh_id = static_cast<int>(args.get("id").get<double>()); + auto fh = opened_files.find(fh_id); + if (opened_files.end() == fh) { + LogAndReportError(IOException("Invalid FileHandle"), out); + return; + } + + std::shared_ptr<FileHandle> handle = fh->second; + opened_files.erase(fh); + + auto logic = [handle](decltype(out) out) { + if (!handle->file_handle) { + LogAndReportError(IOException("File handle already closed."), out); + return; + } + int ret = fclose(handle->file_handle); + handle->file_handle = nullptr; + if (ret) { + std::string error_message = + std::string("close failed, error message: ") + GetErrorString(errno); + LogAndReportError(IOException(error_message.c_str()), out); + } + }; + + bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true; + + if (blocking) { + bool ready = false; + bool done = false; + std::mutex mutex; + std::condition_variable conditional_variable; + // adding empty job to worker's queue, in order to wait for all jobs to be done before closing + // FILE* + this->worker.add_job([] {}, + [&conditional_variable, &mutex, &ready, &done, logic, &out] { + // wait for close + std::unique_lock<std::mutex> lock(mutex); + conditional_variable.wait(lock, [&ready] { return ready; }); + + logic(out); + done = true; + conditional_variable.notify_one(); + }); + + { + // let know that close is ready + std::unique_lock<std::mutex> lock(mutex); + ready = true; + } + conditional_variable.notify_one(); + + { + // wait for worker + std::unique_lock<std::mutex> lock(mutex); + conditional_variable.wait(lock, [&done] { return done; }); + } + handle->file_handle = nullptr; + } else { + // Async logic + double callback_id = args.get("callbackId").get<double>(); + this->worker.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get<picojson::object>(); + async_out["callbackId"] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } +} + #undef CHECK_EXIST } // namespace filesystem diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index 07fb8fa8..2de2da3b 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -17,20 +17,46 @@ #ifndef FILESYSTEM_FILESYSTEM_INSTANCE_H_ #define FILESYSTEM_FILESYSTEM_INSTANCE_H_ +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <cerrno> +#include <cstdio> #include "common/extension.h" #include "common/filesystem/filesystem_storage.h" +#include "common/worker.h" #include "filesystem/filesystem_manager.h" #include "filesystem_utils.h" namespace extension { namespace filesystem { +class FileHandle; + +typedef std::map<int, std::shared_ptr<FileHandle>> FileHandleMap; + +class FileHandle { + public: + FileHandle(FILE* file_handle) : file_handle(file_handle){}; + ~FileHandle(); + + FileHandle(const FileHandle&) = delete; + FileHandle(FileHandle&&) = delete; + FileHandle& operator=(const FileHandle&) = delete; + FileHandle& operator=(FileHandle&&) = delete; + + FILE* file_handle; +}; + class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeListener { public: FilesystemInstance(); virtual ~FilesystemInstance(); private: + FileHandleMap opened_files; + common::Worker worker; + void FileCreateSync(const picojson::value& args, picojson::object& out); void FileRename(const picojson::value& args, picojson::object& out); void FileStat(const picojson::value& args, picojson::object& out); @@ -54,6 +80,70 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL void onFilesystemStateChangeSuccessCallback(const common::Storage& storage); void PrepareError(const FilesystemError& error, picojson::object& out); void FileSystemManagerGetCanonicalPath(const picojson::value& args, picojson::object& out); + + void FileSystemManagerOpenFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerCreateDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerDeleteFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerDeleteDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerCopyFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerCopyDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerMoveFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerMoveDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerRename(const picojson::value& args, picojson::object& out); + void FileSystemManagerListDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerIsFile(const picojson::value& args, picojson::object& out); + void FileSystemManagerIsDirectory(const picojson::value& args, picojson::object& out); + void FileSystemManagerPathExists(const picojson::value& args, picojson::object& out); + void FileSystemManagerToURI(const picojson::value& args, picojson::object& out); + void FileSystemManagerGetLimits(const picojson::value& args, picojson::object& out); + + /** + * @brief wrapper for std::fseek function. + * Sets the file position indicator for the file stream stream. + * @parameter out has always set status, either success or error. + */ + void FileHandleSeek(const picojson::value& args, picojson::object& out); + + /** + * @brief Reads file contents as string. + * @parameter out has always set status, either success or error. + * In case of success, string value is passed. + */ + void FileHandleReadString(const picojson::value& args, picojson::object& out); + + /** + * @brief Writes string to file. + * @parameter out has always set status, either success or error. + */ + void FileHandleWriteString(const picojson::value& args, picojson::object& out); + + /** + * @brief Reads file contents as binary data, can use worker and not block GUI. + * @parameter out has always set status, either success or error. + * In case of success, encoded uint8_t array is passed. + */ + void FileHandleReadData(const picojson::value& args, picojson::object& out); + + /** + * @brief Writes binary data to file, can use worker and not block GUI. + * @parameter out has always set status, either success or error. + */ + void FileHandleWriteData(const picojson::value& args, picojson::object& out); + + /** + * @brief wrapper for std::fflush function. + * Writes any unwritten data from the stream's buffer to the associated output device. + * @parameter out has always set status, either success or error. + */ + void FileHandleFlush(const picojson::value& args, picojson::object& out); + void FileHandleSync(const picojson::value& args, picojson::object& out); + + /** + * @brief wrapper for std::fclose function. + * Closes the given file stream. + * @parameter out has always set status, either success or error. + */ + void FileHandleClose(const picojson::value& args, picojson::object& out); }; } // namespace filesystem diff --git a/src/filesystem/filesystem_manager.cc b/src/filesystem/filesystem_manager.cc index 755defb5..8ca885d3 100644 --- a/src/filesystem/filesystem_manager.cc +++ b/src/filesystem/filesystem_manager.cc @@ -196,7 +196,7 @@ FilesystemError make_directory_worker(const std::string& path) { } } - std::string parent_path = FilesystemUtils::get_dirname(path); + std::string parent_path = FilesystemUtils::GetDirname(path); auto parent_result = make_directory_worker(parent_path); if (parent_result == FilesystemError::DirectoryExists) { @@ -289,8 +289,7 @@ void FilesystemManager::MakeDirectory(const std::string& path, result_cb(make_directory_worker(path)); } -// pass oldPath by copy to prevent possible TOCTOU bug -void FilesystemManager::Rename(const std::string oldPath, const std::string& newPath, +void FilesystemManager::Rename(const std::string& oldPath, const std::string& newPath, const std::function<void(const FilesystemStat&)>& success_cb, const std::function<void(FilesystemError)>& error_cb) { ScopeLogger(); diff --git a/src/filesystem/filesystem_manager.h b/src/filesystem/filesystem_manager.h index eaadf9ba..e0d2d12b 100644 --- a/src/filesystem/filesystem_manager.h +++ b/src/filesystem/filesystem_manager.h @@ -69,7 +69,7 @@ class FilesystemManager { const std::function<void(const FilesystemStat&)>& success_cb, const std::function<void(FilesystemError)>& error_cb); - void Rename(const std::string oldPath, const std::string& newPath, + void Rename(const std::string& oldPath, const std::string& newPath, const std::function<void(const FilesystemStat&)>& success_cb, const std::function<void(FilesystemError)>& error_cb); diff --git a/src/filesystem/filesystem_stat.cc b/src/filesystem/filesystem_stat.cc index bb5bc89b..feb7bed6 100644 --- a/src/filesystem/filesystem_stat.cc +++ b/src/filesystem/filesystem_stat.cc @@ -59,8 +59,7 @@ picojson::value FilesystemStat::toJSON() const { return retval; } -// pass path by copy to prevent possible TOCTOU bug -FilesystemStat FilesystemStat::getStat(const std::string path) { +FilesystemStat FilesystemStat::getStat(const std::string& path) { ScopeLogger(); struct stat aStatObj; FilesystemStat _result; diff --git a/src/filesystem/filesystem_stat.h b/src/filesystem/filesystem_stat.h index 31c58875..a984ab1d 100755 --- a/src/filesystem/filesystem_stat.h +++ b/src/filesystem/filesystem_stat.h @@ -49,7 +49,7 @@ class FilesystemStat { picojson::value toJSON() const; - static FilesystemStat getStat(const std::string path); + static FilesystemStat getStat(const std::string& path); }; } // namespace filesystem } // namespace extension diff --git a/src/filesystem/filesystem_utils.cc b/src/filesystem/filesystem_utils.cc index c6d04e5a..00b8c819 100644 --- a/src/filesystem/filesystem_utils.cc +++ b/src/filesystem/filesystem_utils.cc @@ -14,27 +14,350 @@ * limitations under the License. */ #include "filesystem_utils.h" +#include "common/logger.h" +#include "common/platform_exception.h" +#include <dirent.h> +#include <fcntl.h> +#include <ftw.h> #include <glib.h> #include <libgen.h> -#include "common/logger.h" +#include <sys/sendfile.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> namespace FilesystemUtils { -std::string get_storage_dir_path(int id, storage_directory_e typeToCheck) { +using namespace std::string_literals; +using namespace common; +using common::tools::ReportError; +using common::tools::GetErrorString; + +void Mkdir(const std::string& path) { + ScopeLogger("%s", path.c_str()); + int ret = ::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + + if (0 != ret) { + throw std::system_error{errno, std::generic_category()}; + } +} + +void Mkdir(const std::string& path, bool parents) { + // ScopeLogger("%s, %d", path.c_str(), parents); // disabled in recursive function + if (!parents) { + Mkdir(path); + return; + } + + struct ::stat buf; + std::vector<std::string> stack; + // iterate path up to first existing element + for (std::string s = path; 0 != ::stat(s.c_str(), &buf); s = Dirname(s)) { + if (ENOENT == errno) { + stack.push_back(s); + } else { + throw std::system_error{errno, std::generic_category()}; + } + } + + if (stack.empty()) { // this means, that path exists, let Mkdir handle error + Mkdir(path); + return; + } + + // from top to bottom + for (auto rit = stack.rbegin(); rit != stack.rend(); ++rit) { + try { + Mkdir(*rit); + } catch (const std::system_error& error) { + if (stack.rbegin() != rit) { + try { + RemoveDirectoryRecursively(*stack.rbegin()); + } catch (const std::system_error& removalError) { + LoggerW( + "Could not remove parent directories created so far: %s." + "Some of them might still exist", + removalError.what()); + } + } + throw; + } + } +} + +void Unlink(const std::string& path) { ScopeLogger(); - char* platformPath = NULL; - int result = storage_get_directory(id, typeToCheck, &platformPath); - if (STORAGE_ERROR_NONE != result) { - LoggerD("Cannot retrieve path for type %i: %d (%s)", typeToCheck, result, - get_error_message(result)); - return std::string(); + int ret = ::unlink(path.c_str()); + if (0 != ret) { + throw std::system_error{errno, std::generic_category()}; } - std::string path = std::string(platformPath); - free(platformPath); - return path; } -std::string get_dirname(const std::string& path) { +std::string PosixBasename(const std::string& path) { + ScopeLogger(); + char* s = ::strdup(path.c_str()); + if (!s) { + throw std::system_error{errno, std::generic_category(), path}; + } + + // basename will never fail + std::string name{::basename(s)}; + free(s); + return name; +} + +std::string Dirname(const std::string& path) { + ScopeLogger(); + char* s = ::strdup(path.c_str()); + if (!s) { + throw std::system_error{errno, std::generic_category(), path}; + } + // dirname will never fail + std::string dir{::dirname(s)}; + free(s); + return dir; +} + +void CopyFileOverExistingDirectory(const std::string& src, const std::string& dest, + bool overwrite) { + ScopeLogger("From: %s, To %s", src.c_str(), dest.c_str()); + struct stat buf {}; + if (CheckIfExists(dest, &buf) && CheckIfDir(buf)) { + if (overwrite) { + RemoveDirectoryRecursively(dest); + } else { + throw std::system_error{EIO, std::generic_category(), + "Failed to copy file: overwrite is not allowed."}; + } + } + CopyFile(src, dest, overwrite); +} + +void CopyFile(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("From: %s; To %s", src.c_str(), dest.c_str()); + + GError* error = nullptr; + auto source_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>( + g_file_new_for_path(src.c_str()), g_object_unref); + auto dest_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>( + g_file_new_for_path(dest.c_str()), g_object_unref); + int flags = G_FILE_COPY_ALL_METADATA; + if (overwrite) { + flags |= G_FILE_COPY_OVERWRITE; + } + + gboolean success = + g_file_copy(source_ptr.get(), dest_ptr.get(), static_cast<GFileCopyFlags>(flags), nullptr, + nullptr, nullptr, &error); + if (!success) { + std::string why{}; + if (error) { + why = error->message; + g_error_free(error); + } + throw std::system_error{EIO, std::generic_category(), "Failed to copy file: "s + why}; + } +} + +void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("From: %s, To %s", src.c_str(), dest.c_str()); + ListDirectory(src, [&](const char* name, unsigned char type) { + if (DT_DIR == type) { + std::string dest_dir = dest + '/' + name; + struct stat buf {}; + bool exists = CheckIfExists(dest_dir, &buf); + if (exists && !CheckIfDir(buf)) { + if (overwrite) { + Unlink(dest_dir); + Mkdir(dest_dir); + } else { + throw std::system_error{EIO, std::generic_category(), + "Failed to copy directory: overwriting is not allowed."}; + } + } else if (!exists) { + Mkdir(dest_dir); + } + + CopyDirectory(src + '/' + name, dest_dir, overwrite); + } else { // copying of regular files as well as other types of items pointed by src + CopyFileOverExistingDirectory(src + '/' + name, dest + '/' + name, overwrite); + } + // Set errno to 0 to prevent from reporting readdir error after successful iterating through + // directory. + errno = 0; + }); +} + +void ListDirectory(const std::string& path, std::function<void(const char*, unsigned char)> next) { + ScopeLogger("%s", path.c_str()); + DIR* d = ::opendir(path.c_str()); + if (nullptr == d) { + throw std::system_error{errno, std::generic_category(), + "Failed to open directory: "s + GetErrorString(errno)}; + } + + std::unique_ptr<DIR, void (*)(DIR*)> dir_ptr(d, [](DIR* d) { + if (::closedir(d)) { + LoggerW("closedir failed"); + } + }); + errno = 0; + + for (dirent* entry; (entry = ::readdir(d));) { + if (0 == std::strcmp(entry->d_name, ".") || 0 == std::strcmp(entry->d_name, "..")) { + continue; + } + next(entry->d_name, entry->d_type); + } + + if (0 != errno) { + throw std::system_error{errno, std::generic_category(), + "Failed to read directory: "s + GetErrorString(errno)}; + } +} + +void RemoveDirectoryRecursively(const std::string& path) { + ScopeLogger("%s", path.c_str()); + auto res = + nftw(path.c_str(), + [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int { + // if number of nested directories is large + // below log could decrease readability + // ScopeLogger("%s", fpath); + + auto res = remove(fpath); + if (res) { + LoggerD("Failed to remove %s: %s", fpath, GetErrorString(errno).c_str()); + return errno; + } + return 0; + }, + 128, FTW_DEPTH | FTW_PHYS); + + if (res) { + if (-1 == res) { + // -1 can be returned by nftw() function, to prevent invalid translation of error in + // std::system_error constructor, such situation will be treated as generic IOError + res = EIO; + } + throw std::system_error{res, std::generic_category(), + "Failed to remove directory recursively: "s + GetErrorString(res)}; + } +} + +void RemoveDirectory(const std::string& path) { + ScopeLogger(); + if (rmdir(path.c_str())) { + throw std::system_error{errno, std::generic_category(), "Failed to remove directory"}; + } +} + +std::string RealPath(const std::string& path) { + ScopeLogger(); + char* real_path = realpath(path.c_str(), nullptr); + if (nullptr == real_path) { + throw std::system_error{errno, std::generic_category(), "Path is not valid."}; + } + std::string s{real_path}; + free(real_path); + return s; +} + +bool CheckIfExists(const std::string& path, struct stat* buf) { + ScopeLogger(); + if (stat(path.c_str(), buf)) { + if (ENOENT == errno) { + return false; + } else { + throw std::system_error{errno, std::generic_category(), + "Unable to check file existence: "s + GetErrorString(errno)}; + } + } + return true; +} + +bool CheckIfDir(const struct stat& buf) { + ScopeLogger(); + if (S_ISDIR(buf.st_mode)) { + return true; + } + return false; +} + +bool CheckIfFile(const struct stat& buf) { + ScopeLogger(); + if (S_ISREG(buf.st_mode)) { + return true; + } + return false; +} + +void Rename(const std::string& path, const std::string& new_path) { + ScopeLogger(); + if (::rename(path.c_str(), new_path.c_str())) { + throw std::system_error{errno, std::generic_category(), + "Unable to rename file or directory: "s + GetErrorString(errno)}; + } +} + +void MoveFile(const std::string& path, const std::string& new_path, bool overwrite) { + ScopeLogger(); + GError* error = nullptr; + auto source_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>( + g_file_new_for_path(path.c_str()), g_object_unref); + auto dest_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>( + g_file_new_for_path(new_path.c_str()), g_object_unref); + int flags = G_FILE_COPY_ALL_METADATA; + if (overwrite) { + flags |= G_FILE_COPY_OVERWRITE; + } + gboolean success = + g_file_move(source_ptr.get(), dest_ptr.get(), static_cast<GFileCopyFlags>(flags), nullptr, + nullptr, nullptr, &error); + if (!success) { + std::string why{}; + if (error) { + why = error->message; + g_error_free(error); + } + throw std::system_error{EIO, std::generic_category(), "Failed to move file: "s + why}; + } +} + +void MoveDirectory(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("%s %s", src.c_str(), dest.c_str()); + struct stat buf {}; + const std::string& new_path = dest + '/' + PosixBasename(src); + // If directories are on the same mount point, we can simply try to rename them. + // However, it might be done only if new_path does not exist because move_directory should merge + // directories. + if (!CheckIfExists(new_path, &buf)) { + LoggerD("new_path %s", new_path.c_str()); + auto result = ::rename(src.c_str(), new_path.c_str()); + if (!result) { + return; + } else if (EXDEV != errno) { + // The directories are in the same mount point, but the operation has just failed. + throw std::system_error{EIO, std::generic_category(), + "Unable to move directory: "s + GetErrorString(errno)}; + } + } + + // Move directory to other move point. + CopyDirectory(src, dest, overwrite); + RemoveDirectoryRecursively(src); +} + +void TranslateException(const std::system_error& e, picojson::object& obj) { + ScopeLogger(); + if (std::errc::no_such_file_or_directory == e.code()) { + LogAndReportError(NotFoundException(e.what()), obj); + } else { + LogAndReportError(IOException(e.what()), obj); + } +} + +std::string GetDirname(const std::string& path) { ScopeLogger(); char* dir = g_path_get_dirname(path.c_str()); if (dir) { @@ -45,11 +368,4 @@ std::string get_dirname(const std::string& path) { return std::string("."); } } - -std::string get_basename(const std::string& path) { - ScopeLogger(); - // basename will modify content: pass a copy - std::string buf = path.c_str(); - return std::string(basename(const_cast<char*>(buf.c_str()))); -} } diff --git a/src/filesystem/filesystem_utils.h b/src/filesystem/filesystem_utils.h index f59048e9..58882847 100644 --- a/src/filesystem/filesystem_utils.h +++ b/src/filesystem/filesystem_utils.h @@ -17,9 +17,16 @@ #ifndef FILESYSTEM_FILESYSTEM_UTILS_H #define FILESYSTEM_FILESYSTEM_UTILS_H -#include <storage-expand.h> -#include <string> #include "common/picojson.h" +#include "common/tools.h" + +#include <gio/gio.h> +#include <glib-object.h> +#include <sys/stat.h> +#include <functional> +#include <memory> +#include <string> +#include <system_error> namespace extension { namespace filesystem { @@ -40,14 +47,111 @@ enum class FilesystemError { namespace FilesystemUtils { /** - * @brief get_storage_dir_path attempts to get path from storage. - * If path cannot be retrieved then an empty string is returned. - * + * @brief Wrapper for POSIX mkdir function. + * @throw std::system_error + */ +void Mkdir(const std::string& path); + +/** + * @brief Make directory using mkdir. If 'parents' is true, make parent directories as needed + * @throw std::system_error */ -std::string get_storage_dir_path(int id, storage_directory_e typeToCheck); +void Mkdir(const std::string& path, bool parents); + +/** + * @brief Wrapper for POSIX unlink function + * @throw std::system_error + */ +void Unlink(const std::string& path); + +/** + * @brief Returns last element of path (wrapper for POSIX basename function) + * @throw std::system_error + */ +std::string PosixBasename(const std::string& path); + +/** + * @brief Returns parent directory of path (wrapper for POSIX dirname function) + * @throw std::system_error + */ +std::string Dirname(const std::string& path); + +/** + * @brief Wrapper for GLib g_file_copy function. + * @throw std::system_error + */ +void CopyFile(const std::string& src, const std::string& dest, bool overwrite); + +/** + * @brief Copies directory recursively + * @throw std::system_error + */ +void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite); + +/** + * @brief Calls 'next' function with name for every entry in given directory + * @throw std::system_error + */ +void ListDirectory(const std::string& path, std::function<void(const char*, unsigned char)> next); + +/** + * @brief Removes directory recursively pointed by path. + * @throw std::system_error + */ +void RemoveDirectoryRecursively(const std::string& path); + +/** + * @brief Removes directory pointed by path. + * @throw std::system_error + */ +void RemoveDirectory(const std::string& path); + +/** + * @brief Returns the real path. + * @throw std::system_error + */ +std::string RealPath(const std::string& path); + +/** + * @brief Checks if path points to file or directory. + * @throw std::system_error + */ +bool CheckIfExists(const std::string& path, struct stat* buf); + +/** + * @brief Checks if path points to directory. + * @throw std::system_error + */ +bool CheckIfDir(const struct stat& buf); + +/** + * @brief Checks if path points to file. + * @throw std::system_error + */ +bool CheckIfFile(const struct stat& buf); + +/** + * @brief Renames file or directory. + * @throw std::system_error + */ +void Rename(const std::string& path, const std::string& new_path); + +/** + * @brief Wrapper for GLib g_file_move function. + * @throw std::system_error + */ +void MoveFile(const std::string& path, const std::string& new_path, bool overwrite); + +/** + * @brief Moves directory by recursively calling move_file. + * @throw std::system_error + */ +void MoveDirectory(const std::string& path, const std::string& new_path, bool overwrite); + +void TranslateException(const std::system_error& e, picojson::object& obj); -std::string get_dirname(const std::string& path); -std::string get_basename(const std::string& path); +// This function is left only for compatibility with previous implementation in FilesystemManager +std::string GetDirname(const std::string& path); } #endif // FILESYSTEM_FILESYSTEM_UTILS_H diff --git a/src/filesystem/js/common.js b/src/filesystem/js/common.js index 30dc3594..f90e6537 100644 --- a/src/filesystem/js/common.js +++ b/src/filesystem/js/common.js @@ -22,17 +22,39 @@ var validator_ = privUtils_.validator; var types_ = validator_.Types; var native_ = new xwalk.utils.NativeManager(extension); +/* + * Create new array-like object of numbers: UTF-16 char codes from string. + * As type pass Array, Uint8Array, etc. + * Useful for passing data through crosswalk. + */ +function StringToArray(str, type) { + var len = str.length; + var output = new type(len); + for (var i = 0; i < len; i++) { + output[i] = str.charCodeAt(i); + } + return output; +} + +/* + * Pass array-like object of numbers (Array, Uint8Array, etc.), returns string. + * Each char has codepoint equal to value from array cropped with & 0xFF + * Useful for passing data through crosswalk. + */ +function ArrayToString(data) { + var output = ''; + var len = data.length; + for (var i = 0; i < len; i++) { + output += String.fromCharCode(data[i] & 0xff); // conversion to octet + } + return output; +} + function SetReadOnlyProperty(obj, n, v) { - Object.defineProperty(obj, n, { - value: v, - writable: false - }); + Object.defineProperty(obj, n, { value: v, writable: false }); } -var FileSystemStorageType = { - INTERNAL: 'INTERNAL', - EXTERNAL: 'EXTERNAL' -}; +var FileSystemStorageType = { INTERNAL: 'INTERNAL', EXTERNAL: 'EXTERNAL' }; var FileSystemStorageState = { MOUNTED: 'MOUNTED', @@ -40,17 +62,14 @@ var FileSystemStorageState = { UNMOUNTABLE: 'UNMOUNTABLE' }; -var FileMode = { - r: 'r', - rw: 'rw', - w: 'w', - a: 'a' -}; +var FileMode = { a: 'a', r: 'r', rw: 'rw', rwo: 'rwo', w: 'w' }; + +var BaseSeekPosition = { BEGIN: 'BEGIN', CURRENT: 'CURRENT', END: 'END' }; var tizen24home = '/opt/usr/media'; -// this variable need to match same variable -// in common/filesystem/filesystem_provider_storage.cc +// this variable need to match same variable in +// common/filesystem/filesystem_provider_storage.cc var kVirtualRootImages = 'images'; var commonFS_ = (function() { @@ -244,9 +263,10 @@ var commonFS_ = (function() { } else { _fileRealPath = aPath; } - // this line makes that '.' and '..' is supported in paths, - // but each method handle those cases - // and return error (see commonFS_.checkPathWithoutDots() method) + // removeDotsFromPath execution here, results with '.' and '..' beeing + // supported in paths, next methods throw an error when getting argument + // with '.' or '..' in it + // (see commonFS_.checkPathWithoutDots() method) _fileRealPath = removeDotsFromPath(_fileRealPath); // convert path to be compatibile with previous version of Tizen // (global paths usage issue workaround) @@ -386,11 +406,7 @@ var commonFS_ = (function() { } function cloneStorage(storage) { - return { - label: storage.label, - type: storage.type, - state: storage.state - }; + return { label: storage.label, type: storage.type, state: storage.state }; } function getStorage(label) { @@ -438,6 +454,7 @@ var commonFS_ = (function() { f_isSubDir: f_isSubDir, f_isCorrectRelativePath: f_isCorrectRelativePath, getStorage: getStorage, - getAllStorages: getAllStorages + getAllStorages: getAllStorages, + mergeMultipleSlashes: mergeMultipleSlashes }; })(); diff --git a/src/filesystem/js/file.js b/src/filesystem/js/file.js index aa6fb1ed..0848a8d2 100644 --- a/src/filesystem/js/file.js +++ b/src/filesystem/js/file.js @@ -60,6 +60,11 @@ function File(data) { } function toURI() { + privUtils_.warn( + 'DEPRECATION WARNING: File.toURI() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.toURI() instead.' + ); + xwalk.utils.checkPrivilegeAccess(xwalk.utils.privilege.FILESYSTEM_READ); return 'file://' + commonFS_.toRealPath(this.fullPath); } @@ -71,10 +76,10 @@ File.prototype.toURI = function() { function stringToRegex(str) { var _regString = '^'; if (str === '') { - return new RegExp(_regString, 'i'); + return new RegExp(_regString + '$', 'i'); } - // single '\' sign is not visible in JS string, - // escaping % wildcard need to be done by '\\%' + // single '\' sign is not visible in JS string, escaping % wildcard need to + // be done by '\\%' str = str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); var _percentTokens = str.split('%'); @@ -82,8 +87,8 @@ function stringToRegex(str) { for (i = 0; i < _percentTokens.length - 1; ++i) { _regString = _regString + _percentTokens[i]; if (_regString[_regString.length - 1] === '\\') { - // special handling \\% sequence - '%' sign - // is threaten as regular sign - not wildcard + // special handling \\% sequence - '%' sign is threaten as + // regular sign - not wildcard _regString = _regString.split(''); _regString.pop(); _regString = _regString.join('') + '%'; @@ -165,6 +170,11 @@ function checkFile(file, fileFilter) { } function listFiles() { + privUtils_.warn( + 'DEPRECATION WARNING: File.listFiles() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.listDirectory() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'onsuccess', type: types_.FUNCTION }, { name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true }, @@ -253,6 +263,11 @@ function _checkEncoding(encoding) { } function openStream() { + privUtils_.warn( + 'DEPRECATION WARNING: File.openStream() is deprecated since Tizen 5.0. ' + + 'Use FileHandle interface to read/write operations instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'mode', type: types_.ENUM, values: ['r', 'rw', 'w', 'a'] }, { name: 'onsuccess', type: types_.FUNCTION }, @@ -309,6 +324,11 @@ File.prototype.openStream = function() { }; function readAsText() { + privUtils_.warn( + 'DEPRECATION WARNING: File.readAsText() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.readString() or FileHandle.readStringNonBlocking() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'onsuccess', type: types_.FUNCTION }, { name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true }, @@ -364,6 +384,12 @@ File.prototype.readAsText = function() { }; function copyTo() { + privUtils_.warn( + 'DEPRECATION WARNING: File.copyTo() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.CopyFile() or FileSystemManager.CopyDirectory() ' + + 'instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'originFilePath', type: types_.STRING }, { name: 'destinationFilePath', type: types_.STRING }, @@ -560,6 +586,12 @@ File.prototype.copyTo = function() { }; function moveTo() { + privUtils_.warn( + 'DEPRECATION WARNING: File.moveTo() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.moveFile() or FileSystemManager.moveDirectory() ' + + 'instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'originFilePath', type: types_.STRING }, { name: 'destinationFilePath', type: types_.STRING }, @@ -710,6 +742,11 @@ File.prototype.moveTo = function() { }; function createDirectory() { + privUtils_.warn( + 'DEPRECATION WARNING: File.createDirectory() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.createDirectory() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'dirPath', type: types_.STRING } ]); @@ -788,6 +825,11 @@ File.prototype.createDirectory = function() { }; function createFile() { + privUtils_.warn( + 'DEPRECATION WARNING: File.createFile() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.createFile() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'relativeFilePath', type: types_.STRING } ]); @@ -855,6 +897,11 @@ File.prototype.createFile = function() { }; function resolveFile() { + privUtils_.warn( + 'DEPRECATION WARNING: File.resolve() is deprecated since Tizen 5.0. ' + + 'Use FileHandle and FileSystemManager interfaces instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'filePath', type: types_.STRING } ]); @@ -915,6 +962,11 @@ File.prototype.resolve = function() { }; function deleteDirectory() { + privUtils_.warn( + 'DEPRECATION WARNING: File.deleteDirectory() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.deleteDirectory() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'directoryPath', type: types_.STRING }, { name: 'recursive', type: types_.BOOLEAN }, @@ -1044,6 +1096,11 @@ File.prototype.deleteDirectory = function() { }; function deleteFile() { + privUtils_.warn( + 'DEPRECATION WARNING: File.deleteFile() is deprecated since Tizen 5.0. ' + + 'Use FileSystemManager.deleteFile() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'filePath', type: types_.STRING }, { name: 'onsuccess', type: types_.FUNCTION, optional: true, nullable: true }, diff --git a/src/filesystem/js/file_handle.js b/src/filesystem/js/file_handle.js new file mode 100644 index 00000000..47ab9836 --- /dev/null +++ b/src/filesystem/js/file_handle.js @@ -0,0 +1,802 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function FileHandle(_id, _path, _mode) { + Object.defineProperties(this, { + id: { value: _id, writable: false, enumerable: false }, + path: { value: _path, writable: false, enumerable: true }, + mode: { value: _mode, writable: false, enumerable: false }, + state: { value: 'opened', writable: true, enumerable: false } + }); +} + +FileHandle.prototype.seek = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'offset', type: types_.LONG }, + { + name: 'whence', + type: types_.ENUM, + values: type_.getValues(BaseSeekPosition), + optional: true + } + ]); + + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + var data = { id: this.id, offset: args.offset }; + if (undefined === args.whence) { + data.whence = 'BEGIN'; + } else { + data.whence = args.whence; + } + var result = native_.callSync('FileHandle_seek', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); +}; + +FileHandle.prototype.seekNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'offset', type: types_.LONG }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { + name: 'whence', + type: types_.ENUM, + values: type_.getValues(BaseSeekPosition), + optional: true + } + ]); + + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + var data = { id: this.id, offset: args.offset, blocking: false }; + if (undefined === args.whence) { + data.whence = 'BEGIN'; + } else { + data.whence = args.whence; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_seek', data, callback); + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + }, 0); + } +}; + +FileHandle.prototype.readString = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'count', type: types_.LONG, optional: true, nullable: true }, + { name: 'inputEncoding', type: types_.STRING, optional: true } + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'w' || this.mode === 'a') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is write-only' + ); + } + var data = { id: this.id, encoding: args.inputEncoding }; + if (!type_.isNullOrUndefined(args.count)) { + data.count = args.count; + } + var result = native_.callSync('FileHandle_readString', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); +}; + +FileHandle.prototype.readStringNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'count', type: types_.LONG, optional: true }, + { name: 'inputEncoding', type: types_.STRING, optional: true } + ]); + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + if (this.mode === 'w' || this.mode === 'a') { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is write-only' + ) + ); + }, 0); + return; + } + var data = { id: this.id, encoding: args.inputEncoding, blocking: false }; + if (!type_.isNullOrUndefined(args.count)) { + data.count = args.count; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_readString', data, callback); + + if (native_.isFailure(result)) { + var err = native_.getErrorObject(result); + if ('IOError' === err.name) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } else { + throw native_.getErrorObject(result); + } + } +}; + +FileHandle.prototype.writeString = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'string', type: types_.STRING }, + { name: 'outputEncoding', type: types_.STRING, optional: true } + ]); + if (!('opened' === this.state)) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if ('r' === this.mode) { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ); + } + var data = { id: this.id, string: args.string, encoding: args.outputEncoding }; + var result = native_.callSync('FileHandle_writeString', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + return native_.getResultObject(result); +}; + +FileHandle.prototype.writeStringNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'string', type: types_.STRING }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'outputEncoding', type: types_.STRING, optional: true } + ]); + if (!('opened' === this.state)) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + if ('r' === this.mode) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ) + ); + }, 0); + return; + } + var data = { + id: this.id, + string: args.string, + encoding: args.outputEncoding, + blocking: false + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_writeString', data, callback); + + if (native_.isFailure(result)) { + var err = native_.getErrorObject(result); + if ('IOError' === err.name) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } else { + throw native_.getErrorObject(result); + } + } +}; + +FileHandle.prototype.readBlob = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'size', type: types_.LONG, optional: true } + ]); + + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'w' || this.mode === 'a') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is write-only' + ); + } + var data = { id: this.id }; + if (!type_.isNullOrUndefined(args.size)) { + data.size = args.size; + } + var result = native_.call('FileHandle_readData', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + var encodedData = native_.getResultObject(result); + var data = StringToArray(encodedData, Uint8Array); + return new Blob([data]); +}; + +FileHandle.prototype.readBlobNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'size', type: types_.LONG, optional: true, nullable: true } + ]); + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + + var data = { id: this.id, blocking: false }; + if (!type_.isNullOrUndefined(args.size)) { + data.size = args.size; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var encodedData = native_.getResultObject(result); + var data = StringToArray(encodedData, Uint8Array); + native_.callIfPossible(args.successCallback, new Blob([data])); + } + }; + + var result = native_.call('FileHandle_readData', data, callback); + + if (native_.isFailure(result)) { + var err = native_.getErrorObject(result); + if ('IOError' === err.name) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } else { + throw native_.getErrorObject(result); + } + } +}; + +function blobToUint8Array(b) { + var uri = URL.createObjectURL(b), + xhr = new XMLHttpRequest(), + i, + ui8; + xhr.open('GET', uri, false); + xhr.send(); + URL.revokeObjectURL(uri); + var stringUtf8 = unescape(encodeURIComponent(xhr.response)); + ui8 = new Uint8Array(stringUtf8.length); + for (i = 0; i < stringUtf8.length; ++i) { + ui8[i] = stringUtf8.charCodeAt(i); + } + return ui8; +} + +FileHandle.prototype.writeBlob = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'blob', type: types_.PLATFORM_OBJECT, values: Blob } + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ); + } + + var encodedData = ArrayToString(blobToUint8Array(args.blob)); + var data = { id: this.id, data: encodedData }; + var result = native_.callSync('FileHandle_writeData', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.writeBlobNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'blob', type: types_.PLATFORM_OBJECT, values: Blob }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + if (!('opened' === this.state)) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } else if (this.mode === 'r') { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ) + ); + }, 0); + return; + } + + var encodedData = ArrayToString(blobToUint8Array(args.blob)); + var data = { id: this.id, data: encodedData, blocking: false }; + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_writeData', data, callback); + + // Only IOError is possible to be returned synchronously, so it is passed to + // errorCallback in each case. + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + }, 0); + return; + } +}; + +FileHandle.prototype.readData = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'size', type: types_.LONG, optional: true } + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'w' || this.mode === 'a') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is write-only' + ); + } + var data = { id: this.id }; + if (!type_.isNullOrUndefined(args.size)) { + data.size = args.size; + } + var result = native_.callSync('FileHandle_readData', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + var encodedData = native_.getResultObject(result); + var data = StringToArray(encodedData, Uint8Array); + return new Uint8Array(data); +}; + +FileHandle.prototype.readDataNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'size', type: types_.LONG, optional: true, nullable: true } + ]); + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + if (this.mode === 'w' || this.mode === 'a') { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is write-only' + ) + ); + }, 0); + return; + } + + var data = { id: this.id, blocking: false }; + if (!type_.isNullOrUndefined(args.size)) { + data.size = args.size; + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var data_out = new Uint8Array( + StringToArray(native_.getResultObject(result), Uint8Array) + ); + native_.callIfPossible(args.successCallback, data_out); + } + }; + + var result = native_.call('FileHandle_readData', data, callback); + + if (native_.isFailure(result)) { + var err = native_.getErrorObject(result); + if ('IOError' === err.name) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } else { + throw native_.getErrorObject(result); + } + } +}; + +FileHandle.prototype.writeData = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'data', type: types_.PLATFORM_OBJECT, values: Uint8Array } + ]); + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } else if (this.mode === 'r') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ); + } + var encodedData = ArrayToString(args.data); + var data = { id: this.id, data: encodedData }; + var result = native_.callSync('FileHandle_writeData', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.writeDataNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'data', type: types_.PLATFORM_OBJECT, values: Uint8Array }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + if (!('opened' === this.state)) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } else if (this.mode === 'r') { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ) + ); + }, 0); + return; + } + + var encodedData = ArrayToString(args.data); + + var data = { id: this.id, data: encodedData, blocking: false }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_writeData', data, callback); + + // Only IOError is possible to be returned synchronously, so it is passed to + // errorCallback in each case. + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } +}; + +FileHandle.prototype.flush = function() { + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ); + } + var data = { id: this.id }; + var result = native_.callSync('FileHandle_flush', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.flushNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + if (this.mode === 'r') { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ) + ); + }, 0); + return; + } + var data = { id: this.id, blocking: false }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_flush', data, callback); + + // Only IOError is possible to be returned synchronously, so it is passed to + // errorCallback in each case. + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } +}; + +FileHandle.prototype.sync = function() { + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + if (this.mode === 'r') { + throw new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ); + } + + var data = { id: this.id }; + var result = native_.callSync('FileHandle_sync', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.syncNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + if (this.mode === 'r') { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException( + WebAPIException.IO_ERR, + 'FileHandle state is read-only' + ) + ); + }, 0); + return; + } + var data = { id: this.id, blocking: false }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_sync', data, callback); + + // Only IOError is possible to be returned synchronously, so it is passed to + // errorCallback in each case. + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } +}; + +FileHandle.prototype.close = function() { + if (!(this.state === 'opened')) { + throw new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened'); + } + var data = { id: this.id }; + var result = native_.callSync('FileHandle_close', data); + this.state = 'closed'; + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileHandle.prototype.closeNonBlocking = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + if (!(this.state === 'opened')) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + new WebAPIException(WebAPIException.IO_ERR, 'FileHandle is not opened') + ); + }, 0); + return; + } + + var data = { id: this.id, blocking: false }; + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + native_.callIfPossible(args.successCallback, native_.getResultObject(result)); + } + }; + + var result = native_.call('FileHandle_close', data, callback); + this.state = 'closed'; + + // Only IOError is possible to be returned synchronously, so it is passed to + // errorCallback in each case. + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible(args.errorCallback, err); + }, 0); + } +}; diff --git a/src/filesystem/js/file_stream.js b/src/filesystem/js/file_stream.js index abc389d1..61add68f 100644 --- a/src/filesystem/js/file_stream.js +++ b/src/filesystem/js/file_stream.js @@ -47,28 +47,12 @@ function FileStream(data, mode, encoding) { set: function(v) {}, enumerable: true }, - _mode: { - value: mode, - writable: false, - enumerable: false - }, - _encoding: { - value: encoding, - writable: false, - enumerable: false - }, - _file: { - value: data, - writable: false, - enumerable: false - }, - _closed: { - value: false, - writable: true, - enumerable: false - }, - _rewrite: { - value: mode === 'w' ? true : false, + _mode: { value: mode, writable: false, enumerable: false }, + _encoding: { value: encoding, writable: false, enumerable: false }, + _file: { value: data, writable: false, enumerable: false }, + _closed: { value: false, writable: true, enumerable: false }, + _truncate: { + value: mode === 'w', // 'w' truncates file to zero length writable: true, enumerable: false } @@ -82,6 +66,11 @@ function _checkClosed(stream) { } function closeFileStream() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.close() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.close() instead.' + ); + this._closed = true; } @@ -101,32 +90,14 @@ function _checkWriteAccess(mode) { } } -/* returns array of numbers */ -function string_to_array(str) { - var output = []; - var len = str.length; - for (var i = 0; i < len; i++) { - output.push(str.charCodeAt(i)); - } - return output; -} - -/* receives array of numbers, returns string */ -function array_to_string(data) { - var output = ''; - var len = data.length; - for (var i = 0; i < len; i++) { - output += String.fromCharCode(data[i] & 0xff); // conversion to octet - } - return output; -} - function read() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.read() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.readString() or FileHandle.readStringNonBlocking() instead.' + ); + var args = validator_.validateArgs(arguments, [ - { - name: 'charCount', - type: types_.LONG - } + { name: 'charCount', type: types_.LONG } ]); _checkClosed(this); @@ -186,10 +157,7 @@ FileStream.prototype.read = function() { function readBytes() { var args = validator_.validateArgs(arguments, [ - { - name: 'byteCount', - type: types_.LONG - } + { name: 'byteCount', type: types_.LONG } ]); _checkClosed(this); @@ -215,7 +183,7 @@ function readBytes() { throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Could not read'); } - var decoded = string_to_array(native_.getResultObject(result)); + var decoded = StringToArray(native_.getResultObject(result), Array); if (decoded.length) { can_change_size = true; @@ -229,10 +197,21 @@ function readBytes() { } FileStream.prototype.readBytes = function() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.readBytes() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.readData() or FileHandle.readDataNonBlocking() instead.' + ); + return readBytes.apply(this, arguments); }; FileStream.prototype.readBase64 = function() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.readBase64() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.readData() or FileHandle.readDataNonBlocking() in ' + + 'combination with atob() and btoa() functions instead.' + ); + return base64_encode(readBytes.apply(this, arguments)); }; @@ -249,11 +228,13 @@ function check_characters_outside_latin1(str) { } function write() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.write() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.writeString() or FileHandle.writeStringNonBlocking() instead.' + ); + var args = validator_.validateArgs(arguments, [ - { - name: 'stringData', - type: types_.STRING - } + { name: 'stringData', type: types_.STRING } ]); _checkClosed(this); @@ -271,7 +252,7 @@ function write() { encoding: this._encoding, offset: this.position, data: args.stringData, - rewrite: this._rewrite + truncate: this._truncate }; if (data.encoding == 'iso-8859-1') { @@ -286,7 +267,7 @@ function write() { can_change_size = true; this.position = this.position + args.stringData.length; can_change_size = false; - this._rewrite = false; + this._truncate = false; } FileStream.prototype.write = function() { @@ -294,12 +275,17 @@ FileStream.prototype.write = function() { }; function writeBytes() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.writeBytes() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.writeData() or FileHandle.writeDataNonBlocking() instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'byteData', type: types_.ARRAY, - values: undefined /* was types_.OCTET, but checking moved to - array_to_string for performance */ + values: undefined /* was types_.OCTET, but checking moved to ArrayToString for + performance */ } ]); @@ -316,8 +302,8 @@ function writeBytes() { var data = { location: commonFS_.toRealPath(this._file.fullPath), offset: this.position, - data: array_to_string(args.byteData), - rewrite: this._rewrite + data: ArrayToString(args.byteData), + truncate: this._truncate }; var result = native_.callSync('File_writeBytes', data); @@ -328,7 +314,7 @@ function writeBytes() { can_change_size = true; this.position = this.position + args.byteData.length; can_change_size = false; - this._rewrite = false; + this._truncate = false; } FileStream.prototype.writeBytes = function() { @@ -336,11 +322,14 @@ FileStream.prototype.writeBytes = function() { }; function writeBase64() { + privUtils_.warn( + 'DEPRECATION WARNING: FileStream.writeBase64() is deprecated since Tizen 5.0. ' + + 'Use FileHandle.writeData() or FileHandle.writeDataNonBlocking() in ' + + 'combination with atob() and btoa() functions instead.' + ); + var args = validator_.validateArgs(arguments, [ - { - name: 'base64Data', - type: types_.STRING - } + { name: 'base64Data', type: types_.STRING } ]); _checkClosed(this); @@ -350,7 +339,7 @@ function writeBase64() { location: commonFS_.toRealPath(this._file.fullPath), offset: this.position, data: args.base64Data, - rewrite: this._rewrite + truncate: this._truncate }; var result = native_.callSync('File_writeBase64', data); @@ -364,7 +353,7 @@ function writeBase64() { can_change_size = true; this.position += written_bytes; can_change_size = false; - this._rewrite = false; + this._truncate = false; } FileStream.prototype.writeBase64 = function() { diff --git a/src/filesystem/js/file_system_manager.js b/src/filesystem/js/file_system_manager.js index 4de93738..943453fc 100755..100644 --- a/src/filesystem/js/file_system_manager.js +++ b/src/filesystem/js/file_system_manager.js @@ -22,15 +22,595 @@ function FileSystemStorage(data) { }); } -var PATH_MAX = 4096; +var FileStreamManager = function() { + this.nextId = 0; +}; + +FileStreamManager.prototype.getNextFileHandleId = function() { + return ++this.nextId; +}; + +var fileStreamManager = new FileStreamManager(); function FileSystemManager() { + var limits = native_.getResultObject(native_.callSync('FileSystemManager_getLimits')); Object.defineProperties(this, { - maxPathLength: { value: PATH_MAX, writable: false, enumerable: true } + maxNameLength: { value: limits[0], writable: false, enumerable: true }, + maxPathLength: { value: limits[1], writable: false, enumerable: true } }); } +FileSystemManager.prototype.openFile = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'openMode', type: types_.ENUM, values: type_.getValues(FileMode) }, + { name: 'makeParents', type: types_.BOOLEAN, optional: true } + ]); + + if (!args.has.makeParents) { + args.makeParents = true; + } + + var data = { + path: commonFS_.toRealPath(args.path), + openMode: args.openMode, + makeParents: args.makeParents, + id: fileStreamManager.getNextFileHandleId() + }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.NOT_FOUND_ERR, + 'Invalid path: ' + args.path + ); + } + + var result = native_.callSync('FileSystemManager_openFile', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return new FileHandle(data.id, args.path, args.openMode); + } +}; + +FileSystemManager.prototype.createDirectory = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'makeParents', type: types_.BOOLEAN, optional: true }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + if (!args.has.makeParents) { + args.makeParents = true; + } + + var data = { path: commonFS_.toRealPath(args.path), makeParents: args.makeParents }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Invalid path: ' + args.path + ); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_createDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.deleteFile = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + var data = { path: commonFS_.toRealPath(args.path) }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Invalid path: ' + args.path + ); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_deleteFile', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.deleteDirectory = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'recursive', type: types_.BOOLEAN, optional: true }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + if (!args.has.recursive) { + args.recursive = true; + } + + var realPath = commonFS_.toRealPath(args.path); + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = { path: realPath, recursive: args.recursive }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_deleteDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.copyFile = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'destinationPath', type: types_.STRING }, + { name: 'overwrite', type: types_.BOOLEAN, optional: true }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: commonFS_.toRealPath(args.path), + destinationPath: commonFS_.toRealPath(args.destinationPath), + overwrite: args.overwrite + }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Invalid path: ' + args.path + ); + } + if (!data.destinationPath) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Invalid path: ' + args.destinationPath + ); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_copyFile', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.copyDirectory = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'destinationPath', type: types_.STRING }, + { name: 'overwrite', type: types_.BOOLEAN, optional: true }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + var realPath = commonFS_.toRealPath(args.path); + var realDestinationPath = commonFS_.toRealPath(args.destinationPath); + if (!realPath || !realDestinationPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: realPath, + destinationPath: realDestinationPath, + overwrite: args.overwrite + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_copyDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.moveFile = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'destinationPath', type: types_.STRING }, + { name: 'overwrite', type: types_.BOOLEAN, optional: true }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + var realPath = commonFS_.toRealPath(args.path); + var realDestinationPath = commonFS_.toRealPath(args.destinationPath); + if (!realPath || !realDestinationPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: realPath, + destinationPath: realDestinationPath, + overwrite: args.overwrite + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_moveFile', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.moveDirectory = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'destinationPath', type: types_.STRING }, + { name: 'overwrite', type: types_.BOOLEAN, optional: true }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + var realPath = commonFS_.toRealPath(args.path); + var realDestinationPath = commonFS_.toRealPath(args.destinationPath); + if (!realPath || !realDestinationPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + if (!args.has.overwrite) { + args.overwrite = false; + } + + var data = { + path: realPath, + destinationPath: realDestinationPath, + overwrite: args.overwrite + }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_moveDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.rename = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'newName', type: types_.STRING }, + { + name: 'successCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + if (-1 !== args.newName.indexOf('/') || -1 !== args.newName.indexOf('\x00')) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'newName contains invalid character.' + ); + } + + var realPath = commonFS_.toRealPath(args.path); + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = { path: realPath, newName: args.newName }; + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var path = native_.getResultObject(result); + native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path)); + } + }; + + var result = native_.call('FileSystemManager_rename', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +function throwIfNotDate(argument, name) { + if (argument instanceof Date) { + return true; + } + throw new WebAPIException( + WebAPIException.TYPE_MISMATCH_ERR, + 'Argument "' + name + '" in a filter is not of type Date.' + ); +} + +FileSystemManager.prototype.listDirectory = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING }, + { name: 'successCallback', type: types_.FUNCTION, optional: true }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }, + { name: 'filter', type: types_.DICTIONARY, optional: true, nullable: true } + ]); + + if (!args.has.filter) { + args.filter = {}; + } + + if (args.filter.hasOwnProperty('startModified')) { + throwIfNotDate(args.filter.startModified, 'startModified'); + args.filter.startModified = args.filter.startModified.getTime() / 1000; + } + if (args.filter.hasOwnProperty('endModified')) { + throwIfNotDate(args.filter.endModified, 'endModified'); + args.filter.endModified = args.filter.endModified.getTime() / 1000; + } + if (args.filter.hasOwnProperty('startCreated')) { + throwIfNotDate(args.filter.startCreated, 'startCreated'); + args.filter.startCreated = args.filter.startCreated.getTime() / 1000; + } + if (args.filter.hasOwnProperty('endCreated')) { + throwIfNotDate(args.filter.endCreated, 'endCreated'); + args.filter.endCreated = args.filter.endCreated.getTime() / 1000; + } + + var data = { path: commonFS_.toRealPath(args.path), filter: args.filter }; + + if (!data.path) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Invalid path: ' + args.path + ); + } + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var obj = native_.getResultObject(result); + var names = obj.names; + if (args.filter.hasOwnProperty('name')) { + var regex_name = stringToRegex(args.filter.name); + for (var i = names.length - 1; i >= 0; i--) { + if (!regex_name.test(names[i])) { + names.splice(i, 1); + } + } + } + native_.callIfPossible( + args.successCallback, + names, + commonFS_.toVirtualPath(obj.path) + ); + } + }; + + var result = native_.call('FileSystemManager_listDirectory', data, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +FileSystemManager.prototype.toURI = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING } + ]); + + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing + // virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + return 'file://' + realPath; +}; + +FileSystemManager.prototype.isFile = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING } + ]); + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing + // virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = { path: realPath }; + + var result = native_.callSync('FileSystemManager_isFile', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return native_.getResultObject(result); + } +}; + +FileSystemManager.prototype.isDirectory = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING } + ]); + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing + // virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + + var data = { path: realPath }; + + var result = native_.callSync('FileSystemManager_isDirectory', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return native_.getResultObject(result); + } +}; + +FileSystemManager.prototype.pathExists = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING } + ]); + // The toRealPath function will convert any string to absolute path, if possible. + // The function returns undefined for path, which starts with not-existing + // virtual root. + var realPath = commonFS_.toRealPath(args.path); + + if (!realPath) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.'); + } + var data = { path: realPath }; + + var result = native_.callSync('FileSystemManager_pathExists', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + return native_.getResultObject(result); + } +}; + +FileSystemManager.prototype.getDirName = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'path', type: types_.STRING } + ]); + var path = args.path; + + path = commonFS_.mergeMultipleSlashes(path); + if (path.startsWith('file://')) { + path = path.substring('file://'.length - 1, path.length - 1); + } + + if (path.startsWith('/') && 0 === path.lastIndexOf('/')) { + // handle the "/" and "/file.ext" + return '/'; + } else if (path.endsWith('/')) { + // cut the last '/' + path = path.substring(0, path.length - 1); + } + + var index = path.lastIndexOf('/'); + if (-1 !== index) { + path = path.substring(0, index); // cut the directory/file the path points to + } + return path; +}; + function resolve() { + privUtils_.warn( + 'DEPRECATION WARNING: FileSystemManager.resolve() is deprecated since ' + + 'Tizen 5.0. Use FileHandle and FileSystemManager interfaces instead.' + ); + var args = validator_.validateArgs(arguments, [ { name: 'location', type: types_.STRING }, { name: 'onsuccess', type: types_.FUNCTION }, @@ -46,6 +626,12 @@ function resolve() { if (!args.has.mode) { args.mode = 'rw'; + } else if ('rwo' == args.mode) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'rwo mode was introduced in version 5.0 and is not supported in earlier ' + + 'version methods' + ); } // resolving a path on unmounted storage should result in exception @@ -86,10 +672,7 @@ function resolve() { setTimeout(function() { native_.callIfPossible( args.onerror, - new WebAPIException( - WebAPIException.NOT_FOUND_ERR, - 'Specified virtual root does not exist.' - ) + new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Invalid path.') ); }, 0); return; @@ -110,9 +693,7 @@ function resolve() { return; } - var data = { - location: _realPath - }; + var data = { location: _realPath }; var callback = function(result) { if (native_.isFailure(result)) { diff --git a/src/humanactivitymonitor/humanactivitymonitor_api.js b/src/humanactivitymonitor/humanactivitymonitor_api.js index 4f635f48..8397d1af 100755 --- a/src/humanactivitymonitor/humanactivitymonitor_api.js +++ b/src/humanactivitymonitor/humanactivitymonitor_api.js @@ -15,6 +15,7 @@ */ var utils_ = xwalk.utils; +var privilege_ = utils_.privilege; var type_ = utils_.type; var converter_ = utils_.converter; var validator_ = utils_.validator; @@ -43,7 +44,9 @@ var HumanActivityType = { WRIST_UP: 'WRIST_UP', HRM: 'HRM', GPS: 'GPS', - SLEEP_MONITOR: 'SLEEP_MONITOR' + SLEEP_MONITOR: 'SLEEP_MONITOR', + SLEEP_DETECTOR: 'SLEEP_DETECTOR', + STRESS_MONITOR: 'STRESS_MONITOR' }; var HumanActivityRecorderType = { @@ -108,6 +111,10 @@ function convertActivityData(type, data) { return new HumanActivityGPSInfoArray(gpsInfo); case HumanActivityType.SLEEP_MONITOR: return new HumanActivitySleepMonitorData(data); + case HumanActivityType.SLEEP_DETECTOR: + return new HumanActivitySleepDetectorData(data); + case HumanActivityType.STRESS_MONITOR: + return new HumanActivityStressMonitorData(data); default: utils_.error('Uknown human activity type: ' + type); } @@ -146,6 +153,50 @@ function convertActivityRecorderData(type, data) { return createRecorderData(func, data); } +function StressMonitorDataRange(label, min, max) { + validator_.validateConstructorCall(this, tizen.StressMonitorDataRange); + + var args = validator_.validateArgs(arguments, [ + { name: 'label', type: types_.STRING, optional: true, nullable: false }, + { name: 'min', type: types_.UNSIGNED_LONG, optional: true, nullable: false }, + { name: 'max', type: types_.UNSIGNED_LONG, optional: true, nullable: false } + ]); + + var _label = !type_.isNullOrUndefined(args.label) ? args.label : ''; + var _min = !type_.isNullOrUndefined(args.min) ? args.min : 0; + var _max = !type_.isNull(args.max) ? args.max : undefined; + + Object.defineProperties(this, { + label: { + get: function() { + return _label; + }, + set: function(v) { + _label = !type_.isNullOrUndefined(v) ? v : _label; + }, + enumerable: true + }, + min: { + get: function() { + return _min; + }, + set: function(v) { + _min = !type_.isNullOrUndefined(v) ? converter_.toUnsignedLong(v) : _min; + }, + enumerable: true + }, + max: { + get: function() { + return _max; + }, + set: function(v) { + _max = !type_.isNullOrUndefined(v) ? converter_.toUnsignedLong(v) : _max; + }, + enumerable: true + } + }); +} + function ActivityRecognitionListenerManager() { this.listeners = {}; this.nextId = 1; @@ -254,7 +305,7 @@ function startListener(listenerId, listener, method, data) { } // always set the listener - //if it's another call to startListener() overwrite the old one + // if it's another call to startListener() overwrite the old one native_.addListener(listenerId, listener); } @@ -306,6 +357,8 @@ function GPSCallback(result) { } } +var stressListener = null; + HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { var args = validator_.validateArgs(arguments, [ { name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType) }, @@ -364,6 +417,9 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { case HumanActivityType.GPS: listener = GPSCallback; break; + case HumanActivityType.STRESS_MONITOR: + listener = stressMonitorListener.onListener; + break; default: listener = function(result) { native_.callIfPossible( @@ -387,7 +443,10 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { pedometerListener = args.changedCallback; } - if (HumanActivityType.GPS === args.type) { + if ( + HumanActivityType.GPS === args.type || + HumanActivityType.STRESS_MONITOR === args.type + ) { var callback = function(result) { if (native_.isFailure(result)) { native_.callIfPossible( @@ -402,7 +461,11 @@ HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { } }; - GPSListener = callback; + if (HumanActivityType.GPS === args.type) { + GPSListener = callback; + } else if (HumanActivityType.STRESS_MONITOR === args.type) { + stressListener = callback; + } } }; @@ -439,6 +502,10 @@ HumanActivityMonitorManager.prototype.stop = function(type) { if (HumanActivityType.GPS === args.type) { GPSListener = null; } + + if (HumanActivityType.STRESS_MONITOR === args.type) { + stressListener = null; + } }; HumanActivityMonitorManager.prototype.setAccumulativePedometerListener = function() { @@ -794,6 +861,91 @@ HumanActivityMonitorManager.prototype.removeGestureRecognitionListener = functio gestureRecognitionListener.removeListener(args.watchId); }; +function StressMonitorListenerManager() { + this.listeners = {}; + this.nextId = 1; +} + +StressMonitorListenerManager.prototype.onListener = function(data) { + if (stressListener) { + stressListener(data); + } + var score = data.stressScore; + for (var watchId in stressMonitorListener.listeners) { + if (stressMonitorListener.listeners.hasOwnProperty(watchId)) { + var _listener = stressMonitorListener.listeners[watchId]; + var rangeArray = _listener.ranges; + for (var id in rangeArray) { + var _min = rangeArray[id].min; + var _max = !type_.isUndefined(rangeArray[id].max) + ? rangeArray[id].max + : Number.MAX_VALUE; + if ( + score >= _min && + score < _max && + (_listener.lastStressScore < _min || + _listener.lastStressScore >= _max) + ) { + _listener.listener(rangeArray[id].label); + } + } + _listener.lastStressScore = score; + } + } +}; + +StressMonitorListenerManager.prototype.addListener = function( + ranges, + listener, + errorCallback +) { + var id = this.nextId++; + + this.listeners[id] = { + ranges: ranges, + listener: listener, + lastStressScore: -1 + }; + + return id; +}; + +StressMonitorListenerManager.prototype.removeListener = function(watchId) { + if (this.listeners.hasOwnProperty(watchId)) { + delete this.listeners[watchId]; + } +}; + +var stressMonitorListener = new StressMonitorListenerManager(); + +HumanActivityMonitorManager.prototype.addStressMonitorChangeListener = function() { + utils_.checkPrivilegeAccess(privilege_.HEALTHINFO); + var args = validator_.validateMethod(arguments, [ + { + name: 'ranges', + type: types_.ARRAY, + values: StressMonitorDataRange + }, + { + name: 'listener', + type: types_.FUNCTION + } + ]); + + return stressMonitorListener.addListener(args.ranges, args.listener); +}; + +HumanActivityMonitorManager.prototype.removeStressMonitorChangeListener = function() { + var args = validator_.validateMethod(arguments, [ + { + name: 'watchId', + type: types_.LONG + } + ]); + + stressMonitorListener.removeListener(args.watchId); +}; + function StepDifference(data) { SetReadOnlyProperty(this, 'stepCountDifference', data.stepCountDifference); SetReadOnlyProperty(this, 'timestamp', data.timestamp); @@ -892,6 +1044,20 @@ function HumanActivitySleepMonitorData(data) { HumanActivitySleepMonitorData.prototype = new HumanActivityData(); HumanActivitySleepMonitorData.prototype.constructor = HumanActivitySleepMonitorData; +function HumanActivitySleepDetectorData(data) { + SetReadOnlyProperty(this, 'status', data.status); +} + +HumanActivitySleepDetectorData.prototype = new HumanActivityData(); +HumanActivitySleepDetectorData.prototype.constructor = HumanActivitySleepMonitorData; + +function HumanActivityStressMonitorData(data) { + SetReadOnlyProperty(this, 'stressScore', data.stressScore); +} + +HumanActivityStressMonitorData.prototype = new HumanActivityData(); +HumanActivityStressMonitorData.prototype.constructor = HumanActivityStressMonitorData; + //Recorded data function HumanActivityRecorderData(data) { if (data) { @@ -960,4 +1126,6 @@ HumanActivityRecorderPressureData.prototype = new HumanActivityRecorderData(); HumanActivityRecorderPressureData.prototype.constructor = HumanActivityRecorderPressureData; +tizen.StressMonitorDataRange = StressMonitorDataRange; + exports = new HumanActivityMonitorManager(); diff --git a/src/humanactivitymonitor/humanactivitymonitor_extension.cc b/src/humanactivitymonitor/humanactivitymonitor_extension.cc index 6074271e..e17fc87c 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_extension.cc +++ b/src/humanactivitymonitor/humanactivitymonitor_extension.cc @@ -27,6 +27,9 @@ common::Extension* CreateExtension() { HumanActivityMonitorExtension::HumanActivityMonitorExtension() { SetExtensionName("tizen.humanactivitymonitor"); SetJavaScriptAPI(kSource_humanactivitymonitor_api); + + const char* entry_points[] = {"tizen.StressMonitorDataRange", NULL}; + SetExtraJSEntryPoints(entry_points); } HumanActivityMonitorExtension::~HumanActivityMonitorExtension() { diff --git a/src/humanactivitymonitor/humanactivitymonitor_manager.cc b/src/humanactivitymonitor/humanactivitymonitor_manager.cc index a6a7d3ae..0762f54c 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_manager.cc +++ b/src/humanactivitymonitor/humanactivitymonitor_manager.cc @@ -48,6 +48,8 @@ const std::string kActivityTypeWristUp = "WRIST_UP"; const std::string kActivityTypeHrm = "HRM"; const std::string kActivityTypeSleepMonitor = "SLEEP_MONITOR"; const std::string kActivityTypePressure = "PRESSURE"; +const std::string kActivityTypeSleepDetector = "SLEEP_DETECTOR"; +const std::string kActivityTypeStressMonitor = "STRESS_MONITOR"; const std::string kSleepStateAwake = "AWAKE"; const std::string kSleepStateAsleep = "ASLEEP"; @@ -58,6 +60,7 @@ const std::string kSampleInterval = "sampleInterval"; const std::string kStatus = "status"; const std::string kTimestamp = "timestamp"; +const std::string kStressScore = "stressScore"; const std::string kStepStatus = "stepStatus"; const std::string kSpeed = "speed"; @@ -1480,6 +1483,53 @@ HumanActivityMonitorManager::HumanActivityMonitorManager() return PlatformResult(ErrorCode::NO_ERROR); }; + auto convert_sleep_detector = [](sensor_event_s* event, + picojson::object* data) -> PlatformResult { + ScopeLogger("convert_sleep_detector"); + + if (event->value_count < 1) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "To few values of SLEEP event"); + } + + sensor_sleep_state_e state = static_cast<sensor_sleep_state_e>(event->values[0]); + std::string sleep_state; + + switch (state) { + case SENSOR_SLEEP_STATE_WAKE: + sleep_state = kSleepStateAwake; + break; + + case SENSOR_SLEEP_STATE_SLEEP: + sleep_state = kSleepStateAsleep; + break; + + case SENSOR_SLEEP_STATE_UNKNOWN: + sleep_state = kSleepStateUnknown; + break; + + default: + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown sleep state", + ("Unknown sleep state: %d", state)); + } + + data->insert(std::make_pair(kStatus, picojson::value(sleep_state))); + + return PlatformResult(ErrorCode::NO_ERROR); + }; + + auto convert_stress = [](sensor_event_s* event, picojson::object* data) -> PlatformResult { + ScopeLogger("convert_stress"); + + if (event->value_count < 1) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "To few values of STRESS event"); + } + + float stress_score = event->values[0]; + data->insert(std::make_pair(kStressScore, picojson::value(static_cast<double>(stress_score)))); + + return PlatformResult(ErrorCode::NO_ERROR); + }; + auto convert_recorded_hrm = [](void* data, picojson::object* obj) -> PlatformResult { ScopeLogger("Entered into asynchronous function, convert_recorded_hrm"); @@ -1527,6 +1577,14 @@ HumanActivityMonitorManager::HumanActivityMonitorManager() std::make_pair(kActivityTypePedometer, std::make_shared<Monitor::PedometerMonitor>())); monitors_.insert(std::make_pair(kActivityTypeWristUp, std::make_shared<Monitor::GestureMonitor>(kActivityTypeWristUp))); + monitors_.insert(std::make_pair(kActivityTypeSleepDetector, + std::make_shared<Monitor::SensorMonitor>( + kActivityTypeSleepDetector, SENSOR_HUMAN_SLEEP_DETECTOR, + convert_sleep_detector, nullptr))); + monitors_.insert(std::make_pair( + kActivityTypeStressMonitor, + std::make_shared<Monitor::SensorMonitor>( + kActivityTypeStressMonitor, SENSOR_HUMAN_STRESS_MONITOR, convert_stress, nullptr))); monitors_.insert(std::make_pair( kActivityTypeHrm, std::make_shared<Monitor::SensorMonitor>( kActivityTypeHrm, SENSOR_HRM, convert_hrm, convert_recorded_hrm))); diff --git a/src/inputdevice/inputdevice_api.js b/src/inputdevice/inputdevice_api.js index d349d27e..e8281980 100644 --- a/src/inputdevice/inputdevice_api.js +++ b/src/inputdevice/inputdevice_api.js @@ -41,8 +41,8 @@ function InputDeviceKey(dict) { } /** - * This class provides access to the API functionalities - * through the tizen.tvinputdevice interface. + * This class provides access to the API functionalities through the + * tizen.tvinputdevice interface. * @constructor */ function InputDeviceManager() { @@ -87,8 +87,8 @@ InputDeviceManager.prototype.getKey = function(keyName) { }; /** - * Registers an input device key to receive DOM keyboard event - * when it is pressed or released. + * Registers an input device key to receive DOM keyboard event when it is pressed or + * released. * @param {!string} keyName The key name */ InputDeviceManager.prototype.registerKey = function(keyName) { diff --git a/src/iotcon/iotcon_instance.cc b/src/iotcon/iotcon_instance.cc index 6bc88a8f..3eee1479 100644 --- a/src/iotcon/iotcon_instance.cc +++ b/src/iotcon/iotcon_instance.cc @@ -1814,6 +1814,8 @@ common::TizenResult IotconInstance::Initialize(const picojson::object& args) { std::string realPath = common::FilesystemProvider::Create().GetRealPath(filePath.get<std::string>()); + CHECK_STORAGE_ACCESS_AND_RETURN(realPath); + auto result = IotconUtils::ConvertIotconError(iotcon_initialize(realPath.c_str())); if (!result) { LogAndReturnTizenError(result); diff --git a/src/mediacontroller/mediacontroller_api.js b/src/mediacontroller/mediacontroller_api.js index be155e55..9a2c6df0 100755 --- a/src/mediacontroller/mediacontroller_api.js +++ b/src/mediacontroller/mediacontroller_api.js @@ -32,6 +32,7 @@ function ListenerManager(native, listenerName, handle) { this.native = native; this.listenerName = listenerName; this.handle = handle || function(msg, listener, watchId) {}; + this.requestIdToListenerId = {}; } ListenerManager.prototype.addListener = function(callback) { @@ -124,7 +125,7 @@ var ServerCommandListener = new ListenerManager( var nativeData = { clientName: msg.clientName, - replyId: msg.replyId, + requestId: msg.requestId, data: data }; @@ -140,12 +141,12 @@ var ReplyCommandListener = new ListenerManager(native_, '_ReplyCommandListener', listener, watchId ) { - if (msg.replyId === watchId) { - listener(msg.data); + if (this.requestIdToListenerId[watchId] === msg.requestId) { + listener(msg); this.removeListener(watchId); + delete this.requestIdToListenerId[watchId]; return true; } - return false; }); @@ -224,6 +225,14 @@ var MediaControllerPlaybackState = { REWIND: 'REWIND' }; +var MediaControllerContentType = { + IMAGE: 'IMAGE', + MUSIC: 'MUSIC', + VIDEO: 'VIDEO', + OTHER: 'OTHER', + UNDECIDED: 'UNDECIDED' +}; + function MediaControllerManager() {} MediaControllerManager.prototype.getClient = function() { @@ -845,25 +854,28 @@ MediaControllerServerInfo.prototype.sendCommand = function( { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } ]); - var nativeData = { - command: args.command, - data: args.data, - name: this.name - }; - - var replyId = ReplyCommandListener.addListener(successCallback); - - nativeData.replyId = replyId; - nativeData.listenerId = ReplyCommandListener.listenerName; var callback = function(result) { if (native_.isFailure(result)) { native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); return; } - args.successCallback(native_.getResultObject(result)); + native_.callIfPossible( + args.successCallback, + native_.getResultObject(result).data + ); }; - native_.call('MediaControllerServerInfo_sendCommand', nativeData, callback); + var nativeData = { + command: args.command, + data: args.data, + name: this.name, + listenerId: ReplyCommandListener.listenerName + }; + + var replyListenerId = ReplyCommandListener.addListener(callback); + var result = native_.callSync('MediaControllerServerInfo_sendCommand', nativeData); + + ReplyCommandListener.requestIdToListenerId[replyListenerId] = result.requestId; }; MediaControllerServerInfo.prototype.addServerStatusChangeListener = function(listener) { diff --git a/src/mediacontroller/mediacontroller_client.cc b/src/mediacontroller/mediacontroller_client.cc index cce90a29..05effb8c 100644 --- a/src/mediacontroller/mediacontroller_client.cc +++ b/src/mediacontroller/mediacontroller_client.cc @@ -22,6 +22,7 @@ #include "common/logger.h" #include "common/scope_exit.h" +#include "common/tools.h" #include "mediacontroller/mediacontroller_types.h" @@ -30,6 +31,8 @@ namespace mediacontroller { using common::PlatformResult; using common::ErrorCode; +using common::tools::ReportError; +using common::tools::ReportSuccess; MediaControllerClient::MediaControllerClient() : handle_(nullptr) { ScopeLogger(); @@ -38,6 +41,12 @@ MediaControllerClient::MediaControllerClient() : handle_(nullptr) { MediaControllerClient::~MediaControllerClient() { ScopeLogger(); + int ret = mc_client_unset_cmd_reply_received_cb(handle_); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + LoggerE("Failed to unset cmd reply callback. Error code: %d; Error message: %s", + ret, get_error_message(ret)); + } + if (nullptr != server_status_listener_ && !UnsetServerStatusChangeListener()) { LoggerE("Failed to unset server status change listener"); } @@ -60,6 +69,13 @@ PlatformResult MediaControllerClient::Init() { ("mc_client_create() error: %d, message: %s", ret, get_error_message(ret))); } + ret = mc_client_set_cmd_reply_received_cb(handle_, OnCommandReply, this); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Unable to register cmd reply received callback", + ("mc_client_set_cmd_reply_received_cb() error: %d, message: %s", ret, get_error_message(ret))); + } + return PlatformResult(ErrorCode::NO_ERROR); } @@ -138,10 +154,9 @@ PlatformResult MediaControllerClient::GetLatestServerInfo(picojson::value* serve } std::string state_str; - PlatformResult result = Types::PlatformEnumToString(Types::kMediaControllerServerState, - static_cast<int>(state), &state_str); + PlatformResult result = types::MediaControllerServerStateEnum.getName(state, &state_str); if (!result) { - LoggerE("PlatformEnumToString failed, error: %s", result.message().c_str()); + LoggerE("MediaControllerServerStateEnum.getName() failed, error: %s", result.message().c_str()); return result; } @@ -172,7 +187,7 @@ PlatformResult MediaControllerClient::GetPlaybackInfo(const std::string& server_ // playback state std::string state; - PlatformResult result = Types::ConvertPlaybackState(playback_h, &state); + PlatformResult result = types::ConvertPlaybackState(playback_h, &state); if (!result) { LoggerE("ConvertPlaybackState failed, error: %s", result.message().c_str()); return result; @@ -180,7 +195,7 @@ PlatformResult MediaControllerClient::GetPlaybackInfo(const std::string& server_ // playback position double position; - result = Types::ConvertPlaybackPosition(playback_h, &position); + result = types::ConvertPlaybackPosition(playback_h, &position); if (!result) { LoggerE("ConvertPlaybackPosition failed, error: %s", result.message().c_str()); return result; @@ -226,7 +241,7 @@ PlatformResult MediaControllerClient::GetMetadata(const std::string& server_name ScopeLogger(); int ret; - mc_metadata_h metadata_h; + mc_metadata_h metadata_h = nullptr; ret = mc_client_get_server_metadata(handle_, server_name.c_str(), &metadata_h); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( @@ -235,10 +250,10 @@ PlatformResult MediaControllerClient::GetMetadata(const std::string& server_name } SCOPE_EXIT { - mc_client_destroy_metadata(metadata_h); + mc_metadata_destroy(metadata_h); }; - PlatformResult result = Types::ConvertMetadata(metadata_h, metadata); + PlatformResult result = types::ConvertMetadata(metadata_h, metadata); if (!result) { return result; } @@ -249,11 +264,11 @@ PlatformResult MediaControllerClient::GetMetadata(const std::string& server_name PlatformResult MediaControllerClient::SetServerStatusChangeListener(const JsonCallback& callback) { ScopeLogger(); - int ret = mc_client_set_server_update_cb(handle_, OnServerStatusUpdate, this); + int ret = mc_client_set_server_updated_cb(handle_, OnServerStatusUpdate, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Unable to set server status listener", - ("mc_client_set_server_update_cb() error: %d, message: %s", ret, get_error_message(ret))); + ("mc_client_set_server_updated_cb() error: %d, message: %s", ret, get_error_message(ret))); } server_status_listener_ = callback; @@ -263,11 +278,11 @@ PlatformResult MediaControllerClient::SetServerStatusChangeListener(const JsonCa PlatformResult MediaControllerClient::UnsetServerStatusChangeListener() { ScopeLogger(); - int ret = mc_client_unset_server_update_cb(handle_); + int ret = mc_client_unset_server_updated_cb(handle_); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "Unable to unset server status listener", - ("mc_client_unset_server_update_cb() error: %d, message: %s", ret, get_error_message(ret))); + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to unset server status listener", + ("mc_client_unset_server_updated_cb() error: %d, message: %s", ret, + get_error_message(ret))); } server_status_listener_ = nullptr; return PlatformResult(ErrorCode::NO_ERROR); @@ -280,10 +295,9 @@ void MediaControllerClient::OnServerStatusUpdate(const char* server_name, mc_ser // server state std::string state_str; - PlatformResult result = Types::PlatformEnumToString(Types::kMediaControllerServerState, - static_cast<int>(state), &state_str); + PlatformResult result = types::MediaControllerServerStateEnum.getName(state, &state_str); if (!result) { - LoggerE("PlatformEnumToString failed, error: %s", result.message().c_str()); + LoggerE("MediaControllerServerStateEnum.getName() failed, error: %s", result.message().c_str()); return; } @@ -305,9 +319,9 @@ PlatformResult MediaControllerClient::SetPlaybackInfoListener(const JsonCallback // The purpose of this lambda is to unset as many setters as we can in case of failure. int (*unsetters[])(mc_client_h) = { - mc_client_unset_playback_update_cb, mc_client_unset_shuffle_mode_update_cb, - mc_client_unset_repeat_mode_update_cb, - /*mc_client_unset_metadata_update_cb the last unsetter will never be used*/}; + mc_client_unset_playback_updated_cb, mc_client_unset_shuffle_mode_updated_cb, + mc_client_unset_repeat_mode_updated_cb, + /*mc_client_unset_metadata_updated_cb the last unsetter will never be used*/}; // This loop is no-op in case of success. for (int i = 0; i < failed_setter; ++i) { @@ -318,35 +332,35 @@ PlatformResult MediaControllerClient::SetPlaybackInfoListener(const JsonCallback } }; - int ret = mc_client_set_playback_update_cb(handle_, OnPlaybackUpdate, this); + int ret = mc_client_set_playback_updated_cb(handle_, OnPlaybackUpdate, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "Unable to register playback listener", - ("mc_client_set_playback_update_cb() error: %d, message: %s", ret, get_error_message(ret))); + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to register playback listener", + ("mc_client_set_playback_updated_cb() error: %d, message: %s", ret, + get_error_message(ret))); } - ret = mc_client_set_shuffle_mode_update_cb(handle_, OnShuffleModeUpdate, this); + ret = mc_client_set_shuffle_mode_updated_cb(handle_, OnShuffleModeUpdate, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { failed_setter = 1; return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to register shuffle mode listener", - ("mc_client_set_shuffle_mode_update_cb() error: %d, message: %s", ret, - get_error_message(ret))); + ("mc_client_set_shuffle_mode_updated_cb() error: %d, message: %s", + ret, get_error_message(ret))); } - ret = mc_client_set_repeat_mode_update_cb(handle_, OnRepeatModeUpdate, this); + ret = mc_client_set_repeat_mode_updated_cb(handle_, OnRepeatModeUpdate, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { failed_setter = 2; return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to register repeat mode listener", - ("mc_client_set_repeat_mode_update_cb() error: %d, message: %s", ret, + ("mc_client_set_repeat_mode_updated_cb() error: %d, message: %s", ret, get_error_message(ret))); } - ret = mc_client_set_metadata_update_cb(handle_, OnMetadataUpdate, this); + ret = mc_client_set_metadata_updated_cb(handle_, OnMetadataUpdate, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { failed_setter = 3; - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "Unable to register metadata listener", - ("mc_client_set_metadata_update_cb() error: %d, message: %s", ret, get_error_message(ret))); + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to register metadata listener", + ("mc_client_set_metadata_updated_cb() error: %d, message: %s", ret, + get_error_message(ret))); } playback_info_listener_ = callback; @@ -360,10 +374,10 @@ PlatformResult MediaControllerClient::UnsetPlaybackInfoListener() { // In the Javascript layer, the removePlaybackInfoChangeListener() method always succeeds, so we // do not need to catch the returned value. - mc_client_unset_playback_update_cb(handle_); - mc_client_unset_shuffle_mode_update_cb(handle_); - mc_client_unset_repeat_mode_update_cb(handle_); - mc_client_unset_metadata_update_cb(handle_); + mc_client_unset_playback_updated_cb(handle_); + mc_client_unset_shuffle_mode_updated_cb(handle_); + mc_client_unset_repeat_mode_updated_cb(handle_); + mc_client_unset_metadata_updated_cb(handle_); playback_info_listener_ = nullptr; @@ -377,7 +391,7 @@ void MediaControllerClient::OnPlaybackUpdate(const char* server_name, mc_playbac // playback state std::string state; - PlatformResult result = Types::ConvertPlaybackState(playback, &state); + PlatformResult result = types::ConvertPlaybackState(playback, &state); if (!result) { LoggerE("ConvertPlaybackState failed, error: %s", result.message().c_str()); return; @@ -385,7 +399,7 @@ void MediaControllerClient::OnPlaybackUpdate(const char* server_name, mc_playbac // playback position double position; - result = Types::ConvertPlaybackPosition(playback, &position); + result = types::ConvertPlaybackPosition(playback, &position); if (!result) { LoggerE("ConvertPlaybackPosition failed, error: %s", result.message().c_str()); return; @@ -441,7 +455,7 @@ void MediaControllerClient::OnMetadataUpdate(const char* server_name, mc_metadat picojson::object& data_o = data.get<picojson::object>(); picojson::value metadata = picojson::value(picojson::object()); - PlatformResult result = Types::ConvertMetadata(metadata_h, &metadata.get<picojson::object>()); + PlatformResult result = types::ConvertMetadata(metadata_h, &metadata.get<picojson::object>()); if (!result) { LoggerE("ConvertMetadata failed, error: %s", result.message().c_str()); return; @@ -457,8 +471,8 @@ void MediaControllerClient::OnMetadataUpdate(const char* server_name, mc_metadat PlatformResult MediaControllerClient::SendCommand(const std::string& server_name, const std::string& command, const picojson::value& data, - const std::string& reply_id, - const JsonCallback& reply_cb) { + const JsonCallback& reply_cb, + char** request_id) { ScopeLogger(); bundle* bundle = bundle_create(); SCOPE_EXIT { @@ -466,13 +480,6 @@ PlatformResult MediaControllerClient::SendCommand(const std::string& server_name }; int ret; - ret = bundle_add(bundle, "replyId", reply_id.c_str()); - if (MEDIA_CONTROLLER_ERROR_NONE != ret) { - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "Unable to add replyId to bundle", - ("bundle_add(replyId) error: %d, message: %s", ret, get_error_message(ret))); - } - ret = bundle_add(bundle, "data", data.serialize().c_str()); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( @@ -480,12 +487,11 @@ PlatformResult MediaControllerClient::SendCommand(const std::string& server_name ("bundle_add(data) error: %d, message: %s", ret, get_error_message(ret))); } - ret = mc_client_send_custom_command(handle_, server_name.c_str(), command.c_str(), bundle, - OnCommandReply, this); + ret = mc_client_send_custom_cmd(handle_, server_name.c_str(), command.c_str(), bundle, request_id); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Error sending custom command", - ("mc_client_send_custom_command() error: %d, message: %s", ret, get_error_message(ret))); + ("mc_client_send_custom_cmd() error: %d, message: %s", ret, get_error_message(ret))); } command_reply_callback_ = reply_cb; @@ -493,66 +499,73 @@ PlatformResult MediaControllerClient::SendCommand(const std::string& server_name return PlatformResult(ErrorCode::NO_ERROR); } -void MediaControllerClient::OnCommandReply(const char* server_name, int result_code, bundle* bundle, - void* user_data) { +void MediaControllerClient::OnCommandReply(const char* server_name, const char* request_id, + int result_code, bundle* bundle, void* user_data) { ScopeLogger(); MediaControllerClient* client = static_cast<MediaControllerClient*>(user_data); + picojson::value out = picojson::value(picojson::object()); + picojson::object& out_o = out.get<picojson::object>(); picojson::value reply = picojson::value(picojson::object()); picojson::object& reply_o = reply.get<picojson::object>(); int ret; - char* reply_id_str = nullptr; char* data_str = nullptr; SCOPE_EXIT { - free(reply_id_str); free(data_str); }; - - ret = bundle_get_str(bundle, "replyId", &reply_id_str); - if (MEDIA_CONTROLLER_ERROR_NONE != ret) { - LoggerE("bundle_get_str(replyId) failed, error: %d", ret); - return; - } - - reply_o["replyId"] = picojson::value(std::string(reply_id_str)); + picojson::value data; ret = bundle_get_str(bundle, "data", &data_str); - if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + if (BUNDLE_ERROR_NONE != ret || nullptr == data_str) { LoggerE("bundle_get_str(data) failed, error: %d", ret); - return; + } else { + std::string err; + picojson::parse(data, data_str, data_str + strlen(data_str), &err); + if (!err.empty()) { + LoggerE("Failed to parse bundle data: %s", err.c_str()); + ReportError(out_o); + client->command_reply_callback_(&out); + return; + } } - picojson::value data; - std::string err; - picojson::parse(data, data_str, data_str + strlen(data_str), &err); - if (!err.empty()) { - LoggerE("Failed to parse bundle data: %s", err.c_str()); - return; - } reply_o["data"] = data; reply_o["name"] = picojson::value(server_name); - client->command_reply_callback_(&reply); + if (nullptr == request_id) { + LoggerE("Request id is null."); + ReportError(out_o); + client->command_reply_callback_(&out); + return; + } + out_o["requestId"] = picojson::value(std::string(request_id)); + + ReportSuccess(reply, out_o); + client->command_reply_callback_(&out); } PlatformResult MediaControllerClient::SendPlaybackState(const std::string& server_name, const std::string& state) { ScopeLogger(); - int state_e; - PlatformResult result = - Types::StringToPlatformEnum(Types::kMediaControllerPlaybackState, state, &state_e); + // In Native API, since Tizen 5.0 an action instead of a state is sent to change the state of a + // server. In Web API the names were not refactored. + mc_playback_action_e action_e; + PlatformResult result = types::MediaControllerPlaybackActionEnum.getValue(state, &action_e); if (!result) { return result; } - int ret; - ret = mc_client_send_playback_state_command(handle_, server_name.c_str(), - static_cast<mc_playback_states_e>(state_e)); + /* TODO: Prepare an ACR and propose use case for request_id. + char* request_id = nullptr; + SCOPE_EXIT { + free(request_id); + };*/ + int ret = mc_client_send_playback_action_cmd(handle_, server_name.c_str(), action_e, nullptr); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error sending playback state", - ("mc_client_send_playback_state_command() error: %d, message: %s", - ret, get_error_message(ret))); + ("mc_client_send_playback_action_cmd() error: %d, message: %s", ret, + get_error_message(ret))); } return PlatformResult(ErrorCode::NO_ERROR); diff --git a/src/mediacontroller/mediacontroller_client.h b/src/mediacontroller/mediacontroller_client.h index bb7cb8a1..e0e669d0 100644 --- a/src/mediacontroller/mediacontroller_client.h +++ b/src/mediacontroller/mediacontroller_client.h @@ -46,8 +46,8 @@ class MediaControllerClient { common::PlatformResult SendRepeatMode(const std::string& server_name, bool mode); common::PlatformResult SendCommand(const std::string& server_name, const std::string& command, - const picojson::value& data, const std::string& reply_id, - const JsonCallback& reply_cb); + const picojson::value& data, const JsonCallback& reply_cb, + char** request_id); common::PlatformResult SetServerStatusChangeListener(const JsonCallback& callback); common::PlatformResult UnsetServerStatusChangeListener(); @@ -66,8 +66,8 @@ class MediaControllerClient { static void OnServerStatusUpdate(const char* server_name, mc_server_state_e state, void* user_data); - static void OnCommandReply(const char* server_name, int result_code, bundle* bundle, - void* user_data); + static void OnCommandReply(const char* server_name, const char* request_id, + int result_code, bundle* bundle, void* user_data); static void OnPlaybackUpdate(const char* server_name, mc_playback_h playback, void* user_data); static void OnShuffleModeUpdate(const char* server_name, mc_shuffle_mode_e mode, void* user_data); static void OnRepeatModeUpdate(const char* server_name, mc_repeat_mode_e mode, void* user_data); diff --git a/src/mediacontroller/mediacontroller_instance.cc b/src/mediacontroller/mediacontroller_instance.cc index 350c4d69..ab7d8aa7 100644 --- a/src/mediacontroller/mediacontroller_instance.cc +++ b/src/mediacontroller/mediacontroller_instance.cc @@ -21,6 +21,7 @@ #include "common/platform_result.h" #include "common/task-queue.h" #include "common/tools.h" +#include "common/scope_exit.h" #include "mediacontroller/mediacontroller_types.h" @@ -134,7 +135,7 @@ void MediaControllerInstance::MediaControllerServerUpdatePlaybackState(const pic CHECK_EXIST(args, "state", out) if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -153,7 +154,7 @@ void MediaControllerInstance::MediaControllerServerUpdatePlaybackPosition( const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -174,7 +175,7 @@ void MediaControllerInstance::MediaControllerServerUpdateShuffleMode(const picoj picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -197,7 +198,7 @@ void MediaControllerInstance::MediaControllerServerUpdateRepeatMode(const picojs ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -219,7 +220,7 @@ void MediaControllerInstance::MediaControllerServerUpdateMetadata(const picojson picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -241,7 +242,7 @@ void MediaControllerInstance::MediaControllerServerAddChangeRequestPlaybackInfoL const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -272,7 +273,7 @@ void MediaControllerInstance::MediaControllerServerRemoveChangeRequestPlaybackIn const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -287,7 +288,7 @@ void MediaControllerInstance::MediaControllerServerAddCommandListener(const pico picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -311,17 +312,24 @@ void MediaControllerInstance::MediaControllerServerReplyCommand(const picojson:: picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } CHECK_EXIST(args, "clientName", out) - CHECK_EXIST(args, "replyId", out) + CHECK_EXIST(args, "requestId", out) CHECK_EXIST(args, "data", out) - server_->CommandReply(args.get("clientName").get<std::string>(), args.get("replyId").to_str(), - args.get("data")); + auto result = server_->CommandReply( + args.get("clientName").get<std::string>(), + args.get("requestId").get<std::string>(), + args.get("data")); + + if (!result) { + LogAndReportError(result, &out); + return; + } ReportSuccess(out); } @@ -330,7 +338,7 @@ void MediaControllerInstance::MediaControllerServerRemoveCommandListener( const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!server_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Server not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: server_")); return; } @@ -368,7 +376,7 @@ void MediaControllerInstance::MediaControllerClientFindServers(const picojson::v picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -401,7 +409,7 @@ void MediaControllerInstance::MediaControllerClientGetLatestServerInfo(const pic picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -420,7 +428,7 @@ void MediaControllerInstance::MediaControllerClientGetPlaybackInfo(const picojso picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -443,7 +451,7 @@ void MediaControllerInstance::MediaControllerServerInfoSendPlaybackState( const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -478,7 +486,7 @@ void MediaControllerInstance::MediaControllerServerInfoSendPlaybackPosition( const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -514,7 +522,7 @@ void MediaControllerInstance::MediaControllerServerInfoSendShuffleMode(const pic ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -549,7 +557,7 @@ void MediaControllerInstance::MediaControllerServerInfoSendRepeatMode(const pico picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -584,31 +592,37 @@ void MediaControllerInstance::MediaControllerServerInfoSendCommand(const picojso picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } CHECK_EXIST(args, "listenerId", out) - CHECK_EXIST(args, "replyId", out) CHECK_EXIST(args, "name", out) CHECK_EXIST(args, "command", out) CHECK_EXIST(args, "data", out) JsonCallback reply_cb = [this, args](picojson::value* reply) -> void { picojson::object& reply_obj = reply->get<picojson::object>(); - reply_obj["listenerId"] = args.get("listenerId"); - Instance::PostMessage(this, reply->serialize().c_str()); }; + char* request_id = nullptr; + SCOPE_EXIT { + free(request_id); + }; + PlatformResult result = client_->SendCommand( - args.get("name").get<std::string>(), args.get("command").get<std::string>(), args.get("data"), - args.get("replyId").to_str(), reply_cb); + args.get("name").get<std::string>(), + args.get("command").get<std::string>(), + args.get("data"), + reply_cb, + &request_id); if (result) { ReportSuccess(out); + out["requestId"] = picojson::value(std::string(request_id)); } else { LogAndReportError(result, &out, ("Failed to send command.")); } @@ -618,7 +632,7 @@ void MediaControllerInstance::MediaControllerServerInfoAddServerStatusChangeList const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -649,7 +663,7 @@ void MediaControllerInstance::MediaControllerServerInfoRemoveServerStatusChangeL const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -664,7 +678,7 @@ void MediaControllerInstance::MediaControllerServerInfoAddPlaybackInfoChangeList const picojson::value& args, picojson::object& out) { ScopeLogger(); if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } @@ -694,7 +708,7 @@ void MediaControllerInstance::MediaControllerServerInfoAddPlaybackInfoChangeList void MediaControllerInstance::MediaControllerServerInfoRemovePlaybackInfoChangeListener( const picojson::value& args, picojson::object& out) { if (!client_) { - LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Client not initialized."), &out, + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, ("Failed: client_")); return; } diff --git a/src/mediacontroller/mediacontroller_server.cc b/src/mediacontroller/mediacontroller_server.cc index b2a3e83e..dc82787b 100644 --- a/src/mediacontroller/mediacontroller_server.cc +++ b/src/mediacontroller/mediacontroller_server.cc @@ -74,27 +74,25 @@ PlatformResult MediaControllerServer::Init() { PlatformResult MediaControllerServer::SetPlaybackState(const std::string& state) { ScopeLogger(); - int state_int; - PlatformResult result = - Types::StringToPlatformEnum(Types::kMediaControllerPlaybackState, state, &state_int); - + mc_playback_states_e state_e; + PlatformResult result = types::MediaControllerPlaybackStateEnum.getValue(state, &state_e); if (!result) { return result; } - if (static_cast<mc_playback_states_e>(state_int) == playback_state_) { + if (state_e == playback_state_) { LoggerD("No change in playback state requested, skipping"); return PlatformResult(ErrorCode::NO_ERROR); } - int ret = mc_server_set_playback_state(handle_, static_cast<mc_playback_states_e>(state_int)); + int ret = mc_server_set_playback_state(handle_, state_e); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Error setting playback state", ("mc_server_set_playback_state() error: %d, message: %s", ret, get_error_message(ret))); } - playback_state_ = static_cast<mc_playback_states_e>(state_int); + playback_state_ = state_e; ret = mc_server_update_playback_info(handle_); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { @@ -180,16 +178,14 @@ PlatformResult MediaControllerServer::SetRepeatMode(bool mode) { PlatformResult MediaControllerServer::SetMetadata(const picojson::object& metadata) { ScopeLogger(); - int attribute_int, ret; for (picojson::object::const_iterator i = metadata.begin(); i != metadata.end(); ++i) { - PlatformResult result = Types::StringToPlatformEnum(Types::kMediaControllerMetadataAttribute, - i->first, &attribute_int); + mc_meta_e attr_e; + PlatformResult result = types::MediaControllerMetadataAttributeEnum.getValue(i->first, &attr_e); if (!result) { return result; } - ret = mc_server_set_metadata(handle_, static_cast<mc_meta_e>(attribute_int), - i->second.to_str().c_str()); + int ret = mc_server_set_metadata(handle_, attr_e, i->second.to_str().c_str()); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error setting metadata", ("mc_server_set_metadata(%s) error: %d, message: %s", @@ -197,7 +193,7 @@ PlatformResult MediaControllerServer::SetMetadata(const picojson::object& metada } } - ret = mc_server_update_metadata(handle_); + int ret = mc_server_update_metadata(handle_); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Error updating metadata", @@ -207,38 +203,29 @@ PlatformResult MediaControllerServer::SetMetadata(const picojson::object& metada return PlatformResult(ErrorCode::NO_ERROR); } -void MediaControllerServer::OnCommandReceived(const char* client_name, const char* command, - bundle* bundle, void* user_data) { +void MediaControllerServer::OnCommandReceived(const char* client_name, const char* request_id, + const char* command, bundle* bundle, void* user_data) { ScopeLogger(); MediaControllerServer* server = static_cast<MediaControllerServer*>(user_data); int ret; char* data_str = nullptr; - char* reply_id_str = nullptr; SCOPE_EXIT { free(data_str); - free(reply_id_str); }; + picojson::value data; ret = bundle_get_str(bundle, "data", &data_str); - if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + if (BUNDLE_ERROR_NONE != ret || nullptr == data_str) { LoggerE("bundle_get_str(data) failed, error: %d", ret); - return; - } - - ret = bundle_get_str(bundle, "replyId", &reply_id_str); - if (MEDIA_CONTROLLER_ERROR_NONE != ret) { - LoggerE("bundle_get_str(replyId) failed, error: %d", ret); - return; - } - - picojson::value data; - std::string err; - picojson::parse(data, data_str, data_str + strlen(data_str), &err); - if (!err.empty()) { - LoggerE("Failed to parse bundle data: %s", err.c_str()); - return; + } else { + std::string err; + picojson::parse(data, data_str, data_str + strlen(data_str), &err); + if (!err.empty()) { + LoggerE("Failed to parse bundle data: %s", err.c_str()); + return; + } } picojson::value request = picojson::value(picojson::object()); @@ -246,14 +233,14 @@ void MediaControllerServer::OnCommandReceived(const char* client_name, const cha request_o["clientName"] = picojson::value(std::string(client_name)); request_o["command"] = picojson::value(std::string(command)); - request_o["replyId"] = picojson::value(std::string(reply_id_str)); + request_o["requestId"] = picojson::value(std::string(request_id)); request_o["data"] = data; server->command_listener_(&request); } PlatformResult MediaControllerServer::CommandReply(const std::string& client_name, - const std::string& reply_id, + const std::string& request_id, const picojson::value& data) { ScopeLogger(); @@ -264,13 +251,6 @@ PlatformResult MediaControllerServer::CommandReply(const std::string& client_nam bundle_free(bundle); }; - ret = bundle_add(bundle, "replyId", reply_id.c_str()); - if (MEDIA_CONTROLLER_ERROR_NONE != ret) { - return LogAndCreateResult( - ErrorCode::UNKNOWN_ERR, "Unable to add replyId to bundle", - ("bundle_add(replyId) error: %d, message: %s", ret, get_error_message(ret))); - } - ret = bundle_add(bundle, "data", data.serialize().c_str()); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( @@ -278,11 +258,11 @@ PlatformResult MediaControllerServer::CommandReply(const std::string& client_nam ("bundle_add(data) error: %d, message: %s", ret, get_error_message(ret))); } - ret = mc_server_send_command_reply(handle_, client_name.c_str(), 0, bundle); + ret = mc_server_send_cmd_reply(handle_, client_name.c_str(), request_id.c_str(), 0, bundle); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Error sending command reply", - ("mc_server_send_command_reply() error: %d, message: %s", ret, get_error_message(ret))); + ("mc_server_send_cmd_reply() error: %d, message: %s", ret, get_error_message(ret))); } return PlatformResult(ErrorCode::NO_ERROR); @@ -291,10 +271,10 @@ PlatformResult MediaControllerServer::CommandReply(const std::string& client_nam PlatformResult MediaControllerServer::SetCommandListener(const JsonCallback& callback) { ScopeLogger(); - int ret = mc_server_set_custom_command_received_cb(handle_, OnCommandReceived, this); + int ret = mc_server_set_custom_cmd_received_cb(handle_, OnCommandReceived, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to set command callback", - ("mc_server_set_custom_command_received_cb() error: %d, message: %s", + ("mc_server_set_custom_cmd_received_cb() error: %d, message: %s", ret, get_error_message(ret))); } command_listener_ = callback; @@ -305,10 +285,10 @@ PlatformResult MediaControllerServer::SetCommandListener(const JsonCallback& cal PlatformResult MediaControllerServer::UnsetCommandListener() { ScopeLogger(); - int ret = mc_server_unset_custom_command_received_cb(handle_); + int ret = mc_server_unset_custom_cmd_received_cb(handle_); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to unset command callback", - ("mc_server_set_custom_command_received_cb() error: %d, message: %s", + ("mc_server_unset_custom_cmd_received_cb() error: %d, message: %s", ret, get_error_message(ret))); } command_listener_ = nullptr; @@ -326,7 +306,7 @@ PlatformResult MediaControllerServer::SetChangeRequestPlaybackInfoListener( // The purpose of this lambda is to unset as many setters as we can in case of failure. int (*unsetters[])(mc_server_h) = { - mc_server_unset_playback_state_command_received_cb, + mc_server_unset_playback_action_cmd_received_cb, mc_server_unset_playback_position_cmd_received_cb, mc_server_unset_shuffle_mode_cmd_received_cb, /*mc_server_unset_repeat_mode_cmd_received_cb the last unsetter will never be used*/}; @@ -340,11 +320,13 @@ PlatformResult MediaControllerServer::SetChangeRequestPlaybackInfoListener( } }; - int ret = mc_server_set_playback_state_command_received_cb(handle_, OnPlaybackStateCommand, this); + // In Native API, since Tizen 5.0 an action instead of a state is sent to change the state of a + // server. In Web API the names were not refactored. + int ret = mc_server_set_playback_action_cmd_received_cb(handle_, OnPlaybackActionCommand, this); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Unable to set playback state command listener", - ("mc_server_set_playback_state_command_received_cb() error: %d, message: %s", ret, + ("mc_server_set_playback_action_cmd_received_cb() error: %d, message: %s", ret, get_error_message(ret))); } @@ -382,11 +364,11 @@ PlatformResult MediaControllerServer::SetChangeRequestPlaybackInfoListener( PlatformResult MediaControllerServer::UnsetChangeRequestPlaybackInfoListener() { ScopeLogger(); - int ret = mc_server_unset_playback_state_command_received_cb(handle_); + int ret = mc_server_unset_playback_action_cmd_received_cb(handle_); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Unable to unset playback state command listener", - ("mc_server_unset_playback_state_command_received_cb() error: %d, message: %s", ret, + ("mc_server_unset_playback_action_cmd_received_cb() error: %d, message: %s", ret, get_error_message(ret))); } @@ -419,22 +401,29 @@ PlatformResult MediaControllerServer::UnsetChangeRequestPlaybackInfoListener() { return PlatformResult(ErrorCode::NO_ERROR); } -void MediaControllerServer::OnPlaybackStateCommand(const char* client_name, - mc_playback_states_e state_e, void* user_data) { +void MediaControllerServer::OnPlaybackActionCommand(const char* client_name, const char* request_id, + mc_playback_action_e action, void* user_data) { ScopeLogger(); MediaControllerServer* server = static_cast<MediaControllerServer*>(user_data); - if (server->playback_state_ == state_e) { - LoggerD("The media playback state did not change, skipping"); + // Here, we need to convert mc_playback_action_e enum to mc_playback_states_e enum. + std::string action_str; + PlatformResult result = types::MediaControllerPlaybackActionEnum.getName(action, &action_str); + if (!result) { + LoggerW("MediaControllerPlaybackActionEnum.getName() failed, error: %s", result.message().c_str()); return; } - std::string state; - PlatformResult result = Types::PlatformEnumToString(Types::kMediaControllerPlaybackState, - static_cast<int>(state_e), &state); + mc_playback_states_e state_e; + result = types::MediaControllerPlaybackStateEnum.getValue(action_str, &state_e); if (!result) { - LoggerE("PlatformEnumToString failed, error: %s", result.message().c_str()); + LoggerE("MediaControllerPlaybackStateEnum.getValue() failed, error: %s", result.message().c_str()); + return; + } + + if (server->playback_state_ == state_e) { + LoggerD("The media playback state did not change, skipping"); return; } @@ -442,7 +431,7 @@ void MediaControllerServer::OnPlaybackStateCommand(const char* client_name, picojson::object& data_o = data.get<picojson::object>(); data_o["action"] = picojson::value(std::string("onplaybackstaterequest")); - data_o["state"] = picojson::value(state); + data_o["state"] = picojson::value(action_str); server->change_request_playback_info_listener_(&data); } diff --git a/src/mediacontroller/mediacontroller_server.h b/src/mediacontroller/mediacontroller_server.h index 2fc1803a..010f59b0 100644 --- a/src/mediacontroller/mediacontroller_server.h +++ b/src/mediacontroller/mediacontroller_server.h @@ -41,7 +41,7 @@ class MediaControllerServer { common::PlatformResult SetChangeRequestPlaybackInfoListener(const JsonCallback& callback); common::PlatformResult UnsetChangeRequestPlaybackInfoListener(); - common::PlatformResult CommandReply(const std::string& client_name, const std::string& reply_id, + common::PlatformResult CommandReply(const std::string& client_name, const std::string& request_id, const picojson::value& data); common::PlatformResult SetCommandListener(const JsonCallback& callback); @@ -60,8 +60,8 @@ class MediaControllerServer { JsonCallback command_listener_; - static void OnPlaybackStateCommand(const char* client_name, mc_playback_states_e state_e, - void* user_data); + static void OnPlaybackActionCommand(const char* client_name, const char* request_id, + mc_playback_action_e action, void* user_data); static void OnPlaybackPositionCommand(const char* client_name, const char* request_id, unsigned long long position, void* user_data); static void OnShuffleModeCommand(const char* client_name, const char* request_id, @@ -69,8 +69,8 @@ class MediaControllerServer { static void OnRepeatModeCommand(const char* client_name, const char* request_id, mc_repeat_mode_e mode, void* user_data); - static void OnCommandReceived(const char* client_name, const char* command, bundle* data, - void* user_data); + static void OnCommandReceived(const char* client_name, const char* request_id, + const char* command, bundle* data, void* user_data); }; } // namespace mediacontroller diff --git a/src/mediacontroller/mediacontroller_types.cc b/src/mediacontroller/mediacontroller_types.cc index f56c431b..4e9cde12 100644 --- a/src/mediacontroller/mediacontroller_types.cc +++ b/src/mediacontroller/mediacontroller_types.cc @@ -28,104 +28,77 @@ namespace mediacontroller { using common::PlatformResult; using common::ErrorCode; -const std::string Types::kMediaControllerServerState = "MediaControllerServerState"; -const std::string Types::kMediaControllerPlaybackState = "MediaControllerPlaybackState"; -const std::string Types::kMediaControllerMetadataAttribute = "MediaControllerMetadataAttribute"; - -const PlatformEnumMap Types::platform_enum_map_ = {{kMediaControllerServerState, - {{"NONE", MC_SERVER_STATE_NONE}, - {"ACTIVE", MC_SERVER_STATE_ACTIVATE}, - {"INACTIVE", MC_SERVER_STATE_DEACTIVATE}}}, - {kMediaControllerPlaybackState, - {{"PLAY", MC_PLAYBACK_STATE_PLAYING}, - {"PAUSE", MC_PLAYBACK_STATE_PAUSED}, - {"STOP", MC_PLAYBACK_STATE_STOPPED}, - {"NEXT", MC_PLAYBACK_STATE_NEXT_FILE}, - {"PREV", MC_PLAYBACK_STATE_PREV_FILE}, - {"FORWARD", MC_PLAYBACK_STATE_FAST_FORWARD}, - {"REWIND", MC_PLAYBACK_STATE_REWIND}}}, - {kMediaControllerMetadataAttribute, - {{"title", MC_META_MEDIA_TITLE}, - {"artist", MC_META_MEDIA_ARTIST}, - {"album", MC_META_MEDIA_ALBUM}, - {"author", MC_META_MEDIA_AUTHOR}, - {"genre", MC_META_MEDIA_GENRE}, - {"duration", MC_META_MEDIA_DURATION}, - {"date", MC_META_MEDIA_DATE}, - {"copyright", MC_META_MEDIA_COPYRIGHT}, - {"description", MC_META_MEDIA_DESCRIPTION}, - {"trackNum", MC_META_MEDIA_TRACK_NUM}, - {"picture", MC_META_MEDIA_PICTURE}}}}; - -PlatformEnumReverseMap Types::platform_enum_reverse_map_ = {}; - -PlatformResult Types::GetPlatformEnumMap(const std::string& type, - std::map<std::string, int>* enum_map) { - ScopeLogger(); - - auto iter = platform_enum_map_.find(type); - if (iter == platform_enum_map_.end()) { - return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, - std::string("Undefined platform enum type ") + type); - } - - *enum_map = platform_enum_map_.at(type); - - return PlatformResult(ErrorCode::NO_ERROR); -} - -PlatformResult Types::StringToPlatformEnum(const std::string& type, const std::string& value, - int* platform_enum) { - ScopeLogger(); - - std::map<std::string, int> def; - PlatformResult result = GetPlatformEnumMap(type, &def); - if (!result) { - return result; - } - - auto def_iter = def.find(value); - if (def_iter != def.end()) { - *platform_enum = def_iter->second; - return PlatformResult(ErrorCode::NO_ERROR); - } - - std::string message = "Platform enum value " + value + " not found for " + type; - return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, message); -} - -PlatformResult Types::PlatformEnumToString(const std::string& type, int value, - std::string* platform_str) { - ScopeLogger(); - - if (platform_enum_reverse_map_.empty()) { - for (auto& def : platform_enum_map_) { - platform_enum_reverse_map_[def.first] = {}; - - for (auto& key : def.second) { - platform_enum_reverse_map_[def.first][key.second] = key.first; - } - } - } - - auto it = platform_enum_reverse_map_.find(type); - if (it == platform_enum_reverse_map_.end()) { - return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, - std::string("Undefined platform enum type ") + type); - } - - auto def = platform_enum_reverse_map_.at(type); - auto def_it = def.find(value); - if (def_it != def.end()) { - *platform_str = def_it->second; - return PlatformResult(ErrorCode::NO_ERROR); - } - - std::string message = "Platform enum value " + std::to_string(value) + " not found for " + type; - return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, message); -} - -PlatformResult Types::ConvertPlaybackState(mc_playback_h playback_h, std::string* state) { +namespace types { + +const common::PlatformEnum<mc_server_state_e> MediaControllerServerStateEnum { + {"NONE", MC_SERVER_STATE_NONE}, + {"ACTIVE", MC_SERVER_STATE_ACTIVATE}, + {"INACTIVE", MC_SERVER_STATE_DEACTIVATE} +}; + +const common::PlatformEnum<mc_playback_states_e> MediaControllerPlaybackStateEnum { + {"PLAY", MC_PLAYBACK_STATE_PLAYING}, + {"PAUSE", MC_PLAYBACK_STATE_PAUSED}, + {"STOP", MC_PLAYBACK_STATE_STOPPED}, + {"NEXT", MC_PLAYBACK_STATE_MOVING_TO_NEXT}, + {"PREV", MC_PLAYBACK_STATE_MOVING_TO_PREVIOUS}, + {"FORWARD", MC_PLAYBACK_STATE_FAST_FORWARDING}, + {"REWIND", MC_PLAYBACK_STATE_REWINDING} +}; + +const common::PlatformEnum<mc_playback_action_e> MediaControllerPlaybackActionEnum { + {"PLAY", MC_PLAYBACK_ACTION_PLAY}, + {"PAUSE", MC_PLAYBACK_ACTION_PAUSE}, + {"STOP", MC_PLAYBACK_ACTION_STOP}, + {"NEXT", MC_PLAYBACK_ACTION_NEXT}, + {"PREV", MC_PLAYBACK_ACTION_PREV}, + {"FORWARD", MC_PLAYBACK_ACTION_FAST_FORWARD}, + {"REWIND", MC_PLAYBACK_ACTION_REWIND} +}; + +const common::PlatformEnum<mc_meta_e> MediaControllerMetadataAttributeEnum { + {"title", MC_META_MEDIA_TITLE}, + {"artist", MC_META_MEDIA_ARTIST}, + {"album", MC_META_MEDIA_ALBUM}, + {"author", MC_META_MEDIA_AUTHOR}, + {"genre", MC_META_MEDIA_GENRE}, + {"duration", MC_META_MEDIA_DURATION}, + {"date", MC_META_MEDIA_DATE}, + {"copyright", MC_META_MEDIA_COPYRIGHT}, + {"description", MC_META_MEDIA_DESCRIPTION}, + {"trackNum", MC_META_MEDIA_TRACK_NUM}, + {"picture", MC_META_MEDIA_PICTURE} +}; + +const common::PlatformEnum<mc_repeat_mode_e> MediaControllerRepeatModeEnum { + {"REPEAT_OFF", MC_REPEAT_MODE_OFF}, + {"REPEAT_ONE", MC_REPEAT_MODE_ONE_MEDIA}, + {"REPEAT_ALL", MC_REPEAT_MODE_ON} +}; + +const common::PlatformEnum<mc_content_age_rating_e> MediaControllerContentAgeRatingEnum { + {"ALL", MC_CONTENT_RATING_ALL}, {"1", MC_CONTENT_RATING_1_PLUS}, + {"2", MC_CONTENT_RATING_2_PLUS}, {"3", MC_CONTENT_RATING_3_PLUS}, + {"4", MC_CONTENT_RATING_4_PLUS}, {"5", MC_CONTENT_RATING_5_PLUS}, + {"6", MC_CONTENT_RATING_6_PLUS}, {"7", MC_CONTENT_RATING_7_PLUS}, + {"8", MC_CONTENT_RATING_8_PLUS}, {"9", MC_CONTENT_RATING_9_PLUS}, + {"10", MC_CONTENT_RATING_10_PLUS}, {"11", MC_CONTENT_RATING_11_PLUS}, + {"12", MC_CONTENT_RATING_12_PLUS}, {"13", MC_CONTENT_RATING_13_PLUS}, + {"14", MC_CONTENT_RATING_14_PLUS}, {"15", MC_CONTENT_RATING_15_PLUS}, + {"16", MC_CONTENT_RATING_16_PLUS}, {"17", MC_CONTENT_RATING_17_PLUS}, + {"18", MC_CONTENT_RATING_18_PLUS}, {"19", MC_CONTENT_RATING_19_PLUS} +}; + +const common::PlatformEnum<mc_content_type_e> MediaControllerContentTypeEnum { + {"IMAGE", MC_CONTENT_TYPE_IMAGE}, + {"MUSIC", MC_CONTENT_TYPE_MUSIC}, + {"VIDEO", MC_CONTENT_TYPE_VIDEO}, + {"OTHER", MC_CONTENT_TYPE_OTHER}, + {"UNDECIDED", MC_CONTENT_TYPE_UNDECIDED} +}; + + +PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state) { ScopeLogger(); int ret; @@ -140,17 +113,16 @@ PlatformResult Types::ConvertPlaybackState(mc_playback_h playback_h, std::string state_e = MC_PLAYBACK_STATE_STOPPED; } - PlatformResult result = Types::PlatformEnumToString(Types::kMediaControllerPlaybackState, - static_cast<int>(state_e), state); + PlatformResult result = MediaControllerPlaybackStateEnum.getName(state_e, state); if (!result) { - LoggerE("PlatformEnumToString failed, error: %s", result.message().c_str()); + LoggerE("MediaControllerPlaybackStateEnum.getName() failed, error: %s", result.message().c_str()); return result; } return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult Types::ConvertPlaybackPosition(mc_playback_h playback_h, double* position) { +PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* position) { ScopeLogger(); int ret; @@ -168,35 +140,30 @@ PlatformResult Types::ConvertPlaybackPosition(mc_playback_h playback_h, double* return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult Types::ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metadata) { +PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metadata) { ScopeLogger(); - std::map<std::string, int> metadata_fields; - PlatformResult result = - GetPlatformEnumMap(Types::kMediaControllerMetadataAttribute, &metadata_fields); - if (!result) { - LoggerE("GetPlatformEnumMap failed, error: %s", result.message().c_str()); - return result; - } - char* value = nullptr; SCOPE_EXIT { free(value); }; - for (auto& field : metadata_fields) { - int ret = mc_client_get_metadata(metadata_h, static_cast<mc_meta_e>(field.second), &value); + for (auto entry: MediaControllerMetadataAttributeEnum) { + int ret = mc_metadata_get(metadata_h, entry.second, &value); if (ret != MEDIA_CONTROLLER_ERROR_NONE) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting metadata", - ("mc_client_get_metadata(%s) error: %d, message: %s", - field.first.c_str(), ret, get_error_message(ret))); + ("mc_metadata_get(%s) error: %d, message: %s", entry.first.c_str(), + ret, get_error_message(ret))); } - (*metadata)[field.first] = picojson::value(std::string(value ? value : "")); + (*metadata)[entry.first] = picojson::value(std::string(value ? value : "")); } return PlatformResult(ErrorCode::NO_ERROR); } +} // types + + } // namespace mediacontroller } // namespace extension diff --git a/src/mediacontroller/mediacontroller_types.h b/src/mediacontroller/mediacontroller_types.h index 20966aaf..6b9eeab9 100644 --- a/src/mediacontroller/mediacontroller_types.h +++ b/src/mediacontroller/mediacontroller_types.h @@ -23,38 +23,32 @@ #include <string> #include "common/platform_result.h" +#include "common/platform_enum.h" + namespace extension { namespace mediacontroller { -typedef std::map<std::string, std::map<std::string, int>> PlatformEnumMap; -typedef std::map<std::string, std::map<int, std::string>> PlatformEnumReverseMap; typedef std::function<void(picojson::value*)> JsonCallback; -class Types { - public: - static const std::string kMediaControllerServerState; - static const std::string kMediaControllerPlaybackState; - static const std::string kMediaControllerMetadataAttribute; - - static common::PlatformResult GetPlatformEnumMap(const std::string& type, - std::map<std::string, int>* platform_str); +namespace types { - static common::PlatformResult StringToPlatformEnum(const std::string& type, - const std::string& value, int* platform_enum); +common::PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state); +common::PlatformResult ConvertContentAgeRating(mc_playback_h playback_h, std::string* state); +common::PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* position); +common::PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metadata); +common::PlatformResult ConvertContentType(mc_playback_h playback_h, std::string* contentType); - static common::PlatformResult PlatformEnumToString(const std::string& type, int value, - std::string* platform_str); +extern const common::PlatformEnum<mc_server_state_e> MediaControllerServerStateEnum; +extern const common::PlatformEnum<mc_playback_states_e> MediaControllerPlaybackStateEnum; +extern const common::PlatformEnum<mc_playback_action_e> MediaControllerPlaybackActionEnum; +extern const common::PlatformEnum<mc_meta_e> MediaControllerMetadataAttributeEnum; +extern const common::PlatformEnum<mc_repeat_mode_e> MediaControllerRepeatModeEnum; +extern const common::PlatformEnum<mc_content_age_rating_e> MediaControllerContentAgeRatingEnum; +extern const common::PlatformEnum<mc_content_type_e> MediaControllerContentTypeEnum; - static common::PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state); - static common::PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* position); - static common::PlatformResult ConvertMetadata(mc_metadata_h metadata_h, - picojson::object* metadata); +} // namespace types - private: - static const PlatformEnumMap platform_enum_map_; - static PlatformEnumReverseMap platform_enum_reverse_map_; -}; } // namespace mediacontroller } // namespace extension diff --git a/src/messageport/messageport_api.js b/src/messageport/messageport_api.js index 2cb4de75..00e77435 100755 --- a/src/messageport/messageport_api.js +++ b/src/messageport/messageport_api.js @@ -14,24 +14,22 @@ * limitations under the License. */ -var JSON_ = xwalk.JSON; var validator_ = xwalk.utils.validator; var types_ = validator_.Types; var type_ = xwalk.utils.type; var converter_ = xwalk.utils.converter; -var privUtils_ = xwalk.utils; +var native_ = new xwalk.utils.NativeManager(extension); + +var LOCAL_MESSAGE_PORT_LISTENER_ID = 'LocalMessagePortListener'; var callbackId = 0; var callbacks = {}; var ports = []; -extension.setMessageListener(function(json) { - var msg = JSON_.parse(json); - var listeners = callbacks[msg['local_port_id']]; +function MessagePortChangeCallback(msg) { + var listeners = callbacks[msg['localPortId']]; var rmp; - privUtils_.log('Listeners length:' + listeners.length); - if (!msg.hasOwnProperty('remotePort')) rmp = null; else rmp = new RemoteMessagePort(msg.remotePort, msg.remoteAppId, msg.trusted); for (var i = 0; i < listeners.length; i++) { @@ -40,69 +38,12 @@ extension.setMessageListener(function(json) { func(msg.message, rmp); }, 0); } -}); +} function nextCallbackId() { return callbackId++; } -var ExceptionMap = { - UnknownError: WebAPIException.UNKNOWN_ERR, - TypeMismatchError: WebAPIException.TYPE_MISMATCH_ERR, - InvalidValuesError: WebAPIException.INVALID_VALUES_ERR, - IOError: WebAPIException.IO_ERR, - ServiceNotAvailableError: WebAPIException.SERVICE_NOT_AVAILABLE_ERR, - SecurityError: WebAPIException.SECURITY_ERR, - NetworkError: WebAPIException.NETWORK_ERR, - NotSupportedError: WebAPIException.NOT_SUPPORTED_ERR, - NotFoundError: WebAPIException.NOT_FOUND_ERR, - InvalidAccessError: WebAPIException.INVALID_ACCESS_ERR, - AbortError: WebAPIException.ABORT_ERR, - QuotaExceededError: WebAPIException.QUOTA_EXCEEDED_ERR -}; - -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 WebAPIException(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) { - if (ExceptionMap[err.name]) { - throw new WebAPIException(ExceptionMap[err.name], err.message); - } else { - throw new WebAPIException(WebAPIException.UNKNOWN_ERR, 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 }); -} - function MessagePortManager() { // constructor of MessagePortManager } @@ -119,22 +60,22 @@ MessagePortManager.prototype.requestLocalMessagePort = function(localMessagePort ); } - var localPortId; + var localPortId; // TODO remove var nativeParam = { localMessagePortName: args.localMessagePortName }; - try { - localPortId = callNative( - 'MessagePortManager_requestLocalMessagePort', - nativeParam - ); - } catch (e) { - throw e; - } + var result = native_.callSync( + 'MessagePortManager_requestLocalMessagePort', + nativeParam + ); - var returnObject = new LocalMessagePort(args.localMessagePortName, false); - ports[nativeParam.localMessagePortName] = localPortId; + if (native_.isSuccess(result)) { + var returnObject = new LocalMessagePort(args.localMessagePortName, false); + ports[nativeParam.localMessagePortName] = native_.getResultObject(result); + } else { + throw native_.getErrorObject(result); + } return returnObject; }; @@ -157,17 +98,17 @@ MessagePortManager.prototype.requestTrustedLocalMessagePort = function( localMessagePortName: args.localMessagePortName }; - try { - var localPortId = callNative( - 'MessagePortManager_requestTrustedLocalMessagePort', - nativeParam - ); - } catch (e) { - throw e; - } + var result = native_.callSync( + 'MessagePortManager_requestTrustedLocalMessagePort', + nativeParam + ); - var returnObject = new LocalMessagePort(args.localMessagePortName, true); - ports[nativeParam.localMessagePortName] = localPortId; + if (native_.isSuccess(result)) { + var returnObject = new LocalMessagePort(args.localMessagePortName, true); + ports[nativeParam.localMessagePortName] = native_.getResultObject(result); + } else { + throw native_.getErrorObject(result); + } return returnObject; }; @@ -186,13 +127,13 @@ MessagePortManager.prototype.requestRemoteMessagePort = function( remoteMessagePortName: args.remoteMessagePortName }; - try { - var syncResult = callNative( - 'MessagePortManager_requestRemoteMessagePort', - nativeParam - ); - } catch (e) { - throw e; + var result = native_.callSync( + 'MessagePortManager_requestRemoteMessagePort', + nativeParam + ); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); } var returnObject = new RemoteMessagePort( @@ -218,13 +159,13 @@ MessagePortManager.prototype.requestTrustedRemoteMessagePort = function( remoteMessagePortName: args.remoteMessagePortName }; - try { - var syncResult = callNative( - 'MessagePortManager_requestTrustedRemoteMessagePort', - nativeParam - ); - } catch (e) { - throw e; + var result = native_.callSync( + 'MessagePortManager_requestTrustedRemoteMessagePort', + nativeParam + ); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); } var returnObject = new RemoteMessagePort( @@ -248,6 +189,8 @@ LocalMessagePort.prototype.addMessagePortListener = function(listener) { { name: 'listener', type: types_.FUNCTION, nullable: false } ]); + native_.addListener(LOCAL_MESSAGE_PORT_LISTENER_ID, MessagePortChangeCallback); + var portId = ports[this.messagePortName]; if (!callbacks.hasOwnProperty(portId)) callbacks[portId] = []; @@ -312,10 +255,10 @@ RemoteMessagePort.prototype.sendMessage = function() { } ]); - var filtered_data = new Array(args.data.length); - var unique_data_key = {}; - var data_length = args.data.length; - for (var i = 0; i < data_length; i++) { + var filteredData = new Array(args.data.length); + var uniqueDataKey = {}; + var dataLength = args.data.length; + for (var i = 0; i < dataLength; i++) { if (!args.data[i].hasOwnProperty('key')) { throw new WebAPIException( WebAPIException.INVALID_VALUES_ERR, @@ -329,7 +272,7 @@ RemoteMessagePort.prototype.sendMessage = function() { 'Property \'key\' should not be empty.' ); } - if (true === unique_data_key[key]) { + if (true === uniqueDataKey[key]) { throw new WebAPIException( WebAPIException.INVALID_VALUES_ERR, 'Property \'key\' should not be duplicated.' @@ -337,7 +280,7 @@ RemoteMessagePort.prototype.sendMessage = function() { } var value = args.data[i].value; if (type_.isString(value)) { - filtered_data[i] = { key: key, value: value, valueType: 'stringValueType' }; + filteredData[i] = { key: key, value: value, valueType: 'stringValueType' }; } else if (type_.isArray(value)) { var arrayMember = value[0]; if (type_.isString(arrayMember)) { @@ -349,7 +292,7 @@ RemoteMessagePort.prototype.sendMessage = function() { ); } } - filtered_data[i] = { + filteredData[i] = { key: key, value: value, valueType: 'stringArrayValueType' @@ -361,7 +304,7 @@ RemoteMessagePort.prototype.sendMessage = function() { 'Data is not octet array' ); } - filtered_data[i] = { + filteredData[i] = { key: key, value: value, valueType: 'byteStreamValueType' @@ -375,7 +318,7 @@ RemoteMessagePort.prototype.sendMessage = function() { ); } } - filtered_data[i] = { + filteredData[i] = { key: key, value: value, valueType: 'byteStreamArrayValueType' @@ -383,26 +326,30 @@ RemoteMessagePort.prototype.sendMessage = function() { } } else { // convert any other value to string -> backward compatibility - filtered_data[i] = { + filteredData[i] = { key: key, value: converter_.toString(value), valueType: 'stringValueType' }; } - unique_data_key[key] = true; + uniqueDataKey[key] = true; } var nativeParam = { appId: this.appId, messagePortName: this.messagePortName, - data: filtered_data, + data: filteredData, trusted: this.isTrusted, - local_port_id: args.localMessagePort + localPortId: args.localMessagePort ? ports[args.localMessagePort.messagePortName] : -1 }; - var syncResult = callNative('RemoteMessagePort_sendMessage', nativeParam); + var result = native_.callSync('RemoteMessagePort_sendMessage', nativeParam); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } }; exports = new MessagePortManager(); diff --git a/src/messageport/messageport_instance.cc b/src/messageport/messageport_instance.cc index 28df4999..4b342d46 100644 --- a/src/messageport/messageport_instance.cc +++ b/src/messageport/messageport_instance.cc @@ -163,7 +163,8 @@ static void OnReceiveLocalMessage(int local_port_id, const char* remote_app_id, picojson::value::object o; picojson::value::array data; - o["local_port_id"] = picojson::value(static_cast<double>(local_port_id)); + o["listenerId"] = picojson::value("LocalMessagePortListener"); + o["localPortId"] = picojson::value(static_cast<double>(local_port_id)); if (remote_port) { o["remoteAppId"] = picojson::value(remote_app_id); @@ -282,29 +283,17 @@ void MessageportInstance::MessagePortManagerRequestremotemessageport(const picoj portCheck ? "true" : "false"); LoggerD("Error code: %d (%s)", ret, get_error_message(ret)); - if (ret == MESSAGE_PORT_ERROR_NONE) { + if (MESSAGE_PORT_ERROR_NONE == ret) { if (portCheck) { ReportSuccess(out); } else { LogAndReportError(NotFoundException("The port of the target application is not found"), out); } - } else if (ret == MESSAGE_PORT_ERROR_INVALID_PARAMETER) { - LogAndReportError( - InvalidValuesException("An input parameter contains an invalid value."), out, - ("message_port_check_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_OUT_OF_MEMORY) { - LogAndReportError( - UnknownException("Out of memory."), out, - ("message_port_check_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_IO_ERROR) { + } else if (MESSAGE_PORT_ERROR_IO_ERROR == ret) { // IO error means that remote port does not exist LogAndReportError( NotFoundException("The port of the target application is not found"), out, ("message_port_check_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_PORT_NOT_FOUND) { - LogAndReportError( - NotFoundException("The port of the target application is not found"), out, - ("message_port_check_remote_port error: %d (%s)", ret, get_error_message(ret))); } else { LogAndReportError( UnknownException("Unknown Error"), out, @@ -330,30 +319,18 @@ void MessageportInstance::MessagePortManagerRequesttrustedremotemessageport( portCheck ? "true" : "false"); LoggerD("Error code: %d (%s)", ret, get_error_message(ret)); - if (ret == MESSAGE_PORT_ERROR_NONE) { + if (MESSAGE_PORT_ERROR_NONE == ret) { if (portCheck) { ReportSuccess(out); } else { LogAndReportError(NotFoundException("The port of the target application is not found"), out); } - } else if (ret == MESSAGE_PORT_ERROR_INVALID_PARAMETER) { - LogAndReportError( - InvalidValuesException("An input parameter contains an invalid value."), out, - ("message_port_check_trusted_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_OUT_OF_MEMORY) { - LogAndReportError( - UnknownException("Out of memory."), out, - ("message_port_check_trusted_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_IO_ERROR) { + } else if (MESSAGE_PORT_ERROR_IO_ERROR == ret) { // IO error means that remote port does not exist LogAndReportError( NotFoundException("The port of the target application is not found"), out, ("message_port_check_trusted_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_PORT_NOT_FOUND) { - LogAndReportError( - NotFoundException("The port of the target application is not found"), out, - ("message_port_check_trusted_remote_port error: %d (%s)", ret, get_error_message(ret))); - } else if (ret == MESSAGE_PORT_ERROR_CERTIFICATE_NOT_MATCH) { + } else if (MESSAGE_PORT_ERROR_CERTIFICATE_NOT_MATCH == ret) { LogAndReportError( UnknownException("The remote application is not signed with the same certificate"), out, ("message_port_check_trusted_remote_port error: %d (%s)", ret, get_error_message(ret))); @@ -371,7 +348,7 @@ void MessageportInstance::RemoteMessagePortSendmessage(const picojson::value& ar const std::string& message_port_name = args.get("messagePortName").get<std::string>(); std::vector<picojson::value> data = args.get("data").get<picojson::array>(); - long local_port_id = static_cast<long>(args.get("local_port_id").get<double>()); + long local_port_id = static_cast<long>(args.get("localPortId").get<double>()); bool trusted = args.get("trusted").get<bool>(); int result; diff --git a/src/messaging/DBus/MessageProxy.h b/src/messaging/DBus/MessageProxy.h index 4089fcdd..aeeb96b8 100644 --- a/src/messaging/DBus/MessageProxy.h +++ b/src/messaging/DBus/MessageProxy.h @@ -18,7 +18,6 @@ #ifndef __TIZEN_MESSAGE_PROXY_H #define __TIZEN_MESSAGE_PROXY_H -#include <dbus/dbus-glib.h> #include <dbus/dbus.h> #include <email-types.h> #include <gio/gio.h> diff --git a/src/messaging/MsgCommon/AttributeRangeFilter.h b/src/messaging/MsgCommon/AttributeRangeFilter.h index 6d93cbfe..267faa2a 100644 --- a/src/messaging/MsgCommon/AttributeRangeFilter.h +++ b/src/messaging/MsgCommon/AttributeRangeFilter.h @@ -54,7 +54,6 @@ class AttributeRangeFilter : public AbstractFilter { */ AttributeRangeFilterPtr castToAttributeRangeFilter(AbstractFilterPtr from); - } // Tizen } // DeviceAPI diff --git a/src/messaging/conversations_change_callback.cc b/src/messaging/conversations_change_callback.cc index fe5f7e0f..a53c8d3f 100644 --- a/src/messaging/conversations_change_callback.cc +++ b/src/messaging/conversations_change_callback.cc @@ -74,7 +74,7 @@ ConversationPtrVector ConversationsChangeCallback::filterConversations( LoggerD("[%d] matched filter: %s", i, matched ? "YES" : "NO"); } - LoggerD("returning matching %d of %d conversations", filtered_conversations.size(), + LoggerD("returning matching %zu of %zu conversations", filtered_conversations.size(), source_conversations.size()); return filtered_conversations; @@ -85,7 +85,7 @@ ConversationPtrVector ConversationsChangeCallback::filterConversations( } void ConversationsChangeCallback::added(const ConversationPtrVector& conversations) { - ScopeLogger("conversations.size() = %d", conversations.size()); + ScopeLogger("conversations.size() = %zu", conversations.size()); if (!m_is_act) { return; } @@ -104,14 +104,14 @@ void ConversationsChangeCallback::added(const ConversationPtrVector& conversatio }; for_each(filtered.begin(), filtered.end(), each); - LoggerD("Calling:%s with:%d added conversations", CONVERSATIONSADDED, filtered.size()); + LoggerD("Calling:%s with:%zu added conversations", CONVERSATIONSADDED, filtered.size()); m_callback_data.SetAction(CONVERSATIONSADDED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::MEDIUM); } void ConversationsChangeCallback::updated(const ConversationPtrVector& conversations) { - ScopeLogger("conversations.size() = %d", conversations.size()); + ScopeLogger("conversations.size() = %zu", conversations.size()); if (!m_is_act) { return; } @@ -130,14 +130,14 @@ void ConversationsChangeCallback::updated(const ConversationPtrVector& conversat }; for_each(filtered.begin(), filtered.end(), each); - LoggerD("Calling:%s with:%d added conversations", CONVERSATIONSUPDATED, filtered.size()); + LoggerD("Calling:%s with:%zu added conversations", CONVERSATIONSUPDATED, filtered.size()); m_callback_data.SetAction(CONVERSATIONSUPDATED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::LOW); } void ConversationsChangeCallback::removed(const ConversationPtrVector& conversations) { - ScopeLogger("conversations.size() = %d", conversations.size()); + ScopeLogger("conversations.size() = %zu", conversations.size()); if (!m_is_act) { return; } @@ -156,7 +156,7 @@ void ConversationsChangeCallback::removed(const ConversationPtrVector& conversat }; for_each(filtered.begin(), filtered.end(), each); - LoggerD("Calling:%s with:%d added conversations", CONVERSATIONSREMOVED, filtered.size()); + LoggerD("Calling:%s with:%zu added conversations", CONVERSATIONSREMOVED, filtered.size()); m_callback_data.SetAction(CONVERSATIONSREMOVED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::LAST); diff --git a/src/messaging/email_manager.cc b/src/messaging/email_manager.cc index aeec4e26..5b89dd68 100644 --- a/src/messaging/email_manager.cc +++ b/src/messaging/email_manager.cc @@ -17,8 +17,11 @@ //#include <JSWebAPIErrorFactory.h> //#include <JSWebAPIError.h> //#include <JSUtil.h> +#include <chrono> +#include <list> #include <memory> #include <sstream> +#include <thread> #include "common/logger.h" #include "common/platform_exception.h" #include "common/scope_exit.h" @@ -62,6 +65,7 @@ using namespace common; using namespace extension::tizen; +using namespace std::chrono_literals; namespace extension { namespace messaging { @@ -69,6 +73,16 @@ namespace messaging { namespace { const int ACCOUNT_ID_NOT_INITIALIZED = -1; const std::string FIND_FOLDERS_ATTRIBUTE_ACCOUNTID_NAME = "serviceId"; + +bool isFirstInThread(const Message* message) { + ScopeLogger(); + return message->getId() == message->getConversationId(); +} + +bool isFirstInThread(const email_mail_data_t* mail_data) { + ScopeLogger(); + return mail_data->mail_id == mail_data->thread_id; +} } // anonymous namespace EmailManager::EmailManager() : m_slot_size(-1), m_is_initialized(false) { @@ -251,11 +265,68 @@ PlatformResult EmailManager::addMessagePlatform(int account_id, std::shared_ptr< } } - err = email_get_mail_data(message->getId(), &mail_data_final); - if (EMAIL_ERROR_NONE != err) { - return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Couldn't retrieve added mail data", - ("email_get_mail_data error: %d (%s)", err, get_error_message(err))); + /* + * If the message is first in its thread (its mail_id == thread_id) + * and it has an attachment, there is some delay between addEmailAttachments + * return and the corresponding update of message's record in the internal + * email DB. + * + * The internal DB is queried for mail_data_final below, but due to + * the mentioned delay, it may return mail_data_final, that is not + * up-to-date, i.e with mail_id != thread_id). + * If such situation occurs, up to MAX_RETRIES retries of calling + * email_get_mail_data are performed. + * If returned mail_data_final is up-to-date before reaching retries limit, + * the returned value is used to set mail properties. + * If returned mail_data_final is still not up-to-date, thread_id of the message + * is set manually to mail_id. + */ + + if (isFirstInThread(mail_data)) { + int retry = 0; + const int MAX_RETRIES = 5; + for (; retry < MAX_RETRIES; ++retry) { + err = email_get_mail_data(message->getId(), &mail_data_final); + + if (EMAIL_ERROR_NONE != err) { + /* + * TODO: in the case of email_get_mail_data failure, + * the message should be removed from the databse + */ + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox", + ("email_get_mail_data error: %d (%s)", err, get_error_message(err))); + } + + if (isFirstInThread(mail_data) && isFirstInThread(mail_data_final)) { + LoggerD("Message adding process finished after %d retries. mail_id == thread_id", retry); + break; + } + + int free_error = email_free_mail_data(&mail_data_final, 1); + if (EMAIL_ERROR_NONE != free_error) { + LoggerW("email_free_mail_data error: %d, %s", free_error, get_error_message(free_error)); + } + LoggerD("Retry number %d failed", retry); + std::this_thread::sleep_for(100ms); + } + + if (MAX_RETRIES == retry) { + LoggerD( + "Message adding process not finished after %d retries. Setting proper conversationId " + "manually", + retry); + mail_data_final->thread_id = mail_data_final->mail_id; + } + } else { + err = email_get_mail_data(message->getId(), &mail_data_final); + if (EMAIL_ERROR_NONE != err) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox", + ("email_get_mail_data error: %d (%s)", err, get_error_message(err))); + } } + ret = message->updateEmailMessage(*mail_data_final); if (ret.IsError()) { return ret; @@ -575,7 +646,7 @@ PlatformResult EmailManager::loadMessageAttachment(MessageAttachmentCallbackData return platform_result; } - LoggerD("Mail: [%d] contains: [%d] attachments", msgAttachment->getMessageId(), + LoggerD("Mail: [%d] contains: [%zu] attachments", msgAttachment->getMessageId(), attachments.size()); auto it = attachments.begin(); @@ -821,7 +892,7 @@ void EmailManager::removeStatusCallback(const std::vector<int>& ids, if (it != m_deleteRequests.end()) { LoggerD("Found request"); if (NOTI_MAIL_DELETE_FINISH == status) { - LoggerD("Successfully removed %d mails", ids.size()); + LoggerD("Successfully removed %zu mails", ids.size()); it->messagesDeleted += ids.size(); } MessagesCallbackUserData* callback = it->callback; @@ -915,6 +986,7 @@ PlatformResult EmailManager::UpdateMessagesPlatform(MessagesCallbackUserData* ca std::lock_guard<std::mutex> lock(m_mutex); std::vector<std::shared_ptr<Message>> messages = callback->getMessages(); + std::list<std::shared_ptr<Message>> firstInThreadAndHasAttachment; MessageType type = callback->getMessageServiceType(); for (auto it = messages.begin(); it != messages.end(); ++it) { if ((*it)->getType() != type) { @@ -927,7 +999,11 @@ PlatformResult EmailManager::UpdateMessagesPlatform(MessagesCallbackUserData* ca if (ret.IsError()) return ret; if ((*it)->getHasAttachment()) { - LoggerD("Message has attachments. Workaround need to be used."); + if (isFirstInThread(it->get())) { + // Messages with attachments, that are first in their threads are added + // to the container, to be processed later in this function + firstInThreadAndHasAttachment.push_back(*it); + } // Update of mail on server using function email_update_mail() is not possible. // Attachment is updated only locally (can't be later loaded from server), // so use of workaround is needed: @@ -944,8 +1020,8 @@ PlatformResult EmailManager::UpdateMessagesPlatform(MessagesCallbackUserData* ca // storing old message id (*it)->setOldId(mail->mail_id); // deleting old mail - LoggerD("mail deleted = [%d]\n", mail->mail_id); error = email_delete_mail(mail->mailbox_id, &mail->mail_id, 1, 1); + LoggerD("mail deleted = [%d]\n", mail->mail_id); if (EMAIL_ERROR_NONE != error) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Error while deleting old mail on update", @@ -961,6 +1037,73 @@ PlatformResult EmailManager::UpdateMessagesPlatform(MessagesCallbackUserData* ca } } } + + if (firstInThreadAndHasAttachment.size() == 0) { + return PlatformResult(ErrorCode::NO_ERROR); + } + + /* + * If a message with attachment, that is first in its thread was updated, + * the update process consists of adding a new message - updated version + * of the original and removing the original. + * + * After the original mail is deleted, the thread_id of the updated version + * changes - it is now identical to the mail_id of the new message. + * The change should be reflected in the updated message, sent to the JS + * layer. The code below sets proper thread_id for such messages and unsets + * inResponseTo fields. + * + * A few retries with sleeps between them are performed, to ensure, that after + * this function returns, messages returned to the JS layer and + * corresponding records in the mail DB have the same thread_ids. Due to a + * delay between calling email_delete_mail function and the update of records + * in the database, some time is needed. + */ + email_mail_data_t* mail_data{nullptr}; + int retry = 0; + const int MAX_RETRIES = 5; + for (; retry < MAX_RETRIES; ++retry) { + std::this_thread::sleep_for(100ms); + auto it = firstInThreadAndHasAttachment.begin(); + while (it != firstInThreadAndHasAttachment.end()) { + error = email_get_mail_data((*it)->getId(), &mail_data); + if (EMAIL_ERROR_NONE != error) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Couldn't add message to draft mailbox", + ("email_get_mail_data error: %d (%s)", error, get_error_message(error))); + } + + if (isFirstInThread(mail_data)) { + (*it)->setConversationId(mail_data->thread_id); + (*it)->unsetInResponseTo(); + it = firstInThreadAndHasAttachment.erase(it); + } else { + ++it; + } + + int free_error = email_free_mail_data(&mail_data, 1); + if (EMAIL_ERROR_NONE != free_error) { + LoggerW("email_free_mail_data error: %d, %s", free_error, get_error_message(free_error)); + } + } + + if (firstInThreadAndHasAttachment.size() == 0) { + LoggerD("Message updating process finished after %d retries", retry); + break; + } + + LoggerD("Message update retry number %d finished", retry); + } + + if (MAX_RETRIES == retry) { + LoggerW("Message updating process not finished after %d retries.", retry); + for (auto& message : firstInThreadAndHasAttachment) { + LoggerD("Setting proper conversationId manually, message id: %d", message->getId()); + message->setConversationId(message->getId()); + message->unsetInResponseTo(); + } + } + return PlatformResult(ErrorCode::NO_ERROR); } @@ -1047,7 +1190,7 @@ void EmailManager::findMessages(FindMsgCallbackUserData* callback) { } // Complete task - LoggerD("callback: %p error: %d messages.size() = %d", callback, callback->IsError(), + LoggerD("callback: %p error: %d messages.size() = %zu", callback, callback->IsError(), callback->getMessages().size()); if (callback->IsError()) { @@ -1103,7 +1246,7 @@ void EmailManager::findConversations(ConversationCallbackData* callback) { } // Complete task - LoggerD("callback: %p error:%d conversations.size()=%d", callback, callback->IsError(), + LoggerD("callback: %p error:%d conversations.size()=%zu", callback, callback->IsError(), callback->getConversations().size()); if (callback->IsError()) { @@ -1222,7 +1365,7 @@ void EmailManager::findFolders(FoldersCallbackData* callback) { } // Complete task - LoggerD("callback: %p error:%d folders.size()=%d", callback, callback->IsError(), + LoggerD("callback: %p error:%d folders.size()=%zu", callback, callback->IsError(), callback->getFolders().size()); if (callback->IsError()) { diff --git a/src/messaging/folders_change_callback.cc b/src/messaging/folders_change_callback.cc index 6ceda0b2..8bb67f25 100644 --- a/src/messaging/folders_change_callback.cc +++ b/src/messaging/folders_change_callback.cc @@ -69,7 +69,7 @@ FolderPtrVector FoldersChangeCallback::filterFolders(tizen::AbstractFilterPtr fi } void FoldersChangeCallback::added(const FolderPtrVector& folders) { - ScopeLogger("folders.size() = %d", folders.size()); + ScopeLogger("folders.size() = %zu", folders.size()); if (!m_is_act) { return; } @@ -88,14 +88,14 @@ void FoldersChangeCallback::added(const FolderPtrVector& folders) { }; for_each(filtered.begin(), filtered.end(), each); - LoggerD("Calling:%s with:%d added folders", FOLDERSADDED, filtered.size()); + LoggerD("Calling:%s with:%zu added folders", FOLDERSADDED, filtered.size()); m_callback_data.SetAction(FOLDERSADDED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::MEDIUM); } void FoldersChangeCallback::updated(const FolderPtrVector& folders) { - ScopeLogger("folders.size() = %d", folders.size()); + ScopeLogger("folders.size() = %zu", folders.size()); if (!m_is_act) { return; } @@ -114,14 +114,14 @@ void FoldersChangeCallback::updated(const FolderPtrVector& folders) { }; for_each(filtered.begin(), filtered.end(), each); - LoggerD("Calling:%s with:%d updated folders", FOLDERSUPDATED, filtered.size()); + LoggerD("Calling:%s with:%zu updated folders", FOLDERSUPDATED, filtered.size()); m_callback_data.SetAction(FOLDERSUPDATED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::LOW); } void FoldersChangeCallback::removed(const FolderPtrVector& folders) { - ScopeLogger("folders.size() = %d", folders.size()); + ScopeLogger("folders.size() = %zu", folders.size()); if (!m_is_act) { return; } @@ -140,7 +140,7 @@ void FoldersChangeCallback::removed(const FolderPtrVector& folders) { }; for_each(filtered.begin(), filtered.end(), each); - LoggerD("Calling:%s with:%d removed folders", FOLDERSREMOVED, filtered.size()); + LoggerD("Calling:%s with:%zu removed folders", FOLDERSREMOVED, filtered.size()); m_callback_data.SetAction(FOLDERSREMOVED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::LAST); diff --git a/src/messaging/message.cc b/src/messaging/message.cc index 80825fa4..89d0a05b 100644 --- a/src/messaging/message.cc +++ b/src/messaging/message.cc @@ -279,6 +279,11 @@ void Message::setInResponseTo(int inresp) { m_in_response_set = true; } +void Message::unsetInResponseTo() { + m_in_response = -1; + m_in_response_set = false; +} + void Message::setMessageStatus(MessageStatus status) { m_status = status; } @@ -591,8 +596,8 @@ PlatformResult Message::addEmailAttachments(std::shared_ptr<Message> message) { AttachmentPtrVector attachments = message->getMessageAttachments(); AttachmentPtrVector inlineAttachments = message->getBody()->getInlineAttachments(); - LoggerD("Attachments size: %d", attachments.size()); - LoggerD("Inline attachments size: %d", inlineAttachments.size()); + LoggerD("Attachments size: %zu", attachments.size()); + LoggerD("Inline attachments size: %zu", inlineAttachments.size()); LoggerD("Adding attachments for mail id = [%d]\n", message->getId()); for (auto it = attachments.begin(); it != attachments.end(); ++it) { PlatformResult ret = addSingleEmailAttachment(message, *it, AttachmentType::EXTERNAL); @@ -669,7 +674,7 @@ PlatformResult Message::addSMSRecipientsToStruct(const std::vector<std::string>& for (unsigned int i = 0; i < size; ++i) { char* address = const_cast<char*>(recipients.at(i).c_str()); - LoggerD("[%d] address:[%s]", i, address); + LoggerD("[%u] address:[%s]", i, address); msg_struct_t tmpAddr = NULL; if (MSG_SUCCESS == msg_list_add_item(msg, MSG_MESSAGE_ADDR_LIST_HND, &tmpAddr)) { msg_set_int_value(tmpAddr, MSG_ADDRESS_INFO_ADDRESS_TYPE_INT, MSG_ADDRESS_TYPE_PLMN); @@ -677,7 +682,7 @@ PlatformResult Message::addSMSRecipientsToStruct(const std::vector<std::string>& msg_set_str_value(tmpAddr, MSG_ADDRESS_INFO_ADDRESS_VALUE_STR, address, strlen(address)); } else { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "failed to add address", - ("failed to add address[%d] %s", i, address)); + ("failed to add address[%u] %s", i, address)); } } return PlatformResult(ErrorCode::NO_ERROR); @@ -696,7 +701,7 @@ PlatformResult Message::addMMSRecipientsToStruct(const std::vector<std::string>& } char* address = const_cast<char*>(recipients.at(i).c_str()); - LoggerD("[%d] address:[%s] address_type:%d type:%d", i, address, address_type, type); + LoggerD("[%u] address:[%s] address_type:%d type:%d", i, address, address_type, type); int error = msg_list_add_item(msg, MSG_MESSAGE_ADDR_LIST_HND, &tmpAddr); if (MSG_SUCCESS == error) { @@ -705,7 +710,7 @@ PlatformResult Message::addMMSRecipientsToStruct(const std::vector<std::string>& msg_set_str_value(tmpAddr, MSG_ADDRESS_INFO_ADDRESS_VALUE_STR, address, strlen(address)); } else { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "failed to add address", - ("[%d] failed to add address: [%s], error: %d", i, address, error)); + ("[%u] failed to add address: [%s], error: %d", i, address, error)); } } return PlatformResult(ErrorCode::NO_ERROR); @@ -714,7 +719,7 @@ PlatformResult Message::addMMSRecipientsToStruct(const std::vector<std::string>& PlatformResult Message::addMMSBodyAndAttachmentsToStruct(const AttachmentPtrVector& attach, msg_struct_t& mms_struct, Message* message) { - ScopeLogger("attachments.size() = %zd", attach.size()); + ScopeLogger("attachments.size() = %zu", attach.size()); int size = attach.size(); for (int i = 0; i < size; i++) { @@ -805,7 +810,7 @@ PlatformResult Message::convertPlatformShortMessageToStruct(Message* message, ms return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "msg_get_message() Fail", ("msg_get_message() Fail [%d] (%s)", err, get_error_message(err))); } - LoggerD("Using existing msg for id: %d", id); + LoggerD("Using existing msg for id: %u", id); } else { // id is not set - the message does not exist in database MessageType msgType = message->getType(); if (msgType == MessageType::SMS) { @@ -950,7 +955,7 @@ PlatformResult Message::convertPlatformShortMessageToStruct(Message* message, ms } // Set MMS attachments AttachmentPtrVector attach_list = message->getMessageAttachments(); - LoggerD("Message(%p): id:%d subject:[%s] plainBody:[%s] contains %d attachments", message, + LoggerD("Message(%p): id:%d subject:[%s] plainBody:[%s] contains %zu attachments", message, message->getId(), message->getSubject().c_str(), message->getBody()->getPlainBody().c_str(), attach_list.size()); @@ -1209,7 +1214,8 @@ PlatformResult Message::setMMSBodyAndAttachmentsFromStruct(Message* message, msg LoggerD( "[p:%d, m:%d] added attachment: %p " "(mime:0x%x mime:%s messageId:%d)", - p, m, att, msg_media_type, msg_media_type_str.c_str(), ma->getMessageId()); + p, m, att, (unsigned)msg_media_type, msg_media_type_str.c_str(), + ma->getMessageId()); } msg_release_struct(&media); @@ -1233,7 +1239,7 @@ PlatformResult Message::setMMSBodyAndAttachmentsFromStruct(Message* message, msg LoggerW("Warning: body has not been set!"); } - LoggerD("after MSG_MMS_PAGE_LIST attachments count is:%d", message->m_attachments.size()); + LoggerD("after MSG_MMS_PAGE_LIST attachments count is:%zu", message->m_attachments.size()); // if there are some other attachments add it to attachments vector msg_list_handle_t attach_list = NULL; @@ -1269,8 +1275,8 @@ PlatformResult Message::setMMSBodyAndAttachmentsFromStruct(Message* message, msg ma->setMimeType(type); MessageAttachment* att = ma.get(); - LoggerD("[att:%d] added attachement: %p (mime:0x%x mime:%s path:%s id:%d)", i, att, tempInt, - type.c_str(), infoStr, ma->getId()); + LoggerD("[att:%d] added attachement: %p (mime:0x%x mime:%s path:%s id:%d)", i, att, + (unsigned)tempInt, type.c_str(), infoStr, ma->getId()); message->m_attachments.push_back(ma); message->m_has_attachment = true; @@ -1284,7 +1290,7 @@ PlatformResult Message::setMMSBodyAndAttachmentsFromStruct(Message* message, msg ("msg_get_list_handle error: %d (%s)", error, get_error_message(error))); } - LoggerD("after MSG_MMS_ATTACH_LIST attachments count is:%d", message->m_attachments.size()); + LoggerD("after MSG_MMS_ATTACH_LIST attachments count is:%zu", message->m_attachments.size()); msg_release_struct(&mms_struct); return PlatformResult(ErrorCode::NO_ERROR); } diff --git a/src/messaging/message.h b/src/messaging/message.h index c4c5c15f..7562fc37 100644 --- a/src/messaging/message.h +++ b/src/messaging/message.h @@ -98,6 +98,7 @@ class Message : public FilterableObject { virtual void setIsHighPriority(bool highpriority); virtual void setSubject(std::string subject); virtual void setInResponseTo(int inresp); + virtual void unsetInResponseTo(); virtual void setMessageStatus(MessageStatus status); virtual void setMessageAttachments(AttachmentPtrVector& attachments); virtual void setServiceId(int service_id); diff --git a/src/messaging/messages_change_callback.cc b/src/messaging/messages_change_callback.cc index e0567dcd..e0a48bec 100644 --- a/src/messaging/messages_change_callback.cc +++ b/src/messaging/messages_change_callback.cc @@ -63,7 +63,7 @@ MessagesChangeCallback::~MessagesChangeCallback() { MessagePtrVector MessagesChangeCallback::filterMessages(tizen::AbstractFilterPtr filter, const MessagePtrVector& source_messages, const int service_id) { - ScopeLogger("sourceMessages.size() = %d filter %s", source_messages.size(), + ScopeLogger("sourceMessages.size() = %zu filter %s", source_messages.size(), (filter ? "PRESENT" : "NULL")); if (filter) { @@ -94,7 +94,7 @@ MessagePtrVector MessagesChangeCallback::filterMessages(tizen::AbstractFilterPtr LoggerD("}"); } - LoggerD("returning matching %d of %d messages", filtered_messages.size(), + LoggerD("returning matching %zu of %zu messages", filtered_messages.size(), source_messages.size()); return filtered_messages; } else { @@ -104,7 +104,7 @@ MessagePtrVector MessagesChangeCallback::filterMessages(tizen::AbstractFilterPtr } void MessagesChangeCallback::added(const MessagePtrVector& msgs) { - ScopeLogger("event: msgs.size() = %d", msgs.size()); + ScopeLogger("event: msgs.size() = %zu", msgs.size()); if (!m_is_act) { return; } @@ -123,14 +123,14 @@ void MessagesChangeCallback::added(const MessagePtrVector& msgs) { for_each(filtered_msgs.begin(), filtered_msgs.end(), each); - LoggerD("Calling:%s with:%d added messages", MESSAGESADDED, filtered_msgs.size()); + LoggerD("Calling:%s with:%zu added messages", MESSAGESADDED, filtered_msgs.size()); m_callback_data.SetAction(MESSAGESADDED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::MEDIUM); } void MessagesChangeCallback::updated(const MessagePtrVector& msgs) { - ScopeLogger("event: msgs.size() = %d", msgs.size()); + ScopeLogger("event: msgs.size() = %zu", msgs.size()); if (!m_is_act) { return; } @@ -149,14 +149,14 @@ void MessagesChangeCallback::updated(const MessagePtrVector& msgs) { for_each(filtered_msgs.begin(), filtered_msgs.end(), each); - LoggerD("Calling:%s with:%d updated messages", MESSAGESUPDATED, filtered_msgs.size()); + LoggerD("Calling:%s with:%zu updated messages", MESSAGESUPDATED, filtered_msgs.size()); m_callback_data.SetAction(MESSAGESUPDATED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::LOW); } void MessagesChangeCallback::removed(const MessagePtrVector& msgs) { - ScopeLogger("event: msgs.size() = %d", msgs.size()); + ScopeLogger("event: msgs.size() = %zu", msgs.size()); if (!m_is_act) { return; } @@ -176,7 +176,7 @@ void MessagesChangeCallback::removed(const MessagePtrVector& msgs) { for_each(filtered_msgs.begin(), filtered_msgs.end(), each); - LoggerD("Calling:%s with:%d removed messages", MESSAGESREMOVED, filtered_msgs.size()); + LoggerD("Calling:%s with:%zu removed messages", MESSAGESREMOVED, filtered_msgs.size()); m_callback_data.SetAction(MESSAGESREMOVED, picojson::value(array)); m_callback_data.AddAndPost(PostPriority::LAST); diff --git a/src/messaging/messaging.gyp b/src/messaging/messaging.gyp index 7edb5a62..620dbb73 100644 --- a/src/messaging/messaging.gyp +++ b/src/messaging/messaging.gyp @@ -15,7 +15,6 @@ 'msg-service', 'email-service', 'dbus-1', - 'dbus-glib-1', 'capi-system-info', 'ecore', 'ecore-file', diff --git a/src/messaging/messaging_api.js b/src/messaging/messaging_api.js index 53d4c61f..d46536e4 100755 --- a/src/messaging/messaging_api.js +++ b/src/messaging/messaging_api.js @@ -213,9 +213,7 @@ function Message(type, data) { return _internal.folderId; }, set: function(value) { - if (value instanceof InternalValues_) { - _internal.folderId = value.folderId; - } + if (value instanceof InternalValues_) _internal.folderId = value.folderId; }, enumerable: true }); @@ -299,9 +297,8 @@ function Message(type, data) { return _internal.body; }, set: function(value) { - if (value instanceof InternalValues_) { + if (value instanceof InternalValues_) _internal.body = new MessageBody(value.body); - } if (value instanceof MessageBody) _internal.body = value; }, enumerable: true @@ -363,9 +360,8 @@ function Message(type, data) { return _internal.inResponseTo; }, set: function(value) { - if (value instanceof InternalValues_) { + if (value instanceof InternalValues_) _internal.inResponseTo = value.inResponseTo; - } }, enumerable: true }); @@ -510,9 +506,7 @@ function MessageBody(data) { return _internal.messageId; }, set: function(value) { - if (value instanceof InternalValues_) { - _internal.messageId = value.messageId; - } + if (value instanceof InternalValues_) _internal.messageId = value.messageId; }, enumerable: true }); @@ -600,9 +594,7 @@ function MessageAttachment(first, second) { return _internal.messageId; }, set: function(value) { - if (value instanceof InternalValues_) { - _internal.messageId = value.messageId; - } + if (value instanceof InternalValues_) _internal.messageId = value.messageId; }, enumerable: true }); @@ -647,10 +639,10 @@ function Messaging() {} /** * Gets the messaging service of a given type for a given account. * @param {!MessageServiceTag} messageServiceType Type of the services to be retrieved. - * @param {!MessageServiceArraySuccessCallback} successCallback Callback function - * that is called when the services are successfully retrieved. - * @param {ErrorCallback} errorCallback Callback function that is called - * when an error occurs. + * @param {!MessageServiceArraySuccessCallback} successCallback Callback function that + * is called when the services are successfully retrieved. + * @param {ErrorCallback} errorCallback Callback function that is called when + * an error occurs. */ Messaging.prototype.getMessageServices = function() { var args = validator_.validateArgs(arguments, [ diff --git a/src/messaging/messaging_instance.cc b/src/messaging/messaging_instance.cc index 2cf41a50..91578660 100644 --- a/src/messaging/messaging_instance.cc +++ b/src/messaging/messaging_instance.cc @@ -20,6 +20,7 @@ #include <sstream> #include <stdexcept> +#include "common/filesystem/filesystem_provider.h" #include "common/logger.h" #include "common/tools.h" @@ -203,6 +204,18 @@ void MessagingInstance::MessageServiceSendMessage(const picojson::value& args, picojson::object data = args.get<picojson::object>(); picojson::value v_message = data.at(SEND_MESSAGE_ARGS_MESSAGE); + + bool has_attachments = v_message.get("hasAttachment").get<bool>(); + if (has_attachments) { + const auto& attachments = v_message.get("attachments").get<std::vector<picojson::value>>(); + for (const auto& att : attachments) { + const auto& attachment_path = att.get("filePath").get<std::string>(); + const auto& attachment_real_path = + common::FilesystemProvider::Create().GetRealPath(attachment_path); + CHECK_STORAGE_ACCESS(attachment_real_path, &out); + } + } + const double callbackId = args.get(JSON_CALLBACK_ID).get<double>(); auto json = std::shared_ptr<picojson::value>(new picojson::value(picojson::object())); diff --git a/src/messaging/messaging_util.cc b/src/messaging/messaging_util.cc index 5884f0d4..d7e6f32f 100644 --- a/src/messaging/messaging_util.cc +++ b/src/messaging/messaging_util.cc @@ -36,6 +36,7 @@ #include "common/logger.h" #include "common/platform_exception.h" #include "common/scope_exit.h" +#include "common/tools.h" #include "tizen/tizen.h" using common::ErrorCode; @@ -271,6 +272,7 @@ PlatformResult MessagingUtil::loadFileContentToString(const std::string& file_pa std::string* result) { ScopeLogger(); std::ifstream input_file; + input_file.open(file_path, std::ios::in); if (input_file.is_open()) { @@ -284,6 +286,7 @@ PlatformResult MessagingUtil::loadFileContentToString(const std::string& file_pa input_file.close(); *result = ConvertToUtf8(file_path, outString); } else { + LoggerW("open() error: %s", common::tools::GetErrorString(errno).c_str()); std::stringstream ss_error_msg; ss_error_msg << "Failed to open file: " << file_path; return LogAndCreateResult(ErrorCode::IO_ERR, ss_error_msg.str().c_str()); diff --git a/src/messaging/short_message_manager.cc b/src/messaging/short_message_manager.cc index d1fc95ce..069a6863 100644 --- a/src/messaging/short_message_manager.cc +++ b/src/messaging/short_message_manager.cc @@ -53,7 +53,7 @@ static gboolean sendMessageCompleteCB(void* user_data) { } else { std::shared_ptr<Message> message = callback->getMessage(); - LoggerD("Calling success callback with: %d recipients", message->getTO().size()); + LoggerD("Calling success callback with: %zu recipients", message->getTO().size()); std::vector<picojson::value> recipients; auto addToRecipients = [&recipients](std::string& e) -> void { @@ -393,7 +393,7 @@ PlatformResult ShortMsgManager::callProperEventMessages(EventMessages* event, msg_storage_change_type_t storageChangeType, ShortMsgManager* shortMsgManager) { ScopeLogger( - "event.items.size()=%d event.removed_conversations.size()=%d" + "event.items.size()=%zu event.removed_conversations.size()=%zu" " sChangeType:%d", event->items.size(), event->removed_conversations.size(), storageChangeType); @@ -435,17 +435,17 @@ PlatformResult ShortMsgManager::callProperEventMessages(EventMessages* event, cur_conv->getConversationId(), cur_conv->getLastMessageId()); } - LoggerD("num conversations:all=%d added=%d update=%d", eventConv->items.size(), + LoggerD("num conversations:all=%zu added=%zu update=%zu", eventConv->items.size(), added_conv.size(), updated_conv.size()); if (false == added_conv.empty()) { - LoggerD("%d new conversations, calling onConversationAdded", added_conv.size()); + LoggerD("%zu new conversations, calling onConversationAdded", added_conv.size()); eventConv->items = added_conv; ChangeListenerContainer::getInstance().callConversationAdded(eventConv); } if (false == updated_conv.empty()) { - LoggerD("%d updated conversation, calling onConversationUpdated", updated_conv.size()); + LoggerD("%zu updated conversation, calling onConversationUpdated", updated_conv.size()); eventConv->items = updated_conv; ChangeListenerContainer::getInstance().callConversationUpdated(eventConv); } @@ -535,7 +535,7 @@ void ShortMsgManager::storage_change_cb(msg_handle_t handle, for (int i = 0; i < pMsgIdList->nCount; ++i) { const msg_message_id_t& msg_id = pMsgIdList->msgIdList[i]; - LoggerD("pMsgIdList[%d] = %d", i, msg_id); + LoggerD("pMsgIdList[%d] = %u", i, msg_id); std::map<int, MessagePtr>::iterator it = cur_rem_msgs.find(msg_id); if (it != cur_rem_msgs.end()) { @@ -761,7 +761,7 @@ void ShortMsgManager::updateMessages(MessagesCallbackUserData* callback) { return; } - LoggerD("messages to update: %d", callback->getMessages().size()); + LoggerD("messages to update: %zu", callback->getMessages().size()); { std::lock_guard<std::mutex> lock(m_mutex); @@ -849,7 +849,7 @@ PlatformResult ShortMsgManager::getMessage(int msg_id, msg_struct_t* out_msg) { PlatformResult ShortMsgManager::getConversationsForMessages( MessagePtrVector messages, msg_storage_change_type_t storageChangeType, ConversationPtrVector* result, ShortMsgManager* shortMsgManager) { - ScopeLogger("messages.size()=%d storageChangeType=%d", messages.size(), storageChangeType); + ScopeLogger("messages.size()=%zu storageChangeType=%d", messages.size(), storageChangeType); std::unordered_set<int> unique_conv_ids; ConversationPtrVector convs; @@ -942,7 +942,7 @@ void ShortMsgManager::findMessages(FindMsgCallbackUserData* callback) { if (callback->IsError()) { LoggerD("Calling error callback"); } else { - LoggerD("Calling success callback with %d messages:", callback->getMessages().size()); + LoggerD("Calling success callback with %zu messages:", callback->getMessages().size()); std::vector<picojson::value> response; auto messages = callback->getMessages(); @@ -1072,7 +1072,7 @@ void ShortMsgManager::removeConversations(ConversationCallbackData* callback) { ConversationPtr conv = (*it); msg_thread_id_t conv_id = conv->getConversationId(); - LoggerD("[%d] MessageConversation(%p) conv_id:%d", conv_index, conv.get(), conv_id); + LoggerD("[%d] MessageConversation(%p) conv_id:%u", conv_index, conv.get(), conv_id); msg_struct_list_s conv_view_list; error = msg_get_conversation_view_list(handle, (msg_thread_id_t)conv_id, &conv_view_list); @@ -1088,14 +1088,14 @@ void ShortMsgManager::removeConversations(ConversationCallbackData* callback) { LoggerD( "[%d] message[%d] msg_id:%d," - "saved MessageConversation(%p) with conv_id:%d", + "saved MessageConversation(%p) with conv_id:%u", conv_index, msg_index, cur_msg_id, conv.get(), conv_id); } else { LoggerE("[%d] Couldn't get msg_id, error: %s!", error, get_error_message(error)); } } } else { - LoggerE("[%d] Couldn' get conversation view list for conv_id:%d error: %d", conv_index, + LoggerE("[%d] Couldn' get conversation view list for conv_id:%u error: %d", conv_index, conv_id, error); } @@ -1139,12 +1139,12 @@ ShortMsgManager::ShortMsgManager() : m_msg_handle(NULL) { ShortMsgManager::~ShortMsgManager() { ScopeLogger(); - LoggerD("m_sms_removed_messages.size() = %d", m_sms_removed_messages.size()); - LoggerD("m_mms_removed_messages.size() = %d", m_mms_removed_messages.size()); - LoggerD("m_sms_removed_msg_id_conv_id_map.size() = %d", m_sms_removed_msg_id_conv_id_map.size()); - LoggerD("m_sms_removed_conv_id_object_map.size() = %d", m_sms_removed_conv_id_object_map.size()); - LoggerD("m_mms_removed_msg_id_conv_id_map.size() = %d", m_mms_removed_msg_id_conv_id_map.size()); - LoggerD("m_mms_removed_conv_id_object_map.size() = %d", m_mms_removed_conv_id_object_map.size()); + LoggerD("m_sms_removed_messages.size() = %zu", m_sms_removed_messages.size()); + LoggerD("m_mms_removed_messages.size() = %zu", m_mms_removed_messages.size()); + LoggerD("m_sms_removed_msg_id_conv_id_map.size() = %zu", m_sms_removed_msg_id_conv_id_map.size()); + LoggerD("m_sms_removed_conv_id_object_map.size() = %zu", m_sms_removed_conv_id_object_map.size()); + LoggerD("m_mms_removed_msg_id_conv_id_map.size() = %zu", m_mms_removed_msg_id_conv_id_map.size()); + LoggerD("m_mms_removed_conv_id_object_map.size() = %zu", m_mms_removed_conv_id_object_map.size()); } std::string ShortMsgManager::getMessageStatus(int id) { ScopeLogger(); diff --git a/src/networkbearerselection/networkbearerselection_instance.cc b/src/networkbearerselection/networkbearerselection_instance.cc index ea339722..a88cecc0 100644 --- a/src/networkbearerselection/networkbearerselection_instance.cc +++ b/src/networkbearerselection/networkbearerselection_instance.cc @@ -110,7 +110,13 @@ void NetworkBearerSelectionInstance::NetworkBearerSelectionRequestRouteToHost( NetworkBearerSelectionManager::GetInstance()->requestRouteToHost(domain_name, response); }; - common::TaskQueue::GetInstance().Async(request); + // The RequestRouteToHost has to be executed in the main thread because of Native API + // implementation. The Web API implementation of NBSManager class has a global connection_h + // handle, which is used in every Request() method. + // However, the Native implementation uses std::thread_local-like variables, thus + // an execution of RequestRouteToHost() in thread different than main thread results in returning + // an error from C-API. + common::TaskQueue::GetInstance().ScheduleWorkInMainThread(request); ReportSuccess(out); } @@ -155,7 +161,13 @@ void NetworkBearerSelectionInstance::NetworkBearerSelectionReleaseRouteToHost( NetworkBearerSelectionManager::GetInstance()->releaseRouteToHost(domain_name, response); }; - common::TaskQueue::GetInstance().Async(release); + // The ReleaseRouteToHost has to be executed in the main thread because of Native API + // implementation. The Web API implementation of NBSManager class has a global connection_h + // handle, which is used in every Request() method. + // However, the Native implementation uses std::thread_local-like variables, thus + // an execution of RequestRouteToHost/ReleaseRouteToHost() in thread different than main thread + // results in returning an error from C-API. + common::TaskQueue::GetInstance().ScheduleWorkInMainThread(release); ReportSuccess(out); } diff --git a/src/networkbearerselection/networkbearerselection_manager.cc b/src/networkbearerselection/networkbearerselection_manager.cc index b6206f46..33ec396f 100644 --- a/src/networkbearerselection/networkbearerselection_manager.cc +++ b/src/networkbearerselection/networkbearerselection_manager.cc @@ -17,6 +17,7 @@ #include "networkbearerselection_manager.h" #include "common/logger.h" #include "common/scope_exit.h" +#include "common/task-queue.h" #include <arpa/inet.h> #include <netdb.h> @@ -183,7 +184,8 @@ void NetworkBearerSelectionManager::callResultCallback(const ReplyCallback& repl const PlatformResult result) { ScopeLogger(); - std::thread(reply, result).detach(); + auto reply_wrapper = [=] { reply(result); }; + common::TaskQueue::GetInstance().Async(reply_wrapper); } void NetworkBearerSelectionManager::requestRouteToHost(const std::string& domain_name, @@ -278,25 +280,26 @@ bool NetworkBearerSelectionManager::removeDomainRoute( } int ai_family = iter->second; + connection_address_family_e address_family = CONNECTION_ADDRESS_FAMILY_IPV4; + if (AF_INET == ai_family) { LoggerD("IPv4 address"); - ret = connection_remove_route(m_connection_handle_, interface_name, domain_name); } else if (AF_INET6 == ai_family) { LoggerD("IPv6 address"); - ret = connection_profile_get_gateway_address(m_profile_handle_, CONNECTION_ADDRESS_FAMILY_IPV6, - &gateway); - - if (CONNECTION_ERROR_NONE != ret) { - LoggerE("Failed to get gateway"); - return false; - } - - ret = connection_remove_route_ipv6(m_connection_handle_, interface_name, domain_name, gateway); + address_family = CONNECTION_ADDRESS_FAMILY_IPV6; } else { LoggerE("Incorrect family address"); return false; } + ret = connection_profile_get_gateway_address(m_profile_handle_, address_family, &gateway); + if (CONNECTION_ERROR_NONE != ret) { + LoggerE("Failed to get gateway"); + return false; + } + ret = connection_remove_route_entry(m_connection_handle_, address_family, interface_name, + domain_name, gateway); + if (CONNECTION_ERROR_NONE != ret) { LoggerE("Failed to remove route"); return false; @@ -407,23 +410,27 @@ bool NetworkBearerSelectionManager::registerStateChangeListener(const std::strin return false; } + connection_address_family_e address_family = CONNECTION_ADDRESS_FAMILY_IPV4; + if (AF_INET == serv_info->ai_family) { - LoggerD("IPv4 add route"); - ret = connection_add_route(m_connection_handle_, interface_name, host_addr_ptr.get()); + LoggerD("IPv4 address"); + } else if (AF_INET6 == serv_info->ai_family) { + LoggerD("IPv6 address"); + address_family = CONNECTION_ADDRESS_FAMILY_IPV6; } else { - LoggerD("IPv6 add route"); - ret = connection_profile_get_gateway_address(m_profile_handle_, CONNECTION_ADDRESS_FAMILY_IPV6, - &gateway); - - if (CONNECTION_ERROR_NONE != ret) { - LoggerE("Error while getting gateway address %d", ret); - return false; - } + LoggerE("Incorrect family address"); + return false; + } - ret = connection_add_route_ipv6(m_connection_handle_, interface_name, host_addr_ptr.get(), - gateway); + ret = connection_profile_get_gateway_address(m_profile_handle_, address_family, &gateway); + if (CONNECTION_ERROR_NONE != ret) { + LoggerE("Failed to get gateway"); + return false; } + ret = connection_add_route_entry(m_connection_handle_, address_family, interface_name, + host_addr_ptr.get(), gateway); + if (CONNECTION_ERROR_NONE != ret) { LoggerE("Adding route failed"); return false; diff --git a/src/nfc/nfc_adapter.cc b/src/nfc/nfc_adapter.cc index f1725f3a..65c87c41 100644 --- a/src/nfc/nfc_adapter.cc +++ b/src/nfc/nfc_adapter.cc @@ -436,7 +436,7 @@ PlatformResult NFCAdapter::SetCardEmulationMode(const std::string& mode) { if (result.IsError()) { return result; } - LoggerD("Card emulation mode value: %x", (int)new_mode); + LoggerD("Card emulation mode value: %x", (unsigned)new_mode); std::string current_mode = ""; result = GetCardEmulationMode(¤t_mode); @@ -498,7 +498,7 @@ PlatformResult NFCAdapter::SetActiveSecureElement(std::string element) { LoggerD("Error: %s", result.message().c_str()); return result; } - LoggerD("Secure element type value: %x", (int)new_type); + LoggerD("Secure element type value: %x", (unsigned)new_type); std::string current_type = ""; result = GetActiveSecureElement(¤t_type); @@ -947,7 +947,7 @@ static void tagEventCallback(nfc_discovered_type_e type, nfc_tag_h tag, void* /* obj.insert(make_pair("action", picojson::value("ondetach"))); NFCAdapter::GetInstance()->RespondAsync(event.serialize().c_str()); } else { // ERROR - should never happen - LoggerE("Invalid NFC discovered type: %d (%x)", type, type); + LoggerE("Invalid NFC discovered type: %d (%x)", type, (unsigned)type); } } @@ -1554,7 +1554,7 @@ PlatformResult NFCAdapter::SetPreferredApp() { if (aids.empty()) { return LogAndCreateResult(ErrorCode::ABORT_ERR, "No AID registered"); } - LoggerD("Found %d AIDs", aids.size()); + LoggerD("Found %zu AIDs", aids.size()); int ret = nfc_se_set_preferred_handler(); if (ret != NFC_ERROR_NONE) { diff --git a/src/nfc/nfc_api.js b/src/nfc/nfc_api.js index 5eef7c29..851ee585 100644 --- a/src/nfc/nfc_api.js +++ b/src/nfc/nfc_api.js @@ -274,9 +274,9 @@ function NFCAdapter() { NFCAdapter.prototype.setPowered = function() { privUtils_.warn( - 'DEPRECATION WARNING: setPowered() is deprecated and ' + - 'will be removed from next release. Let the user turn NFC on/off ' + - 'through the Settings application instead.' + 'DEPRECATION WARNING: setPowered() is deprecated and will be removed from ' + + 'next release. Let the user turn NFC on/off through the Settings ' + + 'application instead.' ); var args = validator_.validateArgs(arguments, [ diff --git a/src/nfc/nfc_util.cc b/src/nfc/nfc_util.cc index de51aaac..e91e1a1f 100644 --- a/src/nfc/nfc_util.cc +++ b/src/nfc/nfc_util.cc @@ -221,7 +221,7 @@ PlatformResult NFCUtil::ToStringCardEmulationMode(const nfc_se_card_emulation_mo break; default: return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "No Match Card Emulation mode", - ("No Match Card Emulation mode: %x", card_mode)); + ("No Match Card Emulation mode: %x", (unsigned)card_mode)); } return PlatformResult(ErrorCode::NO_ERROR); } @@ -254,7 +254,7 @@ PlatformResult NFCUtil::ToStringSecureElementType(const nfc_se_type_e se_type, s break; default: return LogAndCreateResult(ErrorCode::TYPE_MISMATCH_ERR, "No Match Secure Element Type", - ("No Match Secure Element Type: %x", se_type)); + ("No Match Secure Element Type: %x", (unsigned)se_type)); } return PlatformResult(ErrorCode::NO_ERROR); } diff --git a/src/notification/common_notification.cc b/src/notification/common_notification.cc index 52cc10be..1f824937 100644 --- a/src/notification/common_notification.cc +++ b/src/notification/common_notification.cc @@ -23,6 +23,7 @@ #include "common/filesystem/filesystem_provider.h" #include "common/logger.h" #include "common/scope_exit.h" +#include "common/tools.h" namespace extension { namespace notification { @@ -1080,6 +1081,8 @@ PlatformResult CommonNotification::SetPathFromJson(const picojson::value& noti_v const std::string& value_str = FromJson<std::string>(noti_obj, key.c_str()); std::string real_path = FilesystemProvider::Create().GetRealPath(value_str); + CHECK_STORAGE_ACCESS_AND_RETURN(real_path); + PlatformResult status = SetImage(noti_handle, type, real_path); CHECK_ERROR(status); } @@ -1314,6 +1317,8 @@ PlatformResult CommonNotification::SetPathsArrayFromJson(const picojson::value& const std::string& text = JsonCast<std::string>(item); std::string real_path = FilesystemProvider::Create().GetRealPath(text); + CHECK_STORAGE_ACCESS_AND_RETURN(real_path); + PlatformResult status = SetImage(noti_handle, map.at(idx), real_path); CHECK_ERROR(status); diff --git a/src/package/package_api.js b/src/package/package_api.js index 9879bc51..fb89be00 100644 --- a/src/package/package_api.js +++ b/src/package/package_api.js @@ -14,72 +14,54 @@ * limitations under the License. */ -var JSON_ = xwalk.JSON; var validator_ = xwalk.utils.validator; var types_ = validator_.Types; +var native_ = new xwalk.utils.NativeManager(extension); var callbackId = 0; var callbacks = {}; -var infoEventListenerId = -1; - -function invokeListener(result) { - if (result.listener === 'infoEvent') { - var listener = callbacks[infoEventListenerId]; - listener(result); +var listener; +var PACKAGE_INFO_LISTENER_ID = 'PackageInfoListener'; +var PACKAGE_PROGRESS_LISTENER_ID = 'PackageProgressListener_'; + +function PackageInfoChangeCallback(result) { + if (result.status == 'installed') { + listener.oninstalled(new PackageInformation(result.info)); + } else if (result.status == 'updated') { + listener.onupdated(new PackageInformation(result.info)); + } else if (result.status == 'uninstalled') { + listener.onuninstalled(result.id); } } -extension.setMessageListener(function(json) { - var result = JSON_.parse(json); - - if (result.hasOwnProperty('listener')) { - setTimeout(function() { - invokeListener(result); - }, 0); - } else { - var callback = callbacks[result['callbackId']]; - setTimeout(function() { - callback(result); - }, 0); - } -}); - -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 WebAPIException(WebAPIException.UNKNOWN_ERR); - } - - if (result['status'] == 'success') { - if (undefined !== result['result']) { - return result['result']; - } - return true; - } else if (result['status'] == 'error') { +function PackageProgressCallback(result) { + if (result.status == 'progress') { + callbacks[result['progressCallbackId']].progressCallback.onprogress( + result.id, + result.progress + ); + } else if (result.status == 'complete') { + callbacks[result['progressCallbackId']].progressCallback.oncomplete(result.id); + delete callbacks[result['progressCallbackId']]; + native_.removeListener( + PACKAGE_PROGRESS_LISTENER_ID + result['progressCallbackId'] + ); + } else if (result.status == 'error') { var err = result['error']; if (err) { - throw new WebAPIException(err); + callbacks[result['progressCallbackId']].errorCallback( + new WebAPIException(err) + ); + delete callbacks[result['progressCallbackId']]; + native_.removeListener( + PACKAGE_PROGRESS_LISTENER_ID + result['progressCallbackId'] + ); } - return false; } } -function callNativeWithCallback(cmd, args, callback) { - if (callback) { - var id = nextCallbackId(); - args['callbackId'] = id; - callbacks[id] = callback; - } - - return callNative(cmd, args); +function nextCallbackId() { + return callbackId++; } function SetReadOnlyProperty(obj, n, v) { @@ -119,11 +101,12 @@ function PackageInformation(obj) { set: function() {}, get: function() { if (undefined === totalSize) { - try { - totalSize = callNative('PackageManager_getTotalSize', { - id: this.id - }); - } catch (e) { + var result = native_.callSync('PackageManager_getTotalSize', { + id: this.id + }); + if (native_.isSuccess(result)) { + totalSize = native_.getResultObject(result); + } else { totalSize = -1; } } @@ -136,9 +119,12 @@ function PackageInformation(obj) { set: function() {}, get: function() { if (undefined === dataSize) { - try { - dataSize = callNative('PackageManager_getDataSize', { id: this.id }); - } catch (e) { + var result = native_.callSync('PackageManager_getDataSize', { + id: this.id + }); + if (native_.isSuccess(result)) { + dataSize = native_.getResultObject(result); + } else { dataSize = -1; } } @@ -167,39 +153,30 @@ var PackageManagerInstall = function() { } ]); + var progressCallbackId = nextCallbackId(); + callbacks[progressCallbackId] = args; + var nativeParam = { - packageFileURI: args.packageFileURI + packageFileURI: args.packageFileURI, + progressCallbackId: progressCallbackId }; - try { - var syncResult = callNativeWithCallback( - 'PackageManager_install', - nativeParam, - function(result) { - if (result.status == 'progress') { - if (args.progressCallback.onprogress) { - args.progressCallback.onprogress(result.id, result.progress); - } - } else if (result.status == 'complete') { - if (args.progressCallback.oncomplete) { - args.progressCallback.oncomplete(result.id); - } - } else if (result.status == 'error') { - var err = result['error']; - if (err) { - args.errorCallback(new WebAPIException(err)); - return; - } - } + var result = native_.call('PackageManager_install', nativeParam, function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + delete callbacks[result['progressCallbackId']]; + } + }); - if (result.status == 'complete' || result.status == 'error') { - delete callbacks[result['callbackId']]; - } - } - ); - } catch (e) { - throw e; + if (native_.isFailure(result)) { + delete callbacks[progressCallbackId]; + throw native_.getErrorObject(result); } + + native_.addListener( + PACKAGE_PROGRESS_LISTENER_ID + progressCallbackId, + PackageProgressCallback + ); }; PackageManager.prototype.install = function(packageFileURI, progressCallback) { @@ -222,39 +199,27 @@ var PackageManagerUninstall = function() { } ]); - var nativeParam = { - id: args.id - }; + var progressCallbackId = nextCallbackId(); + callbacks[progressCallbackId] = args; - try { - var syncResult = callNativeWithCallback( - 'PackageManager_uninstall', - nativeParam, - function(result) { - if (result.status == 'progress') { - if (args.progressCallback.onprogress) { - args.progressCallback.onprogress(result.id, result.progress); - } - } else if (result.status == 'complete') { - if (args.progressCallback.oncomplete) { - args.progressCallback.oncomplete(result.id); - } - } else if (result.status == 'error') { - var err = result['error']; - if (err) { - args.errorCallback(new WebAPIException(err)); - return; - } - } + var nativeParam = { id: args.id, progressCallbackId: progressCallbackId }; - if (result.status == 'complete' || result.status == 'error') { - delete callbacks[result['callbackId']]; - } - } - ); - } catch (e) { - throw e; + var result = native_.call('PackageManager_uninstall', nativeParam, function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + delete callbacks[result['progressCallbackId']]; + } + }); + + if (native_.isFailure(result)) { + delete callbacks[result['progressCallbackId']]; + throw native_.getErrorObject(result); } + + native_.addListener( + PACKAGE_PROGRESS_LISTENER_ID + progressCallbackId, + PackageProgressCallback + ); }; PackageManager.prototype.uninstall = function(id, progressCallback) { @@ -272,33 +237,21 @@ PackageManager.prototype.getPackagesInfo = function(successCallback, errorCallba } ]); - var nativeParam = {}; - - try { - var syncMsg = callNativeWithCallback( - 'PackageManager_getPackagesInfo', - nativeParam, - function(result) { - if (result.status == 'success') { - for (var i = 0; i < result.informationArray.length; i++) { - result.informationArray[i] = new PackageInformation( - result.informationArray[i] - ); - } - args.successCallback(result.informationArray); - } else if (result.status == 'error') { - var err = result['error']; - if (err) { - args.errorCallback(new WebAPIException(err)); - return; - } - } - - delete callbacks[result['callbackId']]; + var result = native_.call('PackageManager_getPackagesInfo', {}, function(result) { + if (native_.isSuccess(result)) { + for (var i = 0; i < result.informationArray.length; i++) { + result.informationArray[i] = new PackageInformation( + result.informationArray[i] + ); } - ); - } catch (e) { - throw e; + args.successCallback(result.informationArray); + } else if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } + }); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); } }; @@ -313,11 +266,11 @@ PackageManager.prototype.getPackageInfo = function() { nativeParam['id'] = args.id; } - try { - var syncResult = callNative('PackageManager_getPackageInfo', nativeParam); - return new PackageInformation(syncResult); - } catch (e) { - throw e; + var result = native_.callSync('PackageManager_getPackageInfo', nativeParam); + if (native_.isSuccess(result)) { + return new PackageInformation(native_.getResultObject(result)); + } else { + throw native_.getErrorObject(result); } }; @@ -330,48 +283,32 @@ PackageManager.prototype.setPackageInfoEventListener = function(eventCallback) { } ]); - var nativeParam = {}; + var result = native_.callSync('PackageManager_setPackageInfoEventListener', {}); - try { - var syncResult = callNativeWithCallback( - 'PackageManager_setPackageInfoEventListener', - nativeParam, - function(result) { - if (result.status == 'installed') { - args.eventCallback.oninstalled(new PackageInformation(result.info)); - } else if (result.status == 'updated') { - args.eventCallback.onupdated(new PackageInformation(result.info)); - } else if (result.status == 'uninstalled') { - args.eventCallback.onuninstalled(result.id); - } - } - ); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } - if (infoEventListenerId === -1) { - infoEventListenerId = nativeParam.callbackId; - } else { - delete callbacks[infoEventListenerId]; - infoEventListenerId = nativeParam.callbackId; - } - } catch (e) { - throw e; + if (!native_.isListenerSet(PACKAGE_INFO_LISTENER_ID)) { + native_.addListener(PACKAGE_INFO_LISTENER_ID, PackageInfoChangeCallback); } + listener = args.eventCallback; }; PackageManager.prototype.unsetPackageInfoEventListener = function() { var nativeParam = {}; - try { - var syncResult = callNative( - 'PackageManager_unsetPackageInfoEventListener', - nativeParam - ); - if (syncResult === true) { - delete callbacks[infoEventListenerId]; - infoEventListenerId = -1; - } - } catch (e) { - throw e; + var result = native_.callSync( + 'PackageManager_unsetPackageInfoEventListener', + nativeParam + ); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + + if (native_.isListenerSet(PACKAGE_INFO_LISTENER_ID)) { + native_.removeListener(PACKAGE_INFO_LISTENER_ID); } }; diff --git a/src/package/package_instance.cc b/src/package/package_instance.cc index d0a11470..a1ca8888 100644 --- a/src/package/package_instance.cc +++ b/src/package/package_instance.cc @@ -16,9 +16,11 @@ #include "package/package_instance.h" +#include <glib.h> #include <functional> #include <string> +#include "common/filesystem/filesystem_provider.h" #include "common/logger.h" #include "common/picojson.h" #include "common/task-queue.h" @@ -51,16 +53,16 @@ typedef enum _PackageThreadWorkType { class PackageUserData { public: - PackageUserData(PackageInstance* ins, int id, PackageThreadWorkType task) { + PackageUserData(PackageInstance* ins, int id, PackageThreadWorkType task, + int progress_callback_id = 0) + : instance_(ins), callback_id_(id), work_(task), progress_callback_id_(progress_callback_id) { ScopeLogger(); - instance_ = ins; - callback_id_ = id; - work_ = task; } PackageInstance* instance_; int callback_id_; PackageThreadWorkType work_; + int progress_callback_id_; picojson::object data_; }; typedef std::shared_ptr<PackageUserData> PackageUserDataPtr; @@ -85,6 +87,10 @@ static gboolean PackageAfterWork(const PackageUserDataPtr& userData) { ScopeLogger(); userData->data_["callbackId"] = picojson::value(static_cast<double>(userData->callback_id_)); + if (PackageThreadWorkNone == userData->work_) { + userData->data_["progressCallbackId"] = + picojson::value(static_cast<double>(userData->progress_callback_id_)); + } picojson::value result = picojson::value(userData->data_); common::Instance::PostMessage(userData->instance_, result.serialize().c_str()); @@ -150,7 +156,7 @@ static void PackageListenerCb(const char* type, const char* package, } picojson::object param; - param["listener"] = picojson::value("infoEvent"); + param["listenerId"] = picojson::value("PackageInfoListener"); LoggerD("Listener type: %d , state: %d, progress: %d", event_type, event_state, progress); if (event_type == PACKAGE_MANAGER_EVENT_TYPE_INSTALL && @@ -280,7 +286,9 @@ void PackageInstance::InvokeCallback(int request_id, picojson::object& param) { int callback_id = callbacks_map_[request_id]; - param["callbackId"] = picojson::value(static_cast<double>(callback_id)); + std::string listenerId = "PackageProgressListener_" + std::to_string(callback_id); + param["listenerId"] = picojson::value(listenerId); + param["progressCallbackId"] = picojson::value(static_cast<double>(callback_id)); picojson::value result = picojson::value(param); Instance::PostMessage(this, result.serialize().c_str()); } @@ -292,14 +300,20 @@ void PackageInstance::PackageManagerInstall(const picojson::value& args, picojso CHECK_EXIST(args, "callbackId", out) CHECK_EXIST(args, "packageFileURI", out) + CHECK_EXIST(args, "progressCallbackId", out) int callback_id = static_cast<int>(args.get("callbackId").get<double>()); const std::string& packageFileURI = convertUriToPath(args.get("packageFileURI").get<std::string>()); + int progress_callback_id = static_cast<int>(args.get("progressCallbackId").get<double>()); + + const std::string real_path = common::FilesystemProvider::Create().GetRealPath(packageFileURI); + + CHECK_STORAGE_ACCESS(real_path, &out); if (!request_) { LoggerE("package_manager_request_h is NULL"); - InvokeErrorCallbackAsync(callback_id, + InvokeErrorCallbackAsync(callback_id, progress_callback_id, UnknownException("It is not allowed to install the package by " "the platform or any other platform error occurs")); return; @@ -311,15 +325,16 @@ void PackageInstance::PackageManagerInstall(const picojson::value& args, picojso if (ret == PACKAGE_MANAGER_ERROR_INVALID_PARAMETER) { LoggerE("The package is not found at the specified location"); InvokeErrorCallbackAsync( - callback_id, NotFoundException("The package is not found at the specified location")); + callback_id, progress_callback_id, + NotFoundException("The package is not found at the specified location")); } else { LoggerE("Failed to install package: %d (%s)", ret, get_error_message(ret)); - InvokeErrorCallbackAsync(callback_id, + InvokeErrorCallbackAsync(callback_id, progress_callback_id, UnknownException("It is not allowed to install the package by " "the platform or any other platform error occurs")); } } else { - RegisterCallback(request_id, callback_id); + RegisterCallback(request_id, progress_callback_id); } ReportSuccess(out); @@ -332,13 +347,15 @@ void PackageInstance::PackageManagerUninstall(const picojson::value& args, picoj CHECK_EXIST(args, "callbackId", out) CHECK_EXIST(args, "id", out) + CHECK_EXIST(args, "progressCallbackId", out) int callback_id = static_cast<int>(args.get("callbackId").get<double>()); const std::string& id = args.get("id").get<std::string>(); + int progress_callback_id = static_cast<int>(args.get("progressCallbackId").get<double>()); if (!request_) { LoggerE("package_manager_request_h is NULL"); - InvokeErrorCallbackAsync(callback_id, + InvokeErrorCallbackAsync(callback_id, progress_callback_id, UnknownException("It is not allowed to install the package by " "the platform or any other platform error occurs")); return; @@ -350,11 +367,12 @@ void PackageInstance::PackageManagerUninstall(const picojson::value& args, picoj if (ret == PACKAGE_MANAGER_ERROR_NO_SUCH_PACKAGE) { LoggerE("The package is not found at the specified location"); InvokeErrorCallbackAsync( - callback_id, NotFoundException("The package is not found at the specified location")); + callback_id, progress_callback_id, + NotFoundException("The package is not found at the specified location")); } else { LoggerE("Failed to get package info: %d (%s)", ret, get_error_message(ret)); InvokeErrorCallbackAsync( - callback_id, + callback_id, progress_callback_id, UnknownException("It is not allowed to get package information by the platform or " "any other platform error occurs")); } @@ -366,11 +384,11 @@ void PackageInstance::PackageManagerUninstall(const picojson::value& args, picoj if (ret != PACKAGE_MANAGER_ERROR_NONE) { LoggerE("Failed to uninstall package: %d (%s)", ret, get_error_message(ret)); InvokeErrorCallbackAsync( - callback_id, + callback_id, progress_callback_id, UnknownException("It is not allowed to install the package by the platform or " "any other platform error occurs")); } else { - RegisterCallback(request_id, callback_id); + RegisterCallback(request_id, progress_callback_id); } } @@ -442,8 +460,6 @@ void PackageInstance::PackageManagerSetpackageinfoeventlistener(const picojson:: CHECK_PRIVILEGE_ACCESS(kPrivilegePackageInfo, &out); - CHECK_EXIST(args, "callbackId", out) - if (is_package_info_listener_set_) { LoggerD("Already set"); ReportSuccess(out); @@ -501,12 +517,14 @@ void PackageInstance::PackageManagerUnsetpackageinfoeventlistener(const picojson ReportSuccess(out); } -void PackageInstance::InvokeErrorCallbackAsync(int callback_id, const PlatformException& ex) { +void PackageInstance::InvokeErrorCallbackAsync(int callback_id, int progress_callback_id, + const PlatformException& ex) { ScopeLogger(); picojson::object param; LogAndReportError(ex, param); - PackageUserDataPtr userData(new PackageUserData(this, callback_id, PackageThreadWorkNone)); + PackageUserDataPtr userData( + new PackageUserData(this, callback_id, PackageThreadWorkNone, progress_callback_id)); userData->data_ = param; TaskQueue::GetInstance().Async<PackageUserData>(PackageAfterWork, userData); } diff --git a/src/package/package_instance.h b/src/package/package_instance.h index 3ae7163b..772d4cb1 100644 --- a/src/package/package_instance.h +++ b/src/package/package_instance.h @@ -43,7 +43,8 @@ class PackageInstance : public common::ParsedInstance { std::map<int, int> callbacks_map_; // <request_id, callbackId> void RegisterCallback(int request_id, int callback_id); - void InvokeErrorCallbackAsync(int callback_id, const common::PlatformException& ex); + void InvokeErrorCallbackAsync(int callback_id, int progress_callback_id, + const common::PlatformException& ex); void PackageManagerInstall(const picojson::value& args, picojson::object& out); void PackageManagerUninstall(const picojson::value& args, picojson::object& out); diff --git a/src/power/power_api.js b/src/power/power_api.js index 2c2a1858..7a93b53d 100755 --- a/src/power/power_api.js +++ b/src/power/power_api.js @@ -104,8 +104,8 @@ PowerManager.prototype.request = function() { if (args.state === PowerScreenState.SCREEN_BRIGHT) { privUtils_.warn( - 'DEPRECATION WARNING: SCREEN_BRIGHT is deprecated ' + - 'and will be removed from next release.' + 'DEPRECATION WARNING: SCREEN_BRIGHT is deprecated and will be removed ' + + 'from next release.' ); } @@ -247,8 +247,8 @@ PowerManager.prototype.restoreScreenBrightness = function() { */ PowerManager.prototype.turnScreenOn = function() { privUtils_.warn( - 'DEPRECATION WARNING: turnScreenOn() is deprecated ' + - 'and will be removed from next release. Use request() instead.' + 'DEPRECATION WARNING: turnScreenOn() is deprecated and will be removed from ' + + 'next release. Use request() instead.' ); var ret = native_.callSync('PowerManager_turnScreenOn', {}); @@ -262,8 +262,8 @@ PowerManager.prototype.turnScreenOn = function() { */ PowerManager.prototype.turnScreenOff = function() { privUtils_.warn( - 'DEPRECATION WARNING: turnScreenOff() is deprecated ' + - 'and will be removed from next release. Use release() instead.' + 'DEPRECATION WARNING: turnScreenOff() is deprecated and will be removed from ' + + 'next release. Use release() instead.' ); var ret = native_.callSync('PowerManager_turnScreenOff', {}); diff --git a/src/ppm/ppm_api.js b/src/ppm/ppm_api.js index e6fa82a0..972b5818 100644 --- a/src/ppm/ppm_api.js +++ b/src/ppm/ppm_api.js @@ -21,6 +21,13 @@ var native_ = new xwalk.utils.NativeManager(extension); function PPMManager() {} +function RequestStatus(privilege, result_) { + Object.defineProperties(this, { + privilege: { value: privilege, writable: false }, + result: { value: result_, writable: false } + }); +} + PPMManager.prototype.checkPermission = function() { var args = validator_.validateArgs(arguments, [ { name: 'privilege', type: types_.STRING } @@ -39,6 +46,32 @@ PPMManager.prototype.checkPermission = function() { return native_.getResultObject(result); }; +PPMManager.prototype.checkPermissions = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'privileges', type: types_.ARRAY, values: types_.STRING } + ]); + + var callArgs = { + privileges: args.privileges + }; + + var result = native_.callSync('PPMManager_checkPermissions', callArgs); + + var data = []; + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } else { + var obj = native_.getResultObject(result); + + obj.forEach(function(o) { + data.push({ privilege: o.privilege, type: o.type }); + }); + } + + return data; +}; + PPMManager.prototype.requestPermission = function() { var args = validator_.validateArgs(arguments, [ { name: 'privilege', type: types_.STRING }, @@ -65,5 +98,37 @@ PPMManager.prototype.requestPermission = function() { } }; +PPMManager.prototype.requestPermissions = function() { + var args = validator_.validateArgs(arguments, [ + { name: 'privileges', type: types_.ARRAY, values: types_.STRING }, + { name: 'successCallback', type: types_.FUNCTION }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + } else { + var data = []; + var obj = native_.getResultObject(result); + + obj.forEach(function(o) { + data.push(new RequestStatus(o.privilege, o.result)); + }); + args.successCallback(data); + } + }; + + var callArgs = { + privileges: args.privileges + }; + + var result = native_.call('PPMManager_requestPermissions', callArgs, callback); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + // Exports exports = new PPMManager(); diff --git a/src/ppm/ppm_instance.cc b/src/ppm/ppm_instance.cc index 7f419a32..039e4303 100644 --- a/src/ppm/ppm_instance.cc +++ b/src/ppm/ppm_instance.cc @@ -46,7 +46,9 @@ PPMInstance::PPMInstance() { #define REGISTER(c, x) RegisterSyncHandler(c, std::bind(&PPMInstance::x, this, _1, _2)); REGISTER("PPMManager_checkPermission", checkPermission); + REGISTER("PPMManager_checkPermissions", checkPermissions); REGISTER("PPMManager_requestPermission", requestPermission); + REGISTER("PPMManager_requestPermissions", requestPermissions); #undef REGISTER } @@ -115,11 +117,11 @@ void PPMInstance::ResponseCallback(ppm_call_cause_e cause, ppm_request_result_e picojson::value event = picojson::value(picojson::object()); picojson::object& obj = event.get<picojson::object>(); - obj.insert(std::make_pair("callbackId", picojson::value(data->callbackId))); + obj.emplace(std::make_pair("callbackId", picojson::value(data->callbackId))); if (PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER == cause) { - obj.insert(std::make_pair("result", picojson::value(CheckRequestResultToString(result)))); - obj.insert(std::make_pair("privilege", picojson::value(privilege))); + obj.emplace(std::make_pair("result", picojson::value(CheckRequestResultToString(result)))); + obj.emplace(std::make_pair("privilege", picojson::value(privilege))); } else { LogAndReportError( common::PlatformResult(common::ErrorCode::ABORT_ERR, "Get callback data failed"), &obj); @@ -127,6 +129,40 @@ void PPMInstance::ResponseCallback(ppm_call_cause_e cause, ppm_request_result_e common::Instance::PostMessage(data->_instance, event.serialize().c_str()); } +void PPMInstance::ResponseMultipleCallback(ppm_call_cause_e cause, + const ppm_request_result_e* result, + const char** privileges, size_t privileges_count, + void* user_data) { + ScopeLogger(); + + std::unique_ptr<ResponseCallbackData> data{static_cast<ResponseCallbackData*>(user_data)}; + + picojson::value event = picojson::value(picojson::object()); + picojson::object& obj = event.get<picojson::object>(); + + obj.emplace(std::make_pair("callbackId", picojson::value(data->callbackId))); + + if (PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER == cause) { + picojson::value result_array = picojson::value(picojson::array()); + picojson::array& array_obj = result_array.get<picojson::array>(); + array_obj.reserve(privileges_count); + + for (size_t i = 0; i < privileges_count; i++) { + picojson::value result_elem = picojson::value(picojson::object()); + picojson::object& obj = result_elem.get<picojson::object>(); + obj["privilege"] = picojson::value(privileges[i]); + obj["result"] = picojson::value(CheckRequestResultToString(result[i])); + array_obj.push_back(std::move(result_elem)); + } + obj.emplace(std::make_pair("result", result_array)); + } else { + LogAndReportError( + common::PlatformResult(common::ErrorCode::ABORT_ERR, "Get callback data failed"), &obj); + } + + common::Instance::PostMessage(data->_instance, event.serialize().c_str()); +} + void PPMInstance::checkPermission(const picojson::value& args, picojson::object& out) { ScopeLogger(); const std::string& privilege = args.get("privilege").get<std::string>(); @@ -145,6 +181,44 @@ void PPMInstance::checkPermission(const picojson::value& args, picojson::object& ReportSuccess(picojson::value(CheckResultToString(result)), out); } +void PPMInstance::checkPermissions(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + const picojson::array& privileges = args.get("privileges").get<picojson::array>(); + std::vector<const char*> privilege_array; + privilege_array.reserve(privileges.size()); + + for (auto iter = privileges.begin(); iter != privileges.end(); ++iter) { + privilege_array.push_back(iter->get<std::string>().c_str()); + } + + std::vector<ppm_check_result_e> results(privileges.size()); + int ret = ppm_check_permissions(privilege_array.data(), privileges.size(), results.data()); + + if (PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE != ret) { + LogAndReportError(convertError(ret), &out, + ("checkPermission error: %d (%s)", ret, get_error_message(ret))); + + return; + } + + picojson::value result_array = picojson::value(picojson::array()); + picojson::array& array_obj = result_array.get<picojson::array>(); + array_obj.reserve(privileges.size()); + + for (size_t i = 0; i < privileges.size(); i++) { + picojson::value result_elem = picojson::value(picojson::object()); + picojson::object& obj = result_elem.get<picojson::object>(); + + obj["privilege"] = picojson::value(privilege_array[i]); + obj["type"] = picojson::value(CheckResultToString(results[i])); + + array_obj.push_back(std::move(result_elem)); + } + + ReportSuccess(result_array, out); +} + void PPMInstance::requestPermission(const picojson::value& args, picojson::object& out) { ScopeLogger(); @@ -169,5 +243,34 @@ void PPMInstance::requestPermission(const picojson::value& args, picojson::objec ReportSuccess(out); } +void PPMInstance::requestPermissions(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + const double callback_id = args.get("callbackId").get<double>(); + const picojson::array& privileges = args.get("privileges").get<picojson::array>(); + std::vector<const char*> privilege_array; + privilege_array.reserve(privileges.size()); + + for (auto iter = privileges.begin(); iter != privileges.end(); ++iter) { + privilege_array.push_back(iter->get<std::string>().c_str()); + } + + ResponseCallbackData* user_data{new ResponseCallbackData()}; + user_data->_instance = this; + user_data->callbackId = callback_id; + + int ret = ppm_request_permissions(privilege_array.data(), privileges.size(), + ResponseMultipleCallback, static_cast<void*>(user_data)); + + if (PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE != ret) { + delete user_data; + LogAndReportError(convertError(ret), &out, + ("checkPermission error: %d (%s)", ret, get_error_message(ret))); + return; + } + + ReportSuccess(out); +} + } // namespace ppm } // namespace extension diff --git a/src/ppm/ppm_instance.h b/src/ppm/ppm_instance.h index 9d1825ce..1df853d9 100644 --- a/src/ppm/ppm_instance.h +++ b/src/ppm/ppm_instance.h @@ -35,13 +35,18 @@ class PPMInstance : public common::ParsedInstance { private: void checkPermission(const picojson::value& args, picojson::object& out); + void checkPermissions(const picojson::value& args, picojson::object& out); void requestPermission(const picojson::value& args, picojson::object& out); + void requestPermissions(const picojson::value& args, picojson::object& out); static common::PlatformResult convertError(int err, const std::string& message = ""); static std::string CheckResultToString(ppm_check_result_e result); static std::string CheckRequestResultToString(ppm_request_result_e result); static void ResponseCallback(ppm_call_cause_e cause, ppm_request_result_e result, const char* privilege, void* user_data); + static void ResponseMultipleCallback(ppm_call_cause_e cause, const ppm_request_result_e* result, + const char** privileges, size_t privileges_count, + void* user_data); }; } // namespace ppm diff --git a/src/preference/preference_manager.cc b/src/preference/preference_manager.cc index ecd9665e..95c82eec 100644 --- a/src/preference/preference_manager.cc +++ b/src/preference/preference_manager.cc @@ -19,6 +19,7 @@ #include <thread> #include "common/logger.h" +#include "common/task-queue.h" #include "common/tools.h" #include "preference/preference_instance.h" #include "preference/preference_manager.h" @@ -173,7 +174,9 @@ common::TizenResult PreferenceManager::GetAll(const common::PostCallback& callba callback(result, response); }; - std::thread(get_all, callback).detach(); + auto get_all_wrapper = [=] { get_all(callback); }; + + common::TaskQueue::GetInstance().Async(get_all_wrapper); return common::TizenSuccess(); } diff --git a/src/push/push_api.js b/src/push/push_api.js index 4e4e3603..6bea2c9f 100644 --- a/src/push/push_api.js +++ b/src/push/push_api.js @@ -58,8 +58,8 @@ function PushManager() { PushManager.prototype.registerService = function() { privUtils_.warn( - 'DEPRECATION WARNING: registerService() is deprecated ' + - 'and will be removed from next release. Use register() instead.' + 'DEPRECATION WARNING: registerService() is deprecated and will be removed ' + + 'from next release. Use register() instead.' ); var data = validator.validateArgs(arguments, [ { @@ -123,8 +123,8 @@ PushManager.prototype.register = function() { PushManager.prototype.unregisterService = function() { privUtils_.warn( - 'DEPRECATION WARNING: unregisterService() is deprecated ' + - 'and will be removed from next release. Use unregister() instead.' + 'DEPRECATION WARNING: unregisterService() is deprecated and will be removed ' + + 'from next release. Use unregister() instead.' ); var data = validator.validateArgs(arguments, [ { @@ -187,8 +187,8 @@ PushManager.prototype.unregister = function() { PushManager.prototype.connectService = function(notificationCallback) { privUtils_.warn( - 'DEPRECATION WARNING: connectService() is deprecated ' + - 'and will be removed from next release. Use connect() instead.' + 'DEPRECATION WARNING: connectService() is deprecated and will be removed from ' + + 'next release. Use connect() instead.' ); var data = validator.validateArgs(arguments, [ { @@ -254,8 +254,8 @@ PushManager.prototype.connect = function(notificationCallback) { PushManager.prototype.disconnectService = function() { privUtils_.warn( - 'DEPRECATION WARNING: disconnectService() is deprecated ' + - 'and will be removed from next release. Use disconnect() instead.' + 'DEPRECATION WARNING: disconnectService() is deprecated and will be removed ' + + 'from next release. Use disconnect() instead.' ); var ret = native.callSync('Push_disconnectService', {}); if (native.isFailure(ret)) { diff --git a/src/radio/radio.gyp b/src/radio/radio.gyp index 79d37d59..6c099001 100644 --- a/src/radio/radio.gyp +++ b/src/radio/radio.gyp @@ -25,9 +25,10 @@ ['tizen == 1', { 'variables': { 'packages': [ + 'capi-system-runtime-info', 'vconf', 'capi-media-radio', - 'capi-system-runtime-info', + 'capi-media-sound-manager', ] }, }], diff --git a/src/radio/radio_manager.cc b/src/radio/radio_manager.cc index 2e9ea1ea..9fe1b40b 100644 --- a/src/radio/radio_manager.cc +++ b/src/radio/radio_manager.cc @@ -88,24 +88,34 @@ PlatformResult CheckError(const std::string& str, int err) { } } -string TranslateInterruptedCode(int code) { - ScopeLogger(); -#define STRINGIFY(c) \ - case c: \ - return #c - switch (code) { - STRINGIFY(RADIO_INTERRUPTED_BY_MEDIA); - STRINGIFY(RADIO_INTERRUPTED_BY_CALL); - STRINGIFY(RADIO_INTERRUPTED_BY_EARJACK_UNPLUG); - STRINGIFY(RADIO_INTERRUPTED_BY_RESOURCE_CONFLICT); - STRINGIFY(RADIO_INTERRUPTED_BY_ALARM); - STRINGIFY(RADIO_INTERRUPTED_BY_EMERGENCY); - STRINGIFY(RADIO_INTERRUPTED_BY_RESUMABLE_MEDIA); - STRINGIFY(RADIO_INTERRUPTED_BY_NOTIFICATION); +string TranslateInterruptedCode(sound_stream_focus_change_reason_e reason) { + ScopeLogger(); + switch (reason) { + case SOUND_STREAM_FOCUS_CHANGED_BY_MEDIA: + return "RADIO_INTERRUPTED_BY_MEDIA"; + case SOUND_STREAM_FOCUS_CHANGED_BY_SYSTEM: + return "RADIO_INTERRUPTED_BY_SYSTEM"; + case SOUND_STREAM_FOCUS_CHANGED_BY_ALARM: + return "RADIO_INTERRUPTED_BY_ALARM"; + case SOUND_STREAM_FOCUS_CHANGED_BY_NOTIFICATION: + return "RADIO_INTERRUPTED_BY_NOTIFICATION"; + case SOUND_STREAM_FOCUS_CHANGED_BY_EMERGENCY: + return "RADIO_INTERRUPTED_BY_EMERGENCY"; + case SOUND_STREAM_FOCUS_CHANGED_BY_VOICE_INFORMATION: + return "RADIO_INTERRUPTED_BY_VOICE_INFORMATION"; + case SOUND_STREAM_FOCUS_CHANGED_BY_VOICE_RECOGNITION: + return "RADIO_INTERRUPTED_BY_VOICE_RECOGNITION"; + case SOUND_STREAM_FOCUS_CHANGED_BY_RINGTONE: + return "RADIO_INTERRUPTED_BY_RINGTONE"; + case SOUND_STREAM_FOCUS_CHANGED_BY_VOIP: + return "RADIO_INTERRUPTED_BY_VOIP"; + case SOUND_STREAM_FOCUS_CHANGED_BY_CALL: + return "RADIO_INTERRUPTED_BY_CALL"; + case SOUND_STREAM_FOCUS_CHANGED_BY_MEDIA_EXTERNAL_ONLY: + return "RADIO_INTERRUPTED_BY_MEDIA_EXTERNAL_ONLY"; default: return "UNKNOWN_INTERRUPTED_ERROR_CODE"; } -#undef STRINGIFY } int TokHz(double frequency) { @@ -185,26 +195,6 @@ void ScanStopCallback(void* user_data) { delete data; } -void RadioInterruptedCallback(radio_interrupted_code_e code, void* user_data) { - ScopeLogger(); - - picojson::value event{picojson::object()}; - auto& obj = event.get<picojson::object>(); - - obj.insert(std::make_pair("listenerId", picojson::value("FMRadio_Interrupted"))); - - if (code == RADIO_INTERRUPTED_COMPLETED) { - obj.insert(std::make_pair("action", picojson::value("oninterruptfinished"))); - } else { - obj.insert(std::make_pair("action", picojson::value("oninterrupted"))); - obj.insert(std::make_pair("reason", picojson::value(TranslateInterruptedCode(code)))); - } - - FMRadioManager* manager = static_cast<FMRadioManager*>(user_data); - common::TaskQueue::GetInstance().Async( - std::bind(&FMRadioManager::PostMessage, manager, event.serialize())); -} - void RadioAntennaCallback(runtime_info_key_e key, void* user_data) { ScopeLogger(); @@ -225,6 +215,51 @@ void RadioAntennaCallback(runtime_info_key_e key, void* user_data) { std::bind(&FMRadioManager::PostMessage, manager, event.serialize())); } +void SoundStreamFocusCallback(sound_stream_info_h stream_info, sound_stream_focus_mask_e focus_mask, + sound_stream_focus_state_e focus_state, + sound_stream_focus_change_reason_e reason_for_change, + int sound_behavior, const char* additional_info, void* user_data) { + ScopeLogger("reason_for_change: %d", reason_for_change); + + FMRadioManager* manager = static_cast<FMRadioManager*>(user_data); + + picojson::value event{picojson::object()}; + auto& obj = event.get<picojson::object>(); + + obj.insert(std::make_pair("listenerId", picojson::value("FMRadio_Interrupted"))); + + if (SOUND_STREAM_FOCUS_STATE_ACQUIRED != focus_state) { + LoggerD("Stopping radio"); + const auto err_radio_stop = radio_stop(manager->GetRadioInstance()); + if (RADIO_ERROR_NONE != err_radio_stop) { + LoggerE("Failed to stop radio: %d", err_radio_stop); + } + + obj.insert(std::make_pair("action", picojson::value("oninterrupted"))); + obj.insert( + std::make_pair("reason", picojson::value(TranslateInterruptedCode(reason_for_change)))); + } else { + // As we stopped radio on first interrupt, we will release focus on second + LoggerD("Preparing to release focus"); + auto release_focus = [manager]() { + ScopeLogger("Entered into asynchronous function, release_focus"); + const auto sound_focus_err = sound_manager_release_focus( + manager->GetStreamInfo(), SOUND_STREAM_FOCUS_FOR_PLAYBACK, SOUND_BEHAVIOR_NONE, NULL); + if (SOUND_MANAGER_ERROR_NONE != sound_focus_err) { + LoggerE("sound_manager_release_focus() failed: %d", sound_focus_err); + } + }; + common::TaskQueue::GetInstance().Async(release_focus); + + obj.insert(std::make_pair("action", picojson::value("oninterruptfinished"))); + } + + if (manager->IsInterruptedListenerSet()) { + common::TaskQueue::GetInstance().Async( + std::bind(&FMRadioManager::PostMessage, manager, event.serialize())); + } +} + } // namespace bool FMRadioManager::IsMuted() { @@ -293,6 +328,18 @@ const char* FMRadioManager::GetState() { } } +bool FMRadioManager::IsInterruptedListenerSet() { + return is_interrupted_listener_set; +} + +radio_h FMRadioManager::GetRadioInstance() { + return radio_instance_; +}; + +sound_stream_info_h FMRadioManager::GetStreamInfo() { + return stream_info_; +} + PlatformResult FMRadioManager::SetFrequency(double frequency) { ScopeLogger(); return CheckError("radio_set_frequency", radio_set_frequency(radio_instance_, TokHz(frequency))); @@ -328,7 +375,11 @@ double FMRadioManager::GetSignalStrength() { } FMRadioManager::FMRadioManager(RadioInstance& instance) - : instance_(instance), radio_instance_(nullptr), scan_data(nullptr) { + : instance_(instance), + radio_instance_(nullptr), + scan_data(nullptr), + stream_info_(nullptr), + is_interrupted_listener_set(false) { ScopeLogger(); const auto err = radio_create(&radio_instance_); @@ -337,6 +388,13 @@ FMRadioManager::FMRadioManager(RadioInstance& instance) LoggerE("radio_create() failed: %d", err); radio_instance_ = nullptr; } + + const auto err_sound = sound_manager_create_stream_information( + SOUND_STREAM_TYPE_MEDIA, SoundStreamFocusCallback, this, &stream_info_); + if (SOUND_MANAGER_ERROR_NONE != err_sound) { + LoggerE("sound_manager_create_stream_information() failed: %d", err_sound); + stream_info_ = nullptr; + } } FMRadioManager::~FMRadioManager() { @@ -349,6 +407,14 @@ FMRadioManager::~FMRadioManager() { LoggerE("radio_destroy() failed: %d", err); } } + + if (nullptr != stream_info_) { + const auto err = sound_manager_destroy_stream_information(stream_info_); + if (RADIO_ERROR_NONE != err) { + LoggerE("sound_manager_destroy_stream_information() failed: %d", err); + } + } + delete scan_data; } @@ -366,6 +432,7 @@ PlatformResult FMRadioManager::Start(double frequency) { if (RADIO_STATE_READY != state && RADIO_STATE_PLAYING != state) { return LogAndCreateResult(ErrorCode::INVALID_STATE_ERR, "Invalid radio state."); } + LoggerD("Current radio state: %d", state); PlatformResult result = SetFrequency(frequency); @@ -373,11 +440,34 @@ PlatformResult FMRadioManager::Start(double frequency) { return result; } - if (RADIO_STATE_READY == state) { - return CheckError("radio_start", radio_start(radio_instance_)); - } else { - return result; + if (RADIO_STATE_PLAYING == state) { + LoggerD("Radio is already started."); + return PlatformResult(ErrorCode::NO_ERROR); + } + // state == RADIO_STATE_READY + sound_stream_focus_state_e state_for_playback; + sound_stream_focus_state_e state_for_recording; + + const auto err_focus_state = + sound_manager_get_focus_state(stream_info_, &state_for_playback, &state_for_recording); + + if (SOUND_MANAGER_ERROR_NONE != err_focus_state) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Checking sound focus state failed.", + ("sound_manager_get_focus_state failed: %d", err_focus_state)); } + + if (SOUND_STREAM_FOCUS_STATE_ACQUIRED != state_for_playback) { + LoggerD("Current sound stream focus state: %d, acquiring focus", state_for_playback); + const auto err_sound_focus = sound_manager_acquire_focus( + stream_info_, SOUND_STREAM_FOCUS_FOR_PLAYBACK, SOUND_BEHAVIOR_NONE, NULL); + + if (SOUND_MANAGER_ERROR_NONE != err_sound_focus) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Acquiring sound focus failed.", + ("sound_manager_acquire_focus failed: %d", err_sound_focus)); + } + } + + return CheckError("radio_start", radio_start(radio_instance_)); } PlatformResult FMRadioManager::Stop() { @@ -395,7 +485,14 @@ PlatformResult FMRadioManager::Stop() { return LogAndCreateResult(ErrorCode::INVALID_STATE_ERR, "Invalid radio state."); } - return CheckError("radio_stop", radio_stop(radio_instance_)); + const auto err_radio_stop = radio_stop(radio_instance_); + const auto sound_focus_err = sound_manager_release_focus( + stream_info_, SOUND_STREAM_FOCUS_FOR_PLAYBACK, SOUND_BEHAVIOR_NONE, NULL); + if (SOUND_MANAGER_ERROR_NONE != sound_focus_err) { + LoggerE("sound_manager_release_focus() failed: %d", sound_focus_err); + } + + return CheckError("radio_stop", err_radio_stop); } void FMRadioManager::SeekUp(double callback_id) { @@ -495,15 +592,15 @@ void FMRadioManager::ScanStop(double callback_id) { common::PlatformResult FMRadioManager::SetFMRadioInterruptedListener() { ScopeLogger(); - const auto err = radio_set_interrupted_cb(radio_instance_, RadioInterruptedCallback, this); - return CheckError("radio_set_interrupted_cb", err); + is_interrupted_listener_set = true; + return PlatformResult(ErrorCode::NO_ERROR); } common::PlatformResult FMRadioManager::UnsetFMRadioInterruptedListener() { ScopeLogger(); - const auto err = radio_unset_interrupted_cb(radio_instance_); - return CheckError("radio_unset_interrupted_cb", err); + is_interrupted_listener_set = false; + return PlatformResult(ErrorCode::NO_ERROR); } common::PlatformResult FMRadioManager::SetAntennaChangeListener() { diff --git a/src/radio/radio_manager.h b/src/radio/radio_manager.h index e32611d5..9484e6bb 100644 --- a/src/radio/radio_manager.h +++ b/src/radio/radio_manager.h @@ -24,6 +24,7 @@ #include <radio.h> #include <runtime_info.h> +#include <sound_manager.h> #include "common/picojson.h" #include "common/platform_result.h" @@ -61,6 +62,9 @@ class FMRadioManager { double GetSignalStrength(); bool HasAntenna(); const char* GetState(); + bool IsInterruptedListenerSet(); + radio_h GetRadioInstance(); + sound_stream_info_h GetStreamInfo(); void PostMessage(const std::string& msg) const; void PostResultSuccess(double callbackId, picojson::value* event) const; @@ -71,6 +75,8 @@ class FMRadioManager { RadioInstance& instance_; radio_h radio_instance_; RadioScanData* scan_data; + sound_stream_info_h stream_info_; + bool is_interrupted_listener_set; }; struct RadioData { diff --git a/src/secureelement/secureelement_instance.cc b/src/secureelement/secureelement_instance.cc index eccb6925..75efd102 100644 --- a/src/secureelement/secureelement_instance.cc +++ b/src/secureelement/secureelement_instance.cc @@ -20,6 +20,7 @@ #include <thread> #include "common/scope_exit.h" +#include "common/task-queue.h" #include "common/tools.h" namespace extension { @@ -186,7 +187,7 @@ TizenResult SecureElementInstance::GetReaders(picojson::object const& args, this->Post(token, result); }; - std::thread(get_readers, token).detach(); + common::TaskQueue::GetInstance().Async(get_readers, token); return TizenSuccess(); } @@ -319,7 +320,7 @@ TizenResult SecureElementInstance::OpenSession(picojson::object const& args, this->Post(token, result); }; - std::thread(open_session, token).detach(); + common::TaskQueue::GetInstance().Async(open_session, token); return TizenSuccess(); } @@ -396,7 +397,7 @@ TizenResult SecureElementInstance::OpenBasicChannel(picojson::object const& args this->Post(token, result); }; - std::thread(open_basic_channel, token).detach(); + common::TaskQueue::GetInstance().Async(open_basic_channel, token); return TizenSuccess(); } @@ -441,7 +442,7 @@ TizenResult SecureElementInstance::OpenLogicalChannel(picojson::object const& ar this->Post(token, result); }; - std::thread(open_basic_logical, token).detach(); + common::TaskQueue::GetInstance().Async(open_basic_logical, token); return TizenSuccess(); } @@ -582,7 +583,7 @@ TizenResult SecureElementInstance::Transmit(picojson::object const& args, this->Post(token, result); }; - std::thread(transmit, token).detach(); + common::TaskQueue::GetInstance().Async(transmit, token); return TizenSuccess(); } diff --git a/src/sensor/sensor_api.js b/src/sensor/sensor_api.js index 9305c4f1..f3380ef0 100755 --- a/src/sensor/sensor_api.js +++ b/src/sensor/sensor_api.js @@ -605,27 +605,28 @@ var GyroscopeRotationVectorSensor = function(data) { GyroscopeRotationVectorSensor.prototype = new Sensor(); GyroscopeRotationVectorSensor.prototype.constructor = Sensor; + // prettier-ignore GyroscopeRotationVectorSensor.prototype.getGyroscopeRotationVectorSensorData = -function() { - var args = validator_.validateArgs(arguments, [ - { - name: 'successCallback', - type: types_.FUNCTION - }, - { - name: 'errorCallback', - type: types_.FUNCTION, - optional: true, - nullable: true - } - ]); + function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION + }, + { + name: 'errorCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + } + ]); - _sensorListeners[this.sensorType].getData( - args.successCallback, - errorWrapper.bind(args) - ); -}; + _sensorListeners[this.sensorType].getData( + args.successCallback, + errorWrapper.bind(args) + ); + }; //// LinearAccelerationSensor var LinearAccelerationSensor = function(data) { @@ -743,7 +744,7 @@ AccelerationSensor.prototype.getAccelerationSensorData = function() { ); }; -////////////////////// Sensor Data classes///////////////////////////// +////////////////////// Sensor Data classes////////////////////////// ////Base SensorData class var SensorData = function() {}; diff --git a/src/sensor/sensor_service.cc b/src/sensor/sensor_service.cc index 1dcc339d..217ec36e 100644 --- a/src/sensor/sensor_service.cc +++ b/src/sensor/sensor_service.cc @@ -16,6 +16,7 @@ #include "sensor_service.h" +#include <glib.h> #include <memory> #include <mutex> #include <string> diff --git a/src/sound/sound.gyp b/src/sound/sound.gyp index e4210895..4466fb2d 100644 --- a/src/sound/sound.gyp +++ b/src/sound/sound.gyp @@ -27,8 +27,9 @@ [ 'tizen == 1', { 'variables': { 'packages': [ - 'vconf', 'capi-media-sound-manager', + 'vconf', + 'capi-base-common', ] }, }], diff --git a/src/sound/sound_api.js b/src/sound/sound_api.js index f8dbabc8..7c3c86d2 100644 --- a/src/sound/sound_api.js +++ b/src/sound/sound_api.js @@ -78,15 +78,12 @@ ListenerManager.prototype.addListener = function(callback) { }; ListenerManager.prototype.removeListener = function(watchId) { - if (this.listeners.hasOwnProperty(watchId)) { - delete this.listeners[watchId]; - } else { - throw new WebAPIException( - WebAPIException.INVALID_VALUES_ERR, - 'Listener with id: ' + watchId + ' does not exist.' - ); + if (!this.listeners.hasOwnProperty(watchId)) { + return; } + delete this.listeners[watchId]; + if (this.nativeSet && type_.isEmptyObject(this.listeners)) { this.native.callSync('SoundManager_removeDeviceStateChangeListener'); this.native.removeListener(this.listenerName); @@ -140,9 +137,25 @@ SoundManager.prototype.getVolume = function(type) { }; var _soundModeChangeListener; +var _currentSoundMode; +var _isFirstSoundModeChange = true; +// Native side sometimes fires sound change callback two times in a row +// with different values of sound mode. One of this value is only transitional +// value caused by hazard of two values which should change simultaneously. +// By waiting whether second callback would fire we bypass this problem. function _soundModeChangeListenerCallback(result) { - native_.callIfPossible(_soundModeChangeListener, native_.getResultObject(result)); + _currentSoundMode = result; + if (_isFirstSoundModeChange) { + _isFirstSoundModeChange = false; + setTimeout(function() { + _isFirstSoundModeChange = true; + native_.callIfPossible( + _soundModeChangeListener, + native_.getResultObject(_currentSoundMode) + ); + }, 100); + } } SoundManager.prototype.setSoundModeChangeListener = function(callback) { diff --git a/src/sound/sound_instance.cc b/src/sound/sound_instance.cc index 89e0b868..c45e7d67 100644 --- a/src/sound/sound_instance.cc +++ b/src/sound/sound_instance.cc @@ -132,6 +132,11 @@ void SoundInstance::SoundManagerUnsetSoundModeChangeListener(const picojson::val void SoundInstance::OnSoundModeChange(const std::string& newmode) { ScopeLogger(); + if (current_sound_mode == newmode) { + LoggerD("New sound mode equals to current sound mode"); + return; + } + current_sound_mode = newmode; picojson::value event = picojson::value(picojson::object()); picojson::object& obj = event.get<picojson::object>(); picojson::value result = picojson::value(newmode); diff --git a/src/sound/sound_instance.h b/src/sound/sound_instance.h index 32a4c004..a800cf3b 100644 --- a/src/sound/sound_instance.h +++ b/src/sound/sound_instance.h @@ -45,6 +45,9 @@ class SoundInstance : public common::ParsedInstance, public SoundManagerSoundMod void OnSoundModeChange(const std::string& newmode); SoundManager manager_; + + // It is used in OnSoundModeChange function to prevent double firing of callback + std::string current_sound_mode; }; } // namespace sound diff --git a/src/sound/sound_manager.cc b/src/sound/sound_manager.cc index 8c98163d..fcbe1691 100644 --- a/src/sound/sound_manager.cc +++ b/src/sound/sound_manager.cc @@ -87,11 +87,12 @@ std::string SoundManager::SoundDeviceTypeToString(sound_device_type_e type) { return "MIC"; case SOUND_DEVICE_AUDIO_JACK: return "AUDIO_JACK"; - case SOUND_DEVICE_BLUETOOTH: + case SOUND_DEVICE_BLUETOOTH_MEDIA: + case SOUND_DEVICE_BLUETOOTH_VOICE: return "BLUETOOTH"; case SOUND_DEVICE_HDMI: return "HDMI"; - case SOUND_DEVICE_MIRRORING: + case SOUND_DEVICE_FORWARDING: return "MIRRORING"; case SOUND_DEVICE_USB_AUDIO: return "USB_AUDIO"; @@ -142,7 +143,7 @@ SoundManager::~SoundManager() { } if (SOUND_MANAGER_ERROR_NONE != - sound_manager_remove_device_state_changed_cb(sound_device_state_listener_id_)) { + sound_manager_remove_device_running_changed_cb(sound_device_state_listener_id_)) { LoggerE("Cannot unregister state listener id == %d", sound_device_state_listener_id_); } } @@ -197,7 +198,7 @@ double SoundManager::ConvertToSystemVolume(int max_volume, int volume) { } void SoundManager::VolumeChangeCallback(sound_type_e type, unsigned int value) { - ScopeLogger("VolumeChangeCallback: type: %d, value: %d", type, value); + ScopeLogger("VolumeChangeCallback: type: %d, value: %u", type, value); // Prepare response picojson::value response = picojson::value(picojson::object()); @@ -506,14 +507,14 @@ PlatformResult SoundManager::GetDeviceInfo(sound_device_h device, bool is_connec obj->insert(std::make_pair("direction", picojson::value(SoundIOTypeToString(direction)))); // get state - sound_device_state_e state = SOUND_DEVICE_STATE_DEACTIVATED; - ret = sound_manager_get_device_state(device, &state); + bool state = false; + ret = sound_manager_is_device_running(device, &state); if (SOUND_MANAGER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Getting device state failed", - ("sound_manager_get_device_state error: %d (%s)", ret, get_error_message(ret))); + ("sound_manager_is_device_running error: %d (%s)", ret, get_error_message(ret))); } - obj->insert(std::make_pair("isActivated", picojson::value(static_cast<bool>(state)))); + obj->insert(std::make_pair("isActivated", picojson::value(state))); // get connection if (check_connection) { @@ -608,7 +609,7 @@ void DeviceConnectionChangedCB(sound_device_h device, bool is_connected, void* u h->DeviceChangeCB(device, is_connected, false); } -void DeviceStateChangedCB(sound_device_h device, sound_device_state_e unused, void* user_data) { +void DeviceStateChangedCB(sound_device_h device, bool unused, void* user_data) { ScopeLogger(); SoundManager* h = static_cast<SoundManager*>(user_data); @@ -630,13 +631,13 @@ PlatformResult SoundManager::AddDeviceStateChangeListener() { ret, get_error_message(ret))); } - ret = sound_manager_add_device_state_changed_cb(mask, DeviceStateChangedCB, this, - &sound_device_state_listener_id_); + ret = sound_manager_add_device_running_changed_cb(mask, DeviceStateChangedCB, this, + &sound_device_state_listener_id_); if (SOUND_MANAGER_ERROR_NONE != ret) { // silently cleanup connection changed callback registered in previous step sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_); return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Setting state listener failed", - ("sound_manager_add_device_state_changed_cb error: %d (%s)", ret, + ("sound_manager_add_device_running_changed_cb error: %d (%s)", ret, get_error_message(ret))); } @@ -658,11 +659,11 @@ PlatformResult SoundManager::RemoveDeviceStateChangeListener() { ret, get_error_message(ret))); } - ret = sound_manager_remove_device_state_changed_cb(sound_device_state_listener_id_); + ret = sound_manager_remove_device_running_changed_cb(sound_device_state_listener_id_); if (SOUND_MANAGER_ERROR_NONE != ret) { return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removing state listener failed", - ("sound_manager_remove_device_state_changed_cb error: %d (%s)", ret, - get_error_message(ret))); + ("sound_manager_remove_device_running_changed_cb error: %d (%s)", + ret, get_error_message(ret))); } is_sound_device_change_listener_ = false; diff --git a/src/systeminfo/systeminfo-utils.cpp b/src/systeminfo/systeminfo-utils.cpp index 88822809..a7e9c43a 100644 --- a/src/systeminfo/systeminfo-utils.cpp +++ b/src/systeminfo/systeminfo-utils.cpp @@ -19,8 +19,8 @@ #include <memory> -#include <device.h> #include <net_connection.h> +#include <runtime_info.h> #include "common/logger.h" #include "common/platform_exception.h" @@ -121,17 +121,16 @@ PlatformResult SysteminfoUtils::CheckIfEthernetNetworkSupported() { PlatformResult SysteminfoUtils::GetTotalMemory(long long *result) { ScopeLogger(); - unsigned int value = 0; - - int ret = device_memory_get_total(&value); - if (ret != DEVICE_ERROR_NONE) { - std::string log_msg = "Failed to get total memory: " + std::to_string(ret); + runtime_memory_info_s info = {0}; + int ret = runtime_info_get_system_memory_info(&info); + if (ret != RUNTIME_INFO_ERROR_NONE) { + std::string log_msg = "Failed to get system memory info: " + std::to_string(ret); return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, log_msg, - ("device_memory_get_total error: %d (%s)", ret, get_error_message(ret))); + ("runtime_info_get_system_memory_info error: %d (%s)", ret, get_error_message(ret))); } - *result = static_cast<long long>(value) * MEMORY_TO_BYTE; + *result = static_cast<long long>(info.total) * MEMORY_TO_BYTE; return PlatformResult(ErrorCode::NO_ERROR); } @@ -139,17 +138,19 @@ PlatformResult SysteminfoUtils::GetTotalMemory(long long *result) { PlatformResult SysteminfoUtils::GetAvailableMemory(long long *result) { ScopeLogger(); - unsigned int value = 0; - - int ret = device_memory_get_available(&value); - if (ret != DEVICE_ERROR_NONE) { - std::string log_msg = "Failed to get total memory: " + std::to_string(ret); + runtime_memory_info_s info = {0}; + int ret = runtime_info_get_system_memory_info(&info); + if (ret != RUNTIME_INFO_ERROR_NONE) { + std::string log_msg = "Failed to get system memory info: " + std::to_string(ret); return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, log_msg, - ("device_memory_get_available error: %d (%s)", ret, get_error_message(ret))); + ("runtime_info_get_system_memory_info error: %d (%s)", ret, get_error_message(ret))); } - *result = static_cast<long long>(value) * MEMORY_TO_BYTE; + // as the WebIDL says the available memory means "amount of memory that is not in use (in bytes)", + // the result value is evaluated exactly this way, basing on total and used memory. + *result = static_cast<long long>(info.total - info.used) * MEMORY_TO_BYTE; + return PlatformResult(ErrorCode::NO_ERROR); } diff --git a/src/systeminfo/systeminfo.gyp b/src/systeminfo/systeminfo.gyp index 5822c008..df47259f 100644 --- a/src/systeminfo/systeminfo.gyp +++ b/src/systeminfo/systeminfo.gyp @@ -33,17 +33,18 @@ ['tizen == 1', { 'variables': { 'packages': [ - 'ecore', - 'vconf', - 'glib-2.0', - 'capi-system-info', - 'capi-network-connection', - 'capi-system-device', - 'capi-system-system-settings', - 'capi-network-wifi-manager', - 'libtzplatform-config', - 'tapi', - 'sensor', + 'capi-system-info', + 'capi-system-device', + 'vconf', + 'capi-network-connection', + 'capi-system-runtime-info', + 'tapi', + 'capi-network-wifi-manager', + 'capi-system-system-settings', + 'vconf-internal-keys', + 'sensor', + 'storage', + 'libtzplatform-config', ] }, }], diff --git a/src/systeminfo/systeminfo_api.js b/src/systeminfo/systeminfo_api.js index 601f6618..d60c3afd 100644 --- a/src/systeminfo/systeminfo_api.js +++ b/src/systeminfo/systeminfo_api.js @@ -478,7 +478,7 @@ function SystemInfoStorageUnit(data) { enumerable: true, get: function() { privUtils_.warn( - 'DEPRECATION WARNING: SystemInfoStorageUnit.isRemoveable is ' + + 'DEPRECATION WARNING: SystemInfoStorageUnit.isRemoveable is is ' + 'deprecated and will be removed from next release. ' + 'Use SystemInfoStorageUnit.isRemovable instead.' ); @@ -977,13 +977,13 @@ function _systeminfoBatteryListenerCallback(eventObj) { * (T_.isUndefined(listener.lowThreshold) && * T_.isUndefined(listener.highThreshold)) || * (!T_.isUndefined(listener.lowThreshold) && - * !T_.isUndefined(listener.highThreshold) && - * (propObj.level <= listener.lowThreshold || - * propObj.level >= listener.highThreshold)) || + * !T_.isUndefined(listener.highThreshold) && + * (propObj.level <= listener.lowThreshold || + * propObj.level >= listener.highThreshold)) || * (!T_.isUndefined(listener.lowThreshold) && - * (propObj.level <= listener.lowThreshold)) || + * (propObj.level <= listener.lowThreshold)) || * (!T_.isUndefined(listener.highThreshold) && - * (propObj.level >= listener.highThreshold)) + * (propObj.level >= listener.highThreshold)) * * but it can be optimized like this: */ diff --git a/src/systeminfo/systeminfo_properties_manager.cc b/src/systeminfo/systeminfo_properties_manager.cc index da705ebd..4b46b1b6 100644 --- a/src/systeminfo/systeminfo_properties_manager.cc +++ b/src/systeminfo/systeminfo_properties_manager.cc @@ -1208,7 +1208,7 @@ PlatformResult SysteminfoPropertiesManager::ReportStorage(picojson::object* out) // handling storages from provider common::FilesystemProvider& provider(common::FilesystemProvider::Create()); auto storages = provider.GetStorages(); - LoggerD("Storages found %d", storages.size()); + LoggerD("Storages found %zu", storages.size()); for (auto storage : storages) { if (storage->state() == common::StorageState::kMounted) { unsigned long long available; diff --git a/src/systeminfo/systeminfo_sim_details_manager.cc b/src/systeminfo/systeminfo_sim_details_manager.cc index 3fb1c228..8b85d5b2 100644 --- a/src/systeminfo/systeminfo_sim_details_manager.cc +++ b/src/systeminfo/systeminfo_sim_details_manager.cc @@ -146,6 +146,8 @@ PlatformResult SimDetailsManager::GatherSimInformation(TapiHandle* handle, picoj if (ret.IsError()) { return ret; } + // The variable used to determine if double mutex-locking is needed. + auto to_process_cached = to_process_; { // All props should be fetched synchronously, but sync function does not work std::lock_guard<std::mutex> lock_to_process(sim_to_process_mutex_); @@ -178,8 +180,16 @@ PlatformResult SimDetailsManager::GatherSimInformation(TapiHandle* handle, picoj LoggerE("Failed getting iccid: %d", result); } } - // prevent returning not filled result - std::lock_guard<std::mutex> lock_sim(sim_info_mutex_); + // Here, the double mutex-locking may hang the application (thread) forever. + // This happens when none of above (4) getters succeeds (the to_process_ variable is not + // incremented). + // Usually, we would return and report an error, but the getPropertyValue and + // getPropertyValueArray methods do not invoke errorCallback with proper error type, thus silent + // error will be reported. + if (to_process_cached != to_process_) { + // Try to lock and wait for unlocking the sim_info_mutex_ by all registered callbacks. + std::lock_guard<std::mutex> lock_sim(sim_info_mutex_); + } // result will come from callbacks return PlatformResult(ErrorCode::NO_ERROR); } diff --git a/src/systemsetting/systemsetting.gyp b/src/systemsetting/systemsetting.gyp index 815fed4f..96462fd7 100644 --- a/src/systemsetting/systemsetting.gyp +++ b/src/systemsetting/systemsetting.gyp @@ -21,7 +21,6 @@ 'variables': { 'packages': [ 'capi-system-system-settings', - 'vconf', ] }, }], diff --git a/src/systemsetting/systemsetting_instance.cc b/src/systemsetting/systemsetting_instance.cc index 8b28ba7c..55c58af5 100644 --- a/src/systemsetting/systemsetting_instance.cc +++ b/src/systemsetting/systemsetting_instance.cc @@ -21,6 +21,7 @@ #include "common/filesystem/filesystem_provider.h" #include "common/logger.h" #include "common/picojson.h" +#include "common/scope_exit.h" #include "common/task-queue.h" #include "common/tools.h" @@ -109,11 +110,15 @@ PlatformResult SystemSettingInstance::getPlatformPropertyValue(const std::string // other values (not specified in the documentation) are handled in JS switch (ret) { - case SYSTEM_SETTINGS_ERROR_NONE: + case SYSTEM_SETTINGS_ERROR_NONE: { + SCOPE_EXIT { + free(value); + }; LoggerD("ret == SYSTEM_SETTINGS_ERROR_NONE"); + CHECK_STORAGE_ACCESS_AND_RETURN(value); result_obj.insert(std::make_pair("value", picojson::value(value ? value : ""))); - free(value); return PlatformResult(ErrorCode::NO_ERROR); + } case SYSTEM_SETTINGS_ERROR_NOT_SUPPORTED: return LogAndCreateResult( ErrorCode::NOT_SUPPORTED_ERR, "This property is not supported.", @@ -137,10 +142,12 @@ void SystemSettingInstance::setProperty(const picojson::value& args, picojson::o const std::string& value = args.get("value").get<std::string>(); LoggerD("Value to set: %s ", value.c_str()); - auto get = [this, type, value, + std::string real_path = common::FilesystemProvider::Create().GetRealPath(value); + CHECK_STORAGE_ACCESS(real_path, &out); + + auto get = [this, type, real_path, callback_id](const std::shared_ptr<picojson::value>& response) -> void { ScopeLogger("Entered into asynchronous function, get"); - std::string real_path = common::FilesystemProvider::Create().GetRealPath(value); PlatformResult status = setPlatformPropertyValue(type, real_path); picojson::object& obj = response->get<picojson::object>(); if (status.IsSuccess()) { @@ -154,7 +161,10 @@ void SystemSettingInstance::setProperty(const picojson::value& args, picojson::o auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object())); - TaskQueue::GetInstance().Async<picojson::value>(get, data); + // Setting properties needs to be executed in main thread because of: + // "Internally, EFL uses data with TLSS(Thread Local Storage). + // Hence, there is no share data between Main thread and other thread." + TaskQueue::GetInstance().ScheduleWorkInMainThread<picojson::value>(get, data); } PlatformResult SystemSettingInstance::setPlatformPropertyValue(const std::string& settingType, diff --git a/src/time/time_api.js b/src/time/time_api.js index 3de42a61..9839b387 100644 --- a/src/time/time_api.js +++ b/src/time/time_api.js @@ -69,7 +69,7 @@ function _getTimezoneOffset(timestamp, tzName) { } function _getLocalTimezoneOffset(utcTimestamp) { - // casting to milliseconds + // cast to milliseconds return -1 * new Date(utcTimestamp).getTimezoneOffset() * 60 * 1000; } @@ -175,15 +175,15 @@ tizen.TZDate = function(p1, p2, day, hours, minutes, seconds, milliseconds, time var offsetObject = _getTimezoneOffset(timezoneTimestamp, timezone); offset = offsetObject.offset; utcTimestamp = timezoneTimestamp - offset; - // correction of missing/extra hour on DST change + //correction of missing/extra hour on DST change var modifier = offsetObject.modifier; if (modifier > 0) { - // this is for case when 2AM becomes 3AM - // (but offset must be corrected - missing one hour) + //this is for case when 2AM becomes 3AM (but offset must be + //corrected - missing one hour) offset += modifier; } else { - // this is for case when extra hour appers - prevents error of - // unnecessary shift of hour when timezone changes + //this is for case when extra hour appers - prevents error of + //unnecessary shift of hour when timezone changes offset -= modifier; utcTimestamp += modifier; } @@ -557,8 +557,8 @@ tizen.TZDate.prototype.toString = function() { tizen.TZDate.prototype.getTimezoneAbbreviation = function() { utils_.log('Entered TZDate.getTimezoneAbbreviation'); utils_.warn( - 'DEPRECATION WARNING: getTimezoneAbbreviation() is deprecated ' + - 'and will be removed from next release.' + 'DEPRECATION WARNING: getTimezoneAbbreviation() is deprecated and will be ' + + 'removed from next release.' ); var result = native_.callSync('TZDate_getTimezoneAbbreviation', { diff --git a/src/tizen/tizen_api.js b/src/tizen/tizen_api.js index ee987071..5232c130 100644 --- a/src/tizen/tizen_api.js +++ b/src/tizen/tizen_api.js @@ -3,10 +3,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/* eslint-disable */ +/*eslint-disable */ // Tizen API Specification: -// https://developer.tizen.org/dev-guide/2.3.0/org.tizen.web.apireference/org.tizen.mobile.web.device.apireference/index.html -/* eslint-enable */ +//https://developer.tizen.org/dev-guide/2.3.0/org.tizen.mobile.web.device.apireference/tizen/tizen.html +/*eslint-enable */ // WebAPIException and WebAPIError definition moved to src/utils/utils_api.js // for compliance reasons. You can find more info there. diff --git a/src/tvinputdevice/tvinputdevice_api.js b/src/tvinputdevice/tvinputdevice_api.js index 00576b18..3c5580f2 100755 --- a/src/tvinputdevice/tvinputdevice_api.js +++ b/src/tvinputdevice/tvinputdevice_api.js @@ -243,8 +243,8 @@ function TVInputDeviceKey(dict) { } /** - * This class provides access to the API functionalities - * through the tizen.tvinputdevice interface. + * This class provides access to the API functionalities through + * the tizen.tvinputdevice interface. * @constructor */ function TVInputDeviceManager() { @@ -299,8 +299,8 @@ TVInputDeviceManager.prototype.getKey = function(keyName) { }; /** - * Registers an input device key to receive DOM keyboard event - * when it is pressed or released. + * Registers an input device key to receive DOM keyboard event when it is + * pressed or released. * @param {!string} keyName The key name */ TVInputDeviceManager.prototype.registerKey = function(keyName) { diff --git a/src/utils/utils_api.js b/src/utils/utils_api.js index bad69c9f..1c5e9a9e 100644 --- a/src/utils/utils_api.js +++ b/src/utils/utils_api.js @@ -510,28 +510,27 @@ Converter.prototype.toString = function(val, nullable) { }; function _toPlatformObject(val, types) { - var v; var t; - if (_type.isArray(val)) { - v = val; - } else { - v = [val]; - } if (_type.isArray(types)) { t = types; } else { t = [types]; } + + if (_type.isArray(val)) { + throw new WebAPIException( + WebAPIException.TYPE_MISMATCH_ERR, + 'Cannot convert ' + String(val) + ' to ' + String(t[0].name) + '.' + ); + } + var match = false; for (var i = 0; i < t.length; ++i) { - for (var j = 0; j < v.length; ++j) { - match = match || v[j] instanceof t[i]; + if (val instanceof t[i]) { + return val; } } - if (match) { - return val; - } throw new WebAPIException( WebAPIException.TYPE_MISMATCH_ERR, @@ -646,8 +645,8 @@ var Validator = function() { * - values - required in case of some objects, value depends on type * - validator - function which accepts a single parameter and returns true or false; * if this property is present, this function will be executed, - * argument converted to expected type is going to be passed to - * this function + * argument converted to expected type is going to be passed to this + * function * * @param {Array} a - arguments of a method * @param {Array} d - description of expected arguments @@ -724,8 +723,8 @@ var Validator = function() { * { * name: 'first', * type: Validator.Types.ARRAY, - * values: Validator.Types.DOUBLE // converts elements, - * only primitive types are supported + * values: Validator.Types.DOUBLE // converts elements, only primitive types are + * supported * } * ] * @code @@ -963,8 +962,8 @@ Validator.prototype.validateMethod = function(a, d) { */ Validator.prototype.isConstructorCall = function(obj, instance) { if (!(obj instanceof instance) || obj._previouslyConstructed) { - // There is no TypeError exception in Tizen 2.3.0 API spec - // but it's required by current TCTs. + // There is no TypeError exception in Tizen 2.3.0 API spec but it's required by + // current TCTs. // For Tizen compliance it's wrapped into WebAPIException. throw new WebAPIException( 'TypeError', @@ -1106,6 +1105,18 @@ var NativeManager = function(extension) { try { var msg = JSON_.parse(json); } catch (error) { + // Because of special handling of power lock in chromium, the special + // signals: + // - __DisableChromiumInternalPowerLock + // - __EnableChromiumInternalPowerLock + // could occur. In such cases we are silently ignroing those messages. + // TODO This is workaround for missing patch in chromium-efl package + // which should handle this special message and don't forward it to + // webapi JS. After chromium-efl will be updated, below checking should + // be removed. + if (json.substring(0, 2) === '__') { + return; + } xwalk.utils.error('Ignoring message - Invalid JSON received: ' + json); return; } @@ -1256,8 +1267,8 @@ NativeManager.prototype.callIfPossible = function(callback) { // WebAPIException and WebAPIError definition moved to Utils for compliance // reasons with blink-wrt environment. -// In blink-wrt the original Tizen module is loaded, -// which is not providing exception constructor. +// In blink-wrt the original Tizen module is loaded, which is not providing +// exception constructor. // As modules needs exceptions internally so they are loaded here for now. // See http://168.219.209.56/gerrit/#/c/23472/ for more details. // In future exception definition could be moved back to Tizen module. @@ -1423,8 +1434,8 @@ var WebAPIException = function(code, message, name) { }); this.constructor.prototype.__proto__ = Error.prototype; - // V8-specific code Error.captureStackTrace && Error.captureStackTrace(this, this.constructor); + // V8-specific code }; WebAPIException.prototype.toString = function() { diff --git a/src/utils/utils_extension.cc b/src/utils/utils_extension.cc index 0b9d7a65..7cc85d3a 100644 --- a/src/utils/utils_extension.cc +++ b/src/utils/utils_extension.cc @@ -23,6 +23,7 @@ UtilsExtension::UtilsExtension() { UtilsExtension::~UtilsExtension() { ScopeLogger(); + common::TaskQueue::GetInstance().Stop(); } common::Instance* UtilsExtension::CreateInstance() { diff --git a/src/utils/utils_instance.h b/src/utils/utils_instance.h index c39a91d4..df065d52 100644 --- a/src/utils/utils_instance.h +++ b/src/utils/utils_instance.h @@ -7,6 +7,7 @@ #define UTILS_UTILS_INSTANCE_H_ #include "common/extension.h" +#include "common/task-queue.h" namespace extension { namespace utils { @@ -15,6 +16,9 @@ class UtilsInstance : public common::ParsedInstance { public: UtilsInstance(); virtual ~UtilsInstance() { + // At this point, frame of the page is destroyed or reloaded, so all the jobs have to be + // removed. + common::TaskQueue::GetInstance().DeleteJobs(); } private: diff --git a/src/widgetservice/widgetservice_api.js b/src/widgetservice/widgetservice_api.js index 74b1f82e..00ba1d79 100644 --- a/src/widgetservice/widgetservice_api.js +++ b/src/widgetservice/widgetservice_api.js @@ -362,10 +362,6 @@ ListenerManager.prototype.addListener = function(callback) { }; ListenerManager.prototype.removeListener = function(watchId) { - if (this.listeners[watchId] === null || this.listeners[watchId] === undefined) { - throw new WebAPIException(0, 'Watch id not found.', 'NotFoundError'); - } - if (this.listeners.hasOwnProperty(watchId)) { delete this.listeners[watchId]; } diff --git a/src/widgetservice/widgetservice_instance.cc b/src/widgetservice/widgetservice_instance.cc index fd8f0baa..a6b6df2b 100644 --- a/src/widgetservice/widgetservice_instance.cc +++ b/src/widgetservice/widgetservice_instance.cc @@ -24,6 +24,7 @@ #include <widget_service.h> #include "common/scope_exit.h" +#include "common/task-queue.h" #include "common/tools.h" #include "widgetservice/widgetservice_utils.h" @@ -257,7 +258,7 @@ TizenResult WidgetServiceInstance::GetWidgets(const picojson::object& args, this->Post(token, result); }; - std::thread(get_widgets, token).detach(); + common::TaskQueue::GetInstance().Async(get_widgets, token); return TizenSuccess(); } @@ -361,7 +362,7 @@ TizenResult WidgetServiceInstance::GetInstances(picojson::object const& args, this->Post(token, result); }; - std::thread(get_instances, token).detach(); + common::TaskQueue::GetInstance().Async(get_instances, token); return TizenSuccess(); } @@ -459,7 +460,7 @@ TizenResult WidgetServiceInstance::GetVariants(picojson::object const& args, } }; - std::thread(get_variants, token).detach(); + common::TaskQueue::GetInstance().Async(get_variants, token); return TizenSuccess(); } @@ -586,7 +587,8 @@ TizenResult WidgetServiceInstance::SendContent(picojson::object const& args) { LogAndReturnTizenError(common::AbortError(ret), ("bundle_add() failed")); } - int ret_widget = widget_service_trigger_update(widget_id.c_str(), instance_id.c_str(), data, force); + int ret_widget = + widget_service_trigger_update(widget_id.c_str(), instance_id.c_str(), data, force); if (WIDGET_ERROR_NONE != ret_widget) { LogAndReturnTizenError(WidgetServiceUtils::ConvertErrorCode(ret_widget), ("widget_service_trigger_update() failed")); @@ -620,8 +622,8 @@ TizenResult WidgetServiceInstance::GetContent(picojson::object const& args, bundle_free(bundle_data); }; - int ret_widget = widget_service_get_content_of_widget_instance(widget_id.c_str(), instance_id.c_str(), - &bundle_data); + int ret_widget = widget_service_get_content_of_widget_instance( + widget_id.c_str(), instance_id.c_str(), &bundle_data); if (WIDGET_ERROR_NONE != ret_widget) { LoggerE("widget_service_get_content_of_widget_instance() failed"); this->Post(token, WidgetServiceUtils::ConvertErrorCode(ret_widget)); @@ -642,7 +644,7 @@ TizenResult WidgetServiceInstance::GetContent(picojson::object const& args, this->Post(token, TizenSuccess{response}); }; - std::thread(get_content, token).detach(); + common::TaskQueue::GetInstance().Async(get_content, token); return TizenSuccess(); } |