Upgrade rust/crates/grpcio to 0.9.0

Test: make
Change-Id: I05fd19472fbf556926a0145466f45235e089ab9f
diff --git a/.cargo/config b/.cargo/config
new file mode 100644
index 0000000..d8c2032
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --manifest-path ./xtask/Cargo.toml --"
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 05823ce..2587a95 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "b35467b1bfe58eedf4e77025432074d361be591f"
+    "sha1": "7a48e0bb843e702832c1c3ac024c468dedf0023c"
   }
 }
diff --git a/.clang-format b/.clang-format
index b641a64..7460950 100644
--- a/.clang-format
+++ b/.clang-format
@@ -3,6 +3,7 @@
 BasedOnStyle: Google
 DerivePointerAlignment: false
 PointerAlignment: Left
+IncludeBlocks: Preserve
 ---
 Language: ObjC
 BasedOnStyle: Google
diff --git a/.clang-tidy b/.clang-tidy
index 752b25e..8714bad 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,37 +1,122 @@
 ---
-# Disable abseil-no-namespace: https://bugs.llvm.org/show_bug.cgi?id=47947
+# Note on checks are disabled on purpose
+#
+# - abseil-no-namespace
+#   https://bugs.llvm.org/show_bug.cgi?id=47947
+#
+# - bugprone-reserved-identifier
+#   Some macros need to be defined for portability purpose; e.g. _BSD_SOURCE.
+#
+# - google-upgrade-googletest-case
+#   This requires googletest 1.10 which is higher than ones installed on many linux distributions.
+#
+# - modernize-redundant-void-arg
+#   Some source should be strictly C99 and func(void) should be used.
+#
+# Note on checks which will be enabled in future. These are good to have but
+# it's not activated yet due to the existing issues with the checks.
+# Once those issues are clear, these checks can be enabled later.
+#
+# - bugprone-branch-clone
+# - bugprone-infinite-loop
+# - bugprone-narrowing-conversions
+# - bugprone-not-null-terminated-result
+# - bugprone-signed-char-misuse
+# - bugprone-sizeof-expression
+# - bugprone-too-small-loop-variable
+# - clang-diagnostic-deprecated-declarations
+# - clang-diagnostic-unused-function
+# - google-readability-avoid-underscore-in-googletest-name
+# - google-runtime-int
+# - google-runtime-references
+# - modernize-avoid-bind
+# - modernize-deprecated-headers
+# - modernize-loop-convert
+# - modernize-pass-by-value
+# - modernize-raw-string-literal
+# - modernize-return-braced-init-list
+# - modernize-use-auto
+# - modernize-use-default-member-init
+# - modernize-use-emplace
+# - modernize-use-equals-default
+# - modernize-use-equals-delete
+# - modernize-use-using
+# - performance-no-automatic-move
+# - performance-unnecessary-copy-initialization
+# - performance-unnecessary-value-param
+# - readability-else-after-return
+# - readability-implicit-bool-conversion
+# - readability-redundant-declaration
+# - readability-static-definition-in-anonymous-namespace
+#
 Checks: '-*,
   abseil-*,
   -abseil-no-namespace,
   bugprone-*,
+  -bugprone-branch-clone,
+  -bugprone-infinite-loop,
   -bugprone-narrowing-conversions,
+  -bugprone-not-null-terminated-result,
+  -bugprone-reserved-identifier,
+  -bugprone-signed-char-misuse,
+  -bugprone-sizeof-expression,
   -bugprone-too-small-loop-variable,
-  performance-*,
-  -performance-unnecessary-copy-initialization,
-  -performance-unnecessary-value-param,
   google-*,
+  -google-readability-avoid-underscore-in-googletest-name,
   -google-runtime-int,
   -google-runtime-references,
+  -google-upgrade-googletest-case,
+  performance-*,
+  -performance-no-automatic-move,
+  -performance-unnecessary-copy-initialization,
+  -performance-unnecessary-value-param,
+  clang-diagnostic-deprecated-register,
+  clang-diagnostic-expansion-to-defined,
+  clang-diagnostic-ignored-attributes,
+  clang-diagnostic-non-pod-varargs,
+  clang-diagnostic-shadow-field,
+  clang-diagnostic-shift-sign-overflow,
+  clang-diagnostic-tautological-undefined-compare,
+  clang-diagnostic-thread-safety*,
+  clang-diagnostic-undefined-bool-conversion,
+  clang-diagnostic-unreachable-code,
+  clang-diagnostic-unreachable-code-loop-increment,
+  clang-diagnostic-unused-const-variable,
+  clang-diagnostic-unused-lambda-capture,
+  clang-diagnostic-unused-local-typedef,
+  clang-diagnostic-unused-private-field,
+  clang-diagnostic-user-defined-warnings,
   misc-definitions-in-headers,
   misc-static-assert,
   misc-unconventional-assign-operator,
   misc-uniqueptr-reset-release,
   misc-unused-alias-decls,
   misc-unused-using-decls,
+  modernize-make-shared,
   modernize-make-unique,
-  -modernize-redundant-void-arg,
   modernize-replace-auto-ptr,
+  modernize-replace-random-shuffle,
   modernize-shrink-to-fit,
+  modernize-unary-static-assert,
   modernize-use-bool-literals,
+  modernize-use-noexcept,
   modernize-use-nullptr,
   modernize-use-override,
+  modernize-use-transparent-functors,
+  readability-const-return-type,
   readability-container-size-empty,
+  readability-delete-null-pointer,
   readability-deleted-default,
   readability-function-size,
   readability-inconsistent-declaration-parameter-name,
+  readability-misleading-indentation,
+  readability-misplaced-array-index,
   readability-redundant-control-flow,
+  readability-redundant-function-ptr-dereference,
   readability-redundant-smartptr-get,
-  readability-string-compare'
+  readability-simplify-boolean-expr,
+  readability-string-compare,
+  readability-uniqueptr-delete-release'
 WarningsAsErrors: '*'
 CheckOptions:
   - key:    readability-function-size.StatementThreshold
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3731f9e..2b542c6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,20 +22,22 @@
     - run: sudo apt-get install -y clang-tidy-9
     - run: sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-9 100
     - run: which go && go version && which cargo && cargo version && clang --version && openssl version && which cmake && cmake --version
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: cargo fmt --all -- --check
     - run: cargo clippy --all -- -D clippy::all && cargo clippy --all --no-default-features --features prost-codec -- -D clippy::all
-    - run: scripts/lint-grpc-sys.sh && git diff-index --quiet HEAD
+    - run: cargo xtask clang-lint && git diff-index --quiet HEAD
 
   Linux-Stable:
     name: Linux-Stable
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
+    - run: sudo apt install -y protobuf-compiler
     - run: which go && go version && which cargo && cargo version && clang --version && openssl version
-    - run: scripts/reset-submodule.cmd
-    - run: env TEST_BIND=0 scripts/generate-bindings.sh && git diff --exit-code HEAD
-    - run: scripts/generate-bindings.sh
+    - run: cargo xtask submodule
+    - run: env TEST_BIND=0 cargo xtask bindgen && git diff --exit-code HEAD
+    - run: cargo xtask codegen && git diff --exit-code HEAD;
+    - run: cargo xtask bindgen
     - run: cargo build --no-default-features
     - run: cargo build --no-default-features --features protobuf-codec
     - run: cargo build --no-default-features --features prost-codec
@@ -49,7 +51,7 @@
     steps:
     - uses: actions/checkout@v2
     - run: which go && go version && which cargo && cargo version && clang --version && openssl version
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: cargo test --features "openssl-vendored" --all
     - run: cargo clean
     - run: cargo test --features "openssl" --all
@@ -61,7 +63,7 @@
     - uses: actions/checkout@v2
     - run: rustup default nightly
     - run: which go && go version && which cargo && cargo version && clang --version && openssl version
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: cargo build --no-default-features
     - run: cargo build --no-default-features --features protobuf-codec
     - run: cargo build --no-default-features --features prost-codec
@@ -75,7 +77,7 @@
     steps:
     - uses: actions/checkout@v2
     - run: which go && go version && which cargo && cargo version && clang --version && openssl version
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: cargo build --no-default-features --features use-bindgen
     - run: cargo build --no-default-features --features "protobuf-codec use-bindgen"
     - run: cargo build --no-default-features --features "prost-codec use-bindgen"
@@ -89,7 +91,7 @@
     - uses: actions/checkout@v2
     - run: brew update && brew upgrade openssl@1.1
     - run: which go && go version && which cargo && cargo version && clang --version && openssl version
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: OPENSSL_ROOT_DIR="/usr/local/opt/openssl@1.1/" cargo test --features "openssl" --all
     - run: cargo test --features "openssl-vendored" --all
 
@@ -103,7 +105,7 @@
     - run: choco install -y llvm
     - run: refreshenv
     - run: go version ; cargo version ; cmake --version 
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: cargo build
     - run: cargo test --all
   
@@ -112,7 +114,7 @@
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
-    - run: scripts/reset-submodule.cmd
+    - run: cargo xtask submodule
     - run: cd grpc-sys && cargo publish --dry-run
     - name: Check generated package size
       run: |
diff --git a/.travis.yml b/.travis.yml
index 9cc0821..4ac52fb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,17 +29,17 @@
     arch: arm64-graviton2
     vm: virt
     before_script:
-      - scripts/reset-submodule.cmd
-      - export GRPC_VERSION=1.35.0
+      - cargo xtask submodule
+      - export GRPC_VERSION=1.38.0
       - export PATH="$PATH:$HOME/.cache/bin:$HOME/.cargo/bin"
       - which cmake && cmake --version && openssl version
       - eval "$(gimme stable)"
     script:
       - if [[ $TRAVIS_OS_NAME == "linux" ]] && [[ $TRAVIS_RUST_VERSION == "stable" ]]; then
           rustup component add rustfmt && cargo fmt --all -- --check;
-          env TEST_BIND=0 scripts/generate-bindings.sh && git diff --exit-code HEAD;
+          env TEST_BIND=0 cargo xtask bindgen && git diff --exit-code HEAD;
         fi
-      - ./scripts/generate-bindings.sh
+      - cargo xtask bindgen
       - cargo build --no-default-features
       - cargo build --no-default-features --features protobuf-codec
       - cargo build --no-default-features --features prost-codec
@@ -49,8 +49,8 @@
     arch: arm64-graviton2
     vm: virt
     before_script:
-      - scripts/reset-submodule.cmd
-      - export GRPC_VERSION=1.35.0
+      - cargo xtask submodule
+      - export GRPC_VERSION=1.38.0
       - export PATH="$PATH:$HOME/.cache/bin:$HOME/.cargo/bin"
       - sudo apt-get update && sudo apt-get -y install libssl-dev
       - which cmake && cmake --version && openssl version
diff --git a/Android.bp b/Android.bp
index 44ad1a2..94c112b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -39,26 +39,27 @@
 }
 
 // dependent_library ["feature_list"]
-//   boringssl-src-0.2.0
-//   cc-1.0.67
+//   autocfg-1.0.1
+//   boringssl-src-0.3.0+688fc5c
+//   cc-1.0.68
 //   cfg-if-1.0.0
 //   cmake-0.1.45
-//   futures-0.3.13 "alloc,async-await,default,executor,futures-executor,std"
-//   futures-channel-0.3.13 "alloc,futures-sink,sink,std"
-//   futures-core-0.3.13 "alloc,std"
-//   futures-executor-0.3.13 "std"
-//   futures-io-0.3.13 "std"
-//   futures-macro-0.3.13
-//   futures-sink-0.3.13 "alloc,std"
-//   futures-task-0.3.13 "alloc,std"
-//   futures-util-0.3.13 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
-//   grpcio-sys-0.8.1
+//   futures-0.3.15 "alloc,async-await,default,executor,futures-executor,std"
+//   futures-channel-0.3.15 "alloc,futures-sink,sink,std"
+//   futures-core-0.3.15 "alloc,std"
+//   futures-executor-0.3.15 "std"
+//   futures-io-0.3.15 "std"
+//   futures-macro-0.3.15
+//   futures-sink-0.3.15 "alloc,std"
+//   futures-task-0.3.15 "alloc,std"
+//   futures-util-0.3.15 "alloc,async-await,async-await-macro,channel,futures-channel,futures-io,futures-macro,futures-sink,io,memchr,proc-macro-hack,proc-macro-nested,sink,slab,std"
+//   grpcio-sys-0.9.0+1.38.0
 //   instant-0.1.9
-//   libc-0.2.92 "default,std"
-//   libz-sys-1.1.2 "default,libc,static,stock-zlib"
-//   lock_api-0.4.2
+//   libc-0.2.97 "default,std"
+//   libz-sys-1.1.3 "libc,static"
+//   lock_api-0.4.4
 //   log-0.4.14
-//   memchr-2.3.4 "default,std"
+//   memchr-2.4.0 "default,std"
 //   parking_lot-0.11.1 "default"
 //   parking_lot_core-0.8.3
 //   pin-project-lite-0.2.6
@@ -66,13 +67,13 @@
 //   pkg-config-0.3.19
 //   proc-macro-hack-0.5.19
 //   proc-macro-nested-0.1.7
-//   proc-macro2-1.0.26 "default,proc-macro"
-//   protobuf-2.22.1
+//   proc-macro2-1.0.27 "default,proc-macro"
+//   protobuf-2.24.1
 //   quote-1.0.9 "default,proc-macro"
 //   same-file-1.0.6
 //   scopeguard-1.1.0
-//   slab-0.4.2
+//   slab-0.4.3 "default,std"
 //   smallvec-1.6.1
-//   syn-1.0.68 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote"
-//   unicode-xid-0.2.1 "default"
+//   syn-1.0.73 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote"
+//   unicode-xid-0.2.2 "default"
 //   walkdir-2.3.2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f928b24..57a34bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,15 @@
-# 0.8.2 - 2012-03-10
+# 0.9.0 - 2021-05-24
+
+- Support rich error (#514)
+- Provide default service implementations (#521)
+- Support abstract UDS (#523)
+- Use default-features=false on libz-sys to allow for zlib-ng (#525)
+- Update grpc to 1.38.0 (#526)
+
+# 0.8.2 - 2021-03-10
 
 - Fix send requirement in connectivity APIs (#516)
+- Add default health service implemetations (#518)
 
 # 0.8.1 - 2021-03-05
 
diff --git a/Cargo.toml b/Cargo.toml
index c8ccf28..a151b8a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 [package]
 edition = "2018"
 name = "grpcio"
-version = "0.8.2"
+version = "0.9.0"
 authors = ["The TiKV Project Developers"]
 autoexamples = false
 description = "The rust language implementation of gRPC, base on the gRPC c core library."
@@ -26,8 +26,6 @@
 repository = "https://github.com/tikv/grpc-rs"
 [package.metadata.docs.rs]
 all-features = true
-[profile.release]
-debug = true
 [dependencies.bytes]
 version = "1.0"
 optional = true
@@ -36,7 +34,7 @@
 version = "0.3"
 
 [dependencies.grpcio-sys]
-version = "0.8"
+version = "0.9"
 default-features = false
 
 [dependencies.libc]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 8938f97..6fecf28 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "grpcio"
-version = "0.8.2"
+version = "0.9.0"
 edition = "2018"
 authors = ["The TiKV Project Developers"]
 license = "Apache-2.0"
@@ -17,7 +17,7 @@
 all-features = true
 
 [dependencies]
-grpcio-sys = { path = "grpc-sys", version = "0.8", default-features = false }
+grpcio-sys = { path = "grpc-sys", version = "0.9", default-features = false }
 libc = "0.2"
 futures = "0.3"
 protobuf = { version = "2.0", optional = true }
@@ -27,7 +27,17 @@
 parking_lot = "0.11"
 
 [workspace]
-members = ["proto", "benchmark", "compiler", "interop", "tests-and-examples"]
+members = [
+    "proto",
+    "benchmark",
+    "compiler",
+    "health",
+    "interop",
+    "tests-and-examples",
+]
+# Don't include it in workspace to make it possible to use different version of
+# rust-protobuf.
+exclude = ["xtask"]
 
 [features]
 default = ["protobuf-codec", "secure", "use-bindgen"]
@@ -39,11 +49,8 @@
 no-omit-frame-pointer = ["grpcio-sys/no-omit-frame-pointer"]
 use-bindgen = ["grpcio-sys/use-bindgen"]
 
-[profile.release]
-debug = true
-
 [badges]
 travis-ci = { repository = "tikv/grpc-rs" }
 
 [patch.crates-io]
-grpcio-compiler = { path = "compiler", version = "0.8.0", default-features = false }
+grpcio-compiler = { path = "compiler", version = "0.9.0", default-features = false }
diff --git a/METADATA b/METADATA
index a81517a..ad40b66 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/grpcio/grpcio-0.8.2.crate"
+    value: "https://static.crates.io/crates/grpcio/grpcio-0.9.0.crate"
   }
-  version: "0.8.2"
+  version: "0.9.0"
   license_type: NOTICE
   last_upgrade_date {
     year: 2021
-    month: 4
-    day: 1
+    month: 6
+    day: 21
   }
 }
diff --git a/README.md b/README.md
index d4d384a..ca9e9da 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@
 ## Build
 
 ```
-$ ./scripts/reset-submodule.cmd # if you just cloned the repository
+$ cargo xtask submodule # if you just cloned the repository
 $ cargo build
 ```
 
@@ -142,5 +142,5 @@
 If the content in grpc-sys/grpc is updated, you may need to regenerate bindings:
 
 ```
-$ ./scripts/generate-bindings.sh
+$ cargo xtask bindgen
 ```
diff --git a/scripts/format-grpc-sys b/scripts/format-grpc-sys
deleted file mode 100755
index d97445a..0000000
--- a/scripts/format-grpc-sys
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-clang-format-5.0 -i grpc-sys/grpc_wrap.cc
diff --git a/scripts/generate-bindings.sh b/scripts/generate-bindings.sh
deleted file mode 100755
index 81a043e..0000000
--- a/scripts/generate-bindings.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-# NOTE: 
-# This script is only used when you want to generate bindings yourself.
-# The generated bindings will overwrite grpc-sys/bindings/*
-
-if [ "$ARCH" == "" ]; then
-    ARCH=`uname -p`
-fi
-export UPDATE_BIND=1
-cargo build -p grpcio-sys --target ${ARCH}-unknown-linux-gnu
-rustfmt grpc-sys/bindings/*
-if [ "$(uname -s)" == "Linux" ]; then
-  sed -i '/^pub type .*= ::std::os::raw::.*/d' grpc-sys/bindings/*
-fi
diff --git a/scripts/lint-grpc-sys.sh b/scripts/lint-grpc-sys.sh
deleted file mode 100755
index a3fc7e0..0000000
--- a/scripts/lint-grpc-sys.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-clang-tidy grpc-sys/grpc_wrap.cc -- -Igrpc-sys/grpc/include -x c++ -std=c++11
diff --git a/scripts/reset-submodule.cmd b/scripts/reset-submodule.cmd
deleted file mode 100755
index bac6a35..0000000
--- a/scripts/reset-submodule.cmd
+++ /dev/null
@@ -1,9 +0,0 @@
-git submodule update --init grpc-sys/grpc
-cd grpc-sys/grpc
-git submodule update --init third_party/cares/cares
-git submodule update --init third_party/abseil-cpp
-git submodule update --init third_party/re2
-rm -rf third_party/boringssl-with-bazel/*
-cd third_party/zlib
-git clean -df
-git reset --hard
diff --git a/src/call/mod.rs b/src/call/mod.rs
index 7f1582f..3674623 100644
--- a/src/call/mod.rs
+++ b/src/call/mod.rs
@@ -8,8 +8,8 @@
 use std::sync::Arc;
 use std::{ptr, slice};
 
-use crate::cq::CompletionQueue;
 use crate::grpc_sys::{self, grpc_call, grpc_call_error, grpcwrap_batch_context};
+use crate::{cq::CompletionQueue, Metadata, MetadataBuilder};
 use futures::future::Future;
 use futures::ready;
 use futures::task::{Context, Poll};
@@ -156,10 +156,15 @@
 #[derive(Debug, Clone)]
 pub struct RpcStatus {
     /// gRPC status code. `Ok` indicates success, all other values indicate an error.
-    pub status: RpcStatusCode,
+    code: RpcStatusCode,
 
-    /// Optional detail string.
-    pub details: Option<String>,
+    /// error message.
+    message: String,
+
+    /// Additional details for rich error model.
+    ///
+    /// See also https://grpc.io/docs/guides/error/#richer-error-model.
+    details: Vec<u8>,
 }
 
 impl Display for RpcStatus {
@@ -170,16 +175,54 @@
 
 impl RpcStatus {
     /// Create a new [`RpcStatus`].
-    pub fn new<T: Into<RpcStatusCode>>(code: T, details: Option<String>) -> RpcStatus {
+    pub fn new<T: Into<RpcStatusCode>>(code: T) -> RpcStatus {
+        RpcStatus::with_message(code, String::new())
+    }
+
+    /// Create a new [`RpcStatus`] with given message.
+    pub fn with_message<T: Into<RpcStatusCode>>(code: T, message: String) -> RpcStatus {
+        RpcStatus::with_details(code, message, vec![])
+    }
+
+    /// Create a new [`RpcStats`] with code, message and details.
+    ///
+    /// If using rich error model, `details` should be binary message that sets `code` and
+    /// `message` to the same value. Or you can use `into` method to do automatical
+    /// transformation if using `grpcio_proto::google::rpc::Status`.
+    pub fn with_details<T: Into<RpcStatusCode>>(
+        code: T,
+        message: String,
+        details: Vec<u8>,
+    ) -> RpcStatus {
         RpcStatus {
-            status: code.into(),
+            code: code.into(),
+            message,
             details,
         }
     }
 
     /// Create a new [`RpcStatus`] that status code is Ok.
     pub fn ok() -> RpcStatus {
-        RpcStatus::new(RpcStatusCode::OK, None)
+        RpcStatus::new(RpcStatusCode::OK)
+    }
+
+    /// Return the instance's error code.
+    #[inline]
+    pub fn code(&self) -> RpcStatusCode {
+        self.code
+    }
+
+    /// Return the instance's error message.
+    #[inline]
+    pub fn message(&self) -> &str {
+        &self.message
+    }
+
+    /// Return the (binary) error details.
+    ///
+    /// Usually it contains a serialized `google.rpc.Status` proto.
+    pub fn details(&self) -> &[u8] {
+        &self.details
     }
 }
 
@@ -216,21 +259,26 @@
             grpc_sys::grpcwrap_batch_context_recv_status_on_client_status(self.ctx)
         });
 
-        let details = if status == RpcStatusCode::OK {
-            None
+        if status == RpcStatusCode::OK {
+            RpcStatus::ok()
         } else {
             unsafe {
-                let mut details_len = 0;
+                let mut msg_len = 0;
                 let details_ptr = grpc_sys::grpcwrap_batch_context_recv_status_on_client_details(
                     self.ctx,
-                    &mut details_len,
+                    &mut msg_len,
                 );
-                let details_slice = slice::from_raw_parts(details_ptr as *const _, details_len);
-                Some(String::from_utf8_lossy(details_slice).into_owned())
+                let msg_slice = slice::from_raw_parts(details_ptr as *const _, msg_len);
+                let message = String::from_utf8_lossy(msg_slice).into_owned();
+                let m_ptr =
+                    grpc_sys::grpcwrap_batch_context_recv_status_on_client_trailing_metadata(
+                        self.ctx,
+                    );
+                let metadata = &*(m_ptr as *const Metadata);
+                let details = metadata.search_binary_error_details().to_vec();
+                RpcStatus::with_details(status, message, details)
             }
-        };
-
-        RpcStatus::new(status, details)
+        }
     }
 
     /// Fetch the response bytes of the rpc call.
@@ -352,22 +400,31 @@
         let _cq_ref = self.cq.borrow()?;
         let send_empty_metadata = if send_empty_metadata { 1 } else { 0 };
         let f = check_run(BatchType::Finish, |ctx, tag| unsafe {
-            let details_ptr = status
-                .details
-                .as_ref()
-                .map_or_else(ptr::null, |s| s.as_ptr() as _);
-            let details_len = status.details.as_ref().map_or(0, String::len);
+            let (msg_ptr, msg_len) = if status.code() == RpcStatusCode::OK {
+                (ptr::null(), 0)
+            } else {
+                (status.message.as_ptr(), status.message.len())
+            };
             let payload_p = match payload {
                 Some(p) => p.as_mut_ptr(),
                 None => ptr::null_mut(),
             };
+            let mut trailing_metadata = if status.details.is_empty() {
+                None
+            } else {
+                let mut builder = MetadataBuilder::new();
+                builder.set_binary_error_details(&status.details);
+                Some(builder.build())
+            };
             grpc_sys::grpcwrap_call_send_status_from_server(
                 self.call,
                 ctx,
-                status.status.into(),
-                details_ptr,
-                details_len,
-                ptr::null_mut(),
+                status.code().into(),
+                msg_ptr as _,
+                msg_len,
+                trailing_metadata
+                    .as_mut()
+                    .map_or_else(ptr::null_mut, |m| m as *mut _ as _),
                 send_empty_metadata,
                 payload_p,
                 write_flags,
@@ -390,17 +447,17 @@
         let (batch_ptr, tag_ptr) = box_batch_tag(tag);
 
         let code = unsafe {
-            let details_ptr = status
-                .details
-                .as_ref()
-                .map_or_else(ptr::null, |s| s.as_ptr() as _);
-            let details_len = status.details.as_ref().map_or(0, String::len);
+            let (msg_ptr, msg_len) = if status.code() == RpcStatusCode::OK {
+                (ptr::null(), 0)
+            } else {
+                (status.message.as_ptr(), status.message.len())
+            };
             grpc_sys::grpcwrap_call_send_status_from_server(
                 call_ptr,
                 batch_ptr,
-                status.status.into(),
-                details_ptr,
-                details_len,
+                status.code().into(),
+                msg_ptr as _,
+                msg_len,
                 ptr::null_mut(),
                 1,
                 ptr::null_mut(),
diff --git a/src/call/server.rs b/src/call/server.rs
index 0fed656..58c63bf 100644
--- a/src/call/server.rs
+++ b/src/call/server.rs
@@ -252,7 +252,7 @@
             return execute(self.request, cq, reader, handler, checker);
         }
 
-        let status = RpcStatus::new(RpcStatusCode::INTERNAL, Some("No payload".to_owned()));
+        let status = RpcStatus::with_message(RpcStatusCode::INTERNAL, "No payload".to_owned());
         self.request.call(cq.clone()).abort(&status)
     }
 }
@@ -703,9 +703,9 @@
     let request = match de(payload) {
         Ok(f) => f,
         Err(e) => {
-            let status = RpcStatus::new(
+            let status = RpcStatus::with_message(
                 RpcStatusCode::INTERNAL,
-                Some(format!("Failed to deserialize response message: {:?}", e)),
+                format!("Failed to deserialize response message: {:?}", e),
             );
             call.abort(&status);
             return;
@@ -749,9 +749,9 @@
     let request = match de(payload) {
         Ok(t) => t,
         Err(e) => {
-            let status = RpcStatus::new(
+            let status = RpcStatus::with_message(
                 RpcStatusCode::INTERNAL,
-                Some(format!("Failed to deserialize response message: {:?}", e)),
+                format!("Failed to deserialize response message: {:?}", e),
             );
             call.abort(&status);
             return;
@@ -786,7 +786,7 @@
     let ctx = ctx;
     let mut call = ctx.call(cq);
     accept_call!(call);
-    call.abort(&RpcStatus::new(RpcStatusCode::UNIMPLEMENTED, None))
+    call.abort(&RpcStatus::new(RpcStatusCode::UNIMPLEMENTED))
 }
 
 // Helper function to call handler.
diff --git a/src/channel.rs b/src/channel.rs
index c8c67b1..3b2e10c 100644
--- a/src/channel.rs
+++ b/src/channel.rs
@@ -31,7 +31,7 @@
 
 /// Ref: http://www.grpc.io/docs/guides/wire.html#user-agents
 fn format_user_agent_string(agent: &str) -> CString {
-    let version = "0.8.2";
+    let version = "0.9.0";
     let trimed_agent = agent.trim();
     let val = if trimed_agent.is_empty() {
         format!("grpc-rust/{}", version)
diff --git a/src/error.rs b/src/error.rs
index f12ffa4..7c180a9 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -38,10 +38,13 @@
 impl fmt::Display for Error {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Error::RpcFailure(RpcStatus { status, details }) => match details {
-                Some(details) => write!(fmt, "RpcFailure: {} {}", status, details),
-                None => write!(fmt, "RpcFailure: {}", status),
-            },
+            Error::RpcFailure(s) => {
+                if s.message().is_empty() {
+                    write!(fmt, "RpcFailure: {}", s.code())
+                } else {
+                    write!(fmt, "RpcFailure: {} {}", s.code(), s.message())
+                }
+            }
             other_error => write!(fmt, "{:?}", other_error),
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index 2bb0c11..4727331 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -80,3 +80,24 @@
 pub use crate::server::{
     CheckResult, Server, ServerBuilder, ServerChecker, Service, ServiceBuilder, ShutdownFuture,
 };
+
+/// A shortcut for implementing a service method by returning `UNIMPLEMENTED` status code.
+///
+/// Compiler will provide a default implementations for all methods to invoke this macro, so
+/// you usually won't call it directly. If you really need to, just call it like:
+/// ```ignored
+/// fn method(&self, ctx: grpcio::RpcContext, req: Request, resp: UnarySink<Response>) {
+///     unimplemented_call!(ctx, resp);
+/// }
+/// ```
+#[macro_export]
+macro_rules! unimplemented_call {
+    ($ctx:ident, $sink:ident) => {{
+        let f = async move {
+            let _ = $sink
+                .fail($crate::RpcStatus::new($crate::RpcStatusCode::UNIMPLEMENTED))
+                .await;
+        };
+        $ctx.spawn(f)
+    }};
+}
diff --git a/src/metadata.rs b/src/metadata.rs
index 893f6e2..caaebc8 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -7,6 +7,8 @@
 
 use crate::error::{Error, Result};
 
+const BINARY_ERROR_DETAILS_KEY: &str = "grpc-status-details-bin";
+
 fn normalize_key(key: &str, binary: bool) -> Result<Cow<'_, str>> {
     if key.is_empty() {
         return Err(Error::InvalidMetadata(
@@ -107,6 +109,13 @@
         Ok(self.add_metadata(&key, value))
     }
 
+    /// Set binary error details to support rich error model.
+    ///
+    /// See also https://grpc.io/docs/guides/error/#richer-error-model.
+    pub(crate) fn set_binary_error_details(&mut self, value: &[u8]) -> &mut MetadataBuilder {
+        self.add_metadata(BINARY_ERROR_DETAILS_KEY, value)
+    }
+
     /// Create `Metadata` with configured entries.
     pub fn build(mut self) -> Metadata {
         unsafe {
@@ -214,6 +223,16 @@
             metadata: p,
         })
     }
+
+    /// Search for binary error details.
+    pub(crate) fn search_binary_error_details(&self) -> &[u8] {
+        for (k, v) in self.iter() {
+            if k == BINARY_ERROR_DETAILS_KEY {
+                return v;
+            }
+        }
+        &[]
+    }
 }
 
 impl Clone for Metadata {
diff --git a/src/server.rs b/src/server.rs
index a612b13..c8e28df 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -68,7 +68,7 @@
 /// Given a host and port, creates a string of the form "host:port" or
 /// "[host]:port", depending on whether the host is an IPv6 literal.
 fn join_host_port(host: &str, port: u16) -> String {
-    if host.starts_with("unix:") {
+    if host.starts_with("unix:") | host.starts_with("unix-abstract:") {
         format!("{}\0", host)
     } else if let Ok(ip) = host.parse::<IpAddr>() {
         format!("{}\0", SocketAddr::new(ip, port))
diff --git a/src/task/promise.rs b/src/task/promise.rs
index 50add06..2d826d4 100644
--- a/src/task/promise.rs
+++ b/src/task/promise.rs
@@ -57,7 +57,7 @@
             let mut guard = self.inner.lock();
             if succeed {
                 let status = self.ctx.rpc_status();
-                if status.status == RpcStatusCode::OK {
+                if status.code() == RpcStatusCode::OK {
                     guard.set_result(Ok(None))
                 } else {
                     guard.set_result(Err(Error::RpcFailure(status)))
@@ -73,7 +73,7 @@
         let task = {
             let mut guard = self.inner.lock();
             let status = self.ctx.rpc_status();
-            if status.status == RpcStatusCode::OK {
+            if status.code() == RpcStatusCode::OK {
                 guard.set_result(Ok(self.ctx.recv_message()))
             } else {
                 guard.set_result(Err(Error::RpcFailure(status)))