[Iotcon] implemented addPresenceEventListener and removePresenceEventListener.
authorLukasz Bardeli <l.bardeli@samsung.com>
Tue, 16 Feb 2016 08:52:30 +0000 (09:52 +0100)
committerLukasz Bardeli <l.bardeli@samsung.com>
Tue, 16 Feb 2016 08:52:30 +0000 (09:52 +0100)
Change-Id: Ibe579a8c20674a275685a40e5e25b8d943b47a12
Signed-off-by: Lukasz Bardeli <l.bardeli@samsung.com>
src/iotcon/iotcon.gyp
src/iotcon/iotcon_api.js
src/iotcon/iotcon_client_manager.cc [new file with mode: 0644]
src/iotcon/iotcon_client_manager.h [new file with mode: 0644]
src/iotcon/iotcon_instance.cc
src/iotcon/iotcon_instance.h
src/iotcon/iotcon_utils.cc
src/iotcon/iotcon_utils.h

index b5fd720f46a62f75db5a2d58ad5d2159cd7c6513..c45581fa8b087ba1e40cd6f561d79ce0e5e04b6c 100644 (file)
@@ -17,6 +17,8 @@
         'iotcon_instance.h',
         'iotcon_server_manager.cc',
         'iotcon_server_manager.h',
+        'iotcon_client_manager.cc',
+        'iotcon_client_manager.h',
         'iotcon_utils.cc',
         'iotcon_utils.h',
       ],
index 1275ffb7575668893071594d02a1aacc64f856dc..cee73089cbdd7335a8e0295ebf81afb3d37f6966 100644 (file)
@@ -899,7 +899,7 @@ Client.prototype.findResource = function() {
 };
 
 var presenceEventListener = createListener('PresenceEventListener', function(response) {
-  return new PresenceResponse(native.getResultObject(response));
+  return new PresenceResponse(response.data);
 });
 
 Client.prototype.addPresenceEventListener = function() {
@@ -909,7 +909,8 @@ Client.prototype.addPresenceEventListener = function() {
     nullable: true
   }, {
     name: 'resourceType',
-    type: types.STRING
+    type: types.STRING,
+    nullable: true
   }, {
     name: 'connectivityType',
     type: types.ENUM,
diff --git a/src/iotcon/iotcon_client_manager.cc b/src/iotcon/iotcon_client_manager.cc
new file mode 100644 (file)
index 0000000..fb840c3
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015 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 "iotcon/iotcon_client_manager.h"
+
+#include "common/logger.h"
+
+namespace extension {
+namespace iotcon {
+
+using common::TizenResult;
+using common::TizenSuccess;
+
+namespace {
+
+long long GetPresenceNextId() {
+  static long long id = 0;
+  return ++id;
+}
+
+}  // namespace
+
+IotconClientManager& IotconClientManager::GetInstance() {
+  static IotconClientManager instance;
+  return instance;
+}
+
+TizenResult IotconClientManager::RestoreHandles() {
+  ScopeLogger();
+
+  for (const auto& it : presence_map_) {
+    LoggerD("Restoring handle for presence event with id: %lld", it.first);
+
+    PresenceEventPtr presence = it.second;
+    char* host = nullptr;
+    char* resource_type = nullptr;
+    iotcon_connectivity_type_e con_type = IOTCON_CONNECTIVITY_IPV4;
+
+    auto res = IotconUtils::ExtractFromPresenceEvent(presence, &host,
+                                                   &con_type, &resource_type);
+    if (!res){
+      return res;
+    }
+
+    const iotcon_presence_h old_handle = presence->handle;
+    int ret = iotcon_add_presence_cb(host, con_type, resource_type,
+                                     PresenceHandler, this,&(presence->handle));
+    if (IOTCON_ERROR_NONE != ret || nullptr == presence->handle) {
+      LogAndReturnTizenError(IotconUtils::ConvertIotconError(ret),
+                             ("iotcon_add_presence_cb() failed: %d (%s)",
+                                 ret, get_error_message(ret)));
+    }
+    if (old_handle) {
+      LoggerD("destroy handle which is currently invalid: %p", old_handle);
+      iotcon_remove_presence_cb(old_handle);
+    }
+    LoggerD("new handle: %p", (presence->handle));
+  }
+
+  return TizenSuccess();
+}
+
+void IotconClientManager::PresenceHandler(iotcon_presence_h presence,
+                                         iotcon_error_e err,
+                                         iotcon_presence_response_h response,
+                                         void *user_data) {
+  ScopeLogger();
+
+  if(IOTCON_ERROR_NONE != err) {
+    LoggerE("Error in presence event callback!");
+    return;
+  }
+  auto that = static_cast<IotconClientManager*>(user_data);
+
+  for (const auto& p : that->presence_map_) {
+    if (p.second->presence_listener && p.second->handle == presence) {
+      picojson::value value{picojson::object{}};
+      auto& obj = value.get<picojson::object>();
+      auto ret = IotconUtils::PresenceResponseToJson(response,
+                                                     &obj);
+      if (!ret) {
+        LoggerE("PresenceResponseToJson() failed");
+        return;
+      }
+      // call listener
+      p.second->presence_listener(TizenSuccess(), value);
+    }
+  }
+};
+
+common::TizenResult IotconClientManager::AddPresenceEventListener(
+    const char* host, const iotcon_connectivity_type_e con_type_e,
+    const char* resource_type, PresenceEventPtr presence) {
+  ScopeLogger();
+
+  auto result = IotconUtils::ConvertIotconError(iotcon_add_presence_cb(
+      host, con_type_e, resource_type, PresenceHandler, this,
+      &(presence->handle)));
+
+  // storing PresenceEvent into map
+  presence->id = GetPresenceNextId();
+  presence_map_.insert(std::make_pair(presence->id, presence));
+
+  return TizenSuccess();
+}
+
+common::TizenResult IotconClientManager::RemovePresenceEventListener(long long id) {
+  auto it = presence_map_.find(id);
+  if (it == presence_map_.end()) {
+    return LogAndCreateTizenError(AbortError, "Presence callback with specified ID does not exist");
+  }
+
+  auto result = IotconUtils::ConvertIotconError(iotcon_remove_presence_cb(it->second->handle));
+
+  if (!result) {
+    LogAndReturnTizenError(result, ("iotcon_remove_presence_cb failed"));
+  }
+
+  presence_map_.erase(id);
+
+  return TizenSuccess();
+}
+
+}  // namespace iotcon
+}  // namespace extension
diff --git a/src/iotcon/iotcon_client_manager.h b/src/iotcon/iotcon_client_manager.h
new file mode 100644 (file)
index 0000000..7473a43
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 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_IOTCON_CLIENT_MANAGER_H__
+#define WEBAPI_PLUGINS_IOTCON_CLIENT_MANAGER_H__
+
+#include <memory>
+#include <map>
+#include <string>
+#include <iotcon.h>
+
+#include "iotcon/iotcon_utils.h"
+
+#include "common/tizen_result.h"
+
+namespace extension {
+namespace iotcon {
+
+class IotconClientManager {
+ public:
+  static IotconClientManager& GetInstance();
+
+  common::TizenResult RestoreHandles();
+  common::TizenResult AddPresenceEventListener(const char* host,
+                                               const iotcon_connectivity_type_e con_type_e,
+                                               const char* resource_type,
+                                               PresenceEventPtr presence);
+  common::TizenResult RemovePresenceEventListener(long long id);
+
+ private:
+  IotconClientManager() = default;
+  IotconClientManager(const IotconClientManager&) = delete;
+  IotconClientManager(IotconClientManager&&) = delete;
+  IotconClientManager& operator=(const IotconClientManager&) = delete;
+  IotconClientManager& operator=(IotconClientManager&&) = delete;
+
+  static void PresenceHandler(iotcon_presence_h  resource,
+                             iotcon_error_e err,
+                             iotcon_presence_response_h response,
+                             void *user_data);
+
+  PresenceMap presence_map_;
+};
+
+} // namespace iotcon
+} // namespace extension
+
+#endif // WEBAPI_PLUGINS_IOTCON_CLIENT_MANAGER_H__
index 6f2e9d153c3fa1a3dfc7ff81b44f20899bf81157..ae913dffab3ed82a9d9ab5c8c9d3b37510beac16 100644 (file)
 #include "iotcon/iotcon_instance.h"
 
 #include <thread>
+#include <iotcon-internal.h>
 
 #include "common/logger.h"
 #include "common/scope_exit.h"
 #include "common/tools.h"
 
 #include "iotcon/iotcon_utils.h"
+#include "iotcon/iotcon_server_manager.h"
+#include "iotcon/iotcon_client_manager.h"
 
 namespace extension {
 namespace iotcon {
@@ -33,8 +36,6 @@ typedef struct {
   common::PostCallback fun;
 } CallbackData;
 
-const std::string kTimeout = "timeout";
-
 #define CHECK_EXIST(args, name) \
   if (args.end() == args.find(name)) { \
     return common::TypeMismatchError(std::string(name) + " is required argument"); \
@@ -57,6 +58,7 @@ const picojson::value& GetArg(const picojson::object& args, const std::string& n
 
 const common::ListenerToken kResourceRequestListenerToken{"ResourceRequestListener"};
 const common::ListenerToken kFindResourceListenerToken{"FindResourceListener"};
+const common::ListenerToken kPresenceEventListenerToken{"PresenceEventListener"};
 
 const std::string kObserverIds = "observerIds";
 const std::string kQos = "qos";
@@ -64,6 +66,7 @@ const std::string kChildId = "childId";
 const std::string kType = "type";
 const std::string kInterface = "iface";
 
+const std::string kTimeout = "timeout";
 }  // namespace
 
 IotconInstance::IotconInstance() {
@@ -126,6 +129,14 @@ IotconInstance::IotconInstance() {
     } else {
       LoggerD("Iotcon connection changed callback is registered");
     }
+
+    ret = iotcon_start_presence(0);
+    if (IOTCON_ERROR_NONE != ret) {
+      LoggerE("Could not start presence: %s",
+                  get_error_message(ret));
+    } else {
+      LoggerD("Iotcon iotcon_start_presence");
+    }
   }
 }
 
@@ -146,12 +157,18 @@ void IotconInstance::ConnectionChangedCallback(bool is_connected, void* user_dat
     if (!ret) {
       LoggerD("Connection recovered, but restoring handles failed");
     }
+
+    ret = IotconClientManager::GetInstance().RestoreHandles();
+    if (!ret) {
+      LoggerD("Connection recovered, but restoring presence failed");
+    }
   }
 }
 
 IotconInstance::~IotconInstance() {
   ScopeLogger();
 
+  iotcon_stop_presence();
   iotcon_remove_connection_changed_cb(ConnectionChangedCallback, this);
   iotcon_disconnect();
 }
@@ -553,15 +570,65 @@ common::TizenResult IotconInstance::ClientFindResource(const picojson::object& a
 
 common::TizenResult IotconInstance::ClientAddPresenceEventListener(const picojson::object& args) {
   ScopeLogger();
-  return common::UnknownError("Not implemented");
+
+  CHECK_EXIST(args, kHostAddress);
+  CHECK_EXIST(args, kResourceType);
+  CHECK_EXIST(args, kConnectivityType);
+
+  char* host = nullptr;
+  if (args.find(kHostAddress)->second.is<std::string>()) {
+    host = const_cast<char*>(args.find(kHostAddress)->second.get<std::string>().c_str());
+  }
+
+  char* resource_type = nullptr;
+  if (args.find(kResourceType)->second.is<std::string>()) {
+    resource_type = const_cast<char*>(args.find(kResourceType)->second.get<std::string>().c_str());
+  }
+
+  auto& con_type = GetArg(args, kConnectivityType);
+  if (!con_type.is<std::string>()) {
+    return common::TypeMismatchError("connectivityType needs to be a string");
+  }
+  iotcon_connectivity_type_e con_type_e = IotconUtils::ToConnectivityType(
+      con_type.get<std::string>());
+
+  PresenceEventPtr presence{new PresenceEvent()};
+  auto ret = IotconClientManager::GetInstance().AddPresenceEventListener(
+      host, con_type_e, resource_type, presence);
+  if (!ret) {
+    return ret;
+  }
+
+  long long id = presence->id;
+
+  presence->presence_listener = [this, id](const common::TizenResult&, const picojson::value& v) {
+    picojson::value response{picojson::object{}};
+    auto& obj = response.get<picojson::object>();
+
+    obj.insert(std::make_pair(kId, picojson::value{static_cast<double>(id)}));
+    obj.insert(std::make_pair("data", v));
+
+    Post(kPresenceEventListenerToken, common::TizenSuccess{response});
+  };
+
+  return common::TizenSuccess(picojson::value{static_cast<double>(id)});
 }
 
 common::TizenResult IotconInstance::ClientRemovePresenceEventListener(const picojson::object& args) {
   ScopeLogger();
-  return common::UnknownError("Not implemented");
+
+  CHECK_EXIST(args, kId);
+
+  auto ret = IotconClientManager::GetInstance().RemovePresenceEventListener(GetId(args));
+
+  if (!ret) {
+    return ret;
+  }
+
+  return common::TizenSuccess();
 }
 
-void IotconPDeviceInfoCb(iotcon_device_info_h device_info,
+void IotconDeviceInfoCb(iotcon_device_info_h device_info,
                           iotcon_error_e result, void *user_data) {
   ScopeLogger();
 
@@ -572,7 +639,7 @@ void IotconPDeviceInfoCb(iotcon_device_info_h device_info,
   if (IOTCON_ERROR_NONE != result) {
     ret = IotconUtils::ConvertIotconError(result);
   } else {
-    auto ret = IotconUtils::DeviceInfoToJson(device_info,&v.get<picojson::object>());
+    ret = IotconUtils::DeviceInfoToJson(device_info,&v.get<picojson::object>());
   }
 
   data->fun(ret, v);
@@ -593,7 +660,7 @@ common::TizenResult IotconInstance::ClientGetDeviceInfo(const picojson::object&
   CallbackData* data = new CallbackData{SimplePost(token)};
 
   auto result = IotconUtils::ConvertIotconError(
-       iotcon_get_device_info(host.c_str(), con_type_e, IotconPDeviceInfoCb,
+       iotcon_get_device_info(host.c_str(), con_type_e, IotconDeviceInfoCb,
                                 data));
 
   if (!result) {
index 0793370a6d2a2547dda9baac9f1150859a8c58e7..af3c184d7aa7a81d040a078dd9c19a43b69e19eb 100644 (file)
@@ -19,7 +19,7 @@
 
 #include "common/tizen_instance.h"
 
-#include "iotcon/iotcon_server_manager.h"
+#include <iotcon.h>
 
 namespace extension {
 namespace iotcon {
index 2d6c192f2787c41a9a2ea27134b6245baa622792..88c8a71169374047275a3ca1ead3d23986d27d34 100644 (file)
@@ -66,6 +66,16 @@ namespace {
   X(IOTCON_QOS_HIGH, "HIGH") \
   XD(IOTCON_QOS_LOW, "unknown")
 
+#define IOTCON_PRESENCE_RESULT_E \
+  X(IOTCON_PRESENCE_OK, "OK") \
+  X(IOTCON_PRESENCE_STOPPED, "STOPPED") \
+  XD(IOTCON_PRESENCE_TIMEOUT, "TIMEOUT")
+
+#define IOTCON_PRESENCE_TRIGGER_E \
+  X(IOTCON_PRESENCE_RESOURCE_CREATED, "CREATED") \
+  X(IOTCON_PRESENCE_RESOURCE_UPDATED, "UPDATED") \
+  XD(IOTCON_PRESENCE_RESOURCE_DESTROYED, "DESTROYED")
+
 }  // namespace
 
 const std::string kIsDiscoverable = "isDiscoverable";
@@ -118,6 +128,9 @@ const std::string kSpecVersion = "specVersion";
 const std::string kOicDeviceId = "oicDeviceId";
 const std::string kDataModelVersion = "dataModelVersion";
 
+const std::string kResultType = "resultType";
+const std::string kTriggerType = "triggerType";
+
 using common::TizenResult;
 using common::TizenSuccess;
 
@@ -1223,6 +1236,108 @@ common::TizenResult IotconUtils::StateListFromJson(const picojson::array& l,
   return TizenSuccess();
 }
 
+common::TizenResult IotconUtils::PresenceResponseToJson(
+    iotcon_presence_response_h presence, picojson::object* out) {
+  ScopeLogger();
+
+  {
+    // hostAddress
+    char* host = nullptr;
+    auto result = ConvertIotconError(iotcon_presence_response_get_host_address(presence,
+                                                                       &host));
+    if (!result || !host) {
+      LogAndReturnTizenError(result, ("iotcon_presence_response_get_host_address() failed"));
+    }
+    out->insert(std::make_pair(kHostAddress, picojson::value{std::string(host)}));
+  }
+
+  {
+    // connectivityType
+    iotcon_connectivity_type_e con_type = IOTCON_CONNECTIVITY_IPV4;
+    auto result = ConvertIotconError(iotcon_presence_response_get_connectivity_type(presence,
+                                                                       &con_type));
+    if (!result) {
+      LogAndReturnTizenError(result, ("iotcon_presence_response_get_connectivity_type() failed"));
+    }
+    out->insert(std::make_pair(kConnectivityType, picojson::value{
+      FromConnectivityType(con_type)}));
+  }
+
+  {
+    // resourceType
+    char* resource_type = nullptr;
+    auto result = ConvertIotconError(iotcon_presence_response_get_resource_type(presence,
+                                                                       &resource_type));
+    if (!result || !resource_type) {
+      LoggerE("iotcon_presence_response_get_resource_type() failed");
+      out->insert(std::make_pair(kResourceType, picojson::value()));
+    } else {
+      out->insert(std::make_pair(kResourceType, picojson::value{std::string(resource_type)}));
+    }
+  }
+
+  // resultType
+  iotcon_presence_result_e result_type = IOTCON_PRESENCE_OK;
+  {
+    auto result = ConvertIotconError(iotcon_presence_response_get_result(presence,
+                                                                       &result_type));
+    if (!result) {
+      LogAndReturnTizenError(result, ("iotcon_presence_response_get_result() failed"));
+    }
+
+    out->insert(std::make_pair(kResultType, picojson::value{
+      FromPresenceResponseResultType(result_type)}));
+  }
+
+  {
+    // triggerType
+    iotcon_presence_trigger_e trigger_type = IOTCON_PRESENCE_RESOURCE_CREATED;
+    if (IOTCON_PRESENCE_OK == result_type) {
+      auto result = ConvertIotconError(iotcon_presence_response_get_trigger(presence,
+                                                                         &trigger_type));
+      if (!result) {
+        LoggerE("iotcon_presence_response_get_trigger() failed");
+        out->insert(std::make_pair(kTriggerType, picojson::value()));
+      } else {
+        out->insert(std::make_pair(kTriggerType, picojson::value{FromPresenceTriggerType(
+            trigger_type)}));
+      }
+    } else {
+      out->insert(std::make_pair(kTriggerType, picojson::value()));
+    }
+
+  }
+
+  return TizenSuccess();
+}
+
+common::TizenResult IotconUtils::ExtractFromPresenceEvent(const PresenceEventPtr& pointer,
+                                                          char** host,
+                                                          iotcon_connectivity_type_e* con_type,
+                                                          char** resource_type) {
+  ScopeLogger();
+
+  auto result = ConvertIotconError(iotcon_presence_get_host_address(pointer->handle,
+                                                                    host));
+  if (!result) {
+   LogAndReturnTizenError(result, ("Gathering presence host address failed"));
+  }
+
+  result = ConvertIotconError(iotcon_presence_get_connectivity_type(pointer->handle,
+                                                                    con_type));
+  if (!result) {
+   LogAndReturnTizenError(result, ("Gathering presence connectivity type failed"));
+  }
+
+  result = ConvertIotconError(iotcon_presence_get_resource_type(pointer->handle,
+                                                                resource_type));
+  if (!result) {
+   LogAndReturnTizenError(result, ("Gathering presence resource type failed"));
+  }
+
+  return TizenSuccess();
+}
+
 common::TizenResult IotconUtils::PlatformInfoGetProperty(iotcon_platform_info_h platform,
                                                          iotcon_platform_info_e property_e,
                                                          const std::string& name,
@@ -1484,6 +1599,22 @@ std::string IotconUtils::FromInterface(iotcon_interface_e e) {
   }
 }
 
+std::string IotconUtils::FromPresenceResponseResultType(iotcon_presence_result_e e) {
+  ScopeLogger();
+
+  switch (e) {
+    IOTCON_PRESENCE_RESULT_E
+  }
+}
+
+std::string IotconUtils::FromPresenceTriggerType(iotcon_presence_trigger_e e) {
+  ScopeLogger();
+
+  switch (e) {
+    IOTCON_PRESENCE_TRIGGER_E
+  }
+}
+
 #undef X
 #undef XD
 
index fdbfc31338ee9519e8fea9e42c295e6de799f9c9..b8fb9345d4d1b76c46cba53f6b52d93f45344968 100644 (file)
@@ -40,6 +40,7 @@ extern const std::string kIsSlow;
 extern const std::string kIsSecure;
 extern const std::string kIsExplicitDiscoverable;
 extern const std::string kResourceTypes;
+extern const std::string kResourceType;
 extern const std::string kResourceInterfaces;
 extern const std::string kResourceChildren;
 extern const std::string kUriPath;
@@ -51,8 +52,12 @@ extern const std::string kConnectivityType;
 extern const std::string kResourceType;
 
 class ResourceInfo;
+class PresenceEvent;
+
 typedef std::shared_ptr<ResourceInfo> ResourceInfoPtr;
 typedef std::map<long long, ResourceInfoPtr> ResourceInfoMap;
+typedef std::shared_ptr<PresenceEvent> PresenceEventPtr;
+typedef std::map<long long, PresenceEventPtr> PresenceMap;
 
 struct ResourceInfo {
   long long id;
@@ -94,6 +99,12 @@ struct RemoteResourceInfo {
   }
 };
 
+struct PresenceEvent {
+  long long id;
+  iotcon_presence_h handle;
+  common::PostCallback presence_listener;
+};
+
 class IotconUtils {
  public:
   static void PropertiesToJson(int properties, picojson::object* res);
@@ -123,6 +134,12 @@ class IotconUtils {
                                            picojson::array* out);
   static common::TizenResult QueryToJson(iotcon_query_h query,
                                          picojson::object* out);
+  static common::TizenResult PresenceResponseToJson(iotcon_presence_response_h presence,
+                                                picojson::object* out);
+  static common::TizenResult ExtractFromPresenceEvent(const PresenceEventPtr& pointer,
+                                                 char** host,
+                                                 iotcon_connectivity_type_e* con_type,
+                                                 char** resource_type);
   static common::TizenResult PlatformInfoToJson(iotcon_platform_info_h platform,
                                                 picojson::object* out);
   static common::TizenResult PlatformInfoGetProperty(iotcon_platform_info_h platform,
@@ -150,6 +167,8 @@ class IotconUtils {
   static std::string FromRequestType(iotcon_request_type_e e);
   static std::string FromObserveType(iotcon_observe_type_e e);
   static std::string FromInterface(iotcon_interface_e e);
+  static std::string FromPresenceResponseResultType(iotcon_presence_result_e e);
+  static std::string FromPresenceTriggerType(iotcon_presence_trigger_e e);
 
   static iotcon_interface_e ToInterface(const std::string& e);
   static iotcon_connectivity_type_e ToConnectivityType(const std::string& e);