Add internal APIs for rpc-port-parcel 51/317751/8
authorpjh9216 <jh9216.park@samsung.com>
Tue, 7 Jan 2025 08:10:06 +0000 (17:10 +0900)
committerpjh9216 <jh9216.park@samsung.com>
Thu, 9 Jan 2025 05:36:01 +0000 (14:36 +0900)
- APIs
  int rpc_port_parcel_read_fd(rpc_port_parcel_h h, int* fd)
  int rpc_port_parcel_write_fd(rpc_port_parcel_h h, int fd)

Change-Id: I637186de5cc38cc051e37022c6896032802f2712
Signed-off-by: pjh9216 <jh9216.park@samsung.com>
CMakeLists.txt
include/rpc-port-parcel-internal.h
src/rpc-port/ac-internal.cc
src/rpc-port/parcel-internal.cc
src/rpc-port/parcel-internal.hh
src/rpc-port/rpc-port-parcel.cc
src/rpc-port/stub-internal.cc
test/unit_tests/rpc_port_test.cc

index 3cdceb0b8bc8e7d94dbe161c76e806cfce54fa12..ac12c21e8ef385c5eab14cabd55c383c5ba821f4 100644 (file)
@@ -18,7 +18,7 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
 SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
 SET(CMAKE_C_FLAGS_RELEASE "-O2")
 
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_C_FLAGS} -std=c++17")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_C_FLAGS} -std=c++23")
 SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
 SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
 
index 916867e65eca51fa4d7b95ca5d1d7eaa97faeaaa..45441e69feb247c533a96560842d0e510335aebc 100644 (file)
@@ -47,6 +47,8 @@ int rpc_port_parcel_get_data_size(rpc_port_parcel_h h, unsigned int* size);
 int rpc_port_parcel_pin(rpc_port_parcel_h h);
 int rpc_port_parcel_get_reader(rpc_port_parcel_h h, unsigned int* reader_pos);
 int rpc_port_parcel_set_reader(rpc_port_parcel_h h, unsigned int reader_pos);
+int rpc_port_parcel_read_fd(rpc_port_parcel_h h, int* fd);
+int rpc_port_parcel_write_fd(rpc_port_parcel_h h, int fd);
 
 #ifdef __cplusplus
 }
index 23b4b998ecf5d4f1158b8648380662da95136a91..73dc2cdfdfbd0ba00c78e80c6e1df65e03cfeead 100644 (file)
@@ -27,8 +27,8 @@
 
 #include "rpc-port/aul-internal.hh"
 #include "rpc-port/cynara_thread.hh"
-#include "rpc-port/log-private.hh"
 #include "rpc-port/glib-internal.hh"
+#include "rpc-port/log-private.hh"
 
 namespace rpc_port {
 namespace internal {
@@ -73,10 +73,12 @@ class Cynara {
     int ret = cynara_creds_socket_get_user(fd, USER_METHOD_DEFAULT, &user);
     if (ret != CYNARA_API_SUCCESS) {
       // LCOV_EXCL_START
-      char buf[128] = { 0, };
+      char buf[128] = {
+          0,
+      };
       cynara_strerror(ret, buf, sizeof(buf));
-      _E("cynara_creds_socket_get_user() is failed. fd(%d), error(%d:%s)",
-         fd, ret, buf);
+      _E("cynara_creds_socket_get_user() is failed. fd(%d), error(%d:%s)", fd,
+         ret, buf);
       return nullptr;
       // LCOV_EXCL_STOP
     }
@@ -86,10 +88,12 @@ class Cynara {
     ret = cynara_creds_socket_get_client(fd, CLIENT_METHOD_DEFAULT, &client);
     if (ret != CYNARA_API_SUCCESS) {
       // LCOV_EXCL_START
-      char buf[128] = { 0, };
+      char buf[128] = {
+          0,
+      };
       cynara_strerror(ret, buf, sizeof(buf));
-      _E("cynara_creds_socket_get_client() is failed. fd(%d), error(%d:%s)",
-          fd, ret, buf);
+      _E("cynara_creds_socket_get_client() is failed. fd(%d), error(%d:%s)", fd,
+         ret, buf);
       return nullptr;
       // LCOV_EXCL_STOP
     }
@@ -101,12 +105,12 @@ class Cynara {
   int Check(const std::shared_ptr<Creds>& creds,
             const std::string& privilege) const {
     std::lock_guard<std::recursive_mutex> lock(mutex_);
-    _W("cynara_check() ++ privilege(%s), user(%s)",
-        privilege.c_str(), creds->GetUser().c_str());
+    _W("cynara_check() ++ privilege(%s), user(%s)", privilege.c_str(),
+       creds->GetUser().c_str());
     int ret = cynara_check(handle_.get(), creds->GetClient().c_str(), "",
                            creds->GetUser().c_str(), privilege.c_str());
-    _W("cynara_check() -- privilege(%s), user(%s)",
-        privilege.c_str(), creds->GetUser().c_str());
+    _W("cynara_check() -- privilege(%s), user(%s)", privilege.c_str(),
+       creds->GetUser().c_str());
     if (ret != CYNARA_API_ACCESS_ALLOWED) {
       _E("cynara_check() is not allowed. privilege(%s), error(%d)",
          privilege.c_str(), ret);
@@ -129,17 +133,14 @@ void AccessController::AddPrivilege(std::string privilege) {
   privileges_.push_back(std::move(privilege));
 }
 
-void AccessController::SetTrusted(const bool trusted) {
-  trusted_ = trusted;
-}
+void AccessController::SetTrusted(const bool trusted) { trusted_ = trusted; }
 
 int AccessController::CheckPrivilege(int fd) {
   auto creds = cynara_inst.FetchCredsFromSocket(fd);
   if (creds == nullptr) return -1;
 
   for (const auto& privilege : privileges_) {
-    if (cynara_inst.Check(creds, privilege) != 0)
-      return -1;
+    if (cynara_inst.Check(creds, privilege) != 0) return -1;
   }
 
   return 0;
@@ -147,16 +148,14 @@ int AccessController::CheckPrivilege(int fd) {
 
 // LCOV_EXCL_START
 int AccessController::CheckTrusted(const std::string& sender_appid) {
-  if (getuid() < kRegularUidMin)
-    return 0;
+  if (getuid() < kRegularUidMin) return 0;
 
-  if (appid_.empty())
-    appid_ = Aul::GetAppId(getpid());
+  if (appid_.empty()) appid_ = Aul::GetAppId(getpid());
 
   _D("CheckCertificate : %s :: %s", appid_.c_str(), sender_appid.c_str());
   pkgmgrinfo_cert_compare_result_type_e res;
-  int ret = pkgmgrinfo_pkginfo_compare_usr_app_cert_info(appid_.c_str(),
-      sender_appid.c_str(), getuid(), &res);
+  int ret = pkgmgrinfo_pkginfo_compare_usr_app_cert_info(
+      appid_.c_str(), sender_appid.c_str(), getuid(), &res);
   if (ret < 0) {
     _E("CheckCertificate() Failed");
     return -1;
@@ -174,12 +173,10 @@ int AccessController::Check(int fd, const std::string& sender_appid) {
   int ret = 0;
   if (!privileges_.empty()) {
     ret = CheckPrivilege(fd);
-    if (ret != 0)
-      return ret;
+    if (ret != 0) return ret;
   }
 
-  if (trusted_)
-    ret = CheckTrusted(sender_appid);
+  if (trusted_) ret = CheckTrusted(sender_appid);
 
   return ret;
 }
@@ -188,17 +185,19 @@ void AccessController::CheckAsync(int fd, std::string sender_appid,
                                   CompleteCallback callback) {
   auto* tmp_handle = new std::shared_ptr<AccessController>(shared_from_this());
   GMainContext* context = g_main_context_ref_thread_default();
-  auto job = std::make_shared<Job>([=] {
+  auto job = std::make_shared<Job>([fd, appid = std::move(sender_appid),
+                                    cb = std::move(callback), tmp_handle,
+                                    context, this] {
     std::lock_guard<std::recursive_mutex> job_lock(GetMutex());
     if ((*tmp_handle).use_count() == 1) {
       delete tmp_handle;           // LCOV_EXCL_LINE
       return Job::Type::Continue;  // LCOV_EXCL_LINE
     }
 
-    int res = Check(fd, sender_appid);
+    int res = Check(fd, appid);
     auto* cbdata = new std::tuple<CompleteCallback, int,
                                   std::shared_ptr<AccessController>*>(
-        std::move(callback), res, tmp_handle);
+        std::move(cb), res, tmp_handle);
 
     auto* source = GLib::IdleAddFull(
         context, G_PRIORITY_DEFAULT,
@@ -215,8 +214,7 @@ void AccessController::CheckAsync(int fd, std::string sender_appid,
           return G_SOURCE_REMOVE;
         },
         cbdata);
-    if (source == nullptr)
-      _E("Failed to add idle source");
+    if (source == nullptr) _E("Failed to add idle source");
 
     g_main_context_unref(context);
     return Job::Type::Continue;
index 55eba889434930e72f7d234146920ece96a246b5..7f8d7b1ecb1b9a3359b168bb467ece79d964634b 100644 (file)
 
 #include "parcel-internal.hh"
 
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <memory>
 #include <utility>
 
@@ -24,6 +29,8 @@
 namespace rpc_port {
 namespace internal {
 
+constexpr const int MAX_NR_OF_DESCRIPTORS = 32;
+
 Parcel::Parcel(bool without_header)
     : header_(without_header ? nullptr : new ParcelHeader()) {
   handle_.reset(new tizen_base::Parcel());
@@ -81,5 +88,160 @@ void Parcel::SetRawParcel(tizen_base::Parcel* raw_parcel) {
 
 tizen_base::Parcel* Parcel::GetRawParcel() const { return raw_parcel_.get(); }
 
+int Parcel::GetParcelableFd(uint32_t idx) const {
+  if (idx >= parcelable_fds_.size()) {
+    return -1;
+  }
+
+  return parcelable_fds_[idx];
+}
+
+void Parcel::PushBackParcelableFd(int fd) { parcelable_fds_.push_back(fd); }
+
+uint32_t Parcel::GetCurParcelableFdIdx() const {
+  return parcelable_fds_.size();
+}
+
+bool Parcel::DoWriteParcelableFds(int socket_fd, int start_idx, int cnt) {
+  union {
+    /*
+     * ancillary data buffer, wrapped in a union in order to ensure
+     * it is suitably aligned
+     */
+    char buf[CMSG_SPACE(sizeof(int) * MAX_NR_OF_DESCRIPTORS)];
+    struct cmsghdr align;
+  } u;
+  char iobuf[1];
+  struct iovec io = {.iov_base = iobuf, .iov_len = sizeof(iobuf)};
+  struct msghdr msg = {
+      0,
+  };
+  msg.msg_iov = &io;
+  msg.msg_iovlen = 1;
+  msg.msg_control = u.buf;
+  msg.msg_controllen = sizeof(u.buf);
+  int ret;
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  size_t msg_len = cmsg->cmsg_len = CMSG_LEN(sizeof(int) * cnt);
+
+  int* fdptr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+  memcpy(fdptr, &parcelable_fds_[start_idx], sizeof(int) * cnt);
+  cmsg = CMSG_NXTHDR(&msg, cmsg);
+  msg.msg_controllen = msg_len;
+
+retry:
+  ret = sendmsg(socket_fd, &msg, 0);
+  if (ret < 0) {
+    if (errno == EINTR) {
+      usleep(100 * 1000);
+      goto retry;
+    }
+
+    _E("sendmsg() failed (errno:%d) (fd:%d)", errno, socket_fd);
+    return false;
+  }
+
+  return true;
+}
+
+void Parcel::WriteParcelableFds(int socket_fd) {
+  int fd_cnt = parcelable_fds_.size();
+  if (fd_cnt == 0) return;
+
+  for (int i = 0; i < fd_cnt;) {
+    int cnt = 0;
+    if (i + MAX_NR_OF_DESCRIPTORS <= fd_cnt)
+      cnt = MAX_NR_OF_DESCRIPTORS;
+    else
+      cnt = fd_cnt - i;
+    if (!DoWriteParcelableFds(socket_fd, i, cnt)) {
+      _E("DoWriteParcelableFds() failed");
+      return;
+    }
+    i += cnt;
+  }
+}
+
+bool Parcel::DoReadParcelableFds(int socket_fd, int cnt) {
+  union {
+    /*
+     * ancillary data buffer, wrapped in a union in order to ensure
+     * it is suitably aligned
+     */
+    char buf[CMSG_SPACE(sizeof(int) * MAX_NR_OF_DESCRIPTORS)];
+    struct cmsghdr align;
+  } u;
+
+  char iobuf[1];
+  struct iovec io = {.iov_base = iobuf, .iov_len = sizeof(iobuf)};
+  struct msghdr msg = {
+      0,
+  };
+
+  msg.msg_iov = &io;
+  msg.msg_iovlen = 1;
+  msg.msg_control = u.buf;
+  msg.msg_controllen = sizeof(u.buf);
+
+retry:
+  int ret = recvmsg(socket_fd, &msg, 0);
+  if (ret == 0) {
+    _W("Socket was disconnected. fd(%d)", socket_fd);
+    return false;
+  } else if (ret < 0) {
+    if (errno == EINTR) {
+      usleep(100 * 1000);
+      goto retry;
+    }
+
+    _E("recvmsg() failed (errno:%d) (fd:%d)", errno, socket_fd);
+    return false;
+  }
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  if (cmsg == nullptr) {
+    return false;
+  }
+
+  for (int i = 0; cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg), ++i) {
+    if (cmsg->cmsg_type == SCM_RIGHTS) break;
+  }
+
+  if (cmsg == nullptr) return false;
+
+  int payload = cmsg->cmsg_len - sizeof(*cmsg);
+  int* recvdesc = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+  int nrdesc = payload / sizeof(int);
+  for (int i = 0; i < nrdesc; i++) {
+    parcelable_fds_.push_back(recvdesc[i]);
+  }
+
+  return true;
+}
+
+void Parcel::ReadParcelableFds(int socket_fd, int cnt) {
+  if (cnt == 0) return;
+
+  int flags = fcntl(socket_fd, F_GETFL, 0);
+  fcntl(socket_fd, F_SETFL, flags & ~O_NONBLOCK);
+
+  for (int i = 0; i < cnt;) {
+    int n = 0;
+    if (i + MAX_NR_OF_DESCRIPTORS <= cnt)
+      n = MAX_NR_OF_DESCRIPTORS;
+    else
+      n = cnt - i;
+    if (!DoReadParcelableFds(socket_fd, n)) {
+      _E("ReadParcelableFds() failed");
+      break;
+    }
+    i += n;
+  }
+
+  fcntl(socket_fd, F_SETFL, flags);
+}
+
 }  // namespace internal
 }  // namespace rpc_port
index 3f45e7de9902be3955671cbd8ef4c1d9c475d83b..b6a381cc205570670a682480372381ce329132a4 100644 (file)
@@ -42,11 +42,20 @@ class Parcel : public tizen_base::Parcelable {
   parcel_h GetHandle() const;
   void SetRawParcel(tizen_base::Parcel* raw_parcel);
   tizen_base::Parcel* GetRawParcel() const;
+  int GetParcelableFd(uint32_t idx) const;
+  uint32_t GetCurParcelableFdIdx() const;
+  void PushBackParcelableFd(int fd);
+  void WriteParcelableFds(int socket_fd);
+  void ReadParcelableFds(int socket_fd, int cnt);
 
  private:
+  bool DoWriteParcelableFds(int socket_fd, int start_idx, int cnt);
+  bool DoReadParcelableFds(int socket_fd, int cnt);
+
   std::unique_ptr<ParcelHeader> header_;
   std::unique_ptr<tizen_base::Parcel> handle_;
   std::unique_ptr<tizen_base::Parcel> raw_parcel_ { nullptr };
+  std::vector<int> parcelable_fds_;
 };
 
 }  // namespace internal
index aad88a8fbf6b40c4b2f0c9a5bf59645cd3678646..5bdc7e0977ab263d07fab40ddf9d4a3e9dab8bde 100644 (file)
@@ -49,6 +49,7 @@ static int __rpc_port_parcel_create_from_port(rpc_port_parcel_h* h,
                                               rpc_port_h port,
                                               bool without_header) {
   int len;
+  int fd_cnt;
   unsigned char* buf;
 
   if (h == nullptr || port == nullptr) return RPC_PORT_ERROR_INVALID_PARAMETER;
@@ -73,6 +74,9 @@ static int __rpc_port_parcel_create_from_port(rpc_port_parcel_h* h,
       free(buf);   // LCOV_EXCL_LINE
       return ret;  // LCOV_EXCL_LINE
     }
+
+    ret = rpc_port_read(port, &fd_cnt, 4);
+    if (ret != 0) return ret;
   }
 
   auto* parcel = new (std::nothrow) internal::Parcel(without_header);
@@ -86,6 +90,8 @@ static int __rpc_port_parcel_create_from_port(rpc_port_parcel_h* h,
 
   tizen_base::Parcel raw_parcel(buf, len, false);
   raw_parcel.ReadParcelable(parcel);
+  parcel->ReadParcelableFds(pt->GetReadFd(), fd_cnt);
+
   *h = static_cast<rpc_port_parcel_h>(parcel);
   return RPC_PORT_ERROR_NONE;
 }
@@ -119,6 +125,11 @@ RPC_API int rpc_port_parcel_send(rpc_port_parcel_h h, rpc_port_h port) {
 
     ret = rpc_port_write(port, raw, len);
     if (ret != 0) return ret;
+
+    uint32_t fd_cnt = parcel->GetCurParcelableFdIdx();
+    ret = rpc_port_write(port, &fd_cnt, sizeof(fd_cnt));
+    if (ret != 0) return ret;
+    parcel->WriteParcelableFds(pt->GetWriteFd());
   }
 
   return RPC_PORT_ERROR_NONE;
@@ -220,6 +231,16 @@ RPC_API int rpc_port_parcel_write_array_count(rpc_port_parcel_h h, int count) {
   return RPC_PORT_ERROR_NONE;
 }
 
+RPC_API int rpc_port_parcel_write_fd(rpc_port_parcel_h h, int fd) {
+  if (h == nullptr) return RPC_PORT_ERROR_INVALID_PARAMETER;
+
+  auto* parcel = static_cast<internal::Parcel*>(h);
+  auto idx = parcel->GetCurParcelableFdIdx();
+  parcel->PushBackParcelableFd(fd);
+  parcel_write_int32(parcel->GetHandle(), idx);
+  return RPC_PORT_ERROR_NONE;
+}
+
 RPC_API int rpc_port_parcel_write(rpc_port_parcel_h h,
                                   rpc_port_parcelable_t* parcelable,
                                   void* data) {
@@ -359,6 +380,19 @@ RPC_API int rpc_port_parcel_read_array_count(rpc_port_parcel_h h, int* count) {
   return RPC_PORT_ERROR_NONE;
 }
 
+RPC_API int rpc_port_parcel_read_fd(rpc_port_parcel_h h, int* fd) {
+  if (h == nullptr || fd == nullptr) return RPC_PORT_ERROR_INVALID_PARAMETER;
+
+  auto* parcel = static_cast<internal::Parcel*>(h);
+  int idx;
+  int ret = parcel_read_int32(parcel->GetHandle(), &idx);
+  if (ret != 0)
+    _E("parcel_read_int32() is failed. error(%d)", ret);  // LCOV_EXCL_LINE
+  *fd = parcel->GetParcelableFd(idx);
+
+  return RPC_PORT_ERROR_NONE;
+}
+
 RPC_API int rpc_port_parcel_read(rpc_port_parcel_h h,
                                  rpc_port_parcelable_t* parcelable,
                                  void* data) {
index 151891cd693ccf0dbbb920d0c9f5948a529d9e92..575c38a0f021f2c6dc40b5d2ec2e3b07c0bce25e 100644 (file)
@@ -61,8 +61,7 @@ Stub::~Stub() {
   if (context_) g_main_context_unref(context_);
 
   for (auto& p : ports_) {
-    if (!p->IsDelegate())
-      DebugPort::RemoveSession(p->GetReadFd());
+    if (!p->IsDelegate()) DebugPort::RemoveSession(p->GetReadFd());
   }
 
   listener_ = nullptr;
@@ -76,12 +75,11 @@ Stub::~Stub() {
 }
 
 int Stub::Listen(IEventListener* ev, int fd) {
-  if (ev == nullptr)
-    return RPC_PORT_ERROR_INVALID_PARAMETER;
+  if (ev == nullptr) return RPC_PORT_ERROR_INVALID_PARAMETER;
 
   std::lock_guard<std::recursive_mutex> lock(GetMutex());
   if (listener_ != nullptr) {
-    _E("Already listening!");  // LCOV_EXCL_LINE
+    _E("Already listening!");                 // LCOV_EXCL_LINE
     return RPC_PORT_ERROR_INVALID_PARAMETER;  // LCOV_EXCL_LINE
   }
 
@@ -131,9 +129,7 @@ std::shared_ptr<Port> Stub::FindDelegatePort(
   return {};
 }
 
-const std::string& Stub::GetPortName() const {
-  return port_name_;
-}
+const std::string& Stub::GetPortName() const { return port_name_; }
 
 void Stub::RemoveAcceptedPorts(std::string instance) {
   std::lock_guard<std::recursive_mutex> lock(GetMutex());
@@ -298,9 +294,7 @@ void Stub::AddAcceptedPort(const std::string& sender_appid,
   listener_->OnConnected(sender_appid, instance);
 }
 
-std::recursive_mutex& Stub::GetMutex() const {
-  return mutex_;
-}
+std::recursive_mutex& Stub::GetMutex() const { return mutex_; }
 
 void Stub::CheckPermission(const std::shared_ptr<Request>& request,
                            const std::shared_ptr<ClientSocket>& client,
@@ -308,7 +302,7 @@ void Stub::CheckPermission(const std::shared_ptr<Request>& request,
   std::string app_id;
   if (cred->GetUid() >= kRegularUidMin) app_id = Aul::GetAppId(cred->GetPid());
 
-  auto response_func = [=](int res) -> void {
+  auto response_func = [this, app_id, request, client, cred](int res) -> void {
     if (freed_stubs_.find(this) != freed_stubs_.end())
       return;  // LCOV_EXCL_LINE
 
@@ -316,8 +310,8 @@ void Stub::CheckPermission(const std::shared_ptr<Request>& request,
     if (pending_request_count_ != 0) pending_request_count_--;
 
     Response response(res);
-    _I("Send response. pid(%d), fd(%d), ret(%d)",
-        cred->GetPid(), client->GetFd(), res);
+    _I("Send response. pid(%d), fd(%d), ret(%d)", cred->GetPid(),
+       client->GetFd(), res);
     int ret = client->Send(response);
     if (ret != 0) return;  // LCOV_EXCL_LINE
 
@@ -347,8 +341,8 @@ void Stub::CheckPermission(const std::shared_ptr<Request>& request,
       return;
     }
   } else {
-    _W("Bypass access control. pid(%d), uid(%u)",
-       cred->GetPid(), cred->GetUid());
+    _W("Bypass access control. pid(%d), uid(%u)", cred->GetPid(),
+       cred->GetUid());
     res = 0;
   }
 
index 17b4687417cb7b8ad8a59577108a773e83e775e2..958926e8d4b43e3194bc5e42ef74550a502140a9 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <errno.h>
+#include <fcntl.h>
 #include <glib.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <unistd.h>
 
 #include <iostream>
-#include <stdexcept>
 #include <memory>
+#include <stdexcept>
 #include <thread>
 
 #include "include/rpc-port-internal.h"
+#include "include/rpc-port-parcel-internal.h"
+#include "include/rpc-port-parcel.h"
 #include "unit_tests/mock/aul_mock.hh"
 #include "unit_tests/mock/inotify_mock.hh"
 #include "unit_tests/mock/test_fixture.hh"
 
 using ::testing::_;
 using ::testing::DoAll;
+using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::SetArgPointee;
-using ::testing::Invoke;
 
 namespace {
 
@@ -47,15 +50,13 @@ class WatchInfo {
  public:
   WatchInfo() {}
   WatchInfo(std::string app_id, std::string port_name,
-      aul_rpc_port_appeared_cb appeared_cb,
-      aul_rpc_port_vanished_cb vanished_cb,
-      void* user_data)
+            aul_rpc_port_appeared_cb appeared_cb,
+            aul_rpc_port_vanished_cb vanished_cb, void* user_data)
       : app_id_(std::move(app_id)),
         port_name_(std::move(port_name)),
         appeared_cb_(appeared_cb),
         vanished_cb_(vanished_cb),
-        user_data_(user_data) {
-  }
+        user_data_(user_data) {}
 
   ~WatchInfo() {}
 
@@ -84,12 +85,14 @@ int CreateSocket(const std::string& path) {
   int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
   if (fd < 0) {
     fd = -errno;
-    fprintf(stderr, "socket() is failed. path(%s), errno(%d)\n",
-        path.c_str(), errno);
+    fprintf(stderr, "socket() is failed. path(%s), errno(%d)\n", path.c_str(),
+            errno);
     return fd;
   }
 
-  struct sockaddr_un addr = { 0, };
+  struct sockaddr_un addr = {
+      0,
+  };
   addr.sun_family = AF_UNIX;
   snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path.c_str());
   unlink(path.c_str());
@@ -98,8 +101,8 @@ int CreateSocket(const std::string& path) {
   int ret = bind(fd, addr_ptr, sizeof(addr));
   if (ret != 0) {
     ret = -errno;
-    fprintf(stderr, "bind() is failed. path(%s), errno(%d)\n",
-        path.c_str(), errno);
+    fprintf(stderr, "bind() is failed. path(%s), errno(%d)\n", path.c_str(),
+            errno);
     close(fd);
     return ret;
   }
@@ -107,18 +110,18 @@ int CreateSocket(const std::string& path) {
   ret = listen(fd, 128);
   if (ret != 0) {
     ret = -errno;
-    fprintf(stderr, "listen() is failed. path(%s), errno(%d)\n",
-        path.c_str(), errno);
+    fprintf(stderr, "listen() is failed. path(%s), errno(%d)\n", path.c_str(),
+            errno);
     close(fd);
     return ret;
   }
 
   mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
-    S_IROTH | S_IWOTH | S_IXOTH;
+                S_IROTH | S_IWOTH | S_IXOTH;
   ret = chmod(path.c_str(), mode);
   if (ret != 0)
-    fprintf(stderr, "chmod(%s, %d) is failed. errno(%d)\n",
-        path.c_str(), mode, errno);
+    fprintf(stderr, "chmod(%s, %d) is failed. errno(%d)\n", path.c_str(), mode,
+            errno);
 
   return fd;
 }
@@ -129,40 +132,38 @@ int FakeAulAppGetAppIdByPid(int pid, char* app_id, int len) {
 }
 
 int FakeAulRpcPortUsrGetPath(const char* app_id, const char* port_name,
-    uid_t uid, char** port_path) {
-  std::string path = "/tmp/." + std::string(app_id) + "_" +
-      std::string(port_name);
+                             uid_t uid, char** port_path) {
+  std::string path =
+      "/tmp/." + std::string(app_id) + "_" + std::string(port_name);
   *port_path = strdup(path.c_str());
   return 0;
 }
 
 int FakeAulRpcPortUsrPrepareStub(const char* app_id, const char* port_name,
-    uid_t uid) {
+                                 uid_t uid) {
   return 0;
 }
 
 int FakeAulRpcPortUsrExist(const char* app_id, const char* port_name, uid_t uid,
-    bool* exist) {
+                           bool* exist) {
   if (!strcmp(port_name, "test_port")) {
     *exist = true;
-    if (test_port_handle.get() != nullptr)
-      test_port_handle->Appear();
+    if (test_port_handle.get() != nullptr) test_port_handle->Appear();
   } else {
     *exist = false;
-    if (test_port_handle.get() != nullptr)
-      test_port_handle->Vanish();
+    if (test_port_handle.get() != nullptr) test_port_handle->Vanish();
   }
 
   return 0;
 }
 
-int FakeAulRpcPortNotifyRpcFinished() {
-  return 0;
-}
+int FakeAulRpcPortNotifyRpcFinished() { return 0; }
 
 int FakeAulRpcPortUsrAddWatch(const char* app_id, const char* port_name,
-    aul_rpc_port_appeared_cb appeared_cb, aul_rpc_port_vanished_cb vanished_cb,
-    void* user_data, uid_t uid, aul_rpc_port_watch_h* handle) {
+                              aul_rpc_port_appeared_cb appeared_cb,
+                              aul_rpc_port_vanished_cb vanished_cb,
+                              void* user_data, uid_t uid,
+                              aul_rpc_port_watch_h* handle) {
   if (!strcmp(port_name, "test_port")) {
     test_port_handle.reset(
         new WatchInfo(app_id, port_name, appeared_cb, vanished_cb, user_data));
@@ -192,14 +193,12 @@ int FakeAulSvcGetAppIdByAliasAppId(const char* alias_appid, char** app_id) {
 int FakeAulRpcPortUsrCreate(const char* port_name, uid_t uid, int* fd) {
   char* port_path = nullptr;
   FakeAulRpcPortUsrGetPath("TestApp", port_name, uid, &port_path);
-  if (port_path == nullptr)
-    return -1;
+  if (port_path == nullptr) return -1;
 
-  std::unique_ptr<char, decltype(std::free)*> port_path_auto(
-      port_path, std::free);
+  std::unique_ptr<char, decltype(std::free)*> port_path_auto(port_path,
+                                                             std::free);
   int socket_fd = CreateSocket(port_path);
-  if (socket_fd < 0)
-    return -1;
+  if (socket_fd < 0) return -1;
 
   *fd = socket_fd;
   return 0;
@@ -208,25 +207,21 @@ int FakeAulRpcPortUsrCreate(const char* port_name, uid_t uid, int* fd) {
 int FakeAulRpcPortUsrDestroy(const char* port_name, uid_t uid) {
   char* port_path = nullptr;
   FakeAulRpcPortUsrGetPath("TestApp", port_name, uid, &port_path);
-  if (port_path == nullptr)
-    return -1;
+  if (port_path == nullptr) return -1;
 
-  std::unique_ptr<char, decltype(std::free)*> port_path_auto(
-      port_path, std::free);
+  std::unique_ptr<char, decltype(std::free)*> port_path_auto(port_path,
+                                                             std::free);
   unlink(port_path);
   return 0;
 }
 
 int FakeAulProcRegister(const char* proc_name, bundle* extra) {
-  if (proc_name == nullptr)
-    return -1;
+  if (proc_name == nullptr) return -1;
 
   return 0;
 }
 
-int FakeAulProcDeregister() {
-  return 0;
-}
+int FakeAulProcDeregister() { return 0; }
 
 }  // namespace
 
@@ -268,51 +263,36 @@ class RpcPortBase : public TestFixture {
   }
 
   void SetFakeFuncs() {
+    EXPECT_CALL(GetMock<AulMock>(), aul_app_get_appid_bypid(_, _, _))
+        .WillRepeatedly(Invoke(FakeAulAppGetAppIdByPid));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_usr_get_path(_, _, _, _))
+        .WillRepeatedly(Invoke(FakeAulRpcPortUsrGetPath));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_usr_prepare_stub(_, _, _))
+        .WillRepeatedly(Invoke(FakeAulRpcPortUsrPrepareStub));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_usr_exist(_, _, _, _))
+        .WillRepeatedly(Invoke(FakeAulRpcPortUsrExist));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_notify_rpc_finished())
+        .WillRepeatedly(Invoke(FakeAulRpcPortNotifyRpcFinished));
     EXPECT_CALL(GetMock<AulMock>(),
-        aul_app_get_appid_bypid(_, _, _))
-            .WillRepeatedly(Invoke(FakeAulAppGetAppIdByPid));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_usr_get_path(_, _, _, _))
-            .WillRepeatedly(Invoke(FakeAulRpcPortUsrGetPath));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_usr_prepare_stub(_, _, _))
-            .WillRepeatedly(Invoke(FakeAulRpcPortUsrPrepareStub));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_usr_exist(_, _, _, _))
-            .WillRepeatedly(Invoke(FakeAulRpcPortUsrExist));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_notify_rpc_finished())
-            .WillRepeatedly(Invoke(FakeAulRpcPortNotifyRpcFinished));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_usr_add_watch(_, _, _, _, _, _, _))
-            .WillRepeatedly(Invoke(FakeAulRpcPortUsrAddWatch));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_remove_watch(_))
-            .WillRepeatedly(Invoke(FakeAulRpcPortRemoveWatch));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_svc_get_appid_by_alias_appid(_, _))
-            .WillRepeatedly(Invoke(FakeAulSvcGetAppIdByAliasAppId));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_usr_create(_, _, _))
-            .WillRepeatedly(Invoke(FakeAulRpcPortUsrCreate));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_rpc_port_usr_destroy(_, _))
-            .WillRepeatedly(Invoke(FakeAulRpcPortUsrDestroy));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_proc_register(_, _))
-            .WillRepeatedly(Invoke(FakeAulProcRegister));
-    EXPECT_CALL(GetMock<AulMock>(),
-        aul_proc_deregister())
-            .WillRepeatedly(Invoke(FakeAulProcDeregister));
+                aul_rpc_port_usr_add_watch(_, _, _, _, _, _, _))
+        .WillRepeatedly(Invoke(FakeAulRpcPortUsrAddWatch));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_remove_watch(_))
+        .WillRepeatedly(Invoke(FakeAulRpcPortRemoveWatch));
+    EXPECT_CALL(GetMock<AulMock>(), aul_svc_get_appid_by_alias_appid(_, _))
+        .WillRepeatedly(Invoke(FakeAulSvcGetAppIdByAliasAppId));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_usr_create(_, _, _))
+        .WillRepeatedly(Invoke(FakeAulRpcPortUsrCreate));
+    EXPECT_CALL(GetMock<AulMock>(), aul_rpc_port_usr_destroy(_, _))
+        .WillRepeatedly(Invoke(FakeAulRpcPortUsrDestroy));
+    EXPECT_CALL(GetMock<AulMock>(), aul_proc_register(_, _))
+        .WillRepeatedly(Invoke(FakeAulProcRegister));
+    EXPECT_CALL(GetMock<AulMock>(), aul_proc_deregister())
+        .WillRepeatedly(Invoke(FakeAulProcDeregister));
   }
 
-  void RunMainLoop() {
-    g_main_loop_run(mainloop_);
-  }
+  void RunMainLoop() { g_main_loop_run(mainloop_); }
 
-  void Finish() {
-    g_main_loop_quit(mainloop_);
-  }
+  void Finish() { g_main_loop_quit(mainloop_); }
 
   void KillStub() {
     int ret = rpc_port_stub_destroy(stub_handle_);
@@ -354,9 +334,10 @@ class RpcPortConnection : public RpcPortBase {
   }
 
   void StubSetup() {
-    int ret = rpc_port_stub_add_received_event_cb(stub_handle_,
-        [](const char* sender, const char* instance,
-            rpc_port_h port, void *data) -> int {
+    int ret = rpc_port_stub_add_received_event_cb(
+        stub_handle_,
+        [](const char* sender, const char* instance, rpc_port_h port,
+           void* data) -> int {
           RpcPortConnection* p = static_cast<RpcPortConnection*>(data);
           p->stub_port_ = port;
           pid_t pid = -1;
@@ -367,35 +348,39 @@ class RpcPortConnection : public RpcPortBase {
 
           rpc_port_h main_port = nullptr;
           ret = rpc_port_stub_get_port(p->stub_handle_, RPC_PORT_PORT_MAIN,
-              instance, &main_port);
+                                       instance, &main_port);
           if (ret != RPC_PORT_ERROR_NONE)
             std::cerr << "Failed to get main port" << std::endl;
 
-          ret = rpc_port_stub_get_port(p->stub_handle_,
-              RPC_PORT_PORT_CALLBACK, instance, &p->stub_callback_port_);
+          ret = rpc_port_stub_get_port(p->stub_handle_, RPC_PORT_PORT_CALLBACK,
+                                       instance, &p->stub_callback_port_);
           p->Finish();
-          if (ret != RPC_PORT_ERROR_NONE)
-            return -1;
+          if (ret != RPC_PORT_ERROR_NONE) return -1;
 
           return RPC_PORT_ERROR_NONE;
-        }, this);
+        },
+        this);
     ASSERT_EQ(ret, RPC_PORT_ERROR_NONE);
 
-    ret = rpc_port_stub_add_disconnected_event_cb(stub_handle_,
-        [](const char* sender, const char* instance, void *data) {
+    ret = rpc_port_stub_add_disconnected_event_cb(
+        stub_handle_,
+        [](const char* sender, const char* instance, void* data) {
           auto* p = static_cast<RpcPortConnection*>(data);
           p->touch_stub_disconnected_event_cb_ = true;
-          g_timeout_add(1, [](void* data) -> gboolean {
-            auto* p = static_cast<RpcPortConnection*>(data);
-            p->Finish();
-            return G_SOURCE_REMOVE;
-          }, p);
+          g_timeout_add(
+              1,
+              [](void* data) -> gboolean {
+                auto* p = static_cast<RpcPortConnection*>(data);
+                p->Finish();
+                return G_SOURCE_REMOVE;
+              },
+              p);
         },
         this);
     ASSERT_EQ(ret, RPC_PORT_ERROR_NONE);
 
-    ret = rpc_port_stub_add_privilege(stub_handle_,
-        "http://tizen.org/privilege/appmanager.launch");
+    ret = rpc_port_stub_add_privilege(
+        stub_handle_, "http://tizen.org/privilege/appmanager.launch");
     ASSERT_EQ(ret, RPC_PORT_ERROR_NONE);
 
     ret = rpc_port_stub_set_trusted(stub_handle_, true);
@@ -406,35 +391,43 @@ class RpcPortConnection : public RpcPortBase {
   }
 
   void ProxySetup() {
-    int ret = rpc_port_proxy_add_connected_event_cb(proxy_handle_,
+    int ret = rpc_port_proxy_add_connected_event_cb(
+        proxy_handle_,
         [](const char* ep, const char* port_name, rpc_port_h port, void* data) {
           RpcPortConnection* p = static_cast<RpcPortConnection*>(data);
           p->proxy_port_ = port;
           rpc_port_proxy_get_port(p->proxy_handle_, RPC_PORT_PORT_CALLBACK,
-              &p->proxy_callback_port_);
+                                  &p->proxy_callback_port_);
           p->Finish();
-        }, this);
+        },
+        this);
     ASSERT_EQ(ret, 0);
 
-    ret = rpc_port_proxy_add_disconnected_event_cb(proxy_handle_,
+    ret = rpc_port_proxy_add_disconnected_event_cb(
+        proxy_handle_,
         [](const char* ep, const char* port_name, void* data) {
           auto* p = static_cast<RpcPortConnection*>(data);
           p->touch_proxy_disconnected_event_cb_ = true;
-          g_timeout_add(1, [](void* data) -> gboolean {
-            auto* p = static_cast<RpcPortConnection*>(data);
-            p->Finish();
-            return G_SOURCE_REMOVE;
-          }, p);
+          g_timeout_add(
+              1,
+              [](void* data) -> gboolean {
+                auto* p = static_cast<RpcPortConnection*>(data);
+                p->Finish();
+                return G_SOURCE_REMOVE;
+              },
+              p);
         },
         this);
     ASSERT_EQ(ret, 0);
 
-    ret = rpc_port_proxy_add_received_event_cb(proxy_handle_,
-        [](const char *ep, const char *port_name, void *data) {
+    ret = rpc_port_proxy_add_received_event_cb(
+        proxy_handle_,
+        [](const char* ep, const char* port_name, void* data) {
           RpcPortConnection* p = static_cast<RpcPortConnection*>(data);
           p->touch_proxy_received_event_cb_ = true;
           p->Finish();
-        }, this);
+        },
+        this);
     ASSERT_EQ(ret, 0);
 
     ret = rpc_port_proxy_connect(proxy_handle_, "TestApp", "test_port");
@@ -451,23 +444,27 @@ class RpcPortConnection : public RpcPortBase {
 };
 
 TEST_F(RpcPortBase, rpc_port_event_connect) {
-  int ret = rpc_port_stub_add_connected_event_cb(stub_handle_,
-      [](const char *sender, const char* instance, void *data) {
+  int ret = rpc_port_stub_add_connected_event_cb(
+      stub_handle_,
+      [](const char* sender, const char* instance, void* data) {
         RpcPortBase* p = static_cast<RpcPortBase*>(data);
 
         p->touch_stub_connected_event_cb_ = true;
-      }, this);
+      },
+      this);
   ASSERT_EQ(ret, 0);
 
   ret = rpc_port_stub_listen(stub_handle_);
   ASSERT_EQ(ret, 0);
 
-  ret = rpc_port_proxy_add_connected_event_cb(proxy_handle_,
-    [](const char *ep, const char *port_name, rpc_port_h port, void *data) {
-      RpcPortBase* p = static_cast<RpcPortBase*>(data);
-      p->touch_proxy_connected_event_cb_ = true;
-      p->Finish();
-    }, this);
+  ret = rpc_port_proxy_add_connected_event_cb(
+      proxy_handle_,
+      [](const char* ep, const char* port_name, rpc_port_h port, void* data) {
+        RpcPortBase* p = static_cast<RpcPortBase*>(data);
+        p->touch_proxy_connected_event_cb_ = true;
+        p->Finish();
+      },
+      this);
   ASSERT_EQ(ret, 0);
 
   ret = rpc_port_proxy_connect(proxy_handle_, "TestApp", "test_port");
@@ -480,29 +477,35 @@ TEST_F(RpcPortBase, rpc_port_event_connect) {
 }
 
 TEST_F(RpcPortBase, rpc_port_event_connect_sync) {
-  int ret = rpc_port_stub_add_connected_event_cb(stub_handle_,
-      [](const char *sender, const char* instance, void *data) {
+  int ret = rpc_port_stub_add_connected_event_cb(
+      stub_handle_,
+      [](const char* sender, const char* instance, void* data) {
         RpcPortBase* p = static_cast<RpcPortBase*>(data);
         p->touch_stub_connected_event_cb_ = true;
-      }, this);
+      },
+      this);
   ASSERT_EQ(ret, 0);
 
   ret = rpc_port_stub_listen(stub_handle_);
   ASSERT_EQ(ret, 0);
 
-  ret = rpc_port_proxy_add_connected_event_cb(proxy_handle_,
-    [](const char *ep, const char *port_name, rpc_port_h port, void *data) {
-      RpcPortBase* p = static_cast<RpcPortBase*>(data);
-      p->touch_proxy_connected_event_cb_ = true;
-      p->Finish();
-    }, this);
+  ret = rpc_port_proxy_add_connected_event_cb(
+      proxy_handle_,
+      [](const char* ep, const char* port_name, rpc_port_h port, void* data) {
+        RpcPortBase* p = static_cast<RpcPortBase*>(data);
+        p->touch_proxy_connected_event_cb_ = true;
+        p->Finish();
+      },
+      this);
   ASSERT_EQ(ret, 0);
 
-  std::thread t([](RpcPortBase* p) {
-      int ret = rpc_port_proxy_connect_sync(p->proxy_handle_, "TestApp",
-          "test_port");
-      ASSERT_EQ(ret, 0);
-    }, this);
+  std::thread t(
+      [](RpcPortBase* p) {
+        int ret = rpc_port_proxy_connect_sync(p->proxy_handle_, "TestApp",
+                                              "test_port");
+        ASSERT_EQ(ret, 0);
+      },
+      this);
 
   RunMainLoop();
   t.join();
@@ -542,24 +545,28 @@ TEST_F(RpcPortBase, rpc_port_get_peer_info_N) {
 }
 
 TEST_F(RpcPortBase, rpc_port_proxy_event_reject_N) {
-  int ret = rpc_port_proxy_add_rejected_event_cb(proxy_handle_,
-    [](const char *ep, const char *port_name, void *data) {
-      RpcPortBase* p = static_cast<RpcPortBase*>(data);
-      p->touch_proxy_rejected_event_cb_ = true;
-      p->Finish();
-    }, this);
+  int ret = rpc_port_proxy_add_rejected_event_cb(
+      proxy_handle_,
+      [](const char* ep, const char* port_name, void* data) {
+        RpcPortBase* p = static_cast<RpcPortBase*>(data);
+        p->touch_proxy_rejected_event_cb_ = true;
+        p->Finish();
+      },
+      this);
   ASSERT_EQ(ret, 0);
 
   rpc_port_set_timeout(5000);
   ret = rpc_port_proxy_connect(proxy_handle_, "TestApp", "wrong_port");
   ASSERT_EQ(ret, 0);
 
-  guint tag = g_timeout_add_seconds(10,
+  guint tag = g_timeout_add_seconds(
+      10,
       [](gpointer data) -> gboolean {
         auto* p = static_cast<RpcPortBase*>(data);
         p->Finish();
         return G_SOURCE_REMOVE;
-      }, this);
+      },
+      this);
 
   RunMainLoop();
   ASSERT_TRUE(touch_proxy_rejected_event_cb_);
@@ -571,15 +578,13 @@ TEST_F(RpcPortConnection, rpc_port_proxy_event_receive_p) {
   char res[] = "OK";
   char r_buf[256];
 
-  if (proxy_port_ == nullptr)
-    RunMainLoop();
+  if (proxy_port_ == nullptr) RunMainLoop();
 
   ASSERT_NE(proxy_port_, nullptr);
   int ret = rpc_port_write(proxy_port_, res, sizeof(res));
   ASSERT_EQ(ret, 0);
 
-  if (stub_port_ == nullptr)
-    RunMainLoop();
+  if (stub_port_ == nullptr) RunMainLoop();
 
   ASSERT_NE(stub_port_, nullptr);
   ASSERT_NE(stub_callback_port_, nullptr);
@@ -611,15 +616,13 @@ TEST_F(RpcPortConnection, rpc_port_read_write) {
   char res[] = "OK";
   char r_buf[256];
 
-  if (proxy_port_ == nullptr)
-    RunMainLoop();
+  if (proxy_port_ == nullptr) RunMainLoop();
 
   ASSERT_NE(proxy_port_, nullptr);
   int ret = rpc_port_write(proxy_port_, buf, sizeof(buf));
   ASSERT_EQ(ret, 0);
 
-  if (stub_port_ == nullptr)
-    RunMainLoop();
+  if (stub_port_ == nullptr) RunMainLoop();
 
   ASSERT_NE(stub_port_, nullptr);
 
@@ -635,6 +638,141 @@ TEST_F(RpcPortConnection, rpc_port_read_write) {
   ASSERT_STREQ("OK", r_buf);
 }
 
+TEST_F(RpcPortConnection, rpc_port_read_write_parcel) {
+  if (proxy_port_ == nullptr) RunMainLoop();
+
+  ASSERT_NE(proxy_port_, nullptr);
+
+  rpc_port_parcel_h parcel;
+  int ret = rpc_port_parcel_create(&parcel);
+  ASSERT_EQ(ret, 0);
+  ret = rpc_port_parcel_write_string(parcel, "test");
+  ASSERT_EQ(ret, 0);
+  ret = rpc_port_parcel_send(parcel, proxy_port_);
+  ASSERT_EQ(ret, 0);
+
+  if (stub_port_ == nullptr) RunMainLoop();
+
+  ASSERT_NE(stub_port_, nullptr);
+
+  rpc_port_parcel_h recv_parcel;
+  ret = rpc_port_parcel_create_from_port(&recv_parcel, stub_port_);
+  ASSERT_EQ(ret, 0);
+
+  char* str = nullptr;
+  ret = rpc_port_parcel_read_string(recv_parcel, &str);
+  ASSERT_EQ(ret, 0);
+
+  ASSERT_STREQ(str, "test");
+  free(str);
+
+  rpc_port_parcel_destroy(parcel);
+  rpc_port_parcel_destroy(recv_parcel);
+}
+
+TEST_F(RpcPortConnection, rpc_port_read_write_parcel_fd_one) {
+  int fd;
+  int ret;
+
+  fd = open("./test.txt", O_CREAT | O_RDWR | O_TRUNC, 0666);
+  ret = write(fd, "test", 5);
+  ASSERT_EQ(ret, 5);
+
+  if (proxy_port_ == nullptr) RunMainLoop();
+
+  ASSERT_NE(proxy_port_, nullptr);
+
+  rpc_port_parcel_h parcel;
+  ret = rpc_port_parcel_create(&parcel);
+  ASSERT_EQ(ret, 0);
+  ret = rpc_port_parcel_write_fd(parcel, fd);
+  ASSERT_EQ(ret, 0);
+  ret = rpc_port_parcel_send(parcel, proxy_port_);
+  ASSERT_EQ(ret, 0);
+
+  if (stub_port_ == nullptr) RunMainLoop();
+
+  ASSERT_NE(stub_port_, nullptr);
+
+  rpc_port_parcel_h recv_parcel;
+  ret = rpc_port_parcel_create_from_port(&recv_parcel, stub_port_);
+  ASSERT_EQ(ret, 0);
+
+  int recv_fd = -1;
+  ret = rpc_port_parcel_read_fd(recv_parcel, &recv_fd);
+  ASSERT_EQ(ret, 0);
+
+  ASSERT_NE(recv_fd, -1);
+
+  rpc_port_parcel_destroy(parcel);
+  rpc_port_parcel_destroy(recv_parcel);
+
+  char text_buf[10] = {
+      0,
+  };
+  lseek(recv_fd, SEEK_SET, 0);
+  ret = read(recv_fd, text_buf, 5);
+  ASSERT_EQ(ret, 5);
+  ASSERT_STREQ(text_buf, "test");
+
+  close(fd);
+  close(recv_fd);
+}
+
+TEST_F(RpcPortConnection, rpc_port_read_write_parcel_fd_multi) {
+  constexpr const int MAX_TEST_FD = 120;
+
+  int fd[MAX_TEST_FD] = {
+      0,
+  };
+  int ret;
+
+  for (int i = 0; i < MAX_TEST_FD; i++) {
+    fd[i] = open(std::format("./test{}.txt", i).data(),
+                 O_CREAT | O_RDWR | O_TRUNC, 0666);
+  }
+
+  if (proxy_port_ == nullptr) RunMainLoop();
+
+  ASSERT_NE(proxy_port_, nullptr);
+
+  rpc_port_parcel_h parcel;
+  ret = rpc_port_parcel_create(&parcel);
+  ASSERT_EQ(ret, 0);
+
+  for (int i = 0; i < MAX_TEST_FD; i++) {
+    ret = rpc_port_parcel_write_fd(parcel, fd[i]);
+    ASSERT_EQ(ret, 0);
+  }
+  ret = rpc_port_parcel_send(parcel, proxy_port_);
+  ASSERT_EQ(ret, 0);
+
+  if (stub_port_ == nullptr) RunMainLoop();
+  ASSERT_NE(stub_port_, nullptr);
+
+  rpc_port_parcel_h recv_parcel;
+  ret = rpc_port_parcel_create_from_port(&recv_parcel, stub_port_);
+  ASSERT_EQ(ret, 0);
+
+  int recv_fd[MAX_TEST_FD] = {
+      0,
+  };
+
+  for (int i = 0; i < MAX_TEST_FD; i++) {
+    ret = rpc_port_parcel_read_fd(recv_parcel, &recv_fd[i]);
+    ASSERT_EQ(ret, 0);
+    EXPECT_TRUE(recv_fd[i] > 0);
+  }
+
+  rpc_port_parcel_destroy(parcel);
+  rpc_port_parcel_destroy(recv_parcel);
+
+  for (int i = 0; i < MAX_TEST_FD; i++) {
+    close(fd[i]);
+    close(recv_fd[i]);
+  }
+}
+
 TEST_F(RpcPortConnection, rpc_port_proxy_disconnected_N) {
   KillStub();
   RunMainLoop();
@@ -651,15 +789,13 @@ TEST_F(RpcPortConnection, rpc_port_stub_disconnected_N) {
 
 TEST_F(RpcPortConnection, rpc_port_disconnect_P) {
   char res[] = "test";
-  if (proxy_port_ == nullptr)
-    RunMainLoop();
+  if (proxy_port_ == nullptr) RunMainLoop();
 
   ASSERT_NE(proxy_port_, nullptr);
   int ret = rpc_port_write(proxy_port_, res, sizeof(res));
   ASSERT_EQ(ret, 0);
 
-  if (stub_port_ == nullptr)
-    RunMainLoop();
+  if (stub_port_ == nullptr) RunMainLoop();
 
   ret = rpc_port_disconnect(stub_port_);
   EXPECT_EQ(ret, 0);