Merge pull request #14448 from kpayson64/remove_poller

Remove Python background poller thread
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 9c0e9dd..fb6f98c 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -5,6 +5,5 @@
 /bazel/** @nicolasnoble @dgquintas @a11r @vjpai
 /cmake/** @jtattermusch @nicolasnoble @matt-kwong
 /src/core/ext/filters/client_channel/** @markdroth @dgquintas @a11r
-/test/cpp/end2end/** @vjpai @yang-g @y-zeng
 /tools/dockerfile/** @jtattermusch @matt-kwong @nicolasnoble
 /tools/run_tests/performance/** @ncteisen @matt-kwong @jtattermusch
diff --git a/BUILD b/BUILD
index 1c03b7d..9c99f95 100644
--- a/BUILD
+++ b/BUILD
@@ -53,12 +53,22 @@
     values = {"define": "GRPC_PORT_ISOLATED_RUNTIME=1"},
 )
 
+config_setting(
+    name = "windows",
+    values = {"cpu": "x64_windows"},
+)
+
+config_setting(
+    name = "windows_msvc",
+    values = {"cpu": "x64_windows_msvc"},
+)
+
 # This should be updated along with build.yaml
-g_stands_for = "glamorous"
+g_stands_for = "gorgeous"
 
 core_version = "6.0.0-dev"
 
-version = "1.10.0-dev"
+version = "1.11.0-dev"
 
 GPR_PUBLIC_HDRS = [
     "include/grpc/support/alloc.h",
@@ -195,6 +205,53 @@
     "include/grpc++/support/stub_options.h",
     "include/grpc++/support/sync_stream.h",
     "include/grpc++/support/time.h",
+    "include/grpcpp/alarm.h",
+    "include/grpcpp/channel.h",
+    "include/grpcpp/client_context.h",
+    "include/grpcpp/completion_queue.h",
+    "include/grpcpp/create_channel.h",
+    "include/grpcpp/create_channel_posix.h",
+    "include/grpcpp/ext/health_check_service_server_builder_option.h",
+    "include/grpcpp/generic/async_generic_service.h",
+    "include/grpcpp/generic/generic_stub.h",
+    "include/grpcpp/grpcpp.h",
+    "include/grpcpp/health_check_service_interface.h",
+    "include/grpcpp/impl/call.h",
+    "include/grpcpp/impl/channel_argument_option.h",
+    "include/grpcpp/impl/client_unary_call.h",
+    "include/grpcpp/impl/codegen/core_codegen.h",
+    "include/grpcpp/impl/grpc_library.h",
+    "include/grpcpp/impl/method_handler_impl.h",
+    "include/grpcpp/impl/rpc_method.h",
+    "include/grpcpp/impl/rpc_service_method.h",
+    "include/grpcpp/impl/serialization_traits.h",
+    "include/grpcpp/impl/server_builder_option.h",
+    "include/grpcpp/impl/server_builder_plugin.h",
+    "include/grpcpp/impl/server_initializer.h",
+    "include/grpcpp/impl/service_type.h",
+    "include/grpcpp/impl/sync_cxx11.h",
+    "include/grpcpp/impl/sync_no_cxx11.h",
+    "include/grpcpp/resource_quota.h",
+    "include/grpcpp/security/auth_context.h",
+    "include/grpcpp/security/auth_metadata_processor.h",
+    "include/grpcpp/security/credentials.h",
+    "include/grpcpp/security/server_credentials.h",
+    "include/grpcpp/server.h",
+    "include/grpcpp/server_builder.h",
+    "include/grpcpp/server_context.h",
+    "include/grpcpp/server_posix.h",
+    "include/grpcpp/support/async_stream.h",
+    "include/grpcpp/support/async_unary_call.h",
+    "include/grpcpp/support/byte_buffer.h",
+    "include/grpcpp/support/channel_arguments.h",
+    "include/grpcpp/support/config.h",
+    "include/grpcpp/support/slice.h",
+    "include/grpcpp/support/status.h",
+    "include/grpcpp/support/status_code_enum.h",
+    "include/grpcpp/support/string_ref.h",
+    "include/grpcpp/support/stub_options.h",
+    "include/grpcpp/support/sync_stream.h",
+    "include/grpcpp/support/time.h",
 ]
 
 grpc_cc_library(
@@ -317,6 +374,7 @@
     ],
     hdrs = [
         "include/grpc++/support/error_details.h",
+        "include/grpcpp/support/error_details.h",
     ],
     language = "c++",
     standalone = True,
@@ -469,9 +527,6 @@
         "src/core/lib/gpr/sync.cc",
         "src/core/lib/gpr/sync_posix.cc",
         "src/core/lib/gpr/sync_windows.cc",
-        "src/core/lib/gpr/thd.cc",
-        "src/core/lib/gpr/thd_posix.cc",
-        "src/core/lib/gpr/thd_windows.cc",
         "src/core/lib/gpr/time.cc",
         "src/core/lib/gpr/time_posix.cc",
         "src/core/lib/gpr/time_precise.cc",
@@ -481,6 +536,8 @@
         "src/core/lib/gpr/tmpfile_posix.cc",
         "src/core/lib/gpr/tmpfile_windows.cc",
         "src/core/lib/gpr/wrap_memcpy.cc",
+        "src/core/lib/gprpp/thd_posix.cc",
+        "src/core/lib/gprpp/thd_windows.cc",
         "src/core/lib/profiling/basic_timers.cc",
         "src/core/lib/profiling/stap_timers.cc",
     ],
@@ -494,7 +551,6 @@
         "src/core/lib/gpr/spinlock.h",
         "src/core/lib/gpr/string.h",
         "src/core/lib/gpr/string_windows.h",
-        "src/core/lib/gpr/thd.h",
         "src/core/lib/gpr/time_precise.h",
         "src/core/lib/gpr/tls.h",
         "src/core/lib/gpr/tls_gcc.h",
@@ -502,6 +558,10 @@
         "src/core/lib/gpr/tls_pthread.h",
         "src/core/lib/gpr/tmpfile.h",
         "src/core/lib/gpr/useful.h",
+        "src/core/lib/gprpp/abstract.h",
+        "src/core/lib/gprpp/manual_constructor.h",
+        "src/core/lib/gprpp/memory.h",
+        "src/core/lib/gprpp/thd.h",
         "src/core/lib/profiling/timers.h",
     ],
     language = "c++",
@@ -544,16 +604,6 @@
 )
 
 grpc_cc_library(
-    name = "gpr++_base",
-    language = "c++",
-    public_hdrs = [
-        "src/core/lib/gprpp/abstract.h",
-        "src/core/lib/gprpp/manual_constructor.h",
-        "src/core/lib/gprpp/memory.h",
-    ],
-)
-
-grpc_cc_library(
     name = "atomic",
     hdrs = [
         "src/core/lib/gprpp/atomic_with_atm.h",
@@ -575,7 +625,7 @@
         "src/core/lib/gprpp/inlined_vector.h",
     ],
     deps = [
-        "gpr++_base",
+        "gpr_base",
     ],
 )
 
@@ -591,7 +641,7 @@
     public_hdrs = ["src/core/lib/gprpp/orphanable.h"],
     deps = [
         "debug_location",
-        "gpr++_base",
+        "gpr_base",
         "grpc_trace",
         "ref_counted_ptr",
     ],
@@ -603,7 +653,7 @@
     public_hdrs = ["src/core/lib/gprpp/ref_counted.h"],
     deps = [
         "debug_location",
-        "gpr++_base",
+        "gpr_base",
         "grpc_trace",
         "ref_counted_ptr",
     ],
@@ -614,7 +664,7 @@
     language = "c++",
     public_hdrs = ["src/core/lib/gprpp/ref_counted_ptr.h"],
     deps = [
-        "gpr++_base",
+        "gpr_base",
     ],
 )
 
@@ -721,7 +771,6 @@
         "src/core/lib/slice/percent_encoding.cc",
         "src/core/lib/slice/slice.cc",
         "src/core/lib/slice/slice_buffer.cc",
-        "src/core/lib/slice/slice_hash_table.cc",
         "src/core/lib/slice/slice_intern.cc",
         "src/core/lib/slice/slice_string_helpers.cc",
         "src/core/lib/surface/api_trace.cc",
@@ -751,6 +800,7 @@
         "src/core/lib/transport/service_config.cc",
         "src/core/lib/transport/static_metadata.cc",
         "src/core/lib/transport/status_conversion.cc",
+        "src/core/lib/transport/status_metadata.cc",
         "src/core/lib/transport/timeout_encoding.cc",
         "src/core/lib/transport/transport.cc",
         "src/core/lib/transport/transport_op_string.cc",
@@ -849,6 +899,7 @@
         "src/core/lib/slice/slice_hash_table.h",
         "src/core/lib/slice/slice_internal.h",
         "src/core/lib/slice/slice_string_helpers.h",
+        "src/core/lib/slice/slice_weak_hash_table.h",
         "src/core/lib/surface/api_trace.h",
         "src/core/lib/surface/call.h",
         "src/core/lib/surface/call_test_only.h",
@@ -873,6 +924,7 @@
         "src/core/lib/transport/service_config.h",
         "src/core/lib/transport/static_metadata.h",
         "src/core/lib/transport/status_conversion.h",
+        "src/core/lib/transport/status_metadata.h",
         "src/core/lib/transport/timeout_encoding.h",
         "src/core/lib/transport/transport.h",
         "src/core/lib/transport/transport_impl.h",
@@ -883,10 +935,12 @@
     language = "c++",
     public_hdrs = GRPC_PUBLIC_HDRS,
     deps = [
-        "gpr++_base",
         "gpr_base",
         "grpc_codegen",
         "grpc_trace",
+        "inlined_vector",
+        "ref_counted",
+        "ref_counted_ptr",
     ],
 )
 
@@ -941,12 +995,14 @@
         "src/core/ext/filters/client_channel/lb_policy.cc",
         "src/core/ext/filters/client_channel/lb_policy_factory.cc",
         "src/core/ext/filters/client_channel/lb_policy_registry.cc",
+        "src/core/ext/filters/client_channel/method_params.cc",
         "src/core/ext/filters/client_channel/parse_address.cc",
         "src/core/ext/filters/client_channel/proxy_mapper.cc",
         "src/core/ext/filters/client_channel/proxy_mapper_registry.cc",
         "src/core/ext/filters/client_channel/resolver.cc",
         "src/core/ext/filters/client_channel/resolver_registry.cc",
         "src/core/ext/filters/client_channel/retry_throttle.cc",
+        "src/core/ext/filters/client_channel/status_util.cc",
         "src/core/ext/filters/client_channel/subchannel.cc",
         "src/core/ext/filters/client_channel/subchannel_index.cc",
         "src/core/ext/filters/client_channel/uri_parser.cc",
@@ -961,6 +1017,7 @@
         "src/core/ext/filters/client_channel/lb_policy.h",
         "src/core/ext/filters/client_channel/lb_policy_factory.h",
         "src/core/ext/filters/client_channel/lb_policy_registry.h",
+        "src/core/ext/filters/client_channel/method_params.h",
         "src/core/ext/filters/client_channel/parse_address.h",
         "src/core/ext/filters/client_channel/proxy_mapper.h",
         "src/core/ext/filters/client_channel/proxy_mapper_registry.h",
@@ -968,6 +1025,7 @@
         "src/core/ext/filters/client_channel/resolver_factory.h",
         "src/core/ext/filters/client_channel/resolver_registry.h",
         "src/core/ext/filters/client_channel/retry_throttle.h",
+        "src/core/ext/filters/client_channel/status_util.h",
         "src/core/ext/filters/client_channel/subchannel.h",
         "src/core/ext/filters/client_channel/subchannel_index.h",
         "src/core/ext/filters/client_channel/uri_parser.h",
@@ -1090,7 +1148,6 @@
     ],
     hdrs = [
         "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
-        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
@@ -1119,7 +1176,6 @@
     ],
     hdrs = [
         "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
-        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
@@ -1270,12 +1326,14 @@
         "src/core/lib/security/credentials/oauth2/oauth2_credentials.cc",
         "src/core/lib/security/credentials/plugin/plugin_credentials.cc",
         "src/core/lib/security/credentials/ssl/ssl_credentials.cc",
+        "src/core/lib/security/credentials/alts/alts_credentials.cc",
+        "src/core/lib/security/security_connector/alts_security_connector.cc",
+        "src/core/lib/security/security_connector/security_connector.cc",
         "src/core/lib/security/transport/client_auth_filter.cc",
-        "src/core/lib/security/transport/lb_targets_info.cc",
         "src/core/lib/security/transport/secure_endpoint.cc",
-        "src/core/lib/security/transport/security_connector.cc",
         "src/core/lib/security/transport/security_handshaker.cc",
         "src/core/lib/security/transport/server_auth_filter.cc",
+        "src/core/lib/security/transport/target_authority_table.cc",
         "src/core/lib/security/transport/tsi_error.cc",
         "src/core/lib/security/util/json_util.cc",
         "src/core/lib/surface/init_secure.cc",
@@ -1293,17 +1351,20 @@
         "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
         "src/core/lib/security/credentials/plugin/plugin_credentials.h",
         "src/core/lib/security/credentials/ssl/ssl_credentials.h",
+        "src/core/lib/security/credentials/alts/alts_credentials.h",
+        "src/core/lib/security/security_connector/alts_security_connector.h",
+        "src/core/lib/security/security_connector/security_connector.h",
         "src/core/lib/security/transport/auth_filters.h",
-        "src/core/lib/security/transport/lb_targets_info.h",
         "src/core/lib/security/transport/secure_endpoint.h",
-        "src/core/lib/security/transport/security_connector.h",
         "src/core/lib/security/transport/security_handshaker.h",
+        "src/core/lib/security/transport/target_authority_table.h",
         "src/core/lib/security/transport/tsi_error.h",
         "src/core/lib/security/util/json_util.h",
     ],
     language = "c++",
     public_hdrs = GRPC_SECURE_PUBLIC_HDRS,
     deps = [
+        "alts_util",
         "grpc_base",
         "grpc_transport_chttp2_alpn",
         "tsi",
@@ -1360,7 +1421,7 @@
     ],
     language = "c++",
     deps = [
-        "gpr++_base",
+        "gpr_base",
         "grpc_base",
         "grpc_http_filters",
         "grpc_transport_chttp2_alpn",
@@ -1527,15 +1588,118 @@
 )
 
 grpc_cc_library(
+    name = "alts_frame_protector",
+    srcs = [
+        "src/core/tsi/alts/crypt/aes_gcm.cc",
+        "src/core/tsi/alts/crypt/gsec.cc",
+        "src/core/tsi/alts/frame_protector/alts_counter.cc",
+        "src/core/tsi/alts/frame_protector/alts_crypter.cc",
+        "src/core/tsi/alts/frame_protector/alts_frame_protector.cc",
+        "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc",
+        "src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc",
+        "src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc",
+        "src/core/tsi/alts/frame_protector/frame_handler.cc",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc",
+    ],
+    hdrs = [
+        "src/core/tsi/alts/crypt/gsec.h",
+        "src/core/tsi/alts/frame_protector/alts_counter.h",
+        "src/core/tsi/alts/frame_protector/alts_crypter.h",
+        "src/core/tsi/alts/frame_protector/alts_frame_protector.h",
+        "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h",
+        "src/core/tsi/alts/frame_protector/frame_handler.h",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h",
+        "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h",
+        "src/core/tsi/transport_security_grpc.h",
+    ],
+    external_deps = [
+        "libssl",
+    ],
+    language = "c++",
+    deps = [
+        "gpr",
+        "grpc_base",
+        "tsi_interface",
+    ],
+)
+
+grpc_cc_library(
+    name = "alts_proto",
+    srcs = [
+        "src/core/tsi/alts/handshaker/altscontext.pb.c",
+        "src/core/tsi/alts/handshaker/handshaker.pb.c",
+        "src/core/tsi/alts/handshaker/transport_security_common.pb.c",
+    ],
+    hdrs = [
+        "src/core/tsi/alts/handshaker/altscontext.pb.h",
+        "src/core/tsi/alts/handshaker/handshaker.pb.h",
+        "src/core/tsi/alts/handshaker/transport_security_common.pb.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c++",
+)
+
+grpc_cc_library(
+    name = "alts_util",
+    srcs = [
+        "src/core/lib/security/credentials/alts/check_gcp_environment.cc",
+        "src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc",
+        "src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc",
+        "src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc",
+        "src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc",
+        "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc",
+        "src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc",
+        "src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc",
+        "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc",
+        "src/core/tsi/alts/handshaker/transport_security_common_api.cc",
+    ],
+    hdrs = [
+        "src/core/lib/security/credentials/alts/check_gcp_environment.h",
+        "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h",
+        "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h",
+        "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h",
+        "src/core/tsi/alts/handshaker/transport_security_common_api.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c++",
+    deps = [
+      "alts_proto",
+      "gpr",
+      "grpc_base",
+    ],
+)
+
+grpc_cc_library(
     name = "tsi",
     srcs = [
         "src/core/tsi/alts_transport_security.cc",
+        "src/core/tsi/alts/handshaker/alts_handshaker_client.cc",
+        "src/core/tsi/alts/handshaker/alts_tsi_event.cc",
+        "src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc",
+        "src/core/tsi/alts/handshaker/alts_tsi_utils.cc",
         "src/core/tsi/fake_transport_security.cc",
         "src/core/tsi/ssl_transport_security.cc",
         "src/core/tsi/transport_security_grpc.cc",
     ],
     hdrs = [
         "src/core/tsi/alts_transport_security.h",
+        "src/core/tsi/alts/handshaker/alts_handshaker_client.h",
+        "src/core/tsi/alts/handshaker/alts_tsi_event.h",
+        "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h",
+        "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h",
+        "src/core/tsi/alts/handshaker/alts_tsi_utils.h",
         "src/core/tsi/fake_transport_security.h",
         "src/core/tsi/ssl_transport_security.h",
         "src/core/tsi/ssl_types.h",
@@ -1546,7 +1710,11 @@
     ],
     language = "c++",
     deps = [
+        "alts_frame_protector",
+        "alts_util",
+        "gpr",
         "grpc_base",
+        "grpc_transport_chttp2_client_insecure",
         "tsi_interface",
     ],
 )
@@ -1609,6 +1777,36 @@
         "include/grpc++/impl/codegen/stub_options.h",
         "include/grpc++/impl/codegen/sync_stream.h",
         "include/grpc++/impl/codegen/time.h",
+        "include/grpcpp/impl/codegen/async_stream.h",
+        "include/grpcpp/impl/codegen/async_unary_call.h",
+        "include/grpcpp/impl/codegen/byte_buffer.h",
+        "include/grpcpp/impl/codegen/call.h",
+        "include/grpcpp/impl/codegen/call_hook.h",
+        "include/grpcpp/impl/codegen/channel_interface.h",
+        "include/grpcpp/impl/codegen/client_context.h",
+        "include/grpcpp/impl/codegen/client_unary_call.h",
+        "include/grpcpp/impl/codegen/completion_queue.h",
+        "include/grpcpp/impl/codegen/completion_queue_tag.h",
+        "include/grpcpp/impl/codegen/config.h",
+        "include/grpcpp/impl/codegen/core_codegen_interface.h",
+        "include/grpcpp/impl/codegen/create_auth_context.h",
+        "include/grpcpp/impl/codegen/grpc_library.h",
+        "include/grpcpp/impl/codegen/metadata_map.h",
+        "include/grpcpp/impl/codegen/method_handler_impl.h",
+        "include/grpcpp/impl/codegen/rpc_method.h",
+        "include/grpcpp/impl/codegen/rpc_service_method.h",
+        "include/grpcpp/impl/codegen/security/auth_context.h",
+        "include/grpcpp/impl/codegen/serialization_traits.h",
+        "include/grpcpp/impl/codegen/server_context.h",
+        "include/grpcpp/impl/codegen/server_interface.h",
+        "include/grpcpp/impl/codegen/service_type.h",
+        "include/grpcpp/impl/codegen/slice.h",
+        "include/grpcpp/impl/codegen/status.h",
+        "include/grpcpp/impl/codegen/status_code_enum.h",
+        "include/grpcpp/impl/codegen/string_ref.h",
+        "include/grpcpp/impl/codegen/stub_options.h",
+        "include/grpcpp/impl/codegen/sync_stream.h",
+        "include/grpcpp/impl/codegen/time.h",
     ],
     deps = [
         "grpc_codegen",
@@ -1631,6 +1829,7 @@
     language = "c++",
     public_hdrs = [
         "include/grpc++/impl/codegen/proto_utils.h",
+        "include/grpcpp/impl/codegen/proto_utils.h",
     ],
     deps = [
         "grpc++_codegen_base",
@@ -1646,6 +1845,7 @@
     language = "c++",
     public_hdrs = [
         "include/grpc++/impl/codegen/config_protobuf.h",
+        "include/grpcpp/impl/codegen/config_protobuf.h",
     ],
 )
 
@@ -1661,6 +1861,7 @@
     language = "c++",
     public_hdrs = [
         "include/grpc++/ext/proto_server_reflection_plugin.h",
+        "include/grpcpp/ext/proto_server_reflection_plugin.h",
     ],
     deps = [
         ":grpc++",
@@ -1674,6 +1875,8 @@
     public_hdrs = [
         "include/grpc++/test/mock_stream.h",
         "include/grpc++/test/server_context_test_spouse.h",
+        "include/grpcpp/test/mock_stream.h",
+        "include/grpcpp/test/server_context_test_spouse.h",
     ],
     deps = [
         ":grpc++",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2f2a51b..5dfbdcb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,7 +24,7 @@
 cmake_minimum_required(VERSION 2.8)
 
 set(PACKAGE_NAME      "grpc")
-set(PACKAGE_VERSION   "1.10.0-dev")
+set(PACKAGE_VERSION   "1.11.0-dev")
 set(PACKAGE_STRING    "${PACKAGE_NAME} ${PACKAGE_VERSION}")
 set(PACKAGE_TARNAME   "${PACKAGE_NAME}-${PACKAGE_VERSION}")
 set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
@@ -37,6 +37,7 @@
 
 # Options
 option(gRPC_BUILD_TESTS "Build tests" OFF)
+option(gRPC_BUILD_CODEGEN "Build codegen" ON)
 
 set(gRPC_INSTALL_default ON)
 if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
@@ -78,6 +79,8 @@
     set(_gRPC_PLATFORM_LINUX ON)
   elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
     set(_gRPC_PLATFORM_MAC ON)
+  elseif(${CMAKE_SYSTEM_NAME} MATCHES "Android")
+    set(_gRPC_PLATFORM_ANDROID ON)
   else()
     set(_gRPC_PLATFORM_POSIX ON)
   endif()
@@ -88,6 +91,8 @@
 
 set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
 
+add_definitions(-DPB_FIELD_16BIT)
+
 if (MSVC)
   include(cmake/msvc_static_runtime.cmake)
   add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS)
@@ -120,6 +125,8 @@
 
 if(_gRPC_PLATFORM_MAC)
   set(_gRPC_ALLTARGETS_LIBRARIES ${CMAKE_DL_LIBS} m pthread)
+elseif(_gRPC_PLATFORM_ANDROID)
+  set(_gRPC_ALLTARGETS_LIBRARIES ${CMAKE_DL_LIBS} m)
 elseif(UNIX)
   set(_gRPC_ALLTARGETS_LIBRARIES ${CMAKE_DL_LIBS} rt m pthread)
 endif()
@@ -344,7 +351,6 @@
 add_dependencies(buildtests_c server_chttp2_test)
 add_dependencies(buildtests_c server_test)
 add_dependencies(buildtests_c slice_buffer_test)
-add_dependencies(buildtests_c slice_hash_table_test)
 add_dependencies(buildtests_c slice_string_helpers_test)
 add_dependencies(buildtests_c slice_test)
 add_dependencies(buildtests_c sockaddr_resolver_test)
@@ -465,6 +471,19 @@
 
 add_custom_target(buildtests_cxx)
 add_dependencies(buildtests_cxx alarm_test)
+add_dependencies(buildtests_cxx alts_counter_test)
+add_dependencies(buildtests_cxx alts_crypt_test)
+add_dependencies(buildtests_cxx alts_crypter_test)
+add_dependencies(buildtests_cxx alts_frame_handler_test)
+add_dependencies(buildtests_cxx alts_frame_protector_test)
+add_dependencies(buildtests_cxx alts_grpc_record_protocol_test)
+add_dependencies(buildtests_cxx alts_handshaker_client_test)
+add_dependencies(buildtests_cxx alts_handshaker_service_api_test)
+add_dependencies(buildtests_cxx alts_iovec_record_protocol_test)
+add_dependencies(buildtests_cxx alts_security_connector_test)
+add_dependencies(buildtests_cxx alts_tsi_handshaker_test)
+add_dependencies(buildtests_cxx alts_tsi_utils_test)
+add_dependencies(buildtests_cxx alts_zero_copy_grpc_protector_test)
 add_dependencies(buildtests_cxx async_end2end_test)
 add_dependencies(buildtests_cxx auth_property_iterator_test)
 add_dependencies(buildtests_cxx backoff_test)
@@ -513,6 +532,8 @@
 endif()
 add_dependencies(buildtests_cxx channel_arguments_test)
 add_dependencies(buildtests_cxx channel_filter_test)
+add_dependencies(buildtests_cxx check_gcp_environment_linux_test)
+add_dependencies(buildtests_cxx check_gcp_environment_windows_test)
 add_dependencies(buildtests_cxx chttp2_settings_timeout_test)
 add_dependencies(buildtests_cxx cli_call_test)
 add_dependencies(buildtests_cxx client_channel_stress_test)
@@ -534,11 +555,11 @@
 add_dependencies(buildtests_cxx filter_end2end_test)
 add_dependencies(buildtests_cxx generic_end2end_test)
 add_dependencies(buildtests_cxx golden_file_test)
+add_dependencies(buildtests_cxx grpc_alts_credentials_options_test)
 add_dependencies(buildtests_cxx grpc_cli)
 add_dependencies(buildtests_cxx grpc_tool_test)
 add_dependencies(buildtests_cxx grpclb_api_test)
 add_dependencies(buildtests_cxx grpclb_end2end_test)
-add_dependencies(buildtests_cxx grpclb_test)
 add_dependencies(buildtests_cxx h2_ssl_cert_test)
 add_dependencies(buildtests_cxx health_service_end2end_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@@ -595,8 +616,12 @@
 add_dependencies(buildtests_cxx server_early_return_test)
 add_dependencies(buildtests_cxx server_request_call_test)
 add_dependencies(buildtests_cxx shutdown_test)
+add_dependencies(buildtests_cxx slice_hash_table_test)
+add_dependencies(buildtests_cxx slice_weak_hash_table_test)
 add_dependencies(buildtests_cxx stats_test)
+add_dependencies(buildtests_cxx status_metadata_test)
 add_dependencies(buildtests_cxx status_test)
+add_dependencies(buildtests_cxx status_util_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx streaming_throughput_test)
 endif()
@@ -604,6 +629,7 @@
 add_dependencies(buildtests_cxx thread_manager_test)
 add_dependencies(buildtests_cxx thread_stress_test)
 add_dependencies(buildtests_cxx transport_pid_controller_test)
+add_dependencies(buildtests_cxx transport_security_common_api_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx writes_per_rpc_test)
 endif()
@@ -624,6 +650,44 @@
   DEPENDS buildtests_c buildtests_cxx)
 endif (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_TESTS)
+
+add_library(alts_test_util
+  test/core/tsi/alts/crypt/gsec_test_util.cc
+  test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc
+)
+
+if(WIN32 AND MSVC)
+  set_target_properties(alts_test_util PROPERTIES COMPILE_PDB_NAME "alts_test_util"
+    COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
+  )
+  if (gRPC_INSTALL)
+    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/alts_test_util.pdb
+      DESTINATION ${gRPC_INSTALL_LIBDIR} OPTIONAL
+    )
+  endif()
+endif()
+
+
+target_include_directories(alts_test_util
+  PUBLIC $<INSTALL_INTERFACE:${gRPC_INSTALL_INCLUDEDIR}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+)
+
+target_link_libraries(alts_test_util
+  ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc
+)
+
+
+endif (gRPC_BUILD_TESTS)
 
 add_library(gpr
   src/core/lib/gpr/alloc.cc
@@ -652,9 +716,6 @@
   src/core/lib/gpr/sync.cc
   src/core/lib/gpr/sync_posix.cc
   src/core/lib/gpr/sync_windows.cc
-  src/core/lib/gpr/thd.cc
-  src/core/lib/gpr/thd_posix.cc
-  src/core/lib/gpr/thd_windows.cc
   src/core/lib/gpr/time.cc
   src/core/lib/gpr/time_posix.cc
   src/core/lib/gpr/time_precise.cc
@@ -664,6 +725,8 @@
   src/core/lib/gpr/tmpfile_posix.cc
   src/core/lib/gpr/tmpfile_windows.cc
   src/core/lib/gpr/wrap_memcpy.cc
+  src/core/lib/gprpp/thd_posix.cc
+  src/core/lib/gprpp/thd_windows.cc
   src/core/lib/profiling/basic_timers.cc
   src/core/lib/profiling/stap_timers.cc
 )
@@ -694,6 +757,12 @@
 target_link_libraries(gpr
   ${_gRPC_ALLTARGETS_LIBRARIES}
 )
+if (_gRPC_PLATFORM_ANDROID)
+  target_link_libraries(gpr
+    android
+    log
+  )
+endif (_gRPC_PLATFORM_ANDROID)
 
 foreach(_hdr
   include/grpc/support/alloc.h
@@ -882,7 +951,6 @@
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
   src/core/lib/slice/slice_buffer.cc
-  src/core/lib/slice/slice_hash_table.cc
   src/core/lib/slice/slice_intern.cc
   src/core/lib/slice/slice_string_helpers.cc
   src/core/lib/surface/api_trace.cc
@@ -913,6 +981,7 @@
   src/core/lib/transport/service_config.cc
   src/core/lib/transport/static_metadata.cc
   src/core/lib/transport/status_conversion.cc
+  src/core/lib/transport/status_metadata.cc
   src/core/lib/transport/timeout_encoding.cc
   src/core/lib/transport/transport.cc
   src/core/lib/transport/transport_op_string.cc
@@ -947,6 +1016,7 @@
   src/core/ext/filters/http/server/http_server_filter.cc
   src/core/lib/http/httpcli_security_connector.cc
   src/core/lib/security/context/security_context.cc
+  src/core/lib/security/credentials/alts/alts_credentials.cc
   src/core/lib/security/credentials/composite/composite_credentials.cc
   src/core/lib/security/credentials/credentials.cc
   src/core/lib/security/credentials/credentials_metadata.cc
@@ -960,23 +1030,55 @@
   src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
   src/core/lib/security/credentials/plugin/plugin_credentials.cc
   src/core/lib/security/credentials/ssl/ssl_credentials.cc
+  src/core/lib/security/security_connector/alts_security_connector.cc
+  src/core/lib/security/security_connector/security_connector.cc
   src/core/lib/security/transport/client_auth_filter.cc
-  src/core/lib/security/transport/lb_targets_info.cc
   src/core/lib/security/transport/secure_endpoint.cc
-  src/core/lib/security/transport/security_connector.cc
   src/core/lib/security/transport/security_handshaker.cc
   src/core/lib/security/transport/server_auth_filter.cc
+  src/core/lib/security/transport/target_authority_table.cc
   src/core/lib/security/transport/tsi_error.cc
   src/core/lib/security/util/json_util.cc
   src/core/lib/surface/init_secure.cc
-  src/core/tsi/alts_transport_security.cc
-  src/core/tsi/fake_transport_security.cc
-  src/core/tsi/ssl_transport_security.cc
-  src/core/tsi/transport_security_grpc.cc
+  src/core/tsi/alts/crypt/aes_gcm.cc
+  src/core/tsi/alts/crypt/gsec.cc
+  src/core/tsi/alts/frame_protector/alts_counter.cc
+  src/core/tsi/alts/frame_protector/alts_crypter.cc
+  src/core/tsi/alts/frame_protector/alts_frame_protector.cc
+  src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
+  src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
+  src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
+  src/core/tsi/alts/frame_protector/frame_handler.cc
+  src/core/tsi/alts/handshaker/alts_handshaker_client.cc
+  src/core/tsi/alts/handshaker/alts_tsi_event.cc
+  src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
+  src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
+  src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
+  src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
+  src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
+  src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
+  src/core/tsi/alts/handshaker/alts_tsi_utils.cc
+  src/core/tsi/alts/handshaker/transport_security_common_api.cc
+  src/core/tsi/alts/handshaker/altscontext.pb.c
+  src/core/tsi/alts/handshaker/handshaker.pb.c
+  src/core/tsi/alts/handshaker/transport_security_common.pb.c
+  third_party/nanopb/pb_common.c
+  third_party/nanopb/pb_decode.c
+  third_party/nanopb/pb_encode.c
   src/core/tsi/transport_security.cc
   src/core/tsi/transport_security_adapter.cc
-  src/core/ext/transport/chttp2/server/chttp2_server.cc
-  src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
+  src/core/ext/transport/chttp2/client/insecure/channel_create.cc
+  src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
+  src/core/ext/transport/chttp2/client/chttp2_connector.cc
   src/core/ext/filters/client_channel/backup_poller.cc
   src/core/ext/filters/client_channel/channel_connectivity.cc
   src/core/ext/filters/client_channel/client_channel.cc
@@ -988,21 +1090,26 @@
   src/core/ext/filters/client_channel/lb_policy.cc
   src/core/ext/filters/client_channel/lb_policy_factory.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
+  src/core/ext/filters/client_channel/method_params.cc
   src/core/ext/filters/client_channel/parse_address.cc
   src/core/ext/filters/client_channel/proxy_mapper.cc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver_registry.cc
   src/core/ext/filters/client_channel/retry_throttle.cc
+  src/core/ext/filters/client_channel/status_util.cc
   src/core/ext/filters/client_channel/subchannel.cc
   src/core/ext/filters/client_channel/subchannel_index.cc
   src/core/ext/filters/client_channel/uri_parser.cc
   src/core/ext/filters/deadline/deadline_filter.cc
-  src/core/ext/transport/chttp2/client/chttp2_connector.cc
+  src/core/tsi/alts_transport_security.cc
+  src/core/tsi/fake_transport_security.cc
+  src/core/tsi/ssl_transport_security.cc
+  src/core/tsi/transport_security_grpc.cc
+  src/core/ext/transport/chttp2/server/chttp2_server.cc
+  src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
   src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
   src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
-  src/core/ext/transport/chttp2/client/insecure/channel_create.cc
-  src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
   src/core/ext/transport/inproc/inproc_plugin.cc
   src/core/ext/transport/inproc/inproc_transport.cc
   src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
@@ -1011,9 +1118,6 @@
   src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
   src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
   src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
-  third_party/nanopb/pb_common.c
-  third_party/nanopb/pb_decode.c
-  third_party/nanopb/pb_encode.c
   src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
   src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
   src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
@@ -1222,7 +1326,6 @@
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
   src/core/lib/slice/slice_buffer.cc
-  src/core/lib/slice/slice_hash_table.cc
   src/core/lib/slice/slice_intern.cc
   src/core/lib/slice/slice_string_helpers.cc
   src/core/lib/surface/api_trace.cc
@@ -1253,6 +1356,7 @@
   src/core/lib/transport/service_config.cc
   src/core/lib/transport/static_metadata.cc
   src/core/lib/transport/status_conversion.cc
+  src/core/lib/transport/status_metadata.cc
   src/core/lib/transport/timeout_encoding.cc
   src/core/lib/transport/transport.cc
   src/core/lib/transport/transport_op_string.cc
@@ -1299,18 +1403,21 @@
   src/core/ext/filters/client_channel/lb_policy.cc
   src/core/ext/filters/client_channel/lb_policy_factory.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
+  src/core/ext/filters/client_channel/method_params.cc
   src/core/ext/filters/client_channel/parse_address.cc
   src/core/ext/filters/client_channel/proxy_mapper.cc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver_registry.cc
   src/core/ext/filters/client_channel/retry_throttle.cc
+  src/core/ext/filters/client_channel/status_util.cc
   src/core/ext/filters/client_channel/subchannel.cc
   src/core/ext/filters/client_channel/subchannel_index.cc
   src/core/ext/filters/client_channel/uri_parser.cc
   src/core/ext/filters/deadline/deadline_filter.cc
   src/core/lib/http/httpcli_security_connector.cc
   src/core/lib/security/context/security_context.cc
+  src/core/lib/security/credentials/alts/alts_credentials.cc
   src/core/lib/security/credentials/composite/composite_credentials.cc
   src/core/lib/security/credentials/credentials.cc
   src/core/lib/security/credentials/credentials_metadata.cc
@@ -1324,22 +1431,59 @@
   src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
   src/core/lib/security/credentials/plugin/plugin_credentials.cc
   src/core/lib/security/credentials/ssl/ssl_credentials.cc
+  src/core/lib/security/security_connector/alts_security_connector.cc
+  src/core/lib/security/security_connector/security_connector.cc
   src/core/lib/security/transport/client_auth_filter.cc
-  src/core/lib/security/transport/lb_targets_info.cc
   src/core/lib/security/transport/secure_endpoint.cc
-  src/core/lib/security/transport/security_connector.cc
   src/core/lib/security/transport/security_handshaker.cc
   src/core/lib/security/transport/server_auth_filter.cc
+  src/core/lib/security/transport/target_authority_table.cc
   src/core/lib/security/transport/tsi_error.cc
   src/core/lib/security/util/json_util.cc
   src/core/lib/surface/init_secure.cc
+  src/core/tsi/alts/crypt/aes_gcm.cc
+  src/core/tsi/alts/crypt/gsec.cc
+  src/core/tsi/alts/frame_protector/alts_counter.cc
+  src/core/tsi/alts/frame_protector/alts_crypter.cc
+  src/core/tsi/alts/frame_protector/alts_frame_protector.cc
+  src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
+  src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
+  src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
+  src/core/tsi/alts/frame_protector/frame_handler.cc
+  src/core/tsi/alts/handshaker/alts_handshaker_client.cc
+  src/core/tsi/alts/handshaker/alts_tsi_event.cc
+  src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
+  src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
+  src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
+  src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
+  src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
+  src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
+  src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
+  src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
+  src/core/tsi/alts/handshaker/alts_tsi_utils.cc
+  src/core/tsi/alts/handshaker/transport_security_common_api.cc
+  src/core/tsi/alts/handshaker/altscontext.pb.c
+  src/core/tsi/alts/handshaker/handshaker.pb.c
+  src/core/tsi/alts/handshaker/transport_security_common.pb.c
+  third_party/nanopb/pb_common.c
+  third_party/nanopb/pb_decode.c
+  third_party/nanopb/pb_encode.c
+  src/core/tsi/transport_security.cc
+  src/core/tsi/transport_security_adapter.cc
+  src/core/ext/transport/chttp2/client/insecure/channel_create.cc
+  src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
+  src/core/ext/transport/chttp2/client/chttp2_connector.cc
   src/core/tsi/alts_transport_security.cc
   src/core/tsi/fake_transport_security.cc
   src/core/tsi/ssl_transport_security.cc
   src/core/tsi/transport_security_grpc.cc
-  src/core/tsi/transport_security.cc
-  src/core/tsi/transport_security_adapter.cc
-  src/core/ext/transport/chttp2/client/chttp2_connector.cc
   src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
   src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc
   src/core/plugin_registry/grpc_cronet_plugin_registry.cc
@@ -1548,7 +1692,6 @@
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
   src/core/lib/slice/slice_buffer.cc
-  src/core/lib/slice/slice_hash_table.cc
   src/core/lib/slice/slice_intern.cc
   src/core/lib/slice/slice_string_helpers.cc
   src/core/lib/surface/api_trace.cc
@@ -1579,6 +1722,7 @@
   src/core/lib/transport/service_config.cc
   src/core/lib/transport/static_metadata.cc
   src/core/lib/transport/status_conversion.cc
+  src/core/lib/transport/status_metadata.cc
   src/core/lib/transport/timeout_encoding.cc
   src/core/lib/transport/transport.cc
   src/core/lib/transport/transport_op_string.cc
@@ -1594,12 +1738,14 @@
   src/core/ext/filters/client_channel/lb_policy.cc
   src/core/ext/filters/client_channel/lb_policy_factory.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
+  src/core/ext/filters/client_channel/method_params.cc
   src/core/ext/filters/client_channel/parse_address.cc
   src/core/ext/filters/client_channel/proxy_mapper.cc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver_registry.cc
   src/core/ext/filters/client_channel/retry_throttle.cc
+  src/core/ext/filters/client_channel/status_util.cc
   src/core/ext/filters/client_channel/subchannel.cc
   src/core/ext/filters/client_channel/subchannel_index.cc
   src/core/ext/filters/client_channel/uri_parser.cc
@@ -1835,7 +1981,6 @@
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
   src/core/lib/slice/slice_buffer.cc
-  src/core/lib/slice/slice_hash_table.cc
   src/core/lib/slice/slice_intern.cc
   src/core/lib/slice/slice_string_helpers.cc
   src/core/lib/surface/api_trace.cc
@@ -1866,6 +2011,7 @@
   src/core/lib/transport/service_config.cc
   src/core/lib/transport/static_metadata.cc
   src/core/lib/transport/status_conversion.cc
+  src/core/lib/transport/status_metadata.cc
   src/core/lib/transport/timeout_encoding.cc
   src/core/lib/transport/transport.cc
   src/core/lib/transport/transport_op_string.cc
@@ -1881,12 +2027,14 @@
   src/core/ext/filters/client_channel/lb_policy.cc
   src/core/ext/filters/client_channel/lb_policy_factory.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
+  src/core/ext/filters/client_channel/method_params.cc
   src/core/ext/filters/client_channel/parse_address.cc
   src/core/ext/filters/client_channel/proxy_mapper.cc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver_registry.cc
   src/core/ext/filters/client_channel/retry_throttle.cc
+  src/core/ext/filters/client_channel/status_util.cc
   src/core/ext/filters/client_channel/subchannel.cc
   src/core/ext/filters/client_channel/subchannel_index.cc
   src/core/ext/filters/client_channel/uri_parser.cc
@@ -2102,7 +2250,6 @@
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
   src/core/lib/slice/slice_buffer.cc
-  src/core/lib/slice/slice_hash_table.cc
   src/core/lib/slice/slice_intern.cc
   src/core/lib/slice/slice_string_helpers.cc
   src/core/lib/surface/api_trace.cc
@@ -2133,6 +2280,7 @@
   src/core/lib/transport/service_config.cc
   src/core/lib/transport/static_metadata.cc
   src/core/lib/transport/status_conversion.cc
+  src/core/lib/transport/status_metadata.cc
   src/core/lib/transport/timeout_encoding.cc
   src/core/lib/transport/transport.cc
   src/core/lib/transport/transport_op_string.cc
@@ -2181,12 +2329,14 @@
   src/core/ext/filters/client_channel/lb_policy.cc
   src/core/ext/filters/client_channel/lb_policy_factory.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
+  src/core/ext/filters/client_channel/method_params.cc
   src/core/ext/filters/client_channel/parse_address.cc
   src/core/ext/filters/client_channel/proxy_mapper.cc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver_registry.cc
   src/core/ext/filters/client_channel/retry_throttle.cc
+  src/core/ext/filters/client_channel/status_util.cc
   src/core/ext/filters/client_channel/subchannel.cc
   src/core/ext/filters/client_channel/subchannel_index.cc
   src/core/ext/filters/client_channel/uri_parser.cc
@@ -2510,6 +2660,51 @@
   include/grpc++/support/stub_options.h
   include/grpc++/support/sync_stream.h
   include/grpc++/support/time.h
+  include/grpcpp/alarm.h
+  include/grpcpp/channel.h
+  include/grpcpp/client_context.h
+  include/grpcpp/completion_queue.h
+  include/grpcpp/create_channel.h
+  include/grpcpp/create_channel_posix.h
+  include/grpcpp/ext/health_check_service_server_builder_option.h
+  include/grpcpp/generic/async_generic_service.h
+  include/grpcpp/generic/generic_stub.h
+  include/grpcpp/grpcpp.h
+  include/grpcpp/health_check_service_interface.h
+  include/grpcpp/impl/call.h
+  include/grpcpp/impl/channel_argument_option.h
+  include/grpcpp/impl/client_unary_call.h
+  include/grpcpp/impl/codegen/core_codegen.h
+  include/grpcpp/impl/grpc_library.h
+  include/grpcpp/impl/method_handler_impl.h
+  include/grpcpp/impl/rpc_method.h
+  include/grpcpp/impl/rpc_service_method.h
+  include/grpcpp/impl/serialization_traits.h
+  include/grpcpp/impl/server_builder_option.h
+  include/grpcpp/impl/server_builder_plugin.h
+  include/grpcpp/impl/server_initializer.h
+  include/grpcpp/impl/service_type.h
+  include/grpcpp/resource_quota.h
+  include/grpcpp/security/auth_context.h
+  include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/credentials.h
+  include/grpcpp/security/server_credentials.h
+  include/grpcpp/server.h
+  include/grpcpp/server_builder.h
+  include/grpcpp/server_context.h
+  include/grpcpp/server_posix.h
+  include/grpcpp/support/async_stream.h
+  include/grpcpp/support/async_unary_call.h
+  include/grpcpp/support/byte_buffer.h
+  include/grpcpp/support/channel_arguments.h
+  include/grpcpp/support/config.h
+  include/grpcpp/support/slice.h
+  include/grpcpp/support/status.h
+  include/grpcpp/support/status_code_enum.h
+  include/grpcpp/support/string_ref.h
+  include/grpcpp/support/stub_options.h
+  include/grpcpp/support/sync_stream.h
+  include/grpcpp/support/time.h
   include/grpc/support/alloc.h
   include/grpc/support/atm.h
   include/grpc/support/atm_gcc_atomic.h
@@ -2590,8 +2785,40 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpcpp/impl/codegen/async_stream.h
+  include/grpcpp/impl/codegen/async_unary_call.h
+  include/grpcpp/impl/codegen/byte_buffer.h
+  include/grpcpp/impl/codegen/call.h
+  include/grpcpp/impl/codegen/call_hook.h
+  include/grpcpp/impl/codegen/channel_interface.h
+  include/grpcpp/impl/codegen/client_context.h
+  include/grpcpp/impl/codegen/client_unary_call.h
+  include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_tag.h
+  include/grpcpp/impl/codegen/config.h
+  include/grpcpp/impl/codegen/core_codegen_interface.h
+  include/grpcpp/impl/codegen/create_auth_context.h
+  include/grpcpp/impl/codegen/grpc_library.h
+  include/grpcpp/impl/codegen/metadata_map.h
+  include/grpcpp/impl/codegen/method_handler_impl.h
+  include/grpcpp/impl/codegen/rpc_method.h
+  include/grpcpp/impl/codegen/rpc_service_method.h
+  include/grpcpp/impl/codegen/security/auth_context.h
+  include/grpcpp/impl/codegen/serialization_traits.h
+  include/grpcpp/impl/codegen/server_context.h
+  include/grpcpp/impl/codegen/server_interface.h
+  include/grpcpp/impl/codegen/service_type.h
+  include/grpcpp/impl/codegen/slice.h
+  include/grpcpp/impl/codegen/status.h
+  include/grpcpp/impl/codegen/status_code_enum.h
+  include/grpcpp/impl/codegen/string_ref.h
+  include/grpcpp/impl/codegen/stub_options.h
+  include/grpcpp/impl/codegen/sync_stream.h
+  include/grpcpp/impl/codegen/time.h
   include/grpc++/impl/codegen/proto_utils.h
+  include/grpcpp/impl/codegen/proto_utils.h
   include/grpc++/impl/codegen/config_protobuf.h
+  include/grpcpp/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -2611,6 +2838,7 @@
 
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc++_core_stats
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/core/stats.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/core/stats.grpc.pb.cc
@@ -2656,6 +2884,7 @@
   grpc++
 )
 
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 
@@ -2824,7 +3053,6 @@
   src/core/lib/slice/percent_encoding.cc
   src/core/lib/slice/slice.cc
   src/core/lib/slice/slice_buffer.cc
-  src/core/lib/slice/slice_hash_table.cc
   src/core/lib/slice/slice_intern.cc
   src/core/lib/slice/slice_string_helpers.cc
   src/core/lib/surface/api_trace.cc
@@ -2855,6 +3083,7 @@
   src/core/lib/transport/service_config.cc
   src/core/lib/transport/static_metadata.cc
   src/core/lib/transport/status_conversion.cc
+  src/core/lib/transport/status_metadata.cc
   src/core/lib/transport/timeout_encoding.cc
   src/core/lib/transport/transport.cc
   src/core/lib/transport/transport_op_string.cc
@@ -2875,12 +3104,14 @@
   src/core/ext/filters/client_channel/lb_policy.cc
   src/core/ext/filters/client_channel/lb_policy_factory.cc
   src/core/ext/filters/client_channel/lb_policy_registry.cc
+  src/core/ext/filters/client_channel/method_params.cc
   src/core/ext/filters/client_channel/parse_address.cc
   src/core/ext/filters/client_channel/proxy_mapper.cc
   src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   src/core/ext/filters/client_channel/resolver.cc
   src/core/ext/filters/client_channel/resolver_registry.cc
   src/core/ext/filters/client_channel/retry_throttle.cc
+  src/core/ext/filters/client_channel/status_util.cc
   src/core/ext/filters/client_channel/subchannel.cc
   src/core/ext/filters/client_channel/subchannel_index.cc
   src/core/ext/filters/client_channel/uri_parser.cc
@@ -2974,6 +3205,51 @@
   include/grpc++/support/stub_options.h
   include/grpc++/support/sync_stream.h
   include/grpc++/support/time.h
+  include/grpcpp/alarm.h
+  include/grpcpp/channel.h
+  include/grpcpp/client_context.h
+  include/grpcpp/completion_queue.h
+  include/grpcpp/create_channel.h
+  include/grpcpp/create_channel_posix.h
+  include/grpcpp/ext/health_check_service_server_builder_option.h
+  include/grpcpp/generic/async_generic_service.h
+  include/grpcpp/generic/generic_stub.h
+  include/grpcpp/grpcpp.h
+  include/grpcpp/health_check_service_interface.h
+  include/grpcpp/impl/call.h
+  include/grpcpp/impl/channel_argument_option.h
+  include/grpcpp/impl/client_unary_call.h
+  include/grpcpp/impl/codegen/core_codegen.h
+  include/grpcpp/impl/grpc_library.h
+  include/grpcpp/impl/method_handler_impl.h
+  include/grpcpp/impl/rpc_method.h
+  include/grpcpp/impl/rpc_service_method.h
+  include/grpcpp/impl/serialization_traits.h
+  include/grpcpp/impl/server_builder_option.h
+  include/grpcpp/impl/server_builder_plugin.h
+  include/grpcpp/impl/server_initializer.h
+  include/grpcpp/impl/service_type.h
+  include/grpcpp/resource_quota.h
+  include/grpcpp/security/auth_context.h
+  include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/credentials.h
+  include/grpcpp/security/server_credentials.h
+  include/grpcpp/server.h
+  include/grpcpp/server_builder.h
+  include/grpcpp/server_context.h
+  include/grpcpp/server_posix.h
+  include/grpcpp/support/async_stream.h
+  include/grpcpp/support/async_unary_call.h
+  include/grpcpp/support/byte_buffer.h
+  include/grpcpp/support/channel_arguments.h
+  include/grpcpp/support/config.h
+  include/grpcpp/support/slice.h
+  include/grpcpp/support/status.h
+  include/grpcpp/support/status_code_enum.h
+  include/grpcpp/support/string_ref.h
+  include/grpcpp/support/stub_options.h
+  include/grpcpp/support/sync_stream.h
+  include/grpcpp/support/time.h
   include/grpc/support/alloc.h
   include/grpc/support/atm.h
   include/grpc/support/atm_gcc_atomic.h
@@ -3054,6 +3330,36 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpcpp/impl/codegen/async_stream.h
+  include/grpcpp/impl/codegen/async_unary_call.h
+  include/grpcpp/impl/codegen/byte_buffer.h
+  include/grpcpp/impl/codegen/call.h
+  include/grpcpp/impl/codegen/call_hook.h
+  include/grpcpp/impl/codegen/channel_interface.h
+  include/grpcpp/impl/codegen/client_context.h
+  include/grpcpp/impl/codegen/client_unary_call.h
+  include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_tag.h
+  include/grpcpp/impl/codegen/config.h
+  include/grpcpp/impl/codegen/core_codegen_interface.h
+  include/grpcpp/impl/codegen/create_auth_context.h
+  include/grpcpp/impl/codegen/grpc_library.h
+  include/grpcpp/impl/codegen/metadata_map.h
+  include/grpcpp/impl/codegen/method_handler_impl.h
+  include/grpcpp/impl/codegen/rpc_method.h
+  include/grpcpp/impl/codegen/rpc_service_method.h
+  include/grpcpp/impl/codegen/security/auth_context.h
+  include/grpcpp/impl/codegen/serialization_traits.h
+  include/grpcpp/impl/codegen/server_context.h
+  include/grpcpp/impl/codegen/server_interface.h
+  include/grpcpp/impl/codegen/service_type.h
+  include/grpcpp/impl/codegen/slice.h
+  include/grpcpp/impl/codegen/status.h
+  include/grpcpp/impl/codegen/status_code_enum.h
+  include/grpcpp/impl/codegen/string_ref.h
+  include/grpcpp/impl/codegen/stub_options.h
+  include/grpcpp/impl/codegen/sync_stream.h
+  include/grpcpp/impl/codegen/time.h
   include/grpc/census.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
@@ -3073,6 +3379,7 @@
 endif()
 
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc++_error_details
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/status/status.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/status/status.grpc.pb.cc
@@ -3117,6 +3424,7 @@
 
 foreach(_hdr
   include/grpc++/support/error_details.h
+  include/grpcpp/support/error_details.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3124,6 +3432,7 @@
     DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}"
   )
 endforeach()
+endif (gRPC_BUILD_CODEGEN)
 
 
 if (gRPC_INSTALL)
@@ -3136,6 +3445,7 @@
 
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc++_proto_reflection_desc_db
   test/cpp/util/proto_reflection_descriptor_database.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.pb.cc
@@ -3184,6 +3494,7 @@
 
 foreach(_hdr
   include/grpc++/impl/codegen/config_protobuf.h
+  include/grpcpp/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3191,9 +3502,11 @@
     DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}"
   )
 endforeach()
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc++_reflection
   src/cpp/ext/proto_server_reflection.cc
   src/cpp/ext/proto_server_reflection_plugin.cc
@@ -3239,6 +3552,7 @@
 
 foreach(_hdr
   include/grpc++/ext/proto_server_reflection_plugin.h
+  include/grpcpp/ext/proto_server_reflection_plugin.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3246,6 +3560,7 @@
     DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}"
   )
 endforeach()
+endif (gRPC_BUILD_CODEGEN)
 
 
 if (gRPC_INSTALL)
@@ -3299,6 +3614,7 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc++_test_util
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.grpc.pb.cc
@@ -3405,6 +3721,36 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpcpp/impl/codegen/async_stream.h
+  include/grpcpp/impl/codegen/async_unary_call.h
+  include/grpcpp/impl/codegen/byte_buffer.h
+  include/grpcpp/impl/codegen/call.h
+  include/grpcpp/impl/codegen/call_hook.h
+  include/grpcpp/impl/codegen/channel_interface.h
+  include/grpcpp/impl/codegen/client_context.h
+  include/grpcpp/impl/codegen/client_unary_call.h
+  include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_tag.h
+  include/grpcpp/impl/codegen/config.h
+  include/grpcpp/impl/codegen/core_codegen_interface.h
+  include/grpcpp/impl/codegen/create_auth_context.h
+  include/grpcpp/impl/codegen/grpc_library.h
+  include/grpcpp/impl/codegen/metadata_map.h
+  include/grpcpp/impl/codegen/method_handler_impl.h
+  include/grpcpp/impl/codegen/rpc_method.h
+  include/grpcpp/impl/codegen/rpc_service_method.h
+  include/grpcpp/impl/codegen/security/auth_context.h
+  include/grpcpp/impl/codegen/serialization_traits.h
+  include/grpcpp/impl/codegen/server_context.h
+  include/grpcpp/impl/codegen/server_interface.h
+  include/grpcpp/impl/codegen/service_type.h
+  include/grpcpp/impl/codegen/slice.h
+  include/grpcpp/impl/codegen/status.h
+  include/grpcpp/impl/codegen/status_code_enum.h
+  include/grpcpp/impl/codegen/string_ref.h
+  include/grpcpp/impl/codegen/stub_options.h
+  include/grpcpp/impl/codegen/sync_stream.h
+  include/grpcpp/impl/codegen/time.h
   include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
@@ -3427,7 +3773,9 @@
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
   include/grpc++/impl/codegen/proto_utils.h
+  include/grpcpp/impl/codegen/proto_utils.h
   include/grpc++/impl/codegen/config_protobuf.h
+  include/grpcpp/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3435,10 +3783,12 @@
     DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}"
   )
 endforeach()
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc++_test_util_unsecure
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.grpc.pb.cc
@@ -3543,6 +3893,36 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpcpp/impl/codegen/async_stream.h
+  include/grpcpp/impl/codegen/async_unary_call.h
+  include/grpcpp/impl/codegen/byte_buffer.h
+  include/grpcpp/impl/codegen/call.h
+  include/grpcpp/impl/codegen/call_hook.h
+  include/grpcpp/impl/codegen/channel_interface.h
+  include/grpcpp/impl/codegen/client_context.h
+  include/grpcpp/impl/codegen/client_unary_call.h
+  include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_tag.h
+  include/grpcpp/impl/codegen/config.h
+  include/grpcpp/impl/codegen/core_codegen_interface.h
+  include/grpcpp/impl/codegen/create_auth_context.h
+  include/grpcpp/impl/codegen/grpc_library.h
+  include/grpcpp/impl/codegen/metadata_map.h
+  include/grpcpp/impl/codegen/method_handler_impl.h
+  include/grpcpp/impl/codegen/rpc_method.h
+  include/grpcpp/impl/codegen/rpc_service_method.h
+  include/grpcpp/impl/codegen/security/auth_context.h
+  include/grpcpp/impl/codegen/serialization_traits.h
+  include/grpcpp/impl/codegen/server_context.h
+  include/grpcpp/impl/codegen/server_interface.h
+  include/grpcpp/impl/codegen/service_type.h
+  include/grpcpp/impl/codegen/slice.h
+  include/grpcpp/impl/codegen/status.h
+  include/grpcpp/impl/codegen/status_code_enum.h
+  include/grpcpp/impl/codegen/string_ref.h
+  include/grpcpp/impl/codegen/stub_options.h
+  include/grpcpp/impl/codegen/sync_stream.h
+  include/grpcpp/impl/codegen/time.h
   include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
@@ -3565,7 +3945,9 @@
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
   include/grpc++/impl/codegen/proto_utils.h
+  include/grpcpp/impl/codegen/proto_utils.h
   include/grpc++/impl/codegen/config_protobuf.h
+  include/grpcpp/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3573,6 +3955,7 @@
     DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}"
   )
 endforeach()
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 
@@ -3695,6 +4078,51 @@
   include/grpc++/support/stub_options.h
   include/grpc++/support/sync_stream.h
   include/grpc++/support/time.h
+  include/grpcpp/alarm.h
+  include/grpcpp/channel.h
+  include/grpcpp/client_context.h
+  include/grpcpp/completion_queue.h
+  include/grpcpp/create_channel.h
+  include/grpcpp/create_channel_posix.h
+  include/grpcpp/ext/health_check_service_server_builder_option.h
+  include/grpcpp/generic/async_generic_service.h
+  include/grpcpp/generic/generic_stub.h
+  include/grpcpp/grpcpp.h
+  include/grpcpp/health_check_service_interface.h
+  include/grpcpp/impl/call.h
+  include/grpcpp/impl/channel_argument_option.h
+  include/grpcpp/impl/client_unary_call.h
+  include/grpcpp/impl/codegen/core_codegen.h
+  include/grpcpp/impl/grpc_library.h
+  include/grpcpp/impl/method_handler_impl.h
+  include/grpcpp/impl/rpc_method.h
+  include/grpcpp/impl/rpc_service_method.h
+  include/grpcpp/impl/serialization_traits.h
+  include/grpcpp/impl/server_builder_option.h
+  include/grpcpp/impl/server_builder_plugin.h
+  include/grpcpp/impl/server_initializer.h
+  include/grpcpp/impl/service_type.h
+  include/grpcpp/resource_quota.h
+  include/grpcpp/security/auth_context.h
+  include/grpcpp/security/auth_metadata_processor.h
+  include/grpcpp/security/credentials.h
+  include/grpcpp/security/server_credentials.h
+  include/grpcpp/server.h
+  include/grpcpp/server_builder.h
+  include/grpcpp/server_context.h
+  include/grpcpp/server_posix.h
+  include/grpcpp/support/async_stream.h
+  include/grpcpp/support/async_unary_call.h
+  include/grpcpp/support/byte_buffer.h
+  include/grpcpp/support/channel_arguments.h
+  include/grpcpp/support/config.h
+  include/grpcpp/support/slice.h
+  include/grpcpp/support/status.h
+  include/grpcpp/support/status_code_enum.h
+  include/grpcpp/support/string_ref.h
+  include/grpcpp/support/stub_options.h
+  include/grpcpp/support/sync_stream.h
+  include/grpcpp/support/time.h
   include/grpc/support/alloc.h
   include/grpc/support/atm.h
   include/grpc/support/atm_gcc_atomic.h
@@ -3775,6 +4203,36 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpcpp/impl/codegen/async_stream.h
+  include/grpcpp/impl/codegen/async_unary_call.h
+  include/grpcpp/impl/codegen/byte_buffer.h
+  include/grpcpp/impl/codegen/call.h
+  include/grpcpp/impl/codegen/call_hook.h
+  include/grpcpp/impl/codegen/channel_interface.h
+  include/grpcpp/impl/codegen/client_context.h
+  include/grpcpp/impl/codegen/client_unary_call.h
+  include/grpcpp/impl/codegen/completion_queue.h
+  include/grpcpp/impl/codegen/completion_queue_tag.h
+  include/grpcpp/impl/codegen/config.h
+  include/grpcpp/impl/codegen/core_codegen_interface.h
+  include/grpcpp/impl/codegen/create_auth_context.h
+  include/grpcpp/impl/codegen/grpc_library.h
+  include/grpcpp/impl/codegen/metadata_map.h
+  include/grpcpp/impl/codegen/method_handler_impl.h
+  include/grpcpp/impl/codegen/rpc_method.h
+  include/grpcpp/impl/codegen/rpc_service_method.h
+  include/grpcpp/impl/codegen/security/auth_context.h
+  include/grpcpp/impl/codegen/serialization_traits.h
+  include/grpcpp/impl/codegen/server_context.h
+  include/grpcpp/impl/codegen/server_interface.h
+  include/grpcpp/impl/codegen/service_type.h
+  include/grpcpp/impl/codegen/slice.h
+  include/grpcpp/impl/codegen/status.h
+  include/grpcpp/impl/codegen/status_code_enum.h
+  include/grpcpp/impl/codegen/string_ref.h
+  include/grpcpp/impl/codegen/stub_options.h
+  include/grpcpp/impl/codegen/sync_stream.h
+  include/grpcpp/impl/codegen/time.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3840,6 +4298,7 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(grpc_cli_libs
   test/cpp/util/cli_call.cc
   test/cpp/util/cli_credentials.cc
@@ -3893,6 +4352,7 @@
 
 foreach(_hdr
   include/grpc++/impl/codegen/config_protobuf.h
+  include/grpcpp/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3900,6 +4360,7 @@
     DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}"
   )
 endforeach()
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 
@@ -3945,6 +4406,7 @@
 
 foreach(_hdr
   include/grpc++/impl/codegen/config_protobuf.h
+  include/grpcpp/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -3964,6 +4426,7 @@
 
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(http2_client_main
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc
@@ -4027,10 +4490,12 @@
   grpc++_test_config
 )
 
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(interop_client_helper
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.cc
@@ -4080,10 +4545,12 @@
   gpr
 )
 
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(interop_client_main
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc
@@ -4151,6 +4618,7 @@
   grpc++_test_config
 )
 
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
@@ -4201,6 +4669,7 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(interop_server_lib
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc
@@ -4267,6 +4736,7 @@
   grpc++_test_config
 )
 
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
@@ -4313,6 +4783,7 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+if (gRPC_BUILD_CODEGEN)
 add_library(qps
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.cc
@@ -4399,6 +4870,7 @@
   grpc
 )
 
+endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
 
@@ -4573,6 +5045,21 @@
   test/core/end2end/tests/request_with_flags.cc
   test/core/end2end/tests/request_with_payload.cc
   test/core/end2end/tests/resource_quota_server.cc
+  test/core/end2end/tests/retry.cc
+  test/core/end2end/tests/retry_cancellation.cc
+  test/core/end2end/tests/retry_disabled.cc
+  test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc
+  test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc
+  test/core/end2end/tests/retry_non_retriable_status.cc
+  test/core/end2end/tests/retry_recv_initial_metadata.cc
+  test/core/end2end/tests/retry_recv_message.cc
+  test/core/end2end/tests/retry_server_pushback_delay.cc
+  test/core/end2end/tests/retry_server_pushback_disabled.cc
+  test/core/end2end/tests/retry_streaming.cc
+  test/core/end2end/tests/retry_streaming_after_commit.cc
+  test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc
+  test/core/end2end/tests/retry_throttled.cc
+  test/core/end2end/tests/retry_too_many_attempts.cc
   test/core/end2end/tests/server_finishes_request.cc
   test/core/end2end/tests/shutdown_finishes_calls.cc
   test/core/end2end/tests/shutdown_finishes_tags.cc
@@ -4673,6 +5160,21 @@
   test/core/end2end/tests/request_with_flags.cc
   test/core/end2end/tests/request_with_payload.cc
   test/core/end2end/tests/resource_quota_server.cc
+  test/core/end2end/tests/retry.cc
+  test/core/end2end/tests/retry_cancellation.cc
+  test/core/end2end/tests/retry_disabled.cc
+  test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc
+  test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc
+  test/core/end2end/tests/retry_non_retriable_status.cc
+  test/core/end2end/tests/retry_recv_initial_metadata.cc
+  test/core/end2end/tests/retry_recv_message.cc
+  test/core/end2end/tests/retry_server_pushback_delay.cc
+  test/core/end2end/tests/retry_server_pushback_disabled.cc
+  test/core/end2end/tests/retry_streaming.cc
+  test/core/end2end/tests/retry_streaming_after_commit.cc
+  test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc
+  test/core/end2end/tests/retry_throttled.cc
+  test/core/end2end/tests/retry_too_many_attempts.cc
   test/core/end2end/tests/server_finishes_request.cc
   test/core/end2end/tests/shutdown_finishes_calls.cc
   test/core/end2end/tests/shutdown_finishes_tags.cc
@@ -5909,7 +6411,7 @@
 if (gRPC_BUILD_TESTS)
 
 add_executable(gpr_thd_test
-  test/core/gpr/thd_test.cc
+  test/core/gprpp/thd_test.cc
 )
 
 
@@ -7585,33 +8087,6 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
-add_executable(slice_hash_table_test
-  test/core/slice/slice_hash_table_test.cc
-)
-
-
-target_include_directories(slice_hash_table_test
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
-  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
-  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
-  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
-  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
-)
-
-target_link_libraries(slice_hash_table_test
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  grpc_test_util
-  grpc
-  gpr_test_util
-  gpr
-)
-
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-
 add_executable(slice_string_helpers_test
   test/core/slice/slice_string_helpers_test.cc
 )
@@ -8314,6 +8789,462 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(alts_counter_test
+  test/core/tsi/alts/frame_protector/alts_counter_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_counter_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_counter_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_crypt_test
+  test/core/tsi/alts/crypt/aes_gcm_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_crypt_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_crypt_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_crypter_test
+  test/core/tsi/alts/frame_protector/alts_crypter_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_crypter_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_crypter_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_frame_handler_test
+  test/core/tsi/alts/frame_protector/frame_handler_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_frame_handler_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_frame_handler_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_frame_protector_test
+  test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc
+  test/core/tsi/transport_security_test_lib.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_frame_protector_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_frame_protector_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_grpc_record_protocol_test
+  test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_grpc_record_protocol_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_grpc_record_protocol_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_handshaker_client_test
+  test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_handshaker_client_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_handshaker_client_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_handshaker_service_api_test
+  test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_handshaker_service_api_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_handshaker_service_api_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_iovec_record_protocol_test
+  test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_iovec_record_protocol_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_iovec_record_protocol_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_security_connector_test
+  test/core/security/alts_security_connector_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_security_connector_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_security_connector_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_tsi_handshaker_test
+  test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_tsi_handshaker_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_tsi_handshaker_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_tsi_utils_test
+  test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_tsi_utils_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_tsi_utils_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(alts_zero_copy_grpc_protector_test
+  test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(alts_zero_copy_grpc_protector_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(alts_zero_copy_grpc_protector_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(async_end2end_test
   test/cpp/end2end/async_end2end_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -9123,6 +10054,74 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(check_gcp_environment_linux_test
+  test/core/security/check_gcp_environment_linux_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(check_gcp_environment_linux_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(check_gcp_environment_linux_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(check_gcp_environment_windows_test
+  test/core/security/check_gcp_environment_windows_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(check_gcp_environment_windows_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(check_gcp_environment_windows_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(chttp2_settings_timeout_test
   test/core/transport/chttp2/settings_timeout_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -9916,6 +10915,40 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(grpc_alts_credentials_options_test
+  test/core/security/grpc_alts_credentials_options_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(grpc_alts_credentials_options_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(grpc_alts_credentials_options_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(grpc_cli
   test/cpp/util/grpc_cli.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -9952,6 +10985,7 @@
 )
 
 endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_cpp_plugin
   src/compiler/cpp_plugin.cc
@@ -9986,6 +11020,8 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_csharp_plugin
   src/compiler/csharp_plugin.cc
@@ -10020,6 +11056,8 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_node_plugin
   src/compiler/node_plugin.cc
@@ -10054,6 +11092,8 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_objective_c_plugin
   src/compiler/objective_c_plugin.cc
@@ -10088,6 +11128,8 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_php_plugin
   src/compiler/php_plugin.cc
@@ -10122,6 +11164,8 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_python_plugin
   src/compiler/python_plugin.cc
@@ -10156,6 +11200,8 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
+if (gRPC_BUILD_CODEGEN)
 
 add_executable(grpc_ruby_plugin
   src/compiler/ruby_plugin.cc
@@ -10190,6 +11236,7 @@
   )
 endif()
 
+endif (gRPC_BUILD_CODEGEN)
 if (gRPC_BUILD_TESTS)
 
 add_executable(grpc_tool_test
@@ -10335,51 +11382,6 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
-add_executable(grpclb_test
-  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.cc
-  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
-  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.h
-  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.h
-  test/cpp/grpclb/grpclb_test.cc
-  third_party/googletest/googletest/src/gtest-all.cc
-  third_party/googletest/googlemock/src/gmock-all.cc
-)
-
-protobuf_generate_grpc_cpp(
-  src/proto/grpc/lb/v1/load_balancer.proto
-)
-
-target_include_directories(grpclb_test
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
-  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
-  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
-  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
-  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
-  PRIVATE third_party/googletest/googletest/include
-  PRIVATE third_party/googletest/googletest
-  PRIVATE third_party/googletest/googlemock/include
-  PRIVATE third_party/googletest/googlemock
-  PRIVATE ${_gRPC_PROTO_GENS_DIR}
-)
-
-target_link_libraries(grpclb_test
-  ${_gRPC_PROTOBUF_LIBRARIES}
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  grpc++_test_util
-  grpc_test_util
-  grpc++
-  grpc
-  gpr_test_util
-  gpr
-  ${_gRPC_GFLAGS_LIBRARIES}
-)
-
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-
 add_executable(h2_ssl_cert_test
   test/core/end2end/h2_ssl_cert_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -11854,6 +12856,78 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(slice_hash_table_test
+  test/core/slice/slice_hash_table_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(slice_hash_table_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(slice_hash_table_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
+add_executable(slice_weak_hash_table_test
+  test/core/slice/slice_weak_hash_table_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(slice_weak_hash_table_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(slice_weak_hash_table_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(stats_test
   test/core/debug/stats_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -11891,6 +12965,39 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(status_metadata_test
+  test/core/transport/status_metadata_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(status_metadata_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(status_metadata_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(status_test
   test/cpp/util/status_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -11927,6 +13034,39 @@
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
+
+add_executable(status_util_test
+  test/core/client_channel/status_util_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(status_util_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(status_util_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
 add_executable(streaming_throughput_test
@@ -12149,6 +13289,41 @@
 
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
+
+add_executable(transport_security_common_api_test
+  test/core/tsi/alts/handshaker/transport_security_common_api_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(transport_security_common_api_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(transport_security_common_api_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  alts_test_util
+  gpr
+  grpc
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
 add_executable(writes_per_rpc_test
diff --git a/Makefile b/Makefile
index 3e5a1a6..7bc2c82 100644
--- a/Makefile
+++ b/Makefile
@@ -338,6 +338,8 @@
 COREFLAGS += -fno-rtti -fno-exceptions
 LDFLAGS += -g
 
+DEFINES += PB_FIELD_16BIT
+
 CPPFLAGS += $(CPPFLAGS_$(CONFIG))
 CFLAGS += $(CFLAGS_$(CONFIG))
 CXXFLAGS += $(CXXFLAGS_$(CONFIG))
@@ -419,8 +421,8 @@
 endif
 
 CORE_VERSION = 6.0.0-dev
-CPP_VERSION = 1.10.0-dev
-CSHARP_VERSION = 1.10.0-dev
+CPP_VERSION = 1.11.0-dev
+CSHARP_VERSION = 1.11.0-dev
 
 CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
 CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@@ -1067,7 +1069,6 @@
 server_fuzzer: $(BINDIR)/$(CONFIG)/server_fuzzer
 server_test: $(BINDIR)/$(CONFIG)/server_test
 slice_buffer_test: $(BINDIR)/$(CONFIG)/slice_buffer_test
-slice_hash_table_test: $(BINDIR)/$(CONFIG)/slice_hash_table_test
 slice_string_helpers_test: $(BINDIR)/$(CONFIG)/slice_string_helpers_test
 slice_test: $(BINDIR)/$(CONFIG)/slice_test
 sockaddr_resolver_test: $(BINDIR)/$(CONFIG)/sockaddr_resolver_test
@@ -1095,6 +1096,19 @@
 uri_parser_test: $(BINDIR)/$(CONFIG)/uri_parser_test
 wakeup_fd_cv_test: $(BINDIR)/$(CONFIG)/wakeup_fd_cv_test
 alarm_test: $(BINDIR)/$(CONFIG)/alarm_test
+alts_counter_test: $(BINDIR)/$(CONFIG)/alts_counter_test
+alts_crypt_test: $(BINDIR)/$(CONFIG)/alts_crypt_test
+alts_crypter_test: $(BINDIR)/$(CONFIG)/alts_crypter_test
+alts_frame_handler_test: $(BINDIR)/$(CONFIG)/alts_frame_handler_test
+alts_frame_protector_test: $(BINDIR)/$(CONFIG)/alts_frame_protector_test
+alts_grpc_record_protocol_test: $(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test
+alts_handshaker_client_test: $(BINDIR)/$(CONFIG)/alts_handshaker_client_test
+alts_handshaker_service_api_test: $(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test
+alts_iovec_record_protocol_test: $(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test
+alts_security_connector_test: $(BINDIR)/$(CONFIG)/alts_security_connector_test
+alts_tsi_handshaker_test: $(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test
+alts_tsi_utils_test: $(BINDIR)/$(CONFIG)/alts_tsi_utils_test
+alts_zero_copy_grpc_protector_test: $(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test
 async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test
 auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test
 backoff_test: $(BINDIR)/$(CONFIG)/backoff_test
@@ -1115,6 +1129,8 @@
 bm_pollset: $(BINDIR)/$(CONFIG)/bm_pollset
 channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test
 channel_filter_test: $(BINDIR)/$(CONFIG)/channel_filter_test
+check_gcp_environment_linux_test: $(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test
+check_gcp_environment_windows_test: $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test
 chttp2_settings_timeout_test: $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test
 cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test
 client_channel_stress_test: $(BINDIR)/$(CONFIG)/client_channel_stress_test
@@ -1134,6 +1150,7 @@
 filter_end2end_test: $(BINDIR)/$(CONFIG)/filter_end2end_test
 generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test
 golden_file_test: $(BINDIR)/$(CONFIG)/golden_file_test
+grpc_alts_credentials_options_test: $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test
 grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli
 grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin
 grpc_csharp_plugin: $(BINDIR)/$(CONFIG)/grpc_csharp_plugin
@@ -1145,7 +1162,6 @@
 grpc_tool_test: $(BINDIR)/$(CONFIG)/grpc_tool_test
 grpclb_api_test: $(BINDIR)/$(CONFIG)/grpclb_api_test
 grpclb_end2end_test: $(BINDIR)/$(CONFIG)/grpclb_end2end_test
-grpclb_test: $(BINDIR)/$(CONFIG)/grpclb_test
 h2_ssl_cert_test: $(BINDIR)/$(CONFIG)/h2_ssl_cert_test
 health_service_end2end_test: $(BINDIR)/$(CONFIG)/health_service_end2end_test
 http2_client: $(BINDIR)/$(CONFIG)/http2_client
@@ -1182,13 +1198,18 @@
 server_early_return_test: $(BINDIR)/$(CONFIG)/server_early_return_test
 server_request_call_test: $(BINDIR)/$(CONFIG)/server_request_call_test
 shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
+slice_hash_table_test: $(BINDIR)/$(CONFIG)/slice_hash_table_test
+slice_weak_hash_table_test: $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test
 stats_test: $(BINDIR)/$(CONFIG)/stats_test
+status_metadata_test: $(BINDIR)/$(CONFIG)/status_metadata_test
 status_test: $(BINDIR)/$(CONFIG)/status_test
+status_util_test: $(BINDIR)/$(CONFIG)/status_util_test
 streaming_throughput_test: $(BINDIR)/$(CONFIG)/streaming_throughput_test
 stress_test: $(BINDIR)/$(CONFIG)/stress_test
 thread_manager_test: $(BINDIR)/$(CONFIG)/thread_manager_test
 thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test
 transport_pid_controller_test: $(BINDIR)/$(CONFIG)/transport_pid_controller_test
+transport_security_common_api_test: $(BINDIR)/$(CONFIG)/transport_security_common_api_test
 writes_per_rpc_test: $(BINDIR)/$(CONFIG)/writes_per_rpc_test
 public_headers_must_be_c89: $(BINDIR)/$(CONFIG)/public_headers_must_be_c89
 gen_hpack_tables: $(BINDIR)/$(CONFIG)/gen_hpack_tables
@@ -1350,7 +1371,7 @@
 
 privatelibs: privatelibs_c privatelibs_cxx
 
-privatelibs_c:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a $(LIBDIR)/$(CONFIG)/libz.a $(LIBDIR)/$(CONFIG)/libares.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a
+privatelibs_c:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a $(LIBDIR)/$(CONFIG)/libz.a $(LIBDIR)/$(CONFIG)/libares.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a
 pc_c: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc
 
 pc_c_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc
@@ -1470,7 +1491,6 @@
   $(BINDIR)/$(CONFIG)/server_chttp2_test \
   $(BINDIR)/$(CONFIG)/server_test \
   $(BINDIR)/$(CONFIG)/slice_buffer_test \
-  $(BINDIR)/$(CONFIG)/slice_hash_table_test \
   $(BINDIR)/$(CONFIG)/slice_string_helpers_test \
   $(BINDIR)/$(CONFIG)/slice_test \
   $(BINDIR)/$(CONFIG)/sockaddr_resolver_test \
@@ -1561,6 +1581,19 @@
 ifeq ($(EMBED_OPENSSL),true)
 buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/alarm_test \
+  $(BINDIR)/$(CONFIG)/alts_counter_test \
+  $(BINDIR)/$(CONFIG)/alts_crypt_test \
+  $(BINDIR)/$(CONFIG)/alts_crypter_test \
+  $(BINDIR)/$(CONFIG)/alts_frame_handler_test \
+  $(BINDIR)/$(CONFIG)/alts_frame_protector_test \
+  $(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test \
+  $(BINDIR)/$(CONFIG)/alts_handshaker_client_test \
+  $(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test \
+  $(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test \
+  $(BINDIR)/$(CONFIG)/alts_security_connector_test \
+  $(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test \
+  $(BINDIR)/$(CONFIG)/alts_tsi_utils_test \
+  $(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test \
   $(BINDIR)/$(CONFIG)/async_end2end_test \
   $(BINDIR)/$(CONFIG)/auth_property_iterator_test \
   $(BINDIR)/$(CONFIG)/backoff_test \
@@ -1581,6 +1614,8 @@
   $(BINDIR)/$(CONFIG)/bm_pollset \
   $(BINDIR)/$(CONFIG)/channel_arguments_test \
   $(BINDIR)/$(CONFIG)/channel_filter_test \
+  $(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test \
+  $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test \
   $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test \
   $(BINDIR)/$(CONFIG)/cli_call_test \
   $(BINDIR)/$(CONFIG)/client_channel_stress_test \
@@ -1600,11 +1635,11 @@
   $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
+  $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
   $(BINDIR)/$(CONFIG)/grpclb_end2end_test \
-  $(BINDIR)/$(CONFIG)/grpclb_test \
   $(BINDIR)/$(CONFIG)/h2_ssl_cert_test \
   $(BINDIR)/$(CONFIG)/health_service_end2end_test \
   $(BINDIR)/$(CONFIG)/http2_client \
@@ -1641,13 +1676,18 @@
   $(BINDIR)/$(CONFIG)/server_early_return_test \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
+  $(BINDIR)/$(CONFIG)/slice_hash_table_test \
+  $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test \
   $(BINDIR)/$(CONFIG)/stats_test \
+  $(BINDIR)/$(CONFIG)/status_metadata_test \
   $(BINDIR)/$(CONFIG)/status_test \
+  $(BINDIR)/$(CONFIG)/status_util_test \
   $(BINDIR)/$(CONFIG)/streaming_throughput_test \
   $(BINDIR)/$(CONFIG)/stress_test \
   $(BINDIR)/$(CONFIG)/thread_manager_test \
   $(BINDIR)/$(CONFIG)/thread_stress_test \
   $(BINDIR)/$(CONFIG)/transport_pid_controller_test \
+  $(BINDIR)/$(CONFIG)/transport_security_common_api_test \
   $(BINDIR)/$(CONFIG)/writes_per_rpc_test \
   $(BINDIR)/$(CONFIG)/boringssl_crypto_test_data \
   $(BINDIR)/$(CONFIG)/boringssl_asn1_test \
@@ -1706,6 +1746,19 @@
 else
 buildtests_cxx: privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/alarm_test \
+  $(BINDIR)/$(CONFIG)/alts_counter_test \
+  $(BINDIR)/$(CONFIG)/alts_crypt_test \
+  $(BINDIR)/$(CONFIG)/alts_crypter_test \
+  $(BINDIR)/$(CONFIG)/alts_frame_handler_test \
+  $(BINDIR)/$(CONFIG)/alts_frame_protector_test \
+  $(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test \
+  $(BINDIR)/$(CONFIG)/alts_handshaker_client_test \
+  $(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test \
+  $(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test \
+  $(BINDIR)/$(CONFIG)/alts_security_connector_test \
+  $(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test \
+  $(BINDIR)/$(CONFIG)/alts_tsi_utils_test \
+  $(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test \
   $(BINDIR)/$(CONFIG)/async_end2end_test \
   $(BINDIR)/$(CONFIG)/auth_property_iterator_test \
   $(BINDIR)/$(CONFIG)/backoff_test \
@@ -1726,6 +1779,8 @@
   $(BINDIR)/$(CONFIG)/bm_pollset \
   $(BINDIR)/$(CONFIG)/channel_arguments_test \
   $(BINDIR)/$(CONFIG)/channel_filter_test \
+  $(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test \
+  $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test \
   $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test \
   $(BINDIR)/$(CONFIG)/cli_call_test \
   $(BINDIR)/$(CONFIG)/client_channel_stress_test \
@@ -1745,11 +1800,11 @@
   $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
+  $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
   $(BINDIR)/$(CONFIG)/grpclb_end2end_test \
-  $(BINDIR)/$(CONFIG)/grpclb_test \
   $(BINDIR)/$(CONFIG)/h2_ssl_cert_test \
   $(BINDIR)/$(CONFIG)/health_service_end2end_test \
   $(BINDIR)/$(CONFIG)/http2_client \
@@ -1786,13 +1841,18 @@
   $(BINDIR)/$(CONFIG)/server_early_return_test \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
+  $(BINDIR)/$(CONFIG)/slice_hash_table_test \
+  $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test \
   $(BINDIR)/$(CONFIG)/stats_test \
+  $(BINDIR)/$(CONFIG)/status_metadata_test \
   $(BINDIR)/$(CONFIG)/status_test \
+  $(BINDIR)/$(CONFIG)/status_util_test \
   $(BINDIR)/$(CONFIG)/streaming_throughput_test \
   $(BINDIR)/$(CONFIG)/stress_test \
   $(BINDIR)/$(CONFIG)/thread_manager_test \
   $(BINDIR)/$(CONFIG)/thread_stress_test \
   $(BINDIR)/$(CONFIG)/transport_pid_controller_test \
+  $(BINDIR)/$(CONFIG)/transport_security_common_api_test \
   $(BINDIR)/$(CONFIG)/writes_per_rpc_test \
   $(BINDIR)/$(CONFIG)/resolver_component_test_unsecure \
   $(BINDIR)/$(CONFIG)/resolver_component_test \
@@ -1997,8 +2057,6 @@
 	$(Q) $(BINDIR)/$(CONFIG)/server_test || ( echo test server_test failed ; exit 1 )
 	$(E) "[RUN]     Testing slice_buffer_test"
 	$(Q) $(BINDIR)/$(CONFIG)/slice_buffer_test || ( echo test slice_buffer_test failed ; exit 1 )
-	$(E) "[RUN]     Testing slice_hash_table_test"
-	$(Q) $(BINDIR)/$(CONFIG)/slice_hash_table_test || ( echo test slice_hash_table_test failed ; exit 1 )
 	$(E) "[RUN]     Testing slice_string_helpers_test"
 	$(Q) $(BINDIR)/$(CONFIG)/slice_string_helpers_test || ( echo test slice_string_helpers_test failed ; exit 1 )
 	$(E) "[RUN]     Testing slice_test"
@@ -2081,6 +2139,32 @@
 test_cxx: buildtests_cxx
 	$(E) "[RUN]     Testing alarm_test"
 	$(Q) $(BINDIR)/$(CONFIG)/alarm_test || ( echo test alarm_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_counter_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_counter_test || ( echo test alts_counter_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_crypt_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_crypt_test || ( echo test alts_crypt_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_crypter_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_crypter_test || ( echo test alts_crypter_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_frame_handler_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_frame_handler_test || ( echo test alts_frame_handler_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_frame_protector_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_frame_protector_test || ( echo test alts_frame_protector_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_grpc_record_protocol_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test || ( echo test alts_grpc_record_protocol_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_handshaker_client_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_handshaker_client_test || ( echo test alts_handshaker_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_handshaker_service_api_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test || ( echo test alts_handshaker_service_api_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_iovec_record_protocol_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test || ( echo test alts_iovec_record_protocol_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_security_connector_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_security_connector_test || ( echo test alts_security_connector_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_tsi_handshaker_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test || ( echo test alts_tsi_handshaker_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_tsi_utils_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_tsi_utils_test || ( echo test alts_tsi_utils_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alts_zero_copy_grpc_protector_test"
+	$(Q) $(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test || ( echo test alts_zero_copy_grpc_protector_test failed ; exit 1 )
 	$(E) "[RUN]     Testing async_end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/async_end2end_test || ( echo test async_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing auth_property_iterator_test"
@@ -2121,6 +2205,10 @@
 	$(Q) $(BINDIR)/$(CONFIG)/channel_arguments_test || ( echo test channel_arguments_test failed ; exit 1 )
 	$(E) "[RUN]     Testing channel_filter_test"
 	$(Q) $(BINDIR)/$(CONFIG)/channel_filter_test || ( echo test channel_filter_test failed ; exit 1 )
+	$(E) "[RUN]     Testing check_gcp_environment_linux_test"
+	$(Q) $(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test || ( echo test check_gcp_environment_linux_test failed ; exit 1 )
+	$(E) "[RUN]     Testing check_gcp_environment_windows_test"
+	$(Q) $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test || ( echo test check_gcp_environment_windows_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_settings_timeout_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test || ( echo test chttp2_settings_timeout_test failed ; exit 1 )
 	$(E) "[RUN]     Testing cli_call_test"
@@ -2157,14 +2245,14 @@
 	$(Q) $(BINDIR)/$(CONFIG)/generic_end2end_test || ( echo test generic_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing golden_file_test"
 	$(Q) $(BINDIR)/$(CONFIG)/golden_file_test || ( echo test golden_file_test failed ; exit 1 )
+	$(E) "[RUN]     Testing grpc_alts_credentials_options_test"
+	$(Q) $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test || ( echo test grpc_alts_credentials_options_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpc_tool_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpc_tool_test || ( echo test grpc_tool_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpclb_api_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpclb_api_test || ( echo test grpclb_api_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpclb_end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpclb_end2end_test || ( echo test grpclb_end2end_test failed ; exit 1 )
-	$(E) "[RUN]     Testing grpclb_test"
-	$(Q) $(BINDIR)/$(CONFIG)/grpclb_test || ( echo test grpclb_test failed ; exit 1 )
 	$(E) "[RUN]     Testing h2_ssl_cert_test"
 	$(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cert_test || ( echo test h2_ssl_cert_test failed ; exit 1 )
 	$(E) "[RUN]     Testing health_service_end2end_test"
@@ -2213,10 +2301,18 @@
 	$(Q) $(BINDIR)/$(CONFIG)/server_request_call_test || ( echo test server_request_call_test failed ; exit 1 )
 	$(E) "[RUN]     Testing shutdown_test"
 	$(Q) $(BINDIR)/$(CONFIG)/shutdown_test || ( echo test shutdown_test failed ; exit 1 )
+	$(E) "[RUN]     Testing slice_hash_table_test"
+	$(Q) $(BINDIR)/$(CONFIG)/slice_hash_table_test || ( echo test slice_hash_table_test failed ; exit 1 )
+	$(E) "[RUN]     Testing slice_weak_hash_table_test"
+	$(Q) $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test || ( echo test slice_weak_hash_table_test failed ; exit 1 )
 	$(E) "[RUN]     Testing stats_test"
 	$(Q) $(BINDIR)/$(CONFIG)/stats_test || ( echo test stats_test failed ; exit 1 )
+	$(E) "[RUN]     Testing status_metadata_test"
+	$(Q) $(BINDIR)/$(CONFIG)/status_metadata_test || ( echo test status_metadata_test failed ; exit 1 )
 	$(E) "[RUN]     Testing status_test"
 	$(Q) $(BINDIR)/$(CONFIG)/status_test || ( echo test status_test failed ; exit 1 )
+	$(E) "[RUN]     Testing status_util_test"
+	$(Q) $(BINDIR)/$(CONFIG)/status_util_test || ( echo test status_util_test failed ; exit 1 )
 	$(E) "[RUN]     Testing streaming_throughput_test"
 	$(Q) $(BINDIR)/$(CONFIG)/streaming_throughput_test || ( echo test streaming_throughput_test failed ; exit 1 )
 	$(E) "[RUN]     Testing thread_manager_test"
@@ -2225,6 +2321,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/thread_stress_test || ( echo test thread_stress_test failed ; exit 1 )
 	$(E) "[RUN]     Testing transport_pid_controller_test"
 	$(Q) $(BINDIR)/$(CONFIG)/transport_pid_controller_test || ( echo test transport_pid_controller_test failed ; exit 1 )
+	$(E) "[RUN]     Testing transport_security_common_api_test"
+	$(Q) $(BINDIR)/$(CONFIG)/transport_security_common_api_test || ( echo test transport_security_common_api_test failed ; exit 1 )
 	$(E) "[RUN]     Testing writes_per_rpc_test"
 	$(Q) $(BINDIR)/$(CONFIG)/writes_per_rpc_test || ( echo test writes_per_rpc_test failed ; exit 1 )
 	$(E) "[RUN]     Testing resolver_component_tests_runner_invoker_unsecure"
@@ -2852,6 +2950,11 @@
 	$(Q) $(INSTALL) -d $(prefix)/bin
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(prefix)/bin/grpc_ruby_plugin
 
+install-grpc-cli: grpc_cli
+	$(E) "[INSTALL] Installing grpc cli"
+	$(Q) $(INSTALL) -d $(prefix)/bin
+	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_cli $(prefix)/bin/grpc_cli
+
 install-pkg-config_c: pc_c pc_c_unsecure
 	$(E) "[INSTALL] Installing C pkg-config files"
 	$(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig
@@ -2877,6 +2980,46 @@
 # The various libraries
 
 
+LIBALTS_TEST_UTIL_SRC = \
+    test/core/tsi/alts/crypt/gsec_test_util.cc \
+    test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc \
+
+PUBLIC_HEADERS_C += \
+
+LIBALTS_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBALTS_TEST_UTIL_SRC))))
+
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL.
+
+$(LIBDIR)/$(CONFIG)/libalts_test_util.a: openssl_dep_error
+
+
+else
+
+
+$(LIBDIR)/$(CONFIG)/libalts_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(CARES_DEP) $(LIBALTS_TEST_UTIL_OBJS) 
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libalts_test_util.a
+	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBALTS_TEST_UTIL_OBJS) 
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libalts_test_util.a
+endif
+
+
+
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBALTS_TEST_UTIL_OBJS:.o=.dep)
+endif
+endif
+
+
 LIBGPR_SRC = \
     src/core/lib/gpr/alloc.cc \
     src/core/lib/gpr/arena.cc \
@@ -2904,9 +3047,6 @@
     src/core/lib/gpr/sync.cc \
     src/core/lib/gpr/sync_posix.cc \
     src/core/lib/gpr/sync_windows.cc \
-    src/core/lib/gpr/thd.cc \
-    src/core/lib/gpr/thd_posix.cc \
-    src/core/lib/gpr/thd_windows.cc \
     src/core/lib/gpr/time.cc \
     src/core/lib/gpr/time_posix.cc \
     src/core/lib/gpr/time_precise.cc \
@@ -2916,6 +3056,8 @@
     src/core/lib/gpr/tmpfile_posix.cc \
     src/core/lib/gpr/tmpfile_windows.cc \
     src/core/lib/gpr/wrap_memcpy.cc \
+    src/core/lib/gprpp/thd_posix.cc \
+    src/core/lib/gprpp/thd_windows.cc \
     src/core/lib/profiling/basic_timers.cc \
     src/core/lib/profiling/stap_timers.cc \
 
@@ -3115,7 +3257,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -3146,6 +3287,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -3180,6 +3322,7 @@
     src/core/ext/filters/http/server/http_server_filter.cc \
     src/core/lib/http/httpcli_security_connector.cc \
     src/core/lib/security/context/security_context.cc \
+    src/core/lib/security/credentials/alts/alts_credentials.cc \
     src/core/lib/security/credentials/composite/composite_credentials.cc \
     src/core/lib/security/credentials/credentials.cc \
     src/core/lib/security/credentials/credentials_metadata.cc \
@@ -3193,23 +3336,55 @@
     src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \
     src/core/lib/security/credentials/plugin/plugin_credentials.cc \
     src/core/lib/security/credentials/ssl/ssl_credentials.cc \
+    src/core/lib/security/security_connector/alts_security_connector.cc \
+    src/core/lib/security/security_connector/security_connector.cc \
     src/core/lib/security/transport/client_auth_filter.cc \
-    src/core/lib/security/transport/lb_targets_info.cc \
     src/core/lib/security/transport/secure_endpoint.cc \
-    src/core/lib/security/transport/security_connector.cc \
     src/core/lib/security/transport/security_handshaker.cc \
     src/core/lib/security/transport/server_auth_filter.cc \
+    src/core/lib/security/transport/target_authority_table.cc \
     src/core/lib/security/transport/tsi_error.cc \
     src/core/lib/security/util/json_util.cc \
     src/core/lib/surface/init_secure.cc \
-    src/core/tsi/alts_transport_security.cc \
-    src/core/tsi/fake_transport_security.cc \
-    src/core/tsi/ssl_transport_security.cc \
-    src/core/tsi/transport_security_grpc.cc \
+    src/core/tsi/alts/crypt/aes_gcm.cc \
+    src/core/tsi/alts/crypt/gsec.cc \
+    src/core/tsi/alts/frame_protector/alts_counter.cc \
+    src/core/tsi/alts/frame_protector/alts_crypter.cc \
+    src/core/tsi/alts/frame_protector/alts_frame_protector.cc \
+    src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc \
+    src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc \
+    src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
+    src/core/tsi/alts/frame_protector/frame_handler.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_utils.cc \
+    src/core/tsi/alts/handshaker/transport_security_common_api.cc \
+    src/core/tsi/alts/handshaker/altscontext.pb.c \
+    src/core/tsi/alts/handshaker/handshaker.pb.c \
+    src/core/tsi/alts/handshaker/transport_security_common.pb.c \
+    third_party/nanopb/pb_common.c \
+    third_party/nanopb/pb_decode.c \
+    third_party/nanopb/pb_encode.c \
     src/core/tsi/transport_security.cc \
     src/core/tsi/transport_security_adapter.cc \
-    src/core/ext/transport/chttp2/server/chttp2_server.cc \
-    src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \
+    src/core/ext/transport/chttp2/client/insecure/channel_create.cc \
+    src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \
+    src/core/ext/transport/chttp2/client/chttp2_connector.cc \
     src/core/ext/filters/client_channel/backup_poller.cc \
     src/core/ext/filters/client_channel/channel_connectivity.cc \
     src/core/ext/filters/client_channel/client_channel.cc \
@@ -3221,21 +3396,26 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
     src/core/ext/filters/deadline/deadline_filter.cc \
-    src/core/ext/transport/chttp2/client/chttp2_connector.cc \
+    src/core/tsi/alts_transport_security.cc \
+    src/core/tsi/fake_transport_security.cc \
+    src/core/tsi/ssl_transport_security.cc \
+    src/core/tsi/transport_security_grpc.cc \
+    src/core/ext/transport/chttp2/server/chttp2_server.cc \
+    src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \
-    src/core/ext/transport/chttp2/client/insecure/channel_create.cc \
-    src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \
     src/core/ext/transport/inproc/inproc_plugin.cc \
     src/core/ext/transport/inproc/inproc_transport.cc \
     src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \
@@ -3244,9 +3424,6 @@
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \
     src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \
     src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
-    third_party/nanopb/pb_common.c \
-    third_party/nanopb/pb_decode.c \
-    third_party/nanopb/pb_encode.c \
     src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \
     src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \
     src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc \
@@ -3457,7 +3634,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -3488,6 +3664,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -3534,18 +3711,21 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
     src/core/ext/filters/deadline/deadline_filter.cc \
     src/core/lib/http/httpcli_security_connector.cc \
     src/core/lib/security/context/security_context.cc \
+    src/core/lib/security/credentials/alts/alts_credentials.cc \
     src/core/lib/security/credentials/composite/composite_credentials.cc \
     src/core/lib/security/credentials/credentials.cc \
     src/core/lib/security/credentials/credentials_metadata.cc \
@@ -3559,22 +3739,59 @@
     src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \
     src/core/lib/security/credentials/plugin/plugin_credentials.cc \
     src/core/lib/security/credentials/ssl/ssl_credentials.cc \
+    src/core/lib/security/security_connector/alts_security_connector.cc \
+    src/core/lib/security/security_connector/security_connector.cc \
     src/core/lib/security/transport/client_auth_filter.cc \
-    src/core/lib/security/transport/lb_targets_info.cc \
     src/core/lib/security/transport/secure_endpoint.cc \
-    src/core/lib/security/transport/security_connector.cc \
     src/core/lib/security/transport/security_handshaker.cc \
     src/core/lib/security/transport/server_auth_filter.cc \
+    src/core/lib/security/transport/target_authority_table.cc \
     src/core/lib/security/transport/tsi_error.cc \
     src/core/lib/security/util/json_util.cc \
     src/core/lib/surface/init_secure.cc \
+    src/core/tsi/alts/crypt/aes_gcm.cc \
+    src/core/tsi/alts/crypt/gsec.cc \
+    src/core/tsi/alts/frame_protector/alts_counter.cc \
+    src/core/tsi/alts/frame_protector/alts_crypter.cc \
+    src/core/tsi/alts/frame_protector/alts_frame_protector.cc \
+    src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc \
+    src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc \
+    src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
+    src/core/tsi/alts/frame_protector/frame_handler.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_utils.cc \
+    src/core/tsi/alts/handshaker/transport_security_common_api.cc \
+    src/core/tsi/alts/handshaker/altscontext.pb.c \
+    src/core/tsi/alts/handshaker/handshaker.pb.c \
+    src/core/tsi/alts/handshaker/transport_security_common.pb.c \
+    third_party/nanopb/pb_common.c \
+    third_party/nanopb/pb_decode.c \
+    third_party/nanopb/pb_encode.c \
+    src/core/tsi/transport_security.cc \
+    src/core/tsi/transport_security_adapter.cc \
+    src/core/ext/transport/chttp2/client/insecure/channel_create.cc \
+    src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \
+    src/core/ext/transport/chttp2/client/chttp2_connector.cc \
     src/core/tsi/alts_transport_security.cc \
     src/core/tsi/fake_transport_security.cc \
     src/core/tsi/ssl_transport_security.cc \
     src/core/tsi/transport_security_grpc.cc \
-    src/core/tsi/transport_security.cc \
-    src/core/tsi/transport_security_adapter.cc \
-    src/core/ext/transport/chttp2/client/chttp2_connector.cc \
     src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \
     src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \
     src/core/plugin_registry/grpc_cronet_plugin_registry.cc \
@@ -3784,7 +4001,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -3815,6 +4031,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -3830,12 +4047,14 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
@@ -4064,7 +4283,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -4095,6 +4313,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -4110,12 +4329,14 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
@@ -4311,7 +4532,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -4342,6 +4562,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -4390,12 +4611,14 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
@@ -4672,6 +4895,51 @@
     include/grpc++/support/stub_options.h \
     include/grpc++/support/sync_stream.h \
     include/grpc++/support/time.h \
+    include/grpcpp/alarm.h \
+    include/grpcpp/channel.h \
+    include/grpcpp/client_context.h \
+    include/grpcpp/completion_queue.h \
+    include/grpcpp/create_channel.h \
+    include/grpcpp/create_channel_posix.h \
+    include/grpcpp/ext/health_check_service_server_builder_option.h \
+    include/grpcpp/generic/async_generic_service.h \
+    include/grpcpp/generic/generic_stub.h \
+    include/grpcpp/grpcpp.h \
+    include/grpcpp/health_check_service_interface.h \
+    include/grpcpp/impl/call.h \
+    include/grpcpp/impl/channel_argument_option.h \
+    include/grpcpp/impl/client_unary_call.h \
+    include/grpcpp/impl/codegen/core_codegen.h \
+    include/grpcpp/impl/grpc_library.h \
+    include/grpcpp/impl/method_handler_impl.h \
+    include/grpcpp/impl/rpc_method.h \
+    include/grpcpp/impl/rpc_service_method.h \
+    include/grpcpp/impl/serialization_traits.h \
+    include/grpcpp/impl/server_builder_option.h \
+    include/grpcpp/impl/server_builder_plugin.h \
+    include/grpcpp/impl/server_initializer.h \
+    include/grpcpp/impl/service_type.h \
+    include/grpcpp/resource_quota.h \
+    include/grpcpp/security/auth_context.h \
+    include/grpcpp/security/auth_metadata_processor.h \
+    include/grpcpp/security/credentials.h \
+    include/grpcpp/security/server_credentials.h \
+    include/grpcpp/server.h \
+    include/grpcpp/server_builder.h \
+    include/grpcpp/server_context.h \
+    include/grpcpp/server_posix.h \
+    include/grpcpp/support/async_stream.h \
+    include/grpcpp/support/async_unary_call.h \
+    include/grpcpp/support/byte_buffer.h \
+    include/grpcpp/support/channel_arguments.h \
+    include/grpcpp/support/config.h \
+    include/grpcpp/support/slice.h \
+    include/grpcpp/support/status.h \
+    include/grpcpp/support/status_code_enum.h \
+    include/grpcpp/support/string_ref.h \
+    include/grpcpp/support/stub_options.h \
+    include/grpcpp/support/sync_stream.h \
+    include/grpcpp/support/time.h \
     include/grpc/support/alloc.h \
     include/grpc/support/atm.h \
     include/grpc/support/atm_gcc_atomic.h \
@@ -4752,8 +5020,40 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/async_stream.h \
+    include/grpcpp/impl/codegen/async_unary_call.h \
+    include/grpcpp/impl/codegen/byte_buffer.h \
+    include/grpcpp/impl/codegen/call.h \
+    include/grpcpp/impl/codegen/call_hook.h \
+    include/grpcpp/impl/codegen/channel_interface.h \
+    include/grpcpp/impl/codegen/client_context.h \
+    include/grpcpp/impl/codegen/client_unary_call.h \
+    include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_tag.h \
+    include/grpcpp/impl/codegen/config.h \
+    include/grpcpp/impl/codegen/core_codegen_interface.h \
+    include/grpcpp/impl/codegen/create_auth_context.h \
+    include/grpcpp/impl/codegen/grpc_library.h \
+    include/grpcpp/impl/codegen/metadata_map.h \
+    include/grpcpp/impl/codegen/method_handler_impl.h \
+    include/grpcpp/impl/codegen/rpc_method.h \
+    include/grpcpp/impl/codegen/rpc_service_method.h \
+    include/grpcpp/impl/codegen/security/auth_context.h \
+    include/grpcpp/impl/codegen/serialization_traits.h \
+    include/grpcpp/impl/codegen/server_context.h \
+    include/grpcpp/impl/codegen/server_interface.h \
+    include/grpcpp/impl/codegen/service_type.h \
+    include/grpcpp/impl/codegen/slice.h \
+    include/grpcpp/impl/codegen/status.h \
+    include/grpcpp/impl/codegen/status_code_enum.h \
+    include/grpcpp/impl/codegen/string_ref.h \
+    include/grpcpp/impl/codegen/stub_options.h \
+    include/grpcpp/impl/codegen/sync_stream.h \
+    include/grpcpp/impl/codegen/time.h \
     include/grpc++/impl/codegen/proto_utils.h \
+    include/grpcpp/impl/codegen/proto_utils.h \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpcpp/impl/codegen/config_protobuf.h \
 
 LIBGRPC++_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_SRC))))
 
@@ -5034,7 +5334,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -5065,6 +5364,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -5085,12 +5385,14 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
@@ -5149,6 +5451,51 @@
     include/grpc++/support/stub_options.h \
     include/grpc++/support/sync_stream.h \
     include/grpc++/support/time.h \
+    include/grpcpp/alarm.h \
+    include/grpcpp/channel.h \
+    include/grpcpp/client_context.h \
+    include/grpcpp/completion_queue.h \
+    include/grpcpp/create_channel.h \
+    include/grpcpp/create_channel_posix.h \
+    include/grpcpp/ext/health_check_service_server_builder_option.h \
+    include/grpcpp/generic/async_generic_service.h \
+    include/grpcpp/generic/generic_stub.h \
+    include/grpcpp/grpcpp.h \
+    include/grpcpp/health_check_service_interface.h \
+    include/grpcpp/impl/call.h \
+    include/grpcpp/impl/channel_argument_option.h \
+    include/grpcpp/impl/client_unary_call.h \
+    include/grpcpp/impl/codegen/core_codegen.h \
+    include/grpcpp/impl/grpc_library.h \
+    include/grpcpp/impl/method_handler_impl.h \
+    include/grpcpp/impl/rpc_method.h \
+    include/grpcpp/impl/rpc_service_method.h \
+    include/grpcpp/impl/serialization_traits.h \
+    include/grpcpp/impl/server_builder_option.h \
+    include/grpcpp/impl/server_builder_plugin.h \
+    include/grpcpp/impl/server_initializer.h \
+    include/grpcpp/impl/service_type.h \
+    include/grpcpp/resource_quota.h \
+    include/grpcpp/security/auth_context.h \
+    include/grpcpp/security/auth_metadata_processor.h \
+    include/grpcpp/security/credentials.h \
+    include/grpcpp/security/server_credentials.h \
+    include/grpcpp/server.h \
+    include/grpcpp/server_builder.h \
+    include/grpcpp/server_context.h \
+    include/grpcpp/server_posix.h \
+    include/grpcpp/support/async_stream.h \
+    include/grpcpp/support/async_unary_call.h \
+    include/grpcpp/support/byte_buffer.h \
+    include/grpcpp/support/channel_arguments.h \
+    include/grpcpp/support/config.h \
+    include/grpcpp/support/slice.h \
+    include/grpcpp/support/status.h \
+    include/grpcpp/support/status_code_enum.h \
+    include/grpcpp/support/string_ref.h \
+    include/grpcpp/support/stub_options.h \
+    include/grpcpp/support/sync_stream.h \
+    include/grpcpp/support/time.h \
     include/grpc/support/alloc.h \
     include/grpc/support/atm.h \
     include/grpc/support/atm_gcc_atomic.h \
@@ -5229,6 +5576,36 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/async_stream.h \
+    include/grpcpp/impl/codegen/async_unary_call.h \
+    include/grpcpp/impl/codegen/byte_buffer.h \
+    include/grpcpp/impl/codegen/call.h \
+    include/grpcpp/impl/codegen/call_hook.h \
+    include/grpcpp/impl/codegen/channel_interface.h \
+    include/grpcpp/impl/codegen/client_context.h \
+    include/grpcpp/impl/codegen/client_unary_call.h \
+    include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_tag.h \
+    include/grpcpp/impl/codegen/config.h \
+    include/grpcpp/impl/codegen/core_codegen_interface.h \
+    include/grpcpp/impl/codegen/create_auth_context.h \
+    include/grpcpp/impl/codegen/grpc_library.h \
+    include/grpcpp/impl/codegen/metadata_map.h \
+    include/grpcpp/impl/codegen/method_handler_impl.h \
+    include/grpcpp/impl/codegen/rpc_method.h \
+    include/grpcpp/impl/codegen/rpc_service_method.h \
+    include/grpcpp/impl/codegen/security/auth_context.h \
+    include/grpcpp/impl/codegen/serialization_traits.h \
+    include/grpcpp/impl/codegen/server_context.h \
+    include/grpcpp/impl/codegen/server_interface.h \
+    include/grpcpp/impl/codegen/service_type.h \
+    include/grpcpp/impl/codegen/slice.h \
+    include/grpcpp/impl/codegen/status.h \
+    include/grpcpp/impl/codegen/status_code_enum.h \
+    include/grpcpp/impl/codegen/string_ref.h \
+    include/grpcpp/impl/codegen/stub_options.h \
+    include/grpcpp/impl/codegen/sync_stream.h \
+    include/grpcpp/impl/codegen/time.h \
     include/grpc/census.h \
 
 LIBGRPC++_CRONET_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_CRONET_SRC))))
@@ -5300,6 +5677,7 @@
 
 PUBLIC_HEADERS_CXX += \
     include/grpc++/support/error_details.h \
+    include/grpcpp/support/error_details.h \
 
 LIBGRPC++_ERROR_DETAILS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_ERROR_DETAILS_SRC))))
 
@@ -5371,6 +5749,7 @@
 
 PUBLIC_HEADERS_CXX += \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpcpp/impl/codegen/config_protobuf.h \
 
 LIBGRPC++_PROTO_REFLECTION_DESC_DB_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_PROTO_REFLECTION_DESC_DB_SRC))))
 
@@ -5424,6 +5803,7 @@
 
 PUBLIC_HEADERS_CXX += \
     include/grpc++/ext/proto_server_reflection_plugin.h \
+    include/grpcpp/ext/proto_server_reflection_plugin.h \
 
 LIBGRPC++_REFLECTION_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_REFLECTION_SRC))))
 
@@ -5583,6 +5963,36 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/async_stream.h \
+    include/grpcpp/impl/codegen/async_unary_call.h \
+    include/grpcpp/impl/codegen/byte_buffer.h \
+    include/grpcpp/impl/codegen/call.h \
+    include/grpcpp/impl/codegen/call_hook.h \
+    include/grpcpp/impl/codegen/channel_interface.h \
+    include/grpcpp/impl/codegen/client_context.h \
+    include/grpcpp/impl/codegen/client_unary_call.h \
+    include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_tag.h \
+    include/grpcpp/impl/codegen/config.h \
+    include/grpcpp/impl/codegen/core_codegen_interface.h \
+    include/grpcpp/impl/codegen/create_auth_context.h \
+    include/grpcpp/impl/codegen/grpc_library.h \
+    include/grpcpp/impl/codegen/metadata_map.h \
+    include/grpcpp/impl/codegen/method_handler_impl.h \
+    include/grpcpp/impl/codegen/rpc_method.h \
+    include/grpcpp/impl/codegen/rpc_service_method.h \
+    include/grpcpp/impl/codegen/security/auth_context.h \
+    include/grpcpp/impl/codegen/serialization_traits.h \
+    include/grpcpp/impl/codegen/server_context.h \
+    include/grpcpp/impl/codegen/server_interface.h \
+    include/grpcpp/impl/codegen/service_type.h \
+    include/grpcpp/impl/codegen/slice.h \
+    include/grpcpp/impl/codegen/status.h \
+    include/grpcpp/impl/codegen/status_code_enum.h \
+    include/grpcpp/impl/codegen/string_ref.h \
+    include/grpcpp/impl/codegen/stub_options.h \
+    include/grpcpp/impl/codegen/sync_stream.h \
+    include/grpcpp/impl/codegen/time.h \
     include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
@@ -5605,7 +6015,9 @@
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
     include/grpc++/impl/codegen/proto_utils.h \
+    include/grpcpp/impl/codegen/proto_utils.h \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpcpp/impl/codegen/config_protobuf.h \
 
 LIBGRPC++_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_TEST_UTIL_SRC))))
 
@@ -5700,6 +6112,36 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/async_stream.h \
+    include/grpcpp/impl/codegen/async_unary_call.h \
+    include/grpcpp/impl/codegen/byte_buffer.h \
+    include/grpcpp/impl/codegen/call.h \
+    include/grpcpp/impl/codegen/call_hook.h \
+    include/grpcpp/impl/codegen/channel_interface.h \
+    include/grpcpp/impl/codegen/client_context.h \
+    include/grpcpp/impl/codegen/client_unary_call.h \
+    include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_tag.h \
+    include/grpcpp/impl/codegen/config.h \
+    include/grpcpp/impl/codegen/core_codegen_interface.h \
+    include/grpcpp/impl/codegen/create_auth_context.h \
+    include/grpcpp/impl/codegen/grpc_library.h \
+    include/grpcpp/impl/codegen/metadata_map.h \
+    include/grpcpp/impl/codegen/method_handler_impl.h \
+    include/grpcpp/impl/codegen/rpc_method.h \
+    include/grpcpp/impl/codegen/rpc_service_method.h \
+    include/grpcpp/impl/codegen/security/auth_context.h \
+    include/grpcpp/impl/codegen/serialization_traits.h \
+    include/grpcpp/impl/codegen/server_context.h \
+    include/grpcpp/impl/codegen/server_interface.h \
+    include/grpcpp/impl/codegen/service_type.h \
+    include/grpcpp/impl/codegen/slice.h \
+    include/grpcpp/impl/codegen/status.h \
+    include/grpcpp/impl/codegen/status_code_enum.h \
+    include/grpcpp/impl/codegen/string_ref.h \
+    include/grpcpp/impl/codegen/stub_options.h \
+    include/grpcpp/impl/codegen/sync_stream.h \
+    include/grpcpp/impl/codegen/time.h \
     include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
@@ -5722,7 +6164,9 @@
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
     include/grpc++/impl/codegen/proto_utils.h \
+    include/grpcpp/impl/codegen/proto_utils.h \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpcpp/impl/codegen/config_protobuf.h \
 
 LIBGRPC++_TEST_UTIL_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_TEST_UTIL_UNSECURE_SRC))))
 
@@ -5859,6 +6303,51 @@
     include/grpc++/support/stub_options.h \
     include/grpc++/support/sync_stream.h \
     include/grpc++/support/time.h \
+    include/grpcpp/alarm.h \
+    include/grpcpp/channel.h \
+    include/grpcpp/client_context.h \
+    include/grpcpp/completion_queue.h \
+    include/grpcpp/create_channel.h \
+    include/grpcpp/create_channel_posix.h \
+    include/grpcpp/ext/health_check_service_server_builder_option.h \
+    include/grpcpp/generic/async_generic_service.h \
+    include/grpcpp/generic/generic_stub.h \
+    include/grpcpp/grpcpp.h \
+    include/grpcpp/health_check_service_interface.h \
+    include/grpcpp/impl/call.h \
+    include/grpcpp/impl/channel_argument_option.h \
+    include/grpcpp/impl/client_unary_call.h \
+    include/grpcpp/impl/codegen/core_codegen.h \
+    include/grpcpp/impl/grpc_library.h \
+    include/grpcpp/impl/method_handler_impl.h \
+    include/grpcpp/impl/rpc_method.h \
+    include/grpcpp/impl/rpc_service_method.h \
+    include/grpcpp/impl/serialization_traits.h \
+    include/grpcpp/impl/server_builder_option.h \
+    include/grpcpp/impl/server_builder_plugin.h \
+    include/grpcpp/impl/server_initializer.h \
+    include/grpcpp/impl/service_type.h \
+    include/grpcpp/resource_quota.h \
+    include/grpcpp/security/auth_context.h \
+    include/grpcpp/security/auth_metadata_processor.h \
+    include/grpcpp/security/credentials.h \
+    include/grpcpp/security/server_credentials.h \
+    include/grpcpp/server.h \
+    include/grpcpp/server_builder.h \
+    include/grpcpp/server_context.h \
+    include/grpcpp/server_posix.h \
+    include/grpcpp/support/async_stream.h \
+    include/grpcpp/support/async_unary_call.h \
+    include/grpcpp/support/byte_buffer.h \
+    include/grpcpp/support/channel_arguments.h \
+    include/grpcpp/support/config.h \
+    include/grpcpp/support/slice.h \
+    include/grpcpp/support/status.h \
+    include/grpcpp/support/status_code_enum.h \
+    include/grpcpp/support/string_ref.h \
+    include/grpcpp/support/stub_options.h \
+    include/grpcpp/support/sync_stream.h \
+    include/grpcpp/support/time.h \
     include/grpc/support/alloc.h \
     include/grpc/support/atm.h \
     include/grpc/support/atm_gcc_atomic.h \
@@ -5939,6 +6428,36 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpcpp/impl/codegen/async_stream.h \
+    include/grpcpp/impl/codegen/async_unary_call.h \
+    include/grpcpp/impl/codegen/byte_buffer.h \
+    include/grpcpp/impl/codegen/call.h \
+    include/grpcpp/impl/codegen/call_hook.h \
+    include/grpcpp/impl/codegen/channel_interface.h \
+    include/grpcpp/impl/codegen/client_context.h \
+    include/grpcpp/impl/codegen/client_unary_call.h \
+    include/grpcpp/impl/codegen/completion_queue.h \
+    include/grpcpp/impl/codegen/completion_queue_tag.h \
+    include/grpcpp/impl/codegen/config.h \
+    include/grpcpp/impl/codegen/core_codegen_interface.h \
+    include/grpcpp/impl/codegen/create_auth_context.h \
+    include/grpcpp/impl/codegen/grpc_library.h \
+    include/grpcpp/impl/codegen/metadata_map.h \
+    include/grpcpp/impl/codegen/method_handler_impl.h \
+    include/grpcpp/impl/codegen/rpc_method.h \
+    include/grpcpp/impl/codegen/rpc_service_method.h \
+    include/grpcpp/impl/codegen/security/auth_context.h \
+    include/grpcpp/impl/codegen/serialization_traits.h \
+    include/grpcpp/impl/codegen/server_context.h \
+    include/grpcpp/impl/codegen/server_interface.h \
+    include/grpcpp/impl/codegen/service_type.h \
+    include/grpcpp/impl/codegen/slice.h \
+    include/grpcpp/impl/codegen/status.h \
+    include/grpcpp/impl/codegen/status_code_enum.h \
+    include/grpcpp/impl/codegen/string_ref.h \
+    include/grpcpp/impl/codegen/stub_options.h \
+    include/grpcpp/impl/codegen/sync_stream.h \
+    include/grpcpp/impl/codegen/time.h \
 
 LIBGRPC++_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_UNSECURE_SRC))))
 
@@ -6049,6 +6568,7 @@
 
 PUBLIC_HEADERS_CXX += \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpcpp/impl/codegen/config_protobuf.h \
 
 LIBGRPC_CLI_LIBS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_CLI_LIBS_SRC))))
 
@@ -6110,6 +6630,7 @@
 
 PUBLIC_HEADERS_CXX += \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpcpp/impl/codegen/config_protobuf.h \
 
 LIBGRPC_PLUGIN_SUPPORT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_PLUGIN_SUPPORT_SRC))))
 
@@ -9107,6 +9628,21 @@
     test/core/end2end/tests/request_with_flags.cc \
     test/core/end2end/tests/request_with_payload.cc \
     test/core/end2end/tests/resource_quota_server.cc \
+    test/core/end2end/tests/retry.cc \
+    test/core/end2end/tests/retry_cancellation.cc \
+    test/core/end2end/tests/retry_disabled.cc \
+    test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc \
+    test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc \
+    test/core/end2end/tests/retry_non_retriable_status.cc \
+    test/core/end2end/tests/retry_recv_initial_metadata.cc \
+    test/core/end2end/tests/retry_recv_message.cc \
+    test/core/end2end/tests/retry_server_pushback_delay.cc \
+    test/core/end2end/tests/retry_server_pushback_disabled.cc \
+    test/core/end2end/tests/retry_streaming.cc \
+    test/core/end2end/tests/retry_streaming_after_commit.cc \
+    test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc \
+    test/core/end2end/tests/retry_throttled.cc \
+    test/core/end2end/tests/retry_too_many_attempts.cc \
     test/core/end2end/tests/server_finishes_request.cc \
     test/core/end2end/tests/shutdown_finishes_calls.cc \
     test/core/end2end/tests/shutdown_finishes_tags.cc \
@@ -9206,6 +9742,21 @@
     test/core/end2end/tests/request_with_flags.cc \
     test/core/end2end/tests/request_with_payload.cc \
     test/core/end2end/tests/resource_quota_server.cc \
+    test/core/end2end/tests/retry.cc \
+    test/core/end2end/tests/retry_cancellation.cc \
+    test/core/end2end/tests/retry_disabled.cc \
+    test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc \
+    test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc \
+    test/core/end2end/tests/retry_non_retriable_status.cc \
+    test/core/end2end/tests/retry_recv_initial_metadata.cc \
+    test/core/end2end/tests/retry_recv_message.cc \
+    test/core/end2end/tests/retry_server_pushback_delay.cc \
+    test/core/end2end/tests/retry_server_pushback_disabled.cc \
+    test/core/end2end/tests/retry_streaming.cc \
+    test/core/end2end/tests/retry_streaming_after_commit.cc \
+    test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc \
+    test/core/end2end/tests/retry_throttled.cc \
+    test/core/end2end/tests/retry_too_many_attempts.cc \
     test/core/end2end/tests/server_finishes_request.cc \
     test/core/end2end/tests/shutdown_finishes_calls.cc \
     test/core/end2end/tests/shutdown_finishes_tags.cc \
@@ -10724,7 +11275,7 @@
 
 
 GPR_THD_TEST_SRC = \
-    test/core/gpr/thd_test.cc \
+    test/core/gprpp/thd_test.cc \
 
 GPR_THD_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_THD_TEST_SRC))))
 ifeq ($(NO_SECURE),true)
@@ -10744,7 +11295,7 @@
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/core/gpr/thd_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/gprpp/thd_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_gpr_thd_test: $(GPR_THD_TEST_OBJS:.o=.dep)
 
@@ -13010,38 +13561,6 @@
 endif
 
 
-SLICE_HASH_TABLE_TEST_SRC = \
-    test/core/slice/slice_hash_table_test.cc \
-
-SLICE_HASH_TABLE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SLICE_HASH_TABLE_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/slice_hash_table_test: openssl_dep_error
-
-else
-
-
-
-$(BINDIR)/$(CONFIG)/slice_hash_table_test: $(SLICE_HASH_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(SLICE_HASH_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/slice_hash_table_test
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/core/slice/slice_hash_table_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_slice_hash_table_test: $(SLICE_HASH_TABLE_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(SLICE_HASH_TABLE_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
 SLICE_STRING_HELPERS_TEST_SRC = \
     test/core/slice/slice_string_helpers_test.cc \
 
@@ -13920,6 +14439,568 @@
 endif
 
 
+ALTS_COUNTER_TEST_SRC = \
+    test/core/tsi/alts/frame_protector/alts_counter_test.cc \
+
+ALTS_COUNTER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_COUNTER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_counter_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_counter_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_counter_test: $(PROTOBUF_DEP) $(ALTS_COUNTER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_COUNTER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_counter_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/frame_protector/alts_counter_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_counter_test: $(ALTS_COUNTER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_COUNTER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_CRYPT_TEST_SRC = \
+    test/core/tsi/alts/crypt/aes_gcm_test.cc \
+
+ALTS_CRYPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_CRYPT_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_crypt_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_crypt_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_crypt_test: $(PROTOBUF_DEP) $(ALTS_CRYPT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_CRYPT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_crypt_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/crypt/aes_gcm_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_crypt_test: $(ALTS_CRYPT_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_CRYPT_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_CRYPTER_TEST_SRC = \
+    test/core/tsi/alts/frame_protector/alts_crypter_test.cc \
+
+ALTS_CRYPTER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_CRYPTER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_crypter_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_crypter_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_crypter_test: $(PROTOBUF_DEP) $(ALTS_CRYPTER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_CRYPTER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_crypter_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/frame_protector/alts_crypter_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_crypter_test: $(ALTS_CRYPTER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_CRYPTER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_FRAME_HANDLER_TEST_SRC = \
+    test/core/tsi/alts/frame_protector/frame_handler_test.cc \
+
+ALTS_FRAME_HANDLER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_FRAME_HANDLER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_frame_handler_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_frame_handler_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_frame_handler_test: $(PROTOBUF_DEP) $(ALTS_FRAME_HANDLER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_FRAME_HANDLER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_frame_handler_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/frame_protector/frame_handler_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_frame_handler_test: $(ALTS_FRAME_HANDLER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_FRAME_HANDLER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_FRAME_PROTECTOR_TEST_SRC = \
+    test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc \
+    test/core/tsi/transport_security_test_lib.cc \
+
+ALTS_FRAME_PROTECTOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_FRAME_PROTECTOR_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_frame_protector_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_frame_protector_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_frame_protector_test: $(PROTOBUF_DEP) $(ALTS_FRAME_PROTECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_FRAME_PROTECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_frame_protector_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/frame_protector/alts_frame_protector_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/transport_security_test_lib.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_frame_protector_test: $(ALTS_FRAME_PROTECTOR_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_FRAME_PROTECTOR_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_GRPC_RECORD_PROTOCOL_TEST_SRC = \
+    test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc \
+
+ALTS_GRPC_RECORD_PROTOCOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_GRPC_RECORD_PROTOCOL_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test: $(PROTOBUF_DEP) $(ALTS_GRPC_RECORD_PROTOCOL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_GRPC_RECORD_PROTOCOL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_grpc_record_protocol_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_grpc_record_protocol_test: $(ALTS_GRPC_RECORD_PROTOCOL_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_GRPC_RECORD_PROTOCOL_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_HANDSHAKER_CLIENT_TEST_SRC = \
+    test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc \
+
+ALTS_HANDSHAKER_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_HANDSHAKER_CLIENT_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_handshaker_client_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_handshaker_client_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_handshaker_client_test: $(PROTOBUF_DEP) $(ALTS_HANDSHAKER_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_HANDSHAKER_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_handshaker_client_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/handshaker/alts_handshaker_client_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_handshaker_client_test: $(ALTS_HANDSHAKER_CLIENT_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_HANDSHAKER_CLIENT_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_HANDSHAKER_SERVICE_API_TEST_SRC = \
+    test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc \
+
+ALTS_HANDSHAKER_SERVICE_API_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_HANDSHAKER_SERVICE_API_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test: $(PROTOBUF_DEP) $(ALTS_HANDSHAKER_SERVICE_API_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_HANDSHAKER_SERVICE_API_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_handshaker_service_api_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_handshaker_service_api_test: $(ALTS_HANDSHAKER_SERVICE_API_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_HANDSHAKER_SERVICE_API_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_IOVEC_RECORD_PROTOCOL_TEST_SRC = \
+    test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc \
+
+ALTS_IOVEC_RECORD_PROTOCOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_IOVEC_RECORD_PROTOCOL_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test: $(PROTOBUF_DEP) $(ALTS_IOVEC_RECORD_PROTOCOL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_IOVEC_RECORD_PROTOCOL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_iovec_record_protocol_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_iovec_record_protocol_test: $(ALTS_IOVEC_RECORD_PROTOCOL_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_IOVEC_RECORD_PROTOCOL_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_SECURITY_CONNECTOR_TEST_SRC = \
+    test/core/security/alts_security_connector_test.cc \
+
+ALTS_SECURITY_CONNECTOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_SECURITY_CONNECTOR_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_security_connector_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_security_connector_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_security_connector_test: $(PROTOBUF_DEP) $(ALTS_SECURITY_CONNECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_SECURITY_CONNECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_security_connector_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/security/alts_security_connector_test.o:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_security_connector_test: $(ALTS_SECURITY_CONNECTOR_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_SECURITY_CONNECTOR_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_TSI_HANDSHAKER_TEST_SRC = \
+    test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc \
+
+ALTS_TSI_HANDSHAKER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_TSI_HANDSHAKER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test: $(PROTOBUF_DEP) $(ALTS_TSI_HANDSHAKER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_TSI_HANDSHAKER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_tsi_handshaker_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_tsi_handshaker_test: $(ALTS_TSI_HANDSHAKER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_TSI_HANDSHAKER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_TSI_UTILS_TEST_SRC = \
+    test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc \
+
+ALTS_TSI_UTILS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_TSI_UTILS_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_tsi_utils_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_tsi_utils_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_tsi_utils_test: $(PROTOBUF_DEP) $(ALTS_TSI_UTILS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_TSI_UTILS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_tsi_utils_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/handshaker/alts_tsi_utils_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_tsi_utils_test: $(ALTS_TSI_UTILS_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_TSI_UTILS_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_SRC = \
+    test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc \
+
+ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test: $(PROTOBUF_DEP) $(ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/alts_zero_copy_grpc_protector_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_alts_zero_copy_grpc_protector_test: $(ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(ALTS_ZERO_COPY_GRPC_PROTECTOR_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 ASYNC_END2END_TEST_SRC = \
     test/cpp/end2end/async_end2end_test.cc \
 
@@ -14794,6 +15875,92 @@
 endif
 
 
+CHECK_GCP_ENVIRONMENT_LINUX_TEST_SRC = \
+    test/core/security/check_gcp_environment_linux_test.cc \
+
+CHECK_GCP_ENVIRONMENT_LINUX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHECK_GCP_ENVIRONMENT_LINUX_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test: $(PROTOBUF_DEP) $(CHECK_GCP_ENVIRONMENT_LINUX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(CHECK_GCP_ENVIRONMENT_LINUX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/check_gcp_environment_linux_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/security/check_gcp_environment_linux_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_check_gcp_environment_linux_test: $(CHECK_GCP_ENVIRONMENT_LINUX_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHECK_GCP_ENVIRONMENT_LINUX_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_SRC = \
+    test/core/security/check_gcp_environment_windows_test.cc \
+
+CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test: $(PROTOBUF_DEP) $(CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/check_gcp_environment_windows_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/security/check_gcp_environment_windows_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_check_gcp_environment_windows_test: $(CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHECK_GCP_ENVIRONMENT_WINDOWS_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_SETTINGS_TIMEOUT_TEST_SRC = \
     test/core/transport/chttp2/settings_timeout_test.cc \
 
@@ -15659,6 +16826,49 @@
 $(OBJDIR)/$(CONFIG)/test/cpp/codegen/golden_file_test.o: $(GENDIR)/src/proto/grpc/testing/compiler_test.pb.cc $(GENDIR)/src/proto/grpc/testing/compiler_test.grpc.pb.cc
 
 
+GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_SRC = \
+    test/core/security/grpc_alts_credentials_options_test.cc \
+
+GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test: $(PROTOBUF_DEP) $(GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpc_alts_credentials_options_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/security/grpc_alts_credentials_options_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_grpc_alts_credentials_options_test: $(GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_ALTS_CREDENTIALS_OPTIONS_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GRPC_CLI_SRC = \
     test/cpp/util/grpc_cli.cc \
 
@@ -16063,53 +17273,6 @@
 $(OBJDIR)/$(CONFIG)/test/cpp/end2end/grpclb_end2end_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
 
 
-GRPCLB_TEST_SRC = \
-    $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \
-    test/cpp/grpclb/grpclb_test.cc \
-
-GRPCLB_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPCLB_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/grpclb_test: openssl_dep_error
-
-else
-
-
-
-
-ifeq ($(NO_PROTOBUF),true)
-
-# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
-
-$(BINDIR)/$(CONFIG)/grpclb_test: protobuf_dep_error
-
-else
-
-$(BINDIR)/$(CONFIG)/grpclb_test: $(PROTOBUF_DEP) $(GRPCLB_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(GRPCLB_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpclb_test
-
-endif
-
-endif
-
-$(OBJDIR)/$(CONFIG)/src/proto/grpc/lb/v1/load_balancer.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-$(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_grpclb_test: $(GRPCLB_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(GRPCLB_TEST_OBJS:.o=.dep)
-endif
-endif
-$(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
-
-
 H2_SSL_CERT_TEST_SRC = \
     test/core/end2end/h2_ssl_cert_test.cc \
 
@@ -17661,6 +18824,92 @@
 endif
 
 
+SLICE_HASH_TABLE_TEST_SRC = \
+    test/core/slice/slice_hash_table_test.cc \
+
+SLICE_HASH_TABLE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SLICE_HASH_TABLE_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/slice_hash_table_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/slice_hash_table_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/slice_hash_table_test: $(PROTOBUF_DEP) $(SLICE_HASH_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(SLICE_HASH_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/slice_hash_table_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/slice/slice_hash_table_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_slice_hash_table_test: $(SLICE_HASH_TABLE_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SLICE_HASH_TABLE_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+SLICE_WEAK_HASH_TABLE_TEST_SRC = \
+    test/core/slice/slice_weak_hash_table_test.cc \
+
+SLICE_WEAK_HASH_TABLE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SLICE_WEAK_HASH_TABLE_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/slice_weak_hash_table_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/slice_weak_hash_table_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/slice_weak_hash_table_test: $(PROTOBUF_DEP) $(SLICE_WEAK_HASH_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(SLICE_WEAK_HASH_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/slice_weak_hash_table_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/slice/slice_weak_hash_table_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_slice_weak_hash_table_test: $(SLICE_WEAK_HASH_TABLE_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SLICE_WEAK_HASH_TABLE_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 STATS_TEST_SRC = \
     test/core/debug/stats_test.cc \
 
@@ -17704,6 +18953,49 @@
 endif
 
 
+STATUS_METADATA_TEST_SRC = \
+    test/core/transport/status_metadata_test.cc \
+
+STATUS_METADATA_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATUS_METADATA_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/status_metadata_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/status_metadata_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/status_metadata_test: $(PROTOBUF_DEP) $(STATUS_METADATA_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(STATUS_METADATA_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/status_metadata_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/transport/status_metadata_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_status_metadata_test: $(STATUS_METADATA_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(STATUS_METADATA_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 STATUS_TEST_SRC = \
     test/cpp/util/status_test.cc \
 
@@ -17747,6 +19039,49 @@
 endif
 
 
+STATUS_UTIL_TEST_SRC = \
+    test/core/client_channel/status_util_test.cc \
+
+STATUS_UTIL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATUS_UTIL_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/status_util_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/status_util_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/status_util_test: $(PROTOBUF_DEP) $(STATUS_UTIL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(STATUS_UTIL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/status_util_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/client_channel/status_util_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_status_util_test: $(STATUS_UTIL_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(STATUS_UTIL_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 STREAMING_THROUGHPUT_TEST_SRC = \
     test/cpp/end2end/streaming_throughput_test.cc \
 
@@ -17987,6 +19322,49 @@
 endif
 
 
+TRANSPORT_SECURITY_COMMON_API_TEST_SRC = \
+    test/core/tsi/alts/handshaker/transport_security_common_api_test.cc \
+
+TRANSPORT_SECURITY_COMMON_API_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TRANSPORT_SECURITY_COMMON_API_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/transport_security_common_api_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/transport_security_common_api_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/transport_security_common_api_test: $(PROTOBUF_DEP) $(TRANSPORT_SECURITY_COMMON_API_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(TRANSPORT_SECURITY_COMMON_API_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/transport_security_common_api_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/alts/handshaker/transport_security_common_api_test.o:  $(LIBDIR)/$(CONFIG)/libalts_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_transport_security_common_api_test: $(TRANSPORT_SECURITY_COMMON_API_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TRANSPORT_SECURITY_COMMON_API_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 WRITES_PER_RPC_TEST_SRC = \
     test/cpp/performance/writes_per_rpc_test.cc \
 
@@ -21957,6 +23335,14 @@
 src/core/ext/transport/cronet/transport/cronet_transport.cc: $(OPENSSL_DEP)
 src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP)
 src/core/lib/security/context/security_context.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/alts_credentials.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/check_gcp_environment.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc: $(OPENSSL_DEP)
+src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/composite/composite_credentials.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/credentials.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/credentials_metadata.cc: $(OPENSSL_DEP)
@@ -21970,17 +23356,42 @@
 src/core/lib/security/credentials/oauth2/oauth2_credentials.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/plugin/plugin_credentials.cc: $(OPENSSL_DEP)
 src/core/lib/security/credentials/ssl/ssl_credentials.cc: $(OPENSSL_DEP)
+src/core/lib/security/security_connector/alts_security_connector.cc: $(OPENSSL_DEP)
+src/core/lib/security/security_connector/security_connector.cc: $(OPENSSL_DEP)
 src/core/lib/security/transport/client_auth_filter.cc: $(OPENSSL_DEP)
-src/core/lib/security/transport/lb_targets_info.cc: $(OPENSSL_DEP)
 src/core/lib/security/transport/secure_endpoint.cc: $(OPENSSL_DEP)
-src/core/lib/security/transport/security_connector.cc: $(OPENSSL_DEP)
 src/core/lib/security/transport/security_handshaker.cc: $(OPENSSL_DEP)
 src/core/lib/security/transport/server_auth_filter.cc: $(OPENSSL_DEP)
+src/core/lib/security/transport/target_authority_table.cc: $(OPENSSL_DEP)
 src/core/lib/security/transport/tsi_error.cc: $(OPENSSL_DEP)
 src/core/lib/security/util/json_util.cc: $(OPENSSL_DEP)
 src/core/lib/surface/init_secure.cc: $(OPENSSL_DEP)
 src/core/plugin_registry/grpc_cronet_plugin_registry.cc: $(OPENSSL_DEP)
 src/core/plugin_registry/grpc_plugin_registry.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/crypt/aes_gcm.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/crypt/gsec.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/alts_counter.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/alts_crypter.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/alts_frame_protector.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/frame_protector/frame_handler.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_handshaker_client.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_tsi_event.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/alts_tsi_utils.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/altscontext.pb.c: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/handshaker.pb.c: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/transport_security_common.pb.c: $(OPENSSL_DEP)
+src/core/tsi/alts/handshaker/transport_security_common_api.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc: $(OPENSSL_DEP)
+src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc: $(OPENSSL_DEP)
 src/core/tsi/alts_transport_security.cc: $(OPENSSL_DEP)
 src/core/tsi/fake_transport_security.cc: $(OPENSSL_DEP)
 src/core/tsi/ssl_transport_security.cc: $(OPENSSL_DEP)
@@ -22008,6 +23419,8 @@
 test/core/end2end/end2end_tests.cc: $(OPENSSL_DEP)
 test/core/end2end/tests/call_creds.cc: $(OPENSSL_DEP)
 test/core/security/oauth2_utils.cc: $(OPENSSL_DEP)
+test/core/tsi/alts/crypt/gsec_test_util.cc: $(OPENSSL_DEP)
+test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc: $(OPENSSL_DEP)
 test/core/util/reconnect_server.cc: $(OPENSSL_DEP)
 test/core/util/test_tcp_server.cc: $(OPENSSL_DEP)
 test/cpp/end2end/test_service_impl.cc: $(OPENSSL_DEP)
diff --git a/README.md b/README.md
index fc72c7c..e66c0b1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-[![Build Status](https://grpc-testing.appspot.com/job/gRPC_master/badge/icon)](https://grpc-testing.appspot.com/job/gRPC_master)
-
 [gRPC - An RPC library and framework](http://github.com/grpc/grpc)
 ===================================
 
@@ -36,10 +34,12 @@
 | C#                      | [src/csharp](src/csharp)            |
 | Objective-C             | [src/objective-c](src/objective-c)  |
 
-Java source code is in the [grpc-java](http://github.com/grpc/grpc-java)
-repository. Go source code is in the
-[grpc-go](http://github.com/grpc/grpc-go) repository. NodeJS source code is in the
-[grpc-node](https://github.com/grpc/grpc-node) repository.
+| Language                | Source repo                                          |
+|-------------------------|------------------------------------------------------|
+| Java                    | [grpc-java](http://github.com/grpc/grpc-java)        |
+| Go                      | [grpc-go](http://github.com/grpc/grpc-go)            |
+| NodeJS                  | [grpc-node](https://github.com/grpc/grpc-node)       |
+| Dart                    | [grpc-dart](https://github.com/grpc/grpc-dart)       |
 
 See [MANIFEST.md](MANIFEST.md) for a listing of top-level items in the
 repository.
diff --git a/WORKSPACE b/WORKSPACE
index adce809..8b68c0d 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,4 +1,5 @@
 workspace(name = "com_github_grpc_grpc")
 
-load("//bazel:grpc_deps.bzl", "grpc_deps")
+load("//bazel:grpc_deps.bzl", "grpc_deps", "grpc_test_only_deps")
 grpc_deps()
+grpc_test_only_deps()
diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl
index 3fbefc4..662068a 100644
--- a/bazel/grpc_build_system.bzl
+++ b/bazel/grpc_build_system.bzl
@@ -26,6 +26,13 @@
 # The set of pollers to test against if a test exercises polling
 POLLERS = ['epollex', 'epollsig', 'epoll1', 'poll', 'poll-cv']
 
+def if_not_windows(a):
+  return select({
+      "//:windows": [],
+      "//:windows_msvc": [],
+      "//conditions:default": a,
+  })
+
 def _get_external_deps(external_deps):
   ret = []
   for dep in external_deps:
@@ -50,17 +57,24 @@
       ret.append(h)
   return ret
 
+def _maybe_update_cc_library_defines(name):
+  ret = []
+  if name == "alts_proto":
+    ret += ["PB_FIELD_16BIT=1"]
+  return ret
+
 def grpc_cc_library(name, srcs = [], public_hdrs = [], hdrs = [],
                     external_deps = [], deps = [], standalone = False,
                     language = "C++", testonly = False, visibility = None,
                     alwayslink = 0):
   copts = []
   if language.upper() == "C":
-    copts = ["-std=c99"]
+    copts = if_not_windows(["-std=c99"])
+  defines = _maybe_update_cc_library_defines(name)
   native.cc_library(
     name = name,
     srcs = srcs,
-    defines = select({"//:grpc_no_ares": ["GRPC_ARES=0"],
+    defines = defines + select({"//:grpc_no_ares": ["GRPC_ARES=0"],
                       "//conditions:default": [],}) +
               select({"//:remote_execution":  ["GRPC_PORT_ISOLATED_RUNTIME=1"],
                       "//conditions:default": [],}) +
@@ -73,7 +87,7 @@
     copts = copts,
     visibility = visibility,
     testonly = testonly,
-    linkopts = ["-pthread"],
+    linkopts = if_not_windows(["-pthread"]),
     includes = [
         "include"
     ],
@@ -104,7 +118,7 @@
 def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data = [], uses_polling = True, language = "C++"):
   copts = []
   if language.upper() == "C":
-    copts = ["-std=c99"]
+    copts = if_not_windows(["-std=c99"])
   args = {
     'name': name,
     'srcs': srcs,
@@ -112,7 +126,7 @@
     'data': data,
     'deps': deps + _get_external_deps(external_deps),
     'copts': copts,
-    'linkopts': ["-pthread"],
+    'linkopts': if_not_windows(["-pthread"]),
   }
   if uses_polling:
     native.cc_test(testonly=True, tags=['manual'], **args)
@@ -144,7 +158,7 @@
     linkshared = linkshared,
     deps = deps + _get_external_deps(external_deps),
     copts = copts,
-    linkopts = ["-pthread"] + linkopts,
+    linkopts = if_not_windows(["-pthread"]) + linkopts,
   )
 
 def grpc_generate_one_off_targets():
@@ -163,15 +177,13 @@
     srcs = srcs,
     data = data)
 
-def grpc_py_binary(name, srcs, data = [], deps = []):
-  if name == "test_dns_server":
-    # TODO: allow running test_dns_server in oss bazel test suite
-    deps = []
+def grpc_py_binary(name, srcs, data = [], deps = [], external_deps = [], testonly = False):
   native.py_binary(
     name = name,
     srcs = srcs,
     data = data,
-    deps = deps)
+    deps = deps + _get_external_deps(external_deps)
+  )
 
 def grpc_package(name, visibility = "private", features = []):
   if visibility == "tests":
diff --git a/bazel/grpc_deps.bzl b/bazel/grpc_deps.bzl
index 47d3308..a441c3f 100644
--- a/bazel/grpc_deps.bzl
+++ b/bazel/grpc_deps.bzl
@@ -120,10 +120,64 @@
     if "com_github_bazelbuild_bazeltoolchains" not in native.existing_rules():
         native.http_archive(
             name = "com_github_bazelbuild_bazeltoolchains",
-            strip_prefix = "bazel-toolchains-f3b09700fae5d7b6e659d7cefe0dcc6e8498504c",
+            strip_prefix = "bazel-toolchains-b850ccdf53fed1ccab7670f52d6b297d74348d1b",
             urls = [
-                "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/f3b09700fae5d7b6e659d7cefe0dcc6e8498504c.tar.gz",
-                "https://github.com/bazelbuild/bazel-toolchains/archive/f3b09700fae5d7b6e659d7cefe0dcc6e8498504c.tar.gz",
+                "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/b850ccdf53fed1ccab7670f52d6b297d74348d1b.tar.gz",
+                "https://github.com/bazelbuild/bazel-toolchains/archive/b850ccdf53fed1ccab7670f52d6b297d74348d1b.tar.gz",
             ],
-            sha256 = "ed829b5eea8af1f405f4cc3d6ecfc3b1365bb7843171036030a31b5127002311",
+            sha256 = "d84d6b2fe88ef99963febf91ddce33503eed14c155ace922e2122271b483be64",
+        )
+
+# TODO: move some dependencies from "grpc_deps" here?
+def grpc_test_only_deps():
+    """Internal, not intended for use by packages that are consuming grpc.
+       Loads dependencies that are only needed to run grpc library's tests."""
+    native.bind(
+        name = "twisted",
+        actual = "@com_github_twisted_twisted//:twisted",
+    )
+
+    native.bind(
+        name = "yaml",
+        actual = "@com_github_yaml_pyyaml//:yaml",
+    )
+
+    if "com_github_twisted_twisted" not in native.existing_rules():
+        native.new_http_archive(
+            name = "com_github_twisted_twisted",
+            strip_prefix = "twisted-twisted-17.5.0",
+            url = "https://github.com/twisted/twisted/archive/twisted-17.5.0.zip",
+            build_file = "@com_github_grpc_grpc//third_party:twisted.BUILD",
+        )
+
+    if "com_github_yaml_pyyaml" not in native.existing_rules():
+        native.new_http_archive(
+            name = "com_github_yaml_pyyaml",
+            strip_prefix = "pyyaml-3.12",
+            url = "https://github.com/yaml/pyyaml/archive/3.12.zip",
+            build_file = "@com_github_grpc_grpc//third_party:yaml.BUILD",
+        )
+
+    if "com_github_twisted_incremental" not in native.existing_rules():
+        native.new_http_archive(
+            name = "com_github_twisted_incremental",
+            strip_prefix = "incremental-incremental-17.5.0",
+            url = "https://github.com/twisted/incremental/archive/incremental-17.5.0.zip",
+            build_file = "@com_github_grpc_grpc//third_party:incremental.BUILD",
+        )
+
+    if "com_github_zopefoundation_zope_interface" not in native.existing_rules():
+        native.new_http_archive(
+            name = "com_github_zopefoundation_zope_interface",
+            strip_prefix = "zope.interface-4.4.3",
+            url = "https://github.com/zopefoundation/zope.interface/archive/4.4.3.zip",
+            build_file = "@com_github_grpc_grpc//third_party:zope_interface.BUILD",
+        )
+
+    if "com_github_twisted_constantly" not in native.existing_rules():
+        native.new_http_archive(
+            name = "com_github_twisted_constantly",
+            strip_prefix = "constantly-15.1.0",
+            url = "https://github.com/twisted/constantly/archive/15.1.0.zip",
+            build_file = "@com_github_grpc_grpc//third_party:constantly.BUILD",
         )
diff --git a/build.yaml b/build.yaml
index 3bf07d7..e2bb8bf 100644
--- a/build.yaml
+++ b/build.yaml
@@ -13,9 +13,87 @@
   '#09': Per-language overrides are possible with (eg) ruby_version tag here
   '#10': See the expand_version.py for all the quirks here
   core_version: 6.0.0-dev
-  g_stands_for: glamorous
-  version: 1.10.0-dev
+  g_stands_for: gorgeous
+  version: 1.11.0-dev
 filegroups:
+- name: alts_proto
+  headers:
+  - src/core/tsi/alts/handshaker/altscontext.pb.h
+  - src/core/tsi/alts/handshaker/handshaker.pb.h
+  - src/core/tsi/alts/handshaker/transport_security_common.pb.h
+  src:
+  - src/core/tsi/alts/handshaker/altscontext.pb.c
+  - src/core/tsi/alts/handshaker/handshaker.pb.c
+  - src/core/tsi/alts/handshaker/transport_security_common.pb.c
+  uses:
+  - nanopb
+- name: alts_tsi
+  headers:
+  - src/core/tsi/alts/crypt/gsec.h
+  - src/core/tsi/alts/frame_protector/alts_counter.h
+  - src/core/tsi/alts/frame_protector/alts_crypter.h
+  - src/core/tsi/alts/frame_protector/alts_frame_protector.h
+  - src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h
+  - src/core/tsi/alts/frame_protector/frame_handler.h
+  - src/core/tsi/alts/handshaker/alts_handshaker_client.h
+  - src/core/tsi/alts/handshaker/alts_tsi_event.h
+  - src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
+  - src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
+  src:
+  - src/core/tsi/alts/crypt/aes_gcm.cc
+  - src/core/tsi/alts/crypt/gsec.cc
+  - src/core/tsi/alts/frame_protector/alts_counter.cc
+  - src/core/tsi/alts/frame_protector/alts_crypter.cc
+  - src/core/tsi/alts/frame_protector/alts_frame_protector.cc
+  - src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
+  - src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
+  - src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
+  - src/core/tsi/alts/frame_protector/frame_handler.cc
+  - src/core/tsi/alts/handshaker/alts_handshaker_client.cc
+  - src/core/tsi/alts/handshaker/alts_tsi_event.cc
+  - src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
+  - src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
+  uses:
+  - alts_util
+  - grpc_base
+  - grpc_transport_chttp2_client_insecure
+  - tsi_interface
+  - tsi
+- name: alts_util
+  headers:
+  - src/core/lib/security/credentials/alts/check_gcp_environment.h
+  - src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h
+  - src/core/tsi/alts/handshaker/alts_handshaker_service_api.h
+  - src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h
+  - src/core/tsi/alts/handshaker/alts_tsi_utils.h
+  - src/core/tsi/alts/handshaker/transport_security_common_api.h
+  src:
+  - src/core/lib/security/credentials/alts/check_gcp_environment.cc
+  - src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
+  - src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
+  - src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
+  - src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
+  - src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
+  - src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
+  - src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
+  - src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
+  - src/core/tsi/alts/handshaker/alts_tsi_utils.cc
+  - src/core/tsi/alts/handshaker/transport_security_common_api.cc
+  uses:
+  - alts_proto
+  - grpc_base
+  - tsi_interface
+  - nanopb
 - name: census
   public_headers:
   - include/grpc/census.h
@@ -59,9 +137,6 @@
   - src/core/lib/gpr/sync.cc
   - src/core/lib/gpr/sync_posix.cc
   - src/core/lib/gpr/sync_windows.cc
-  - src/core/lib/gpr/thd.cc
-  - src/core/lib/gpr/thd_posix.cc
-  - src/core/lib/gpr/thd_windows.cc
   - src/core/lib/gpr/time.cc
   - src/core/lib/gpr/time_posix.cc
   - src/core/lib/gpr/time_precise.cc
@@ -71,6 +146,8 @@
   - src/core/lib/gpr/tmpfile_posix.cc
   - src/core/lib/gpr/tmpfile_windows.cc
   - src/core/lib/gpr/wrap_memcpy.cc
+  - src/core/lib/gprpp/thd_posix.cc
+  - src/core/lib/gprpp/thd_windows.cc
   - src/core/lib/profiling/basic_timers.cc
   - src/core/lib/profiling/stap_timers.cc
   uses:
@@ -104,7 +181,6 @@
   - src/core/lib/gpr/spinlock.h
   - src/core/lib/gpr/string.h
   - src/core/lib/gpr/string_windows.h
-  - src/core/lib/gpr/thd.h
   - src/core/lib/gpr/time_precise.h
   - src/core/lib/gpr/tls.h
   - src/core/lib/gpr/tls_gcc.h
@@ -118,6 +194,7 @@
   - src/core/lib/gprpp/atomic_with_std.h
   - src/core/lib/gprpp/manual_constructor.h
   - src/core/lib/gprpp/memory.h
+  - src/core/lib/gprpp/thd.h
   - src/core/lib/profiling/timers.h
   uses:
   - gpr_codegen
@@ -250,7 +327,6 @@
   - src/core/lib/slice/percent_encoding.cc
   - src/core/lib/slice/slice.cc
   - src/core/lib/slice/slice_buffer.cc
-  - src/core/lib/slice/slice_hash_table.cc
   - src/core/lib/slice/slice_intern.cc
   - src/core/lib/slice/slice_string_helpers.cc
   - src/core/lib/surface/api_trace.cc
@@ -281,6 +357,7 @@
   - src/core/lib/transport/service_config.cc
   - src/core/lib/transport/static_metadata.cc
   - src/core/lib/transport/status_conversion.cc
+  - src/core/lib/transport/status_metadata.cc
   - src/core/lib/transport/timeout_encoding.cc
   - src/core/lib/transport/transport.cc
   - src/core/lib/transport/transport_op_string.cc
@@ -404,6 +481,7 @@
   - src/core/lib/slice/slice_hash_table.h
   - src/core/lib/slice/slice_internal.h
   - src/core/lib/slice/slice_string_helpers.h
+  - src/core/lib/slice/slice_weak_hash_table.h
   - src/core/lib/surface/api_trace.h
   - src/core/lib/surface/call.h
   - src/core/lib/surface/call_test_only.h
@@ -428,6 +506,7 @@
   - src/core/lib/transport/service_config.h
   - src/core/lib/transport/static_metadata.h
   - src/core/lib/transport/status_conversion.h
+  - src/core/lib/transport/status_metadata.h
   - src/core/lib/transport/timeout_encoding.h
   - src/core/lib/transport/transport.h
   - src/core/lib/transport/transport_impl.h
@@ -447,6 +526,7 @@
   - src/core/ext/filters/client_channel/lb_policy.h
   - src/core/ext/filters/client_channel/lb_policy_factory.h
   - src/core/ext/filters/client_channel/lb_policy_registry.h
+  - src/core/ext/filters/client_channel/method_params.h
   - src/core/ext/filters/client_channel/parse_address.h
   - src/core/ext/filters/client_channel/proxy_mapper.h
   - src/core/ext/filters/client_channel/proxy_mapper_registry.h
@@ -454,6 +534,7 @@
   - src/core/ext/filters/client_channel/resolver_factory.h
   - src/core/ext/filters/client_channel/resolver_registry.h
   - src/core/ext/filters/client_channel/retry_throttle.h
+  - src/core/ext/filters/client_channel/status_util.h
   - src/core/ext/filters/client_channel/subchannel.h
   - src/core/ext/filters/client_channel/subchannel_index.h
   - src/core/ext/filters/client_channel/uri_parser.h
@@ -469,12 +550,14 @@
   - src/core/ext/filters/client_channel/lb_policy.cc
   - src/core/ext/filters/client_channel/lb_policy_factory.cc
   - src/core/ext/filters/client_channel/lb_policy_registry.cc
+  - src/core/ext/filters/client_channel/method_params.cc
   - src/core/ext/filters/client_channel/parse_address.cc
   - src/core/ext/filters/client_channel/proxy_mapper.cc
   - src/core/ext/filters/client_channel/proxy_mapper_registry.cc
   - src/core/ext/filters/client_channel/resolver.cc
   - src/core/ext/filters/client_channel/resolver_registry.cc
   - src/core/ext/filters/client_channel/retry_throttle.cc
+  - src/core/ext/filters/client_channel/status_util.cc
   - src/core/ext/filters/client_channel/subchannel.cc
   - src/core/ext/filters/client_channel/subchannel_index.cc
   - src/core/ext/filters/client_channel/uri_parser.cc
@@ -518,7 +601,6 @@
 - name: grpc_lb_policy_grpclb
   headers:
   - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
-  - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
@@ -539,7 +621,6 @@
 - name: grpc_lb_policy_grpclb_secure
   headers:
   - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
-  - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
@@ -639,6 +720,7 @@
   - include/grpc/grpc_security.h
   headers:
   - src/core/lib/security/context/security_context.h
+  - src/core/lib/security/credentials/alts/alts_credentials.h
   - src/core/lib/security/credentials/composite/composite_credentials.h
   - src/core/lib/security/credentials/credentials.h
   - src/core/lib/security/credentials/fake/fake_credentials.h
@@ -650,16 +732,18 @@
   - src/core/lib/security/credentials/oauth2/oauth2_credentials.h
   - src/core/lib/security/credentials/plugin/plugin_credentials.h
   - src/core/lib/security/credentials/ssl/ssl_credentials.h
+  - src/core/lib/security/security_connector/alts_security_connector.h
+  - src/core/lib/security/security_connector/security_connector.h
   - src/core/lib/security/transport/auth_filters.h
-  - src/core/lib/security/transport/lb_targets_info.h
   - src/core/lib/security/transport/secure_endpoint.h
-  - src/core/lib/security/transport/security_connector.h
   - src/core/lib/security/transport/security_handshaker.h
+  - src/core/lib/security/transport/target_authority_table.h
   - src/core/lib/security/transport/tsi_error.h
   - src/core/lib/security/util/json_util.h
   src:
   - src/core/lib/http/httpcli_security_connector.cc
   - src/core/lib/security/context/security_context.cc
+  - src/core/lib/security/credentials/alts/alts_credentials.cc
   - src/core/lib/security/credentials/composite/composite_credentials.cc
   - src/core/lib/security/credentials/credentials.cc
   - src/core/lib/security/credentials/credentials_metadata.cc
@@ -673,17 +757,19 @@
   - src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
   - src/core/lib/security/credentials/plugin/plugin_credentials.cc
   - src/core/lib/security/credentials/ssl/ssl_credentials.cc
+  - src/core/lib/security/security_connector/alts_security_connector.cc
+  - src/core/lib/security/security_connector/security_connector.cc
   - src/core/lib/security/transport/client_auth_filter.cc
-  - src/core/lib/security/transport/lb_targets_info.cc
   - src/core/lib/security/transport/secure_endpoint.cc
-  - src/core/lib/security/transport/security_connector.cc
   - src/core/lib/security/transport/security_handshaker.cc
   - src/core/lib/security/transport/server_auth_filter.cc
+  - src/core/lib/security/transport/target_authority_table.cc
   - src/core/lib/security/transport/tsi_error.cc
   - src/core/lib/security/util/json_util.cc
   - src/core/lib/surface/init_secure.cc
   secure: true
   uses:
+  - alts_tsi
   - grpc_base
   - grpc_transport_chttp2_alpn
   - tsi
@@ -1000,6 +1086,36 @@
   - include/grpc++/impl/codegen/stub_options.h
   - include/grpc++/impl/codegen/sync_stream.h
   - include/grpc++/impl/codegen/time.h
+  - include/grpcpp/impl/codegen/async_stream.h
+  - include/grpcpp/impl/codegen/async_unary_call.h
+  - include/grpcpp/impl/codegen/byte_buffer.h
+  - include/grpcpp/impl/codegen/call.h
+  - include/grpcpp/impl/codegen/call_hook.h
+  - include/grpcpp/impl/codegen/channel_interface.h
+  - include/grpcpp/impl/codegen/client_context.h
+  - include/grpcpp/impl/codegen/client_unary_call.h
+  - include/grpcpp/impl/codegen/completion_queue.h
+  - include/grpcpp/impl/codegen/completion_queue_tag.h
+  - include/grpcpp/impl/codegen/config.h
+  - include/grpcpp/impl/codegen/core_codegen_interface.h
+  - include/grpcpp/impl/codegen/create_auth_context.h
+  - include/grpcpp/impl/codegen/grpc_library.h
+  - include/grpcpp/impl/codegen/metadata_map.h
+  - include/grpcpp/impl/codegen/method_handler_impl.h
+  - include/grpcpp/impl/codegen/rpc_method.h
+  - include/grpcpp/impl/codegen/rpc_service_method.h
+  - include/grpcpp/impl/codegen/security/auth_context.h
+  - include/grpcpp/impl/codegen/serialization_traits.h
+  - include/grpcpp/impl/codegen/server_context.h
+  - include/grpcpp/impl/codegen/server_interface.h
+  - include/grpcpp/impl/codegen/service_type.h
+  - include/grpcpp/impl/codegen/slice.h
+  - include/grpcpp/impl/codegen/status.h
+  - include/grpcpp/impl/codegen/status_code_enum.h
+  - include/grpcpp/impl/codegen/string_ref.h
+  - include/grpcpp/impl/codegen/stub_options.h
+  - include/grpcpp/impl/codegen/sync_stream.h
+  - include/grpcpp/impl/codegen/time.h
   uses:
   - grpc_codegen
 - name: grpc++_codegen_base_src
@@ -1012,6 +1128,7 @@
   language: c++
   public_headers:
   - include/grpc++/impl/codegen/proto_utils.h
+  - include/grpcpp/impl/codegen/proto_utils.h
   uses:
   - grpc++_codegen_base
   - grpc++_config_proto
@@ -1063,6 +1180,51 @@
   - include/grpc++/support/stub_options.h
   - include/grpc++/support/sync_stream.h
   - include/grpc++/support/time.h
+  - include/grpcpp/alarm.h
+  - include/grpcpp/channel.h
+  - include/grpcpp/client_context.h
+  - include/grpcpp/completion_queue.h
+  - include/grpcpp/create_channel.h
+  - include/grpcpp/create_channel_posix.h
+  - include/grpcpp/ext/health_check_service_server_builder_option.h
+  - include/grpcpp/generic/async_generic_service.h
+  - include/grpcpp/generic/generic_stub.h
+  - include/grpcpp/grpcpp.h
+  - include/grpcpp/health_check_service_interface.h
+  - include/grpcpp/impl/call.h
+  - include/grpcpp/impl/channel_argument_option.h
+  - include/grpcpp/impl/client_unary_call.h
+  - include/grpcpp/impl/codegen/core_codegen.h
+  - include/grpcpp/impl/grpc_library.h
+  - include/grpcpp/impl/method_handler_impl.h
+  - include/grpcpp/impl/rpc_method.h
+  - include/grpcpp/impl/rpc_service_method.h
+  - include/grpcpp/impl/serialization_traits.h
+  - include/grpcpp/impl/server_builder_option.h
+  - include/grpcpp/impl/server_builder_plugin.h
+  - include/grpcpp/impl/server_initializer.h
+  - include/grpcpp/impl/service_type.h
+  - include/grpcpp/resource_quota.h
+  - include/grpcpp/security/auth_context.h
+  - include/grpcpp/security/auth_metadata_processor.h
+  - include/grpcpp/security/credentials.h
+  - include/grpcpp/security/server_credentials.h
+  - include/grpcpp/server.h
+  - include/grpcpp/server_builder.h
+  - include/grpcpp/server_context.h
+  - include/grpcpp/server_posix.h
+  - include/grpcpp/support/async_stream.h
+  - include/grpcpp/support/async_unary_call.h
+  - include/grpcpp/support/byte_buffer.h
+  - include/grpcpp/support/channel_arguments.h
+  - include/grpcpp/support/config.h
+  - include/grpcpp/support/slice.h
+  - include/grpcpp/support/status.h
+  - include/grpcpp/support/status_code_enum.h
+  - include/grpcpp/support/string_ref.h
+  - include/grpcpp/support/stub_options.h
+  - include/grpcpp/support/sync_stream.h
+  - include/grpcpp/support/time.h
   headers:
   - src/cpp/client/create_channel_internal.h
   - src/cpp/common/channel_filter.h
@@ -1116,6 +1278,7 @@
   language: c++
   public_headers:
   - include/grpc++/impl/codegen/config_protobuf.h
+  - include/grpcpp/impl/codegen/config_protobuf.h
 - name: grpc++_reflection_proto
   language: c++
   src:
@@ -1125,10 +1288,24 @@
   public_headers:
   - include/grpc++/test/mock_stream.h
   - include/grpc++/test/server_context_test_spouse.h
+  - include/grpcpp/test/mock_stream.h
+  - include/grpcpp/test/server_context_test_spouse.h
   deps:
   - grpc++
   - grpc
 libs:
+- name: alts_test_util
+  build: private
+  language: c
+  headers:
+  - test/core/tsi/alts/crypt/gsec_test_util.h
+  - test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h
+  src:
+  - test/core/tsi/alts/crypt/gsec_test_util.cc
+  - test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc
+  deps:
+  - grpc
+  secure: true
 - name: gpr
   build: all
   language: c
@@ -1312,6 +1489,7 @@
   language: c++
   headers:
   - include/grpc++/impl/codegen/core_codegen.h
+  - include/grpcpp/impl/codegen/core_codegen.h
   - src/cpp/client/secure_credentials.h
   - src/cpp/common/secure_auth_context.h
   - src/cpp/server/secure_server_credentials.h
@@ -1373,6 +1551,7 @@
   language: c++
   public_headers:
   - include/grpc++/support/error_details.h
+  - include/grpcpp/support/error_details.h
   src:
   - src/proto/grpc/status/status.proto
   - src/cpp/util/error_details.cc
@@ -1398,6 +1577,7 @@
   language: c++
   public_headers:
   - include/grpc++/ext/proto_server_reflection_plugin.h
+  - include/grpcpp/ext/proto_server_reflection_plugin.h
   headers:
   - src/cpp/ext/proto_server_reflection.h
   src:
@@ -2244,7 +2424,7 @@
   build: test
   language: c
   src:
-  - test/core/gpr/thd_test.cc
+  - test/core/gprpp/thd_test.cc
   deps:
   - gpr_test_util
   - gpr
@@ -3067,17 +3247,6 @@
   - gpr_test_util
   - gpr
   uses_polling: false
-- name: slice_hash_table_test
-  build: test
-  language: c
-  src:
-  - test/core/slice/slice_hash_table_test.cc
-  deps:
-  - grpc_test_util
-  - grpc
-  - gpr_test_util
-  - gpr
-  uses_polling: false
 - name: slice_string_helpers_test
   build: test
   language: c
@@ -3422,6 +3591,125 @@
   - grpc_unsecure
   - gpr_test_util
   - gpr
+- name: alts_counter_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/frame_protector/alts_counter_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_crypt_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/crypt/aes_gcm_test.cc
+  deps:
+  - alts_test_util
+  - gpr_test_util
+  - gpr
+  - grpc
+- name: alts_crypter_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/frame_protector/alts_crypter_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_frame_handler_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/frame_protector/frame_handler_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_frame_protector_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+  filegroups:
+  - transport_security_test_lib
+- name: alts_grpc_record_protocol_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_handshaker_client_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_handshaker_service_api_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_iovec_record_protocol_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_security_connector_test
+  build: test
+  language: c++
+  src:
+  - test/core/security/alts_security_connector_test.cc
+  deps:
+  - gpr
+  - grpc
+- name: alts_tsi_handshaker_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_tsi_utils_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
+- name: alts_zero_copy_grpc_protector_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
 - name: async_end2end_test
   gtest: true
   build: test
@@ -3805,6 +4093,22 @@
   - grpc
   - gpr
   uses_polling: false
+- name: check_gcp_environment_linux_test
+  build: test
+  language: c++
+  src:
+  - test/core/security/check_gcp_environment_linux_test.cc
+  deps:
+  - grpc
+  - gpr
+- name: check_gcp_environment_windows_test
+  build: test
+  language: c++
+  src:
+  - test/core/security/check_gcp_environment_windows_test.cc
+  deps:
+  - grpc
+  - gpr
 - name: chttp2_settings_timeout_test
   gtest: true
   build: test
@@ -4063,6 +4367,14 @@
   args:
   - --generated_file_path=gens/src/proto/grpc/testing/
   uses_polling: false
+- name: grpc_alts_credentials_options_test
+  build: test
+  language: c++
+  src:
+  - test/core/security/grpc_alts_credentials_options_test.cc
+  deps:
+  - grpc
+  - gpr
 - name: grpc_cli
   build: test
   run: false
@@ -4190,20 +4502,6 @@
   - grpc
   - gpr_test_util
   - gpr
-- name: grpclb_test
-  gtest: false
-  build: test
-  language: c++
-  src:
-  - src/proto/grpc/lb/v1/load_balancer.proto
-  - test/cpp/grpclb/grpclb_test.cc
-  deps:
-  - grpc++_test_util
-  - grpc_test_util
-  - grpc++
-  - grpc
-  - gpr_test_util
-  - gpr
 - name: h2_ssl_cert_test
   gtest: true
   build: test
@@ -4403,6 +4701,7 @@
   language: c++
   headers:
   - include/grpc++/test/mock_stream.h
+  - include/grpcpp/test/mock_stream.h
   src:
   - test/cpp/end2end/mock_test.cc
   deps:
@@ -4760,6 +5059,30 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: slice_hash_table_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/core/slice/slice_hash_table_test.cc
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  uses_polling: false
+- name: slice_weak_hash_table_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/core/slice/slice_weak_hash_table_test.cc
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  uses_polling: false
 - name: stats_test
   gtest: true
   build: test
@@ -4776,6 +5099,15 @@
   - tsan
   timeout_seconds: 1200
   uses_polling: false
+- name: status_metadata_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/core/transport/status_metadata_test.cc
+  deps:
+  - grpc
+  uses_polling: false
 - name: status_test
   build: test
   language: c++
@@ -4788,6 +5120,16 @@
   - gpr_test_util
   - gpr
   uses_polling: false
+- name: status_util_test
+  gtest: true
+  cpu_cost: 0.1
+  build: test
+  language: c++
+  src:
+  - test/core/client_channel/status_util_test.cc
+  deps:
+  - grpc
+  uses_polling: false
 - name: streaming_throughput_test
   gtest: true
   build: test
@@ -4868,6 +5210,15 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: transport_security_common_api_test
+  build: test
+  language: c++
+  src:
+  - test/core/tsi/alts/handshaker/transport_security_common_api_test.cc
+  deps:
+  - alts_test_util
+  - gpr
+  - grpc
 - name: writes_per_rpc_test
   gtest: true
   cpu_cost: 0.5
diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake
index 753dc06..2b4c20f 100644
--- a/cmake/benchmark.cmake
+++ b/cmake/benchmark.cmake
@@ -26,11 +26,12 @@
       message(WARNING "gRPC_BENCHMARK_PROVIDER is \"module\" but BENCHMARK_ROOT_DIR is wrong")
   endif()
 elseif("${gRPC_BENCHMARK_PROVIDER}" STREQUAL "package")
-  find_package(benchmark REQUIRED)
+  # Use "CONFIG" as there is no built-in cmake module for benchmark.
+  find_package(benchmark REQUIRED CONFIG)
   if(TARGET benchmark::benchmark)
     set(_gRPC_BENCHMARK_LIBRARIES benchmark::benchmark)
     # extract the include dir from target's properties
     get_target_property(_gRPC_BENCHMARK_INCLUDE_DIR benchmark::benchmark INTERFACE_INCLUDE_DIRECTORIES)
   endif()
-  set(_gRPC_FIND_BENCHMARK "if(NOT benchmark_FOUND)\n  find_package(benchmark)\nendif()")
+  set(_gRPC_FIND_BENCHMARK "if(NOT benchmark_FOUND)\n  find_package(benchmark CONFIG)\nendif()")
 endif()
diff --git a/cmake/cares.cmake b/cmake/cares.cmake
index 53d7582..3d4a0ca 100644
--- a/cmake/cares.cmake
+++ b/cmake/cares.cmake
@@ -30,6 +30,7 @@
     set(gRPC_INSTALL FALSE)
   endif()
 elseif("${gRPC_CARES_PROVIDER}" STREQUAL "package")
+  # Use "CONFIG" as there is no built-in cmake module for c-ares.
   find_package(c-ares REQUIRED CONFIG)
   if(TARGET c-ares::cares)
     set(_gRPC_CARES_LIBRARIES c-ares::cares)
diff --git a/cmake/gflags.cmake b/cmake/gflags.cmake
index f86a141..01e0a75 100644
--- a/cmake/gflags.cmake
+++ b/cmake/gflags.cmake
@@ -26,10 +26,11 @@
     message(WARNING "gRPC_GFLAGS_PROVIDER is \"module\" but GFLAGS_ROOT_DIR is wrong")
   endif()
 elseif("${gRPC_GFLAGS_PROVIDER}" STREQUAL "package")
-  find_package(gflags REQUIRED)
+  # Use "CONFIG" as there is no built-in cmake module for gflags.
+  find_package(gflags REQUIRED CONFIG)
   if(TARGET gflags::gflags)
     set(_gRPC_GFLAGS_LIBRARIES gflags::gflags)
     set(_gRPC_GFLAGS_INCLUDE_DIR ${GFLAGS_INCLUDE_DIR})
   endif()
-  set(_gRPC_FIND_GFLAGS "if(NOT gflags_FOUND)\n  find_package(gflags)\nendif()")
+  set(_gRPC_FIND_GFLAGS "if(NOT gflags_FOUND)\n  find_package(gflags CONFIG)\nendif()")
 endif()
diff --git a/cmake/ssl.cmake b/cmake/ssl.cmake
index 53d8a15..83f642a 100644
--- a/cmake/ssl.cmake
+++ b/cmake/ssl.cmake
@@ -31,6 +31,10 @@
     set(gRPC_INSTALL FALSE)
   endif()
 elseif("${gRPC_SSL_PROVIDER}" STREQUAL "package")
+  # OpenSSL installation directory can be configured by setting OPENSSL_ROOT_DIR
+  # We expect to locate OpenSSL using the built-in cmake module as the openssl
+  # project itself does not provide installation support in its CMakeLists.txt
+  # See https://cmake.org/cmake/help/v3.6/module/FindOpenSSL.html
   find_package(OpenSSL REQUIRED)
   
   if(TARGET OpenSSL::SSL)
diff --git a/cmake/zlib.cmake b/cmake/zlib.cmake
index e4c94f5..ed7c9c2 100644
--- a/cmake/zlib.cmake
+++ b/cmake/zlib.cmake
@@ -33,6 +33,12 @@
     set(gRPC_INSTALL FALSE)
   endif()
 elseif("${gRPC_ZLIB_PROVIDER}" STREQUAL "package")
+  # zlib installation directory can be configured by setting ZLIB_ROOT
+  # We allow locating zlib using both "CONFIG" and "MODULE" as the expectation
+  # is that many Linux systems will have zlib installed via a distribution
+  # package ("MODULE"), while on Windows the user is likely to have installed
+  # zlib using cmake ("CONFIG").
+  # See https://cmake.org/cmake/help/v3.6/module/FindZLIB.html
   find_package(ZLIB REQUIRED)
 
   if(TARGET ZLIB::ZLIB)
diff --git a/config.m4 b/config.m4
index 5bc2e58..57fc2db 100644
--- a/config.m4
+++ b/config.m4
@@ -65,9 +65,6 @@
     src/core/lib/gpr/sync.cc \
     src/core/lib/gpr/sync_posix.cc \
     src/core/lib/gpr/sync_windows.cc \
-    src/core/lib/gpr/thd.cc \
-    src/core/lib/gpr/thd_posix.cc \
-    src/core/lib/gpr/thd_windows.cc \
     src/core/lib/gpr/time.cc \
     src/core/lib/gpr/time_posix.cc \
     src/core/lib/gpr/time_precise.cc \
@@ -77,6 +74,8 @@
     src/core/lib/gpr/tmpfile_posix.cc \
     src/core/lib/gpr/tmpfile_windows.cc \
     src/core/lib/gpr/wrap_memcpy.cc \
+    src/core/lib/gprpp/thd_posix.cc \
+    src/core/lib/gprpp/thd_windows.cc \
     src/core/lib/profiling/basic_timers.cc \
     src/core/lib/profiling/stap_timers.cc \
     src/core/lib/surface/init.cc \
@@ -180,7 +179,6 @@
     src/core/lib/slice/percent_encoding.cc \
     src/core/lib/slice/slice.cc \
     src/core/lib/slice/slice_buffer.cc \
-    src/core/lib/slice/slice_hash_table.cc \
     src/core/lib/slice/slice_intern.cc \
     src/core/lib/slice/slice_string_helpers.cc \
     src/core/lib/surface/api_trace.cc \
@@ -211,6 +209,7 @@
     src/core/lib/transport/service_config.cc \
     src/core/lib/transport/static_metadata.cc \
     src/core/lib/transport/status_conversion.cc \
+    src/core/lib/transport/status_metadata.cc \
     src/core/lib/transport/timeout_encoding.cc \
     src/core/lib/transport/transport.cc \
     src/core/lib/transport/transport_op_string.cc \
@@ -245,6 +244,7 @@
     src/core/ext/filters/http/server/http_server_filter.cc \
     src/core/lib/http/httpcli_security_connector.cc \
     src/core/lib/security/context/security_context.cc \
+    src/core/lib/security/credentials/alts/alts_credentials.cc \
     src/core/lib/security/credentials/composite/composite_credentials.cc \
     src/core/lib/security/credentials/credentials.cc \
     src/core/lib/security/credentials/credentials_metadata.cc \
@@ -258,23 +258,55 @@
     src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \
     src/core/lib/security/credentials/plugin/plugin_credentials.cc \
     src/core/lib/security/credentials/ssl/ssl_credentials.cc \
+    src/core/lib/security/security_connector/alts_security_connector.cc \
+    src/core/lib/security/security_connector/security_connector.cc \
     src/core/lib/security/transport/client_auth_filter.cc \
-    src/core/lib/security/transport/lb_targets_info.cc \
     src/core/lib/security/transport/secure_endpoint.cc \
-    src/core/lib/security/transport/security_connector.cc \
     src/core/lib/security/transport/security_handshaker.cc \
     src/core/lib/security/transport/server_auth_filter.cc \
+    src/core/lib/security/transport/target_authority_table.cc \
     src/core/lib/security/transport/tsi_error.cc \
     src/core/lib/security/util/json_util.cc \
     src/core/lib/surface/init_secure.cc \
-    src/core/tsi/alts_transport_security.cc \
-    src/core/tsi/fake_transport_security.cc \
-    src/core/tsi/ssl_transport_security.cc \
-    src/core/tsi/transport_security_grpc.cc \
+    src/core/tsi/alts/crypt/aes_gcm.cc \
+    src/core/tsi/alts/crypt/gsec.cc \
+    src/core/tsi/alts/frame_protector/alts_counter.cc \
+    src/core/tsi/alts/frame_protector/alts_crypter.cc \
+    src/core/tsi/alts/frame_protector/alts_frame_protector.cc \
+    src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc \
+    src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc \
+    src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
+    src/core/tsi/alts/frame_protector/frame_handler.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc \
+    src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc \
+    src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc \
+    src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc \
+    src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc \
+    src/core/tsi/alts/handshaker/alts_tsi_utils.cc \
+    src/core/tsi/alts/handshaker/transport_security_common_api.cc \
+    src/core/tsi/alts/handshaker/altscontext.pb.c \
+    src/core/tsi/alts/handshaker/handshaker.pb.c \
+    src/core/tsi/alts/handshaker/transport_security_common.pb.c \
+    third_party/nanopb/pb_common.c \
+    third_party/nanopb/pb_decode.c \
+    third_party/nanopb/pb_encode.c \
     src/core/tsi/transport_security.cc \
     src/core/tsi/transport_security_adapter.cc \
-    src/core/ext/transport/chttp2/server/chttp2_server.cc \
-    src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \
+    src/core/ext/transport/chttp2/client/insecure/channel_create.cc \
+    src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \
+    src/core/ext/transport/chttp2/client/chttp2_connector.cc \
     src/core/ext/filters/client_channel/backup_poller.cc \
     src/core/ext/filters/client_channel/channel_connectivity.cc \
     src/core/ext/filters/client_channel/client_channel.cc \
@@ -286,21 +318,26 @@
     src/core/ext/filters/client_channel/lb_policy.cc \
     src/core/ext/filters/client_channel/lb_policy_factory.cc \
     src/core/ext/filters/client_channel/lb_policy_registry.cc \
+    src/core/ext/filters/client_channel/method_params.cc \
     src/core/ext/filters/client_channel/parse_address.cc \
     src/core/ext/filters/client_channel/proxy_mapper.cc \
     src/core/ext/filters/client_channel/proxy_mapper_registry.cc \
     src/core/ext/filters/client_channel/resolver.cc \
     src/core/ext/filters/client_channel/resolver_registry.cc \
     src/core/ext/filters/client_channel/retry_throttle.cc \
+    src/core/ext/filters/client_channel/status_util.cc \
     src/core/ext/filters/client_channel/subchannel.cc \
     src/core/ext/filters/client_channel/subchannel_index.cc \
     src/core/ext/filters/client_channel/uri_parser.cc \
     src/core/ext/filters/deadline/deadline_filter.cc \
-    src/core/ext/transport/chttp2/client/chttp2_connector.cc \
+    src/core/tsi/alts_transport_security.cc \
+    src/core/tsi/fake_transport_security.cc \
+    src/core/tsi/ssl_transport_security.cc \
+    src/core/tsi/transport_security_grpc.cc \
+    src/core/ext/transport/chttp2/server/chttp2_server.cc \
+    src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \
-    src/core/ext/transport/chttp2/client/insecure/channel_create.cc \
-    src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \
     src/core/ext/transport/inproc/inproc_plugin.cc \
     src/core/ext/transport/inproc/inproc_transport.cc \
     src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \
@@ -309,9 +346,6 @@
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \
     src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \
     src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
-    third_party/nanopb/pb_common.c \
-    third_party/nanopb/pb_decode.c \
-    third_party/nanopb/pb_encode.c \
     src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \
     src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \
     src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc \
@@ -627,12 +661,14 @@
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/compression)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/debug)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/gpr)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/gprpp)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/http)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/json)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/profiling)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/context)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/alts)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/composite)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/fake)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/google_default)
@@ -641,6 +677,7 @@
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/oauth2)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/plugin)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/ssl)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/security_connector)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/transport)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/util)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/slice)
@@ -648,6 +685,10 @@
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/transport)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/plugin_registry)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/crypt)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/frame_protector)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/handshaker)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/zero_copy_frame_protector)
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto)
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/asn1)
   PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/base64)
diff --git a/config.w32 b/config.w32
index 2ef122b..580607d 100644
--- a/config.w32
+++ b/config.w32
@@ -42,9 +42,6 @@
     "src\\core\\lib\\gpr\\sync.cc " +
     "src\\core\\lib\\gpr\\sync_posix.cc " +
     "src\\core\\lib\\gpr\\sync_windows.cc " +
-    "src\\core\\lib\\gpr\\thd.cc " +
-    "src\\core\\lib\\gpr\\thd_posix.cc " +
-    "src\\core\\lib\\gpr\\thd_windows.cc " +
     "src\\core\\lib\\gpr\\time.cc " +
     "src\\core\\lib\\gpr\\time_posix.cc " +
     "src\\core\\lib\\gpr\\time_precise.cc " +
@@ -54,6 +51,8 @@
     "src\\core\\lib\\gpr\\tmpfile_posix.cc " +
     "src\\core\\lib\\gpr\\tmpfile_windows.cc " +
     "src\\core\\lib\\gpr\\wrap_memcpy.cc " +
+    "src\\core\\lib\\gprpp\\thd_posix.cc " +
+    "src\\core\\lib\\gprpp\\thd_windows.cc " +
     "src\\core\\lib\\profiling\\basic_timers.cc " +
     "src\\core\\lib\\profiling\\stap_timers.cc " +
     "src\\core\\lib\\surface\\init.cc " +
@@ -157,7 +156,6 @@
     "src\\core\\lib\\slice\\percent_encoding.cc " +
     "src\\core\\lib\\slice\\slice.cc " +
     "src\\core\\lib\\slice\\slice_buffer.cc " +
-    "src\\core\\lib\\slice\\slice_hash_table.cc " +
     "src\\core\\lib\\slice\\slice_intern.cc " +
     "src\\core\\lib\\slice\\slice_string_helpers.cc " +
     "src\\core\\lib\\surface\\api_trace.cc " +
@@ -188,6 +186,7 @@
     "src\\core\\lib\\transport\\service_config.cc " +
     "src\\core\\lib\\transport\\static_metadata.cc " +
     "src\\core\\lib\\transport\\status_conversion.cc " +
+    "src\\core\\lib\\transport\\status_metadata.cc " +
     "src\\core\\lib\\transport\\timeout_encoding.cc " +
     "src\\core\\lib\\transport\\transport.cc " +
     "src\\core\\lib\\transport\\transport_op_string.cc " +
@@ -222,6 +221,7 @@
     "src\\core\\ext\\filters\\http\\server\\http_server_filter.cc " +
     "src\\core\\lib\\http\\httpcli_security_connector.cc " +
     "src\\core\\lib\\security\\context\\security_context.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\alts_credentials.cc " +
     "src\\core\\lib\\security\\credentials\\composite\\composite_credentials.cc " +
     "src\\core\\lib\\security\\credentials\\credentials.cc " +
     "src\\core\\lib\\security\\credentials\\credentials_metadata.cc " +
@@ -235,23 +235,55 @@
     "src\\core\\lib\\security\\credentials\\oauth2\\oauth2_credentials.cc " +
     "src\\core\\lib\\security\\credentials\\plugin\\plugin_credentials.cc " +
     "src\\core\\lib\\security\\credentials\\ssl\\ssl_credentials.cc " +
+    "src\\core\\lib\\security\\security_connector\\alts_security_connector.cc " +
+    "src\\core\\lib\\security\\security_connector\\security_connector.cc " +
     "src\\core\\lib\\security\\transport\\client_auth_filter.cc " +
-    "src\\core\\lib\\security\\transport\\lb_targets_info.cc " +
     "src\\core\\lib\\security\\transport\\secure_endpoint.cc " +
-    "src\\core\\lib\\security\\transport\\security_connector.cc " +
     "src\\core\\lib\\security\\transport\\security_handshaker.cc " +
     "src\\core\\lib\\security\\transport\\server_auth_filter.cc " +
+    "src\\core\\lib\\security\\transport\\target_authority_table.cc " +
     "src\\core\\lib\\security\\transport\\tsi_error.cc " +
     "src\\core\\lib\\security\\util\\json_util.cc " +
     "src\\core\\lib\\surface\\init_secure.cc " +
-    "src\\core\\tsi\\alts_transport_security.cc " +
-    "src\\core\\tsi\\fake_transport_security.cc " +
-    "src\\core\\tsi\\ssl_transport_security.cc " +
-    "src\\core\\tsi\\transport_security_grpc.cc " +
+    "src\\core\\tsi\\alts\\crypt\\aes_gcm.cc " +
+    "src\\core\\tsi\\alts\\crypt\\gsec.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\alts_counter.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\alts_crypter.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\alts_frame_protector.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\alts_record_protocol_crypter_common.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\alts_seal_privacy_integrity_crypter.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\alts_unseal_privacy_integrity_crypter.cc " +
+    "src\\core\\tsi\\alts\\frame_protector\\frame_handler.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\alts_handshaker_client.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\alts_tsi_event.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\alts_tsi_handshaker.cc " +
+    "src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_grpc_integrity_only_record_protocol.cc " +
+    "src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_grpc_privacy_integrity_record_protocol.cc " +
+    "src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_grpc_record_protocol_common.cc " +
+    "src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_iovec_record_protocol.cc " +
+    "src\\core\\tsi\\alts\\zero_copy_frame_protector\\alts_zero_copy_grpc_protector.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\check_gcp_environment.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\check_gcp_environment_linux.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\check_gcp_environment_no_op.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\check_gcp_environment_windows.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\grpc_alts_credentials_client_options.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\grpc_alts_credentials_options.cc " +
+    "src\\core\\lib\\security\\credentials\\alts\\grpc_alts_credentials_server_options.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\alts_handshaker_service_api.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\alts_handshaker_service_api_util.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\alts_tsi_utils.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\transport_security_common_api.cc " +
+    "src\\core\\tsi\\alts\\handshaker\\altscontext.pb.c " +
+    "src\\core\\tsi\\alts\\handshaker\\handshaker.pb.c " +
+    "src\\core\\tsi\\alts\\handshaker\\transport_security_common.pb.c " +
+    "third_party\\nanopb\\pb_common.c " +
+    "third_party\\nanopb\\pb_decode.c " +
+    "third_party\\nanopb\\pb_encode.c " +
     "src\\core\\tsi\\transport_security.cc " +
     "src\\core\\tsi\\transport_security_adapter.cc " +
-    "src\\core\\ext\\transport\\chttp2\\server\\chttp2_server.cc " +
-    "src\\core\\ext\\transport\\chttp2\\client\\secure\\secure_channel_create.cc " +
+    "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create.cc " +
+    "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create_posix.cc " +
+    "src\\core\\ext\\transport\\chttp2\\client\\chttp2_connector.cc " +
     "src\\core\\ext\\filters\\client_channel\\backup_poller.cc " +
     "src\\core\\ext\\filters\\client_channel\\channel_connectivity.cc " +
     "src\\core\\ext\\filters\\client_channel\\client_channel.cc " +
@@ -263,21 +295,26 @@
     "src\\core\\ext\\filters\\client_channel\\lb_policy.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy_factory.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy_registry.cc " +
+    "src\\core\\ext\\filters\\client_channel\\method_params.cc " +
     "src\\core\\ext\\filters\\client_channel\\parse_address.cc " +
     "src\\core\\ext\\filters\\client_channel\\proxy_mapper.cc " +
     "src\\core\\ext\\filters\\client_channel\\proxy_mapper_registry.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver.cc " +
     "src\\core\\ext\\filters\\client_channel\\resolver_registry.cc " +
     "src\\core\\ext\\filters\\client_channel\\retry_throttle.cc " +
+    "src\\core\\ext\\filters\\client_channel\\status_util.cc " +
     "src\\core\\ext\\filters\\client_channel\\subchannel.cc " +
     "src\\core\\ext\\filters\\client_channel\\subchannel_index.cc " +
     "src\\core\\ext\\filters\\client_channel\\uri_parser.cc " +
     "src\\core\\ext\\filters\\deadline\\deadline_filter.cc " +
-    "src\\core\\ext\\transport\\chttp2\\client\\chttp2_connector.cc " +
+    "src\\core\\tsi\\alts_transport_security.cc " +
+    "src\\core\\tsi\\fake_transport_security.cc " +
+    "src\\core\\tsi\\ssl_transport_security.cc " +
+    "src\\core\\tsi\\transport_security_grpc.cc " +
+    "src\\core\\ext\\transport\\chttp2\\server\\chttp2_server.cc " +
+    "src\\core\\ext\\transport\\chttp2\\client\\secure\\secure_channel_create.cc " +
     "src\\core\\ext\\transport\\chttp2\\server\\insecure\\server_chttp2.cc " +
     "src\\core\\ext\\transport\\chttp2\\server\\insecure\\server_chttp2_posix.cc " +
-    "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create.cc " +
-    "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create_posix.cc " +
     "src\\core\\ext\\transport\\inproc\\inproc_plugin.cc " +
     "src\\core\\ext\\transport\\inproc\\inproc_transport.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\client_load_reporting_filter.cc " +
@@ -286,9 +323,6 @@
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_client_stats.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\load_balancer_api.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\proto\\grpc\\lb\\v1\\load_balancer.pb.c " +
-    "third_party\\nanopb\\pb_common.c " +
-    "third_party\\nanopb\\pb_decode.c " +
-    "third_party\\nanopb\\pb_encode.c " +
     "src\\core\\ext\\filters\\client_channel\\resolver\\fake\\fake_resolver.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\pick_first\\pick_first.cc " +
     "src\\core\\ext\\filters\\client_channel\\lb_policy\\subchannel_list.cc " +
@@ -639,6 +673,7 @@
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\compression");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\debug");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\gpr");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\gprpp");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\http");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\json");
@@ -646,6 +681,7 @@
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\context");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\alts");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\composite");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\fake");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\google_default");
@@ -654,6 +690,7 @@
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\oauth2");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\plugin");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\credentials\\ssl");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\security_connector");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\transport");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\security\\util");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\slice");
@@ -661,6 +698,11 @@
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\transport");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\plugin_registry");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\crypt");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\frame_protector");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\handshaker");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\zero_copy_frame_protector");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php\\ext");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php\\ext\\grpc");
diff --git a/doc/PROTOCOL-HTTP2.md b/doc/PROTOCOL-HTTP2.md
index 107a8e8..bdd00ca3 100644
--- a/doc/PROTOCOL-HTTP2.md
+++ b/doc/PROTOCOL-HTTP2.md
@@ -191,7 +191,7 @@
 Unless explicitly defined to be, gRPC Calls are not assumed to be idempotent.  Specifically:
 
 * Calls that cannot be proven to have started will not be retried.
-* There is no mechanisim for duplicate suppression as it is not necessary.
+* There is no mechanism for duplicate suppression as it is not necessary.
 * Calls that are marked as idempotent may be sent multiple times.
 
 
diff --git a/doc/cpp/pending_api_cleanups.md b/doc/cpp/pending_api_cleanups.md
index 517d503..fa19b52 100644
--- a/doc/cpp/pending_api_cleanups.md
+++ b/doc/cpp/pending_api_cleanups.md
@@ -15,3 +15,5 @@
   `include/grpc++/server_builder.h` (commit `6980362`)
 - remove `ClientContext::set_fail_fast()` method from
   `include/grpc++/impl/codegen/client_context.h` (commit `9477724`)
+- remove directory `include/grpc++` and all headers in it
+  (commit `eb06572`)
diff --git a/doc/environment_variables.md b/doc/environment_variables.md
index 5f0042e..ed46a48 100644
--- a/doc/environment_variables.md
+++ b/doc/environment_variables.md
@@ -18,7 +18,7 @@
 * GRPC_SSL_CIPHER_SUITES
   A colon separated list of cipher suites to use with OpenSSL
   Defaults to:
-    ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-GCM-SHA384
+    ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
 
 * GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
   PEM file to load SSL roots from
diff --git a/doc/g_stands_for.md b/doc/g_stands_for.md
index 19875c3..47ae7c5 100644
--- a/doc/g_stands_for.md
+++ b/doc/g_stands_for.md
@@ -14,3 +14,4 @@
 - 1.8 'g' stands for 'generous'
 - 1.9 'g' stands for 'glossy'
 - 1.10 'g' stands for 'glamorous'
+- 1.11 'g' stands for 'gorgeous'
diff --git a/examples/android/helloworld/.gitignore b/examples/android/helloworld/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/examples/android/helloworld/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/examples/android/helloworld/README.md b/examples/android/helloworld/README.md
new file mode 100644
index 0000000..ebb16d1
--- /dev/null
+++ b/examples/android/helloworld/README.md
@@ -0,0 +1,24 @@
+gRPC on Android
+==============
+
+Note: Building the protobuf dependency for Android requires
+https://github.com/google/protobuf/pull/3878. This fix will be in the next
+protobuf release, but until then must be manually patched in to
+`third_party/protobuf` to build gRPC for Android.
+
+PREREQUISITES
+-------------
+
+- Android SDK
+- Android NDK
+- `protoc` and `grpc_cpp_plugin` binaries on the host system
+
+INSTALL
+-------
+
+The example application can be built via Android Studio or on the command line
+using `gradle`:
+
+  ```sh
+  $ ./gradlew installDebug
+  ```
diff --git a/examples/android/helloworld/app/.gitignore b/examples/android/helloworld/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/examples/android/helloworld/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/examples/android/helloworld/app/CMakeLists.txt b/examples/android/helloworld/app/CMakeLists.txt
new file mode 100644
index 0000000..6ee18da
--- /dev/null
+++ b/examples/android/helloworld/app/CMakeLists.txt
@@ -0,0 +1,123 @@
+cmake_minimum_required(VERSION 3.4.1)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+set(helloworld_PROTOBUF_PROTOC_EXECUTABLE "/usr/local/bin/protoc" CACHE STRING "Protoc binary on host")
+set(helloworld_GRPC_CPP_PLUGIN_EXECUTABLE "/usr/local/bin/grpc_cpp_plugin" CACHE STRING "gRPC CPP plugin binary on host")
+
+set(GRPC_SRC_DIR ../../../../)
+
+set(GRPC_BUILD_DIR ../grpc/outputs/${ANDROID_ABI})
+file(MAKE_DIRECTORY ${GRPC_BUILD_DIR})
+
+add_subdirectory(${GRPC_SRC_DIR} ${GRPC_BUILD_DIR})
+
+include_directories(${GRPC_SRC_DIR}/include)
+
+add_library(libgrpc STATIC IMPORTED)
+set_target_properties(libgrpc PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/libgrpc.a)
+
+add_library(libgrpc++ STATIC IMPORTED)
+set_target_properties(libgrpc++ PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/libgrpc++.a)
+
+add_library(libgpr STATIC IMPORTED)
+set_target_properties(libgpr PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/libgpr.a)
+
+add_library(libcares STATIC IMPORTED)
+set_target_properties(libcares PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/third_party/cares/cares/lib/libcares.a)
+
+add_library(libzlib STATIC IMPORTED)
+set_target_properties(libzlib PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/third_party/zlib/libz.a)
+
+add_library(libcrypto STATIC IMPORTED)
+set_target_properties(libcrypto PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/third_party/boringssl/crypto/libcrypto.a)
+
+add_library(libssl STATIC IMPORTED)
+set_target_properties(libssl PROPERTIES IMPORTED_LOCATION
+  ${GRPC_BUILD_DIR}/third_party/boringssl/ssl/libssl.a)
+
+set(GRPC_PROTO_GENS_DIR ${CMAKE_BINARY_DIR}/gens)
+file(MAKE_DIRECTORY ${GRPC_PROTO_GENS_DIR})
+include_directories(${GRPC_PROTO_GENS_DIR})
+
+function(android_protobuf_grpc_generate_cpp SRC_FILES HDR_FILES INCLUDE_ROOT)
+  if(NOT ARGN)
+    message(SEND_ERROR "Error: android_protobuf_grpc_generate_cpp() called without any proto files")
+    return()
+  endif()
+
+  set(${SRC_FILES})
+  set(${HDR_FILES})
+  set(PROTOBUF_INCLUDE_PATH -I ${INCLUDE_ROOT})
+  foreach(FIL ${ARGN})
+    get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
+    get_filename_component(FIL_WE ${FIL} NAME_WE)
+    file(RELATIVE_PATH REL_FIL ${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_ROOT} ${ABS_FIL})
+    get_filename_component(REL_DIR ${REL_FIL} DIRECTORY)
+    set(RELFIL_WE "${REL_DIR}/${FIL_WE}")
+
+    list(APPEND ${SRC_FILES} "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.cc")
+    list(APPEND ${HDR_FILES} "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h")
+    list(APPEND ${SRC_FILES} "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.cc")
+    list(APPEND ${HDR_FILES} "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.h")
+
+    add_custom_command(
+      OUTPUT "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.cc"
+             "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.h"
+             "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.cc"
+             "${GRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h"
+      COMMAND ${helloworld_PROTOBUF_PROTOC_EXECUTABLE}
+      ARGS --grpc_out=${GRPC_PROTO_GENS_DIR}
+        --cpp_out=${GRPC_PROTO_GENS_DIR}
+        --plugin=protoc-gen-grpc=${helloworld_GRPC_CPP_PLUGIN_EXECUTABLE}
+        ${PROTOBUF_INCLUDE_PATH}
+        ${REL_FIL}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${helloworld_PROTOBUF_PROTOC_EXECUTABLE} ${helloworld_GRPC_CPP_PLUGIN_EXECUTABLE} ${ABS_FIL} )
+  endforeach()
+
+  set_source_files_properties(${${SRC_FILES}} ${${HDR_FILES}} PROPERTIES GENERATED TRUE)
+  set(${SRC_FILES} ${${SRC_FILES}} PARENT_SCOPE)
+  set(${HDR_FILES} ${${HDR_FILES}} PARENT_SCOPE)
+endfunction()
+
+set(PROTO_BASE_DIR ${GRPC_SRC_DIR}/examples/protos)
+
+android_protobuf_grpc_generate_cpp(
+  HELLOWORLD_PROTO_SRCS HELLOWORLD_PROTO_HDRS ${PROTO_BASE_DIR} ${PROTO_BASE_DIR}/helloworld.proto)
+
+add_library(helloworld_proto_lib
+  SHARED ${HELLOWORLD_PROTO_HDRS} ${HELLOWORLD_PROTO_SRCS})
+
+target_link_libraries(helloworld_proto_lib
+  libprotobuf
+  libgrpc++
+  android
+  log)
+
+find_library(log-lib
+ log)
+
+add_library(grpc-helloworld
+  SHARED src/main/cpp/grpc-helloworld.cc)
+
+target_include_directories(grpc-helloworld
+  PRIVATE ${HELLOWORLD_PROTO_HEADERS})
+
+target_link_libraries(grpc-helloworld
+  libgrpc++
+  libgrpc
+  libzlib
+  libcares
+  libssl
+  libcrypto
+  helloworld_proto_lib
+  libgpr
+  android
+  ${log-lib})
diff --git a/examples/android/helloworld/app/build.gradle b/examples/android/helloworld/app/build.gradle
new file mode 100644
index 0000000..c6ab730
--- /dev/null
+++ b/examples/android/helloworld/app/build.gradle
@@ -0,0 +1,53 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 26
+    defaultConfig {
+        applicationId "io.grpc.android.cpp.helloworldexample"
+        minSdkVersion 14
+        targetSdkVersion 26
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        externalNativeBuild {
+            cmake {
+                cppFlags "-std=c++14 -frtti -fexceptions"
+                arguments '-DANDROID_STL=c++_static'
+                arguments '-DRUN_HAVE_POSIX_REGEX=0'
+                arguments '-DRUN_HAVE_STD_REGEX=0'
+                arguments '-DRUN_HAVE_STEADY_CLOCK=0'
+                arguments '-Dprotobuf_BUILD_PROTOC_BINARIES=off'
+                arguments '-DgRPC_BUILD_CODEGEN=off'
+                // Set this to the path to the protoc binary on the host system (codegen is not
+                // cross-compiled to Android)
+                arguments '-Dhelloworld_PROTOBUF_PROTOC_EXECUTABLE=/usr/local/bin/protoc'
+                // Set this to the path to the gRPC C++ protoc plugin binary on the host system
+                // (codegen is not cross-compiled to Android)
+                arguments '-Dhelloworld_GRPC_CPP_PLUGIN_EXECUTABLE=/usr/local/bin/grpc_cpp_plugin'
+            }
+        }
+        ndk.abiFilters 'x86'
+    }
+    buildTypes {
+        debug {
+            minifyEnabled false
+        }
+        release {
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    externalNativeBuild {
+        cmake {
+            path "CMakeLists.txt"
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'com.android.support:appcompat-v7:26.1.0'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'com.android.support.test:runner:1.0.1'
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
+}
diff --git a/examples/android/helloworld/app/proguard-rules.pro b/examples/android/helloworld/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/examples/android/helloworld/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/examples/android/helloworld/app/src/main/AndroidManifest.xml b/examples/android/helloworld/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8109da9
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="io.grpc.helloworldexample.cpp" >
+
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application
+        android:allowBackup="false"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/Base.V7.Theme.AppCompat.Light" >
+        <activity
+            android:name=".HelloworldActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/examples/android/helloworld/app/src/main/cpp/grpc-helloworld.cc b/examples/android/helloworld/app/src/main/cpp/grpc-helloworld.cc
new file mode 100644
index 0000000..7a31b78
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/cpp/grpc-helloworld.cc
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <atomic>
+
+#include <grpc++/grpc++.h>
+#include <jni.h>
+
+#include "helloworld.grpc.pb.h"
+
+using grpc::Channel;
+using grpc::ClientContext;
+using grpc::Server;
+using grpc::ServerBuilder;
+using grpc::ServerContext;
+using grpc::Status;
+using helloworld::Greeter;
+using helloworld::HelloReply;
+using helloworld::HelloRequest;
+
+std::atomic<bool> stop_server(false);
+
+// Logic and data behind the server's behavior.
+class GreeterServiceImpl final : public Greeter::Service {
+  Status SayHello(ServerContext* context, const HelloRequest* request,
+                  HelloReply* reply) override {
+    std::string prefix("Hello ");
+    reply->set_message(prefix + request->name());
+    return Status::OK;
+  }
+};
+
+void StartServer(JNIEnv* env, jobject obj, jmethodID is_cancelled_mid,
+                 int port) {
+  const int host_port_buf_size = 1024;
+  char host_port[host_port_buf_size];
+  snprintf(host_port, host_port_buf_size, "0.0.0.0:%d", port);
+
+  GreeterServiceImpl service;
+  ServerBuilder builder;
+  // Listen on the given address without any authentication mechanism.
+  builder.AddListeningPort(host_port, grpc::InsecureServerCredentials());
+  // Register "service" as the instance through which we'll communicate with
+  // clients. In this case it corresponds to an *synchronous* service.
+  builder.RegisterService(&service);
+  // Finally assemble the server.
+  std::unique_ptr<Server> server(builder.BuildAndStart());
+  while (!stop_server.load()) {
+    // Check with the Java code to see if the user has requested the server stop or the app is no
+    // longer in the foreground.
+    jboolean is_cancelled = env->CallBooleanMethod(obj, is_cancelled_mid);
+    if (is_cancelled == JNI_TRUE) {
+      stop_server = true;
+    }
+  }
+}
+
+class GreeterClient {
+ public:
+  GreeterClient(std::shared_ptr<Channel> channel)
+      : stub_(Greeter::NewStub(channel)) {}
+
+  // Assembles the client's payload, sends it and presents the response back
+  // from the server.
+  std::string SayHello(const std::string& user) {
+    // Data we are sending to the server.
+    HelloRequest request;
+    request.set_name(user);
+
+    // Container for the data we expect from the server.
+    HelloReply reply;
+
+    // Context for the client. It could be used to convey extra information to
+    // the server and/or tweak certain RPC behaviors.
+    ClientContext context;
+    // The actual RPC.
+    Status status = stub_->SayHello(&context, request, &reply);
+
+    if (status.ok()) {
+      return reply.message();
+    } else {
+      return status.error_message();
+    }
+  }
+
+ private:
+  std::unique_ptr<Greeter::Stub> stub_;
+};
+
+// Send an RPC and return the response. Invoked from Java code.
+extern "C" JNIEXPORT jstring JNICALL
+Java_io_grpc_helloworldexample_cpp_HelloworldActivity_sayHello(
+    JNIEnv* env, jobject obj_unused, jstring host_raw, jint port_raw,
+    jstring message_raw) {
+  const char* host_chars = env->GetStringUTFChars(host_raw, (jboolean*)0);
+  std::string host(host_chars, env->GetStringUTFLength(host_raw));
+
+  int port = static_cast<int>(port_raw);
+
+  const char* message_chars = env->GetStringUTFChars(message_raw, (jboolean*)0);
+  std::string message(message_chars, env->GetStringUTFLength(message_raw));
+
+  const int host_port_buf_size = 1024;
+  char host_port[host_port_buf_size];
+  snprintf(host_port, host_port_buf_size, "%s:%d", host.c_str(), port);
+
+  GreeterClient greeter(
+      grpc::CreateChannel(host_port, grpc::InsecureChannelCredentials()));
+  std::string reply = greeter.SayHello(message);
+
+  return env->NewStringUTF(reply.c_str());
+}
+
+// Start the server. Invoked from Java code.
+extern "C" JNIEXPORT void JNICALL
+Java_io_grpc_helloworldexample_cpp_HelloworldActivity_startServer(
+    JNIEnv* env, jobject obj_this, jint port_raw) {
+  int port = static_cast<int>(port_raw);
+
+  jclass cls = env->GetObjectClass(obj_this);
+  jmethodID is_cancelled_mid =
+      env->GetMethodID(cls, "isRunServerTaskCancelled", "()Z");
+
+  stop_server = false;
+
+  StartServer(env, obj_this, is_cancelled_mid, port);
+}
diff --git a/examples/android/helloworld/app/src/main/java/io/grpc/helloworldexample/cpp/HelloworldActivity.java b/examples/android/helloworld/app/src/main/java/io/grpc/helloworldexample/cpp/HelloworldActivity.java
new file mode 100644
index 0000000..ae5c88b
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/java/io/grpc/helloworldexample/cpp/HelloworldActivity.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018, gRPC Authors 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.
+ */
+
+package io.grpc.helloworldexample.cpp;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.lang.ref.WeakReference;
+
+public class HelloworldActivity extends AppCompatActivity {
+
+  static {
+    System.loadLibrary("grpc-helloworld");
+  }
+
+  private Button sendButton;
+  private Button serverButton;
+  private EditText hostEdit;
+  private EditText portEdit;
+  private EditText messageEdit;
+  private EditText serverPortEdit;
+  private TextView resultText;
+  private GrpcTask grpcTask;
+  private RunServerTask runServerTask;
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_helloworld);
+    sendButton = (Button) findViewById(R.id.send_button);
+    serverButton = (Button) findViewById(R.id.server_button);
+    hostEdit = (EditText) findViewById(R.id.host_edit_text);
+    portEdit = (EditText) findViewById(R.id.port_edit_text);
+    messageEdit = (EditText) findViewById(R.id.message_edit_text);
+    serverPortEdit = (EditText) findViewById(R.id.server_port_edit_text);
+    resultText = (TextView) findViewById(R.id.grpc_response_text);
+    resultText.setMovementMethod(new ScrollingMovementMethod());
+  }
+
+  @Override
+  protected void onPause() {
+    super.onPause();
+    if (runServerTask != null) {
+      runServerTask.cancel(true);
+      runServerTask = null;
+      serverButton.setText("Start gRPC Server");
+    }
+    if (grpcTask != null) {
+      grpcTask.cancel(true);
+      grpcTask = null;
+    }
+  }
+
+  public void sendMessage(View view) {
+    ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
+        .hideSoftInputFromWindow(hostEdit.getWindowToken(), 0);
+    sendButton.setEnabled(false);
+    resultText.setText("");
+    grpcTask = new GrpcTask(this);
+    grpcTask.executeOnExecutor(
+        AsyncTask.THREAD_POOL_EXECUTOR,
+        hostEdit.getText().toString(),
+        messageEdit.getText().toString(),
+        portEdit.getText().toString());
+  }
+
+  public void startOrStopServer(View view) {
+    if (runServerTask != null) {
+      runServerTask.cancel(true);
+      runServerTask = null;
+      serverButton.setText("Start gRPC Server");
+      Toast.makeText(this, "Server stopped", Toast.LENGTH_SHORT).show();
+    } else {
+      runServerTask = new RunServerTask(this);
+      String portStr = serverPortEdit.getText().toString();
+      int port = TextUtils.isEmpty(portStr) ? 50051 : Integer.valueOf(portStr);
+      runServerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, port);
+      serverButton.setText("Stop gRPC Server");
+      Toast.makeText(this, "Server started on port " + port, Toast.LENGTH_SHORT).show();
+    }
+  }
+
+  private static class RunServerTask extends AsyncTask<Integer, Void, Void> {
+    private final WeakReference<HelloworldActivity> activityReference;
+
+    private RunServerTask(HelloworldActivity activity) {
+      this.activityReference = new WeakReference<HelloworldActivity>(activity);
+    }
+
+    @Override
+    protected Void doInBackground(Integer... params) {
+      int port = params[0];
+      HelloworldActivity activity = activityReference.get();
+      if (activity != null) {
+        activity.startServer(port);
+      }
+      return null;
+    }
+  }
+
+  private static class GrpcTask extends AsyncTask<String, Void, String> {
+    private final WeakReference<HelloworldActivity> activityReference;
+
+    private GrpcTask(HelloworldActivity activity) {
+      this.activityReference = new WeakReference<HelloworldActivity>(activity);
+    }
+
+    @Override
+    protected String doInBackground(String... params) {
+      String host = params[0];
+      String message = params[1];
+      String portStr = params[2];
+      int port = TextUtils.isEmpty(portStr) ? 50051 : Integer.valueOf(portStr);
+      return sayHello(host, port, message);
+    }
+
+    @Override
+    protected void onPostExecute(String result) {
+      HelloworldActivity activity = activityReference.get();
+      if (activity == null || isCancelled()) {
+        return;
+      }
+      TextView resultText = (TextView) activity.findViewById(R.id.grpc_response_text);
+      Button sendButton = (Button) activity.findViewById(R.id.send_button);
+      resultText.setText(result);
+      sendButton.setEnabled(true);
+    }
+  }
+
+  /**
+   * Invoked by native code to stop server when RunServerTask has been canceled, either by user
+   * request or upon app moving to background.
+   */
+  public boolean isRunServerTaskCancelled() {
+    if (runServerTask != null) {
+      return runServerTask.isCancelled();
+    }
+    return false;
+  }
+
+  public static native String sayHello(String host, int port, String message);
+
+  public native void startServer(int port);
+}
diff --git a/examples/android/helloworld/app/src/main/res/layout/activity_helloworld.xml b/examples/android/helloworld/app/src/main/res/layout/activity_helloworld.xml
new file mode 100644
index 0000000..5280469
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/res/layout/activity_helloworld.xml
@@ -0,0 +1,86 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              tools:context=".HelloworldActivity"
+              android:orientation="vertical" >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
+        android:textSize="16sp"
+        android:text="gRPC Client Configuration"
+        android:textStyle="bold" />
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+        <EditText
+                android:id="@+id/host_edit_text"
+                android:layout_weight="2"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:hint="Enter Host" />
+        <EditText
+                android:id="@+id/port_edit_text"
+                android:layout_weight="1"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:inputType="numberDecimal"
+                android:hint="Enter Port" />
+    </LinearLayout>
+
+
+    <EditText
+            android:id="@+id/message_edit_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="Enter message to send" />
+
+    <Button
+            android:id="@+id/send_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:onClick="sendMessage"
+            android:text="Send gRPC Request" />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
+        android:textSize="16sp"
+        android:text="Response:" />
+
+    <TextView
+        android:id="@+id/grpc_response_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:scrollbars = "vertical"
+        android:textSize="16sp" />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
+        android:textSize="16sp"
+        android:text="gRPC Server Configuration"
+        android:textStyle="bold" />
+
+    <EditText
+        android:id="@+id/server_port_edit_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="Server port" />
+
+    <Button
+        android:id="@+id/server_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="startOrStopServer"
+        android:text="Start gRPC Server" />
+
+</LinearLayout>
diff --git a/examples/android/helloworld/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/android/helloworld/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/examples/android/helloworld/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/android/helloworld/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/examples/android/helloworld/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/android/helloworld/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/examples/android/helloworld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/android/helloworld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/examples/android/helloworld/app/src/main/res/values/strings.xml b/examples/android/helloworld/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7e916e3
--- /dev/null
+++ b/examples/android/helloworld/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">GrpcHelloworldCppExample</string>
+</resources>
diff --git a/examples/android/helloworld/build.gradle b/examples/android/helloworld/build.gradle
new file mode 100644
index 0000000..bd5f337
--- /dev/null
+++ b/examples/android/helloworld/build.gradle
@@ -0,0 +1,24 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.0.1'
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/examples/android/helloworld/gradle.properties b/examples/android/helloworld/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/examples/android/helloworld/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/examples/android/helloworld/gradle/wrapper/gradle-wrapper.jar b/examples/android/helloworld/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
--- /dev/null
+++ b/examples/android/helloworld/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/examples/android/helloworld/gradle/wrapper/gradle-wrapper.properties b/examples/android/helloworld/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4c81831
--- /dev/null
+++ b/examples/android/helloworld/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jan 25 11:45:30 PST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/examples/android/helloworld/gradlew b/examples/android/helloworld/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/examples/android/helloworld/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/examples/android/helloworld/gradlew.bat b/examples/android/helloworld/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/examples/android/helloworld/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/examples/android/helloworld/settings.gradle b/examples/android/helloworld/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/examples/android/helloworld/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm
index 6ff1ca5..18a0972 100644
--- a/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm
+++ b/examples/cpp/helloworld/cocoapods/HelloWorldCpp/ViewController.mm
@@ -17,9 +17,9 @@
  */
 
 #import "ViewController.h"
-#import <grpc++/grpc++.h>
-#include <grpc++/generic/generic_stub.h>
-#include <grpc++/generic/async_generic_service.h>
+#import <grpcpp/grpcpp.h>
+#include <grpcpp/generic/generic_stub.h>
+#include <grpcpp/generic/async_generic_service.h>
 
 static void* tag(int i) { return (void*)(intptr_t)i; }
 
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 2f1f6bd..65a5dc6 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -23,15 +23,15 @@
 Pod::Spec.new do |s|
   s.name     = 'gRPC-C++'
   # TODO (mxyan): use version that match gRPC version when pod is stabilized
-  # version = '1.10.0-dev'
-  version = '0.0.1'
+  # version = '1.11.0-dev'
+  version = '0.0.2'
   s.version  = version
   s.summary  = 'gRPC C++ library'
   s.homepage = 'https://grpc.io'
   s.license  = 'Apache License, Version 2.0'
   s.authors  = { 'The gRPC contributors' => 'grpc-packages@google.com' }
 
-  grpc_version = '1.10.0-dev'
+  grpc_version = '1.11.0-dev'
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
@@ -42,8 +42,14 @@
   s.osx.deployment_target = '10.9'
   s.requires_arc = false
 
-  # Add include prefix `grpc++` (i.e. `#include <grpc++/xxx.h>`).
-  s.header_dir = 'grpc++'
+  name = 'grpcpp'
+  # Use `grpcpp` as framework name so that `#include <grpcpp/xxx.h>` works when built as
+  # framework.
+  s.module_name = name
+
+  # Add include prefix `grpcpp` so that `#include <grpcpp/xxx.h>` works when built as static
+  # library.
+  s.header_dir = name
 
   s.pod_target_xcconfig = {
     'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(PODS_TARGET_SRCROOT)/include"',
@@ -63,84 +69,86 @@
 
   s.default_subspecs = 'Interface', 'Implementation'
 
-  s.subspec 'Interface' do |ss|
-    ss.header_mappings_dir = 'include/grpc++'
+  s.header_mappings_dir = 'include/grpcpp'
 
-    ss.source_files = 'include/grpc++/alarm.h',
-                      'include/grpc++/channel.h',
-                      'include/grpc++/client_context.h',
-                      'include/grpc++/completion_queue.h',
-                      'include/grpc++/create_channel.h',
-                      'include/grpc++/create_channel_posix.h',
-                      'include/grpc++/ext/health_check_service_server_builder_option.h',
-                      'include/grpc++/generic/async_generic_service.h',
-                      'include/grpc++/generic/generic_stub.h',
-                      'include/grpc++/grpc++.h',
-                      'include/grpc++/health_check_service_interface.h',
-                      'include/grpc++/impl/call.h',
-                      'include/grpc++/impl/channel_argument_option.h',
-                      'include/grpc++/impl/client_unary_call.h',
-                      'include/grpc++/impl/codegen/core_codegen.h',
-                      'include/grpc++/impl/grpc_library.h',
-                      'include/grpc++/impl/method_handler_impl.h',
-                      'include/grpc++/impl/rpc_method.h',
-                      'include/grpc++/impl/rpc_service_method.h',
-                      'include/grpc++/impl/serialization_traits.h',
-                      'include/grpc++/impl/server_builder_option.h',
-                      'include/grpc++/impl/server_builder_plugin.h',
-                      'include/grpc++/impl/server_initializer.h',
-                      'include/grpc++/impl/service_type.h',
-                      'include/grpc++/resource_quota.h',
-                      'include/grpc++/security/auth_context.h',
-                      'include/grpc++/security/auth_metadata_processor.h',
-                      'include/grpc++/security/credentials.h',
-                      'include/grpc++/security/server_credentials.h',
-                      'include/grpc++/server.h',
-                      'include/grpc++/server_builder.h',
-                      'include/grpc++/server_context.h',
-                      'include/grpc++/server_posix.h',
-                      'include/grpc++/support/async_stream.h',
-                      'include/grpc++/support/async_unary_call.h',
-                      'include/grpc++/support/byte_buffer.h',
-                      'include/grpc++/support/channel_arguments.h',
-                      'include/grpc++/support/config.h',
-                      'include/grpc++/support/slice.h',
-                      'include/grpc++/support/status.h',
-                      'include/grpc++/support/status_code_enum.h',
-                      'include/grpc++/support/string_ref.h',
-                      'include/grpc++/support/stub_options.h',
-                      'include/grpc++/support/sync_stream.h',
-                      'include/grpc++/support/time.h',
-                      'include/grpc++/impl/codegen/async_stream.h',
-                      'include/grpc++/impl/codegen/async_unary_call.h',
-                      'include/grpc++/impl/codegen/byte_buffer.h',
-                      'include/grpc++/impl/codegen/call.h',
-                      'include/grpc++/impl/codegen/call_hook.h',
-                      'include/grpc++/impl/codegen/channel_interface.h',
-                      'include/grpc++/impl/codegen/client_context.h',
-                      'include/grpc++/impl/codegen/client_unary_call.h',
-                      'include/grpc++/impl/codegen/completion_queue.h',
-                      'include/grpc++/impl/codegen/completion_queue_tag.h',
-                      'include/grpc++/impl/codegen/config.h',
-                      'include/grpc++/impl/codegen/core_codegen_interface.h',
-                      'include/grpc++/impl/codegen/create_auth_context.h',
-                      'include/grpc++/impl/codegen/grpc_library.h',
-                      'include/grpc++/impl/codegen/metadata_map.h',
-                      'include/grpc++/impl/codegen/method_handler_impl.h',
-                      'include/grpc++/impl/codegen/rpc_method.h',
-                      'include/grpc++/impl/codegen/rpc_service_method.h',
-                      'include/grpc++/impl/codegen/security/auth_context.h',
-                      'include/grpc++/impl/codegen/serialization_traits.h',
-                      'include/grpc++/impl/codegen/server_context.h',
-                      'include/grpc++/impl/codegen/server_interface.h',
-                      'include/grpc++/impl/codegen/service_type.h',
-                      'include/grpc++/impl/codegen/slice.h',
-                      'include/grpc++/impl/codegen/status.h',
-                      'include/grpc++/impl/codegen/status_code_enum.h',
-                      'include/grpc++/impl/codegen/string_ref.h',
-                      'include/grpc++/impl/codegen/stub_options.h',
-                      'include/grpc++/impl/codegen/sync_stream.h',
-                      'include/grpc++/impl/codegen/time.h'
+  s.subspec 'Interface' do |ss|
+    ss.header_mappings_dir = 'include/grpcpp'
+
+    ss.source_files = 'include/grpcpp/alarm.h',
+                      'include/grpcpp/channel.h',
+                      'include/grpcpp/client_context.h',
+                      'include/grpcpp/completion_queue.h',
+                      'include/grpcpp/create_channel.h',
+                      'include/grpcpp/create_channel_posix.h',
+                      'include/grpcpp/ext/health_check_service_server_builder_option.h',
+                      'include/grpcpp/generic/async_generic_service.h',
+                      'include/grpcpp/generic/generic_stub.h',
+                      'include/grpcpp/grpcpp.h',
+                      'include/grpcpp/health_check_service_interface.h',
+                      'include/grpcpp/impl/call.h',
+                      'include/grpcpp/impl/channel_argument_option.h',
+                      'include/grpcpp/impl/client_unary_call.h',
+                      'include/grpcpp/impl/codegen/core_codegen.h',
+                      'include/grpcpp/impl/grpc_library.h',
+                      'include/grpcpp/impl/method_handler_impl.h',
+                      'include/grpcpp/impl/rpc_method.h',
+                      'include/grpcpp/impl/rpc_service_method.h',
+                      'include/grpcpp/impl/serialization_traits.h',
+                      'include/grpcpp/impl/server_builder_option.h',
+                      'include/grpcpp/impl/server_builder_plugin.h',
+                      'include/grpcpp/impl/server_initializer.h',
+                      'include/grpcpp/impl/service_type.h',
+                      'include/grpcpp/resource_quota.h',
+                      'include/grpcpp/security/auth_context.h',
+                      'include/grpcpp/security/auth_metadata_processor.h',
+                      'include/grpcpp/security/credentials.h',
+                      'include/grpcpp/security/server_credentials.h',
+                      'include/grpcpp/server.h',
+                      'include/grpcpp/server_builder.h',
+                      'include/grpcpp/server_context.h',
+                      'include/grpcpp/server_posix.h',
+                      'include/grpcpp/support/async_stream.h',
+                      'include/grpcpp/support/async_unary_call.h',
+                      'include/grpcpp/support/byte_buffer.h',
+                      'include/grpcpp/support/channel_arguments.h',
+                      'include/grpcpp/support/config.h',
+                      'include/grpcpp/support/slice.h',
+                      'include/grpcpp/support/status.h',
+                      'include/grpcpp/support/status_code_enum.h',
+                      'include/grpcpp/support/string_ref.h',
+                      'include/grpcpp/support/stub_options.h',
+                      'include/grpcpp/support/sync_stream.h',
+                      'include/grpcpp/support/time.h',
+                      'include/grpcpp/impl/codegen/async_stream.h',
+                      'include/grpcpp/impl/codegen/async_unary_call.h',
+                      'include/grpcpp/impl/codegen/byte_buffer.h',
+                      'include/grpcpp/impl/codegen/call.h',
+                      'include/grpcpp/impl/codegen/call_hook.h',
+                      'include/grpcpp/impl/codegen/channel_interface.h',
+                      'include/grpcpp/impl/codegen/client_context.h',
+                      'include/grpcpp/impl/codegen/client_unary_call.h',
+                      'include/grpcpp/impl/codegen/completion_queue.h',
+                      'include/grpcpp/impl/codegen/completion_queue_tag.h',
+                      'include/grpcpp/impl/codegen/config.h',
+                      'include/grpcpp/impl/codegen/core_codegen_interface.h',
+                      'include/grpcpp/impl/codegen/create_auth_context.h',
+                      'include/grpcpp/impl/codegen/grpc_library.h',
+                      'include/grpcpp/impl/codegen/metadata_map.h',
+                      'include/grpcpp/impl/codegen/method_handler_impl.h',
+                      'include/grpcpp/impl/codegen/rpc_method.h',
+                      'include/grpcpp/impl/codegen/rpc_service_method.h',
+                      'include/grpcpp/impl/codegen/security/auth_context.h',
+                      'include/grpcpp/impl/codegen/serialization_traits.h',
+                      'include/grpcpp/impl/codegen/server_context.h',
+                      'include/grpcpp/impl/codegen/server_interface.h',
+                      'include/grpcpp/impl/codegen/service_type.h',
+                      'include/grpcpp/impl/codegen/slice.h',
+                      'include/grpcpp/impl/codegen/status.h',
+                      'include/grpcpp/impl/codegen/status_code_enum.h',
+                      'include/grpcpp/impl/codegen/string_ref.h',
+                      'include/grpcpp/impl/codegen/stub_options.h',
+                      'include/grpcpp/impl/codegen/sync_stream.h',
+                      'include/grpcpp/impl/codegen/time.h'
   end
 
   s.subspec 'Implementation' do |ss|
@@ -149,7 +157,7 @@
     ss.dependency 'gRPC-Core', grpc_version
     ss.dependency 'nanopb', '~> 0.3'
 
-    ss.source_files = 'include/grpc++/impl/codegen/core_codegen.h',
+    ss.source_files = 'include/grpcpp/impl/codegen/core_codegen.h',
                       'src/cpp/client/secure_credentials.h',
                       'src/cpp/common/secure_auth_context.h',
                       'src/cpp/server/secure_server_credentials.h',
@@ -212,7 +220,6 @@
                       'src/core/lib/gpr/spinlock.h',
                       'src/core/lib/gpr/string.h',
                       'src/core/lib/gpr/string_windows.h',
-                      'src/core/lib/gpr/thd.h',
                       'src/core/lib/gpr/time_precise.h',
                       'src/core/lib/gpr/tls.h',
                       'src/core/lib/gpr/tls_gcc.h',
@@ -226,6 +233,7 @@
                       'src/core/lib/gprpp/atomic_with_std.h',
                       'src/core/lib/gprpp/manual_constructor.h',
                       'src/core/lib/gprpp/memory.h',
+                      'src/core/lib/gprpp/thd.h',
                       'src/core/lib/profiling/timers.h',
                       'src/core/ext/transport/chttp2/transport/bin_decoder.h',
                       'src/core/ext/transport/chttp2/transport/bin_encoder.h',
@@ -252,6 +260,7 @@
                       'src/core/ext/filters/http/message_compress/message_compress_filter.h',
                       'src/core/ext/filters/http/server/http_server_filter.h',
                       'src/core/lib/security/context/security_context.h',
+                      'src/core/lib/security/credentials/alts/alts_credentials.h',
                       'src/core/lib/security/credentials/composite/composite_credentials.h',
                       'src/core/lib/security/credentials/credentials.h',
                       'src/core/lib/security/credentials/fake/fake_credentials.h',
@@ -263,22 +272,43 @@
                       'src/core/lib/security/credentials/oauth2/oauth2_credentials.h',
                       'src/core/lib/security/credentials/plugin/plugin_credentials.h',
                       'src/core/lib/security/credentials/ssl/ssl_credentials.h',
+                      'src/core/lib/security/security_connector/alts_security_connector.h',
+                      'src/core/lib/security/security_connector/security_connector.h',
                       'src/core/lib/security/transport/auth_filters.h',
-                      'src/core/lib/security/transport/lb_targets_info.h',
                       'src/core/lib/security/transport/secure_endpoint.h',
-                      'src/core/lib/security/transport/security_connector.h',
                       'src/core/lib/security/transport/security_handshaker.h',
+                      'src/core/lib/security/transport/target_authority_table.h',
                       'src/core/lib/security/transport/tsi_error.h',
                       'src/core/lib/security/util/json_util.h',
-                      'src/core/tsi/alts_transport_security.h',
-                      'src/core/tsi/fake_transport_security.h',
-                      'src/core/tsi/ssl_transport_security.h',
-                      'src/core/tsi/ssl_types.h',
-                      'src/core/tsi/transport_security_grpc.h',
+                      'src/core/tsi/alts/crypt/gsec.h',
+                      'src/core/tsi/alts/frame_protector/alts_counter.h',
+                      'src/core/tsi/alts/frame_protector/alts_crypter.h',
+                      'src/core/tsi/alts/frame_protector/alts_frame_protector.h',
+                      'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h',
+                      'src/core/tsi/alts/frame_protector/frame_handler.h',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_client.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_event.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_handshaker.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h',
+                      'src/core/lib/security/credentials/alts/check_gcp_environment.h',
+                      'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_service_api.h',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_utils.h',
+                      'src/core/tsi/alts/handshaker/transport_security_common_api.h',
+                      'src/core/tsi/alts/handshaker/altscontext.pb.h',
+                      'src/core/tsi/alts/handshaker/handshaker.pb.h',
+                      'src/core/tsi/alts/handshaker/transport_security_common.pb.h',
                       'src/core/tsi/transport_security.h',
                       'src/core/tsi/transport_security_adapter.h',
                       'src/core/tsi/transport_security_interface.h',
-                      'src/core/ext/transport/chttp2/server/chttp2_server.h',
+                      'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                       'src/core/ext/filters/client_channel/backup_poller.h',
                       'src/core/ext/filters/client_channel/client_channel.h',
                       'src/core/ext/filters/client_channel/client_channel_factory.h',
@@ -288,6 +318,7 @@
                       'src/core/ext/filters/client_channel/lb_policy.h',
                       'src/core/ext/filters/client_channel/lb_policy_factory.h',
                       'src/core/ext/filters/client_channel/lb_policy_registry.h',
+                      'src/core/ext/filters/client_channel/method_params.h',
                       'src/core/ext/filters/client_channel/parse_address.h',
                       'src/core/ext/filters/client_channel/proxy_mapper.h',
                       'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
@@ -295,11 +326,17 @@
                       'src/core/ext/filters/client_channel/resolver_factory.h',
                       'src/core/ext/filters/client_channel/resolver_registry.h',
                       'src/core/ext/filters/client_channel/retry_throttle.h',
+                      'src/core/ext/filters/client_channel/status_util.h',
                       'src/core/ext/filters/client_channel/subchannel.h',
                       'src/core/ext/filters/client_channel/subchannel_index.h',
                       'src/core/ext/filters/client_channel/uri_parser.h',
                       'src/core/ext/filters/deadline/deadline_filter.h',
-                      'src/core/ext/transport/chttp2/client/chttp2_connector.h',
+                      'src/core/tsi/alts_transport_security.h',
+                      'src/core/tsi/fake_transport_security.h',
+                      'src/core/tsi/ssl_transport_security.h',
+                      'src/core/tsi/ssl_types.h',
+                      'src/core/tsi/transport_security_grpc.h',
+                      'src/core/ext/transport/chttp2/server/chttp2_server.h',
                       'src/core/ext/transport/inproc/inproc_transport.h',
                       'src/core/lib/avl/avl.h',
                       'src/core/lib/backoff/backoff.h',
@@ -399,6 +436,7 @@
                       'src/core/lib/slice/slice_hash_table.h',
                       'src/core/lib/slice/slice_internal.h',
                       'src/core/lib/slice/slice_string_helpers.h',
+                      'src/core/lib/slice/slice_weak_hash_table.h',
                       'src/core/lib/surface/api_trace.h',
                       'src/core/lib/surface/call.h',
                       'src/core/lib/surface/call_test_only.h',
@@ -423,12 +461,12 @@
                       'src/core/lib/transport/service_config.h',
                       'src/core/lib/transport/static_metadata.h',
                       'src/core/lib/transport/status_conversion.h',
+                      'src/core/lib/transport/status_metadata.h',
                       'src/core/lib/transport/timeout_encoding.h',
                       'src/core/lib/transport/transport.h',
                       'src/core/lib/transport/transport_impl.h',
                       'src/core/lib/debug/trace.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
-                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h',
@@ -444,7 +482,7 @@
                       'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h',
                       'src/core/ext/filters/workarounds/workaround_utils.h'
 
-    ss.private_header_files = 'include/grpc++/impl/codegen/core_codegen.h',
+    ss.private_header_files = 'include/grpcpp/impl/codegen/core_codegen.h',
                               'src/cpp/client/secure_credentials.h',
                               'src/cpp/common/secure_auth_context.h',
                               'src/cpp/server/secure_server_credentials.h',
@@ -464,7 +502,6 @@
                               'src/core/lib/gpr/spinlock.h',
                               'src/core/lib/gpr/string.h',
                               'src/core/lib/gpr/string_windows.h',
-                              'src/core/lib/gpr/thd.h',
                               'src/core/lib/gpr/time_precise.h',
                               'src/core/lib/gpr/tls.h',
                               'src/core/lib/gpr/tls_gcc.h',
@@ -478,6 +515,7 @@
                               'src/core/lib/gprpp/atomic_with_std.h',
                               'src/core/lib/gprpp/manual_constructor.h',
                               'src/core/lib/gprpp/memory.h',
+                              'src/core/lib/gprpp/thd.h',
                               'src/core/lib/profiling/timers.h',
                               'src/core/lib/avl/avl.h',
                               'src/core/lib/backoff/backoff.h',
@@ -577,6 +615,7 @@
                               'src/core/lib/slice/slice_hash_table.h',
                               'src/core/lib/slice/slice_internal.h',
                               'src/core/lib/slice/slice_string_helpers.h',
+                              'src/core/lib/slice/slice_weak_hash_table.h',
                               'src/core/lib/surface/api_trace.h',
                               'src/core/lib/surface/call.h',
                               'src/core/lib/surface/call_test_only.h',
@@ -601,6 +640,7 @@
                               'src/core/lib/transport/service_config.h',
                               'src/core/lib/transport/static_metadata.h',
                               'src/core/lib/transport/status_conversion.h',
+                              'src/core/lib/transport/status_metadata.h',
                               'src/core/lib/transport/timeout_encoding.h',
                               'src/core/lib/transport/transport.h',
                               'src/core/lib/transport/transport_impl.h',
@@ -608,113 +648,6 @@
                               'src/core/ext/transport/inproc/inproc_transport.h'
   end
 
-  s.subspec 'Tests' do |ss|
-    ss.header_mappings_dir = '.'
-
-    ss.dependency "#{s.name}/Interface", version
-    ss.dependency "#{s.name}/Implementation", version
-    ss.dependency "gRPC-Core/Tests", grpc_version
-
-    ss.source_files = 'test/cpp/util/create_test_channel.cc',
-                      'test/cpp/util/string_ref_helper.cc',
-                      'test/cpp/util/subprocess.cc',
-                      'test/cpp/util/test_credentials_provider.cc',
-                      'test/cpp/util/create_test_channel.h',
-                      'test/cpp/util/string_ref_helper.h',
-                      'test/cpp/util/subprocess.h',
-                      'test/cpp/util/test_credentials_provider.h',
-                      'test/core/util/test_config.h',
-                      'test/core/end2end/data/ssl_test_data.h',
-                      'test/core/security/oauth2_utils.h',
-                      'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h',
-                      'test/core/end2end/cq_verifier.h',
-                      'test/core/end2end/fixtures/http_proxy_fixture.h',
-                      'test/core/end2end/fixtures/proxy.h',
-                      'test/core/iomgr/endpoint_tests.h',
-                      'test/core/util/debugger_macros.h',
-                      'test/core/util/grpc_profiler.h',
-                      'test/core/util/histogram.h',
-                      'test/core/util/memory_counters.h',
-                      'test/core/util/mock_endpoint.h',
-                      'test/core/util/parse_hexstring.h',
-                      'test/core/util/passthru_endpoint.h',
-                      'test/core/util/port.h',
-                      'test/core/util/port_server_client.h',
-                      'test/core/util/slice_splitter.h',
-                      'test/core/util/subprocess.h',
-                      'test/core/util/tracer_util.h',
-                      'test/core/util/trickle_endpoint.h',
-                      'test/core/util/cmdline.h',
-                      'src/core/lib/gpr/arena.h',
-                      'src/core/lib/gpr/env.h',
-                      'src/core/lib/gpr/fork.h',
-                      'src/core/lib/gpr/host_port.h',
-                      'src/core/lib/gpr/mpscq.h',
-                      'src/core/lib/gpr/murmur_hash.h',
-                      'src/core/lib/gpr/spinlock.h',
-                      'src/core/lib/gpr/string.h',
-                      'src/core/lib/gpr/string_windows.h',
-                      'src/core/lib/gpr/thd.h',
-                      'src/core/lib/gpr/time_precise.h',
-                      'src/core/lib/gpr/tls.h',
-                      'src/core/lib/gpr/tls_gcc.h',
-                      'src/core/lib/gpr/tls_msvc.h',
-                      'src/core/lib/gpr/tls_pthread.h',
-                      'src/core/lib/gpr/tmpfile.h',
-                      'src/core/lib/gpr/useful.h',
-                      'src/core/lib/gprpp/abstract.h',
-                      'src/core/lib/gprpp/atomic.h',
-                      'src/core/lib/gprpp/atomic_with_atm.h',
-                      'src/core/lib/gprpp/atomic_with_std.h',
-                      'src/core/lib/gprpp/manual_constructor.h',
-                      'src/core/lib/gprpp/memory.h',
-                      'src/core/lib/profiling/timers.h',
-                      'src/core/ext/filters/client_channel/backup_poller.h',
-                      'src/core/ext/filters/client_channel/client_channel.h',
-                      'src/core/ext/filters/client_channel/client_channel_factory.h',
-                      'src/core/ext/filters/client_channel/connector.h',
-                      'src/core/ext/filters/client_channel/http_connect_handshaker.h',
-                      'src/core/ext/filters/client_channel/http_proxy.h',
-                      'src/core/ext/filters/client_channel/lb_policy.h',
-                      'src/core/ext/filters/client_channel/lb_policy_factory.h',
-                      'src/core/ext/filters/client_channel/lb_policy_registry.h',
-                      'src/core/ext/filters/client_channel/parse_address.h',
-                      'src/core/ext/filters/client_channel/proxy_mapper.h',
-                      'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
-                      'src/core/ext/filters/client_channel/resolver.h',
-                      'src/core/ext/filters/client_channel/resolver_factory.h',
-                      'src/core/ext/filters/client_channel/resolver_registry.h',
-                      'src/core/ext/filters/client_channel/retry_throttle.h',
-                      'src/core/ext/filters/client_channel/subchannel.h',
-                      'src/core/ext/filters/client_channel/subchannel_index.h',
-                      'src/core/ext/filters/client_channel/uri_parser.h',
-                      'src/core/ext/filters/deadline/deadline_filter.h',
-                      'src/core/ext/transport/chttp2/transport/bin_decoder.h',
-                      'src/core/ext/transport/chttp2/transport/bin_encoder.h',
-                      'src/core/ext/transport/chttp2/transport/chttp2_transport.h',
-                      'src/core/ext/transport/chttp2/transport/flow_control.h',
-                      'src/core/ext/transport/chttp2/transport/frame.h',
-                      'src/core/ext/transport/chttp2/transport/frame_data.h',
-                      'src/core/ext/transport/chttp2/transport/frame_goaway.h',
-                      'src/core/ext/transport/chttp2/transport/frame_ping.h',
-                      'src/core/ext/transport/chttp2/transport/frame_rst_stream.h',
-                      'src/core/ext/transport/chttp2/transport/frame_settings.h',
-                      'src/core/ext/transport/chttp2/transport/frame_window_update.h',
-                      'src/core/ext/transport/chttp2/transport/hpack_encoder.h',
-                      'src/core/ext/transport/chttp2/transport/hpack_parser.h',
-                      'src/core/ext/transport/chttp2/transport/hpack_table.h',
-                      'src/core/ext/transport/chttp2/transport/http2_settings.h',
-                      'src/core/ext/transport/chttp2/transport/huffsyms.h',
-                      'src/core/ext/transport/chttp2/transport/incoming_metadata.h',
-                      'src/core/ext/transport/chttp2/transport/internal.h',
-                      'src/core/ext/transport/chttp2/transport/stream_map.h',
-                      'src/core/ext/transport/chttp2/transport/varint.h',
-                      'src/core/ext/transport/chttp2/alpn/alpn.h',
-                      'src/core/ext/filters/http/client/http_client_filter.h',
-                      'src/core/ext/filters/http/message_compress/message_compress_filter.h',
-                      'src/core/ext/filters/http/server/http_server_filter.h'
-  end
-
   s.prepare_command = <<-END_OF_COMMAND
     find src/cpp/ -type f -exec sed -E -i'.back' 's;#include "third_party/nanopb/(.*)";#include <nanopb/\\1>;g' {} \\\;
     find src/cpp/ -name "*.back" -type f -delete
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 95db0d8..7f61719 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -22,7 +22,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.10.0-dev'
+  version = '1.11.0-dev'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'https://grpc.io'
@@ -93,7 +93,7 @@
   }
 
   s.default_subspecs = 'Interface', 'Implementation'
-  s.compiler_flags = '-DGRPC_ARES=0'
+  s.compiler_flags = '-DGRPC_ARES=0', '-DPB_FIELD_16BIT'
   s.libraries = 'c++'
 
   # Like many other C libraries, gRPC-Core has its public headers under `include/<libname>/` and its
@@ -192,7 +192,6 @@
                       'src/core/lib/gpr/spinlock.h',
                       'src/core/lib/gpr/string.h',
                       'src/core/lib/gpr/string_windows.h',
-                      'src/core/lib/gpr/thd.h',
                       'src/core/lib/gpr/time_precise.h',
                       'src/core/lib/gpr/tls.h',
                       'src/core/lib/gpr/tls_gcc.h',
@@ -206,6 +205,7 @@
                       'src/core/lib/gprpp/atomic_with_std.h',
                       'src/core/lib/gprpp/manual_constructor.h',
                       'src/core/lib/gprpp/memory.h',
+                      'src/core/lib/gprpp/thd.h',
                       'src/core/lib/profiling/timers.h',
                       'src/core/lib/gpr/alloc.cc',
                       'src/core/lib/gpr/arena.cc',
@@ -233,9 +233,6 @@
                       'src/core/lib/gpr/sync.cc',
                       'src/core/lib/gpr/sync_posix.cc',
                       'src/core/lib/gpr/sync_windows.cc',
-                      'src/core/lib/gpr/thd.cc',
-                      'src/core/lib/gpr/thd_posix.cc',
-                      'src/core/lib/gpr/thd_windows.cc',
                       'src/core/lib/gpr/time.cc',
                       'src/core/lib/gpr/time_posix.cc',
                       'src/core/lib/gpr/time_precise.cc',
@@ -245,6 +242,8 @@
                       'src/core/lib/gpr/tmpfile_posix.cc',
                       'src/core/lib/gpr/tmpfile_windows.cc',
                       'src/core/lib/gpr/wrap_memcpy.cc',
+                      'src/core/lib/gprpp/thd_posix.cc',
+                      'src/core/lib/gprpp/thd_windows.cc',
                       'src/core/lib/profiling/basic_timers.cc',
                       'src/core/lib/profiling/stap_timers.cc',
                       'src/core/ext/transport/chttp2/transport/bin_decoder.h',
@@ -272,6 +271,7 @@
                       'src/core/ext/filters/http/message_compress/message_compress_filter.h',
                       'src/core/ext/filters/http/server/http_server_filter.h',
                       'src/core/lib/security/context/security_context.h',
+                      'src/core/lib/security/credentials/alts/alts_credentials.h',
                       'src/core/lib/security/credentials/composite/composite_credentials.h',
                       'src/core/lib/security/credentials/credentials.h',
                       'src/core/lib/security/credentials/fake/fake_credentials.h',
@@ -283,22 +283,43 @@
                       'src/core/lib/security/credentials/oauth2/oauth2_credentials.h',
                       'src/core/lib/security/credentials/plugin/plugin_credentials.h',
                       'src/core/lib/security/credentials/ssl/ssl_credentials.h',
+                      'src/core/lib/security/security_connector/alts_security_connector.h',
+                      'src/core/lib/security/security_connector/security_connector.h',
                       'src/core/lib/security/transport/auth_filters.h',
-                      'src/core/lib/security/transport/lb_targets_info.h',
                       'src/core/lib/security/transport/secure_endpoint.h',
-                      'src/core/lib/security/transport/security_connector.h',
                       'src/core/lib/security/transport/security_handshaker.h',
+                      'src/core/lib/security/transport/target_authority_table.h',
                       'src/core/lib/security/transport/tsi_error.h',
                       'src/core/lib/security/util/json_util.h',
-                      'src/core/tsi/alts_transport_security.h',
-                      'src/core/tsi/fake_transport_security.h',
-                      'src/core/tsi/ssl_transport_security.h',
-                      'src/core/tsi/ssl_types.h',
-                      'src/core/tsi/transport_security_grpc.h',
+                      'src/core/tsi/alts/crypt/gsec.h',
+                      'src/core/tsi/alts/frame_protector/alts_counter.h',
+                      'src/core/tsi/alts/frame_protector/alts_crypter.h',
+                      'src/core/tsi/alts/frame_protector/alts_frame_protector.h',
+                      'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h',
+                      'src/core/tsi/alts/frame_protector/frame_handler.h',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_client.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_event.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_handshaker.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h',
+                      'src/core/lib/security/credentials/alts/check_gcp_environment.h',
+                      'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_service_api.h',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h',
+                      'src/core/tsi/alts/handshaker/alts_tsi_utils.h',
+                      'src/core/tsi/alts/handshaker/transport_security_common_api.h',
+                      'src/core/tsi/alts/handshaker/altscontext.pb.h',
+                      'src/core/tsi/alts/handshaker/handshaker.pb.h',
+                      'src/core/tsi/alts/handshaker/transport_security_common.pb.h',
                       'src/core/tsi/transport_security.h',
                       'src/core/tsi/transport_security_adapter.h',
                       'src/core/tsi/transport_security_interface.h',
-                      'src/core/ext/transport/chttp2/server/chttp2_server.h',
+                      'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                       'src/core/ext/filters/client_channel/backup_poller.h',
                       'src/core/ext/filters/client_channel/client_channel.h',
                       'src/core/ext/filters/client_channel/client_channel_factory.h',
@@ -308,6 +329,7 @@
                       'src/core/ext/filters/client_channel/lb_policy.h',
                       'src/core/ext/filters/client_channel/lb_policy_factory.h',
                       'src/core/ext/filters/client_channel/lb_policy_registry.h',
+                      'src/core/ext/filters/client_channel/method_params.h',
                       'src/core/ext/filters/client_channel/parse_address.h',
                       'src/core/ext/filters/client_channel/proxy_mapper.h',
                       'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
@@ -315,11 +337,17 @@
                       'src/core/ext/filters/client_channel/resolver_factory.h',
                       'src/core/ext/filters/client_channel/resolver_registry.h',
                       'src/core/ext/filters/client_channel/retry_throttle.h',
+                      'src/core/ext/filters/client_channel/status_util.h',
                       'src/core/ext/filters/client_channel/subchannel.h',
                       'src/core/ext/filters/client_channel/subchannel_index.h',
                       'src/core/ext/filters/client_channel/uri_parser.h',
                       'src/core/ext/filters/deadline/deadline_filter.h',
-                      'src/core/ext/transport/chttp2/client/chttp2_connector.h',
+                      'src/core/tsi/alts_transport_security.h',
+                      'src/core/tsi/fake_transport_security.h',
+                      'src/core/tsi/ssl_transport_security.h',
+                      'src/core/tsi/ssl_types.h',
+                      'src/core/tsi/transport_security_grpc.h',
+                      'src/core/ext/transport/chttp2/server/chttp2_server.h',
                       'src/core/ext/transport/inproc/inproc_transport.h',
                       'src/core/lib/avl/avl.h',
                       'src/core/lib/backoff/backoff.h',
@@ -419,6 +447,7 @@
                       'src/core/lib/slice/slice_hash_table.h',
                       'src/core/lib/slice/slice_internal.h',
                       'src/core/lib/slice/slice_string_helpers.h',
+                      'src/core/lib/slice/slice_weak_hash_table.h',
                       'src/core/lib/surface/api_trace.h',
                       'src/core/lib/surface/call.h',
                       'src/core/lib/surface/call_test_only.h',
@@ -443,12 +472,12 @@
                       'src/core/lib/transport/service_config.h',
                       'src/core/lib/transport/static_metadata.h',
                       'src/core/lib/transport/status_conversion.h',
+                      'src/core/lib/transport/status_metadata.h',
                       'src/core/lib/transport/timeout_encoding.h',
                       'src/core/lib/transport/transport.h',
                       'src/core/lib/transport/transport_impl.h',
                       'src/core/lib/debug/trace.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
-                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h',
@@ -564,7 +593,6 @@
                       'src/core/lib/slice/percent_encoding.cc',
                       'src/core/lib/slice/slice.cc',
                       'src/core/lib/slice/slice_buffer.cc',
-                      'src/core/lib/slice/slice_hash_table.cc',
                       'src/core/lib/slice/slice_intern.cc',
                       'src/core/lib/slice/slice_string_helpers.cc',
                       'src/core/lib/surface/api_trace.cc',
@@ -595,6 +623,7 @@
                       'src/core/lib/transport/service_config.cc',
                       'src/core/lib/transport/static_metadata.cc',
                       'src/core/lib/transport/status_conversion.cc',
+                      'src/core/lib/transport/status_metadata.cc',
                       'src/core/lib/transport/timeout_encoding.cc',
                       'src/core/lib/transport/transport.cc',
                       'src/core/lib/transport/transport_op_string.cc',
@@ -629,6 +658,7 @@
                       'src/core/ext/filters/http/server/http_server_filter.cc',
                       'src/core/lib/http/httpcli_security_connector.cc',
                       'src/core/lib/security/context/security_context.cc',
+                      'src/core/lib/security/credentials/alts/alts_credentials.cc',
                       'src/core/lib/security/credentials/composite/composite_credentials.cc',
                       'src/core/lib/security/credentials/credentials.cc',
                       'src/core/lib/security/credentials/credentials_metadata.cc',
@@ -642,23 +672,52 @@
                       'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc',
                       'src/core/lib/security/credentials/plugin/plugin_credentials.cc',
                       'src/core/lib/security/credentials/ssl/ssl_credentials.cc',
+                      'src/core/lib/security/security_connector/alts_security_connector.cc',
+                      'src/core/lib/security/security_connector/security_connector.cc',
                       'src/core/lib/security/transport/client_auth_filter.cc',
-                      'src/core/lib/security/transport/lb_targets_info.cc',
                       'src/core/lib/security/transport/secure_endpoint.cc',
-                      'src/core/lib/security/transport/security_connector.cc',
                       'src/core/lib/security/transport/security_handshaker.cc',
                       'src/core/lib/security/transport/server_auth_filter.cc',
+                      'src/core/lib/security/transport/target_authority_table.cc',
                       'src/core/lib/security/transport/tsi_error.cc',
                       'src/core/lib/security/util/json_util.cc',
                       'src/core/lib/surface/init_secure.cc',
-                      'src/core/tsi/alts_transport_security.cc',
-                      'src/core/tsi/fake_transport_security.cc',
-                      'src/core/tsi/ssl_transport_security.cc',
-                      'src/core/tsi/transport_security_grpc.cc',
+                      'src/core/tsi/alts/crypt/aes_gcm.cc',
+                      'src/core/tsi/alts/crypt/gsec.cc',
+                      'src/core/tsi/alts/frame_protector/alts_counter.cc',
+                      'src/core/tsi/alts/frame_protector/alts_crypter.cc',
+                      'src/core/tsi/alts/frame_protector/alts_frame_protector.cc',
+                      'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc',
+                      'src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc',
+                      'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc',
+                      'src/core/tsi/alts/frame_protector/frame_handler.cc',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_client.cc',
+                      'src/core/tsi/alts/handshaker/alts_tsi_event.cc',
+                      'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc',
+                      'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc',
+                      'src/core/lib/security/credentials/alts/check_gcp_environment.cc',
+                      'src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc',
+                      'src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc',
+                      'src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc',
+                      'src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc',
+                      'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc',
+                      'src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc',
+                      'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc',
+                      'src/core/tsi/alts/handshaker/alts_tsi_utils.cc',
+                      'src/core/tsi/alts/handshaker/transport_security_common_api.cc',
+                      'src/core/tsi/alts/handshaker/altscontext.pb.c',
+                      'src/core/tsi/alts/handshaker/handshaker.pb.c',
+                      'src/core/tsi/alts/handshaker/transport_security_common.pb.c',
                       'src/core/tsi/transport_security.cc',
                       'src/core/tsi/transport_security_adapter.cc',
-                      'src/core/ext/transport/chttp2/server/chttp2_server.cc',
-                      'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc',
+                      'src/core/ext/transport/chttp2/client/insecure/channel_create.cc',
+                      'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc',
+                      'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
                       'src/core/ext/filters/client_channel/backup_poller.cc',
                       'src/core/ext/filters/client_channel/channel_connectivity.cc',
                       'src/core/ext/filters/client_channel/client_channel.cc',
@@ -670,21 +729,26 @@
                       'src/core/ext/filters/client_channel/lb_policy.cc',
                       'src/core/ext/filters/client_channel/lb_policy_factory.cc',
                       'src/core/ext/filters/client_channel/lb_policy_registry.cc',
+                      'src/core/ext/filters/client_channel/method_params.cc',
                       'src/core/ext/filters/client_channel/parse_address.cc',
                       'src/core/ext/filters/client_channel/proxy_mapper.cc',
                       'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
                       'src/core/ext/filters/client_channel/resolver.cc',
                       'src/core/ext/filters/client_channel/resolver_registry.cc',
                       'src/core/ext/filters/client_channel/retry_throttle.cc',
+                      'src/core/ext/filters/client_channel/status_util.cc',
                       'src/core/ext/filters/client_channel/subchannel.cc',
                       'src/core/ext/filters/client_channel/subchannel_index.cc',
                       'src/core/ext/filters/client_channel/uri_parser.cc',
                       'src/core/ext/filters/deadline/deadline_filter.cc',
-                      'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
+                      'src/core/tsi/alts_transport_security.cc',
+                      'src/core/tsi/fake_transport_security.cc',
+                      'src/core/tsi/ssl_transport_security.cc',
+                      'src/core/tsi/transport_security_grpc.cc',
+                      'src/core/ext/transport/chttp2/server/chttp2_server.cc',
+                      'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc',
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc',
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc',
-                      'src/core/ext/transport/chttp2/client/insecure/channel_create.cc',
-                      'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc',
                       'src/core/ext/transport/inproc/inproc_plugin.cc',
                       'src/core/ext/transport/inproc/inproc_transport.cc',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc',
@@ -721,7 +785,6 @@
                               'src/core/lib/gpr/spinlock.h',
                               'src/core/lib/gpr/string.h',
                               'src/core/lib/gpr/string_windows.h',
-                              'src/core/lib/gpr/thd.h',
                               'src/core/lib/gpr/time_precise.h',
                               'src/core/lib/gpr/tls.h',
                               'src/core/lib/gpr/tls_gcc.h',
@@ -735,6 +798,7 @@
                               'src/core/lib/gprpp/atomic_with_std.h',
                               'src/core/lib/gprpp/manual_constructor.h',
                               'src/core/lib/gprpp/memory.h',
+                              'src/core/lib/gprpp/thd.h',
                               'src/core/lib/profiling/timers.h',
                               'src/core/ext/transport/chttp2/transport/bin_decoder.h',
                               'src/core/ext/transport/chttp2/transport/bin_encoder.h',
@@ -761,6 +825,7 @@
                               'src/core/ext/filters/http/message_compress/message_compress_filter.h',
                               'src/core/ext/filters/http/server/http_server_filter.h',
                               'src/core/lib/security/context/security_context.h',
+                              'src/core/lib/security/credentials/alts/alts_credentials.h',
                               'src/core/lib/security/credentials/composite/composite_credentials.h',
                               'src/core/lib/security/credentials/credentials.h',
                               'src/core/lib/security/credentials/fake/fake_credentials.h',
@@ -772,22 +837,43 @@
                               'src/core/lib/security/credentials/oauth2/oauth2_credentials.h',
                               'src/core/lib/security/credentials/plugin/plugin_credentials.h',
                               'src/core/lib/security/credentials/ssl/ssl_credentials.h',
+                              'src/core/lib/security/security_connector/alts_security_connector.h',
+                              'src/core/lib/security/security_connector/security_connector.h',
                               'src/core/lib/security/transport/auth_filters.h',
-                              'src/core/lib/security/transport/lb_targets_info.h',
                               'src/core/lib/security/transport/secure_endpoint.h',
-                              'src/core/lib/security/transport/security_connector.h',
                               'src/core/lib/security/transport/security_handshaker.h',
+                              'src/core/lib/security/transport/target_authority_table.h',
                               'src/core/lib/security/transport/tsi_error.h',
                               'src/core/lib/security/util/json_util.h',
-                              'src/core/tsi/alts_transport_security.h',
-                              'src/core/tsi/fake_transport_security.h',
-                              'src/core/tsi/ssl_transport_security.h',
-                              'src/core/tsi/ssl_types.h',
-                              'src/core/tsi/transport_security_grpc.h',
+                              'src/core/tsi/alts/crypt/gsec.h',
+                              'src/core/tsi/alts/frame_protector/alts_counter.h',
+                              'src/core/tsi/alts/frame_protector/alts_crypter.h',
+                              'src/core/tsi/alts/frame_protector/alts_frame_protector.h',
+                              'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h',
+                              'src/core/tsi/alts/frame_protector/frame_handler.h',
+                              'src/core/tsi/alts/handshaker/alts_handshaker_client.h',
+                              'src/core/tsi/alts/handshaker/alts_tsi_event.h',
+                              'src/core/tsi/alts/handshaker/alts_tsi_handshaker.h',
+                              'src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h',
+                              'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h',
+                              'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h',
+                              'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h',
+                              'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h',
+                              'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h',
+                              'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h',
+                              'src/core/lib/security/credentials/alts/check_gcp_environment.h',
+                              'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h',
+                              'src/core/tsi/alts/handshaker/alts_handshaker_service_api.h',
+                              'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h',
+                              'src/core/tsi/alts/handshaker/alts_tsi_utils.h',
+                              'src/core/tsi/alts/handshaker/transport_security_common_api.h',
+                              'src/core/tsi/alts/handshaker/altscontext.pb.h',
+                              'src/core/tsi/alts/handshaker/handshaker.pb.h',
+                              'src/core/tsi/alts/handshaker/transport_security_common.pb.h',
                               'src/core/tsi/transport_security.h',
                               'src/core/tsi/transport_security_adapter.h',
                               'src/core/tsi/transport_security_interface.h',
-                              'src/core/ext/transport/chttp2/server/chttp2_server.h',
+                              'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                               'src/core/ext/filters/client_channel/backup_poller.h',
                               'src/core/ext/filters/client_channel/client_channel.h',
                               'src/core/ext/filters/client_channel/client_channel_factory.h',
@@ -797,6 +883,7 @@
                               'src/core/ext/filters/client_channel/lb_policy.h',
                               'src/core/ext/filters/client_channel/lb_policy_factory.h',
                               'src/core/ext/filters/client_channel/lb_policy_registry.h',
+                              'src/core/ext/filters/client_channel/method_params.h',
                               'src/core/ext/filters/client_channel/parse_address.h',
                               'src/core/ext/filters/client_channel/proxy_mapper.h',
                               'src/core/ext/filters/client_channel/proxy_mapper_registry.h',
@@ -804,11 +891,17 @@
                               'src/core/ext/filters/client_channel/resolver_factory.h',
                               'src/core/ext/filters/client_channel/resolver_registry.h',
                               'src/core/ext/filters/client_channel/retry_throttle.h',
+                              'src/core/ext/filters/client_channel/status_util.h',
                               'src/core/ext/filters/client_channel/subchannel.h',
                               'src/core/ext/filters/client_channel/subchannel_index.h',
                               'src/core/ext/filters/client_channel/uri_parser.h',
                               'src/core/ext/filters/deadline/deadline_filter.h',
-                              'src/core/ext/transport/chttp2/client/chttp2_connector.h',
+                              'src/core/tsi/alts_transport_security.h',
+                              'src/core/tsi/fake_transport_security.h',
+                              'src/core/tsi/ssl_transport_security.h',
+                              'src/core/tsi/ssl_types.h',
+                              'src/core/tsi/transport_security_grpc.h',
+                              'src/core/ext/transport/chttp2/server/chttp2_server.h',
                               'src/core/ext/transport/inproc/inproc_transport.h',
                               'src/core/lib/avl/avl.h',
                               'src/core/lib/backoff/backoff.h',
@@ -908,6 +1001,7 @@
                               'src/core/lib/slice/slice_hash_table.h',
                               'src/core/lib/slice/slice_internal.h',
                               'src/core/lib/slice/slice_string_helpers.h',
+                              'src/core/lib/slice/slice_weak_hash_table.h',
                               'src/core/lib/surface/api_trace.h',
                               'src/core/lib/surface/call.h',
                               'src/core/lib/surface/call_test_only.h',
@@ -932,12 +1026,12 @@
                               'src/core/lib/transport/service_config.h',
                               'src/core/lib/transport/static_metadata.h',
                               'src/core/lib/transport/status_conversion.h',
+                              'src/core/lib/transport/status_metadata.h',
                               'src/core/lib/transport/timeout_encoding.h',
                               'src/core/lib/transport/transport.h',
                               'src/core/lib/transport/transport_impl.h',
                               'src/core/lib/debug/trace.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
-                              'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h',
@@ -968,8 +1062,15 @@
 
     ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc',
                       'src/core/ext/transport/cronet/transport/cronet_transport.cc',
+                      'third_party/nanopb/pb_common.c',
+                      'third_party/nanopb/pb_decode.c',
+                      'third_party/nanopb/pb_encode.c',
                       'src/core/ext/transport/cronet/transport/cronet_transport.h',
-                      'third_party/objective_c/Cronet/bidirectional_stream_c.h'
+                      'third_party/objective_c/Cronet/bidirectional_stream_c.h',
+                      'third_party/nanopb/pb.h',
+                      'third_party/nanopb/pb_common.h',
+                      'third_party/nanopb/pb_decode.h',
+                      'third_party/nanopb/pb_encode.h'
   end
 
   s.subspec 'Tests' do |ss|
@@ -1072,6 +1173,21 @@
                       'test/core/end2end/tests/request_with_flags.cc',
                       'test/core/end2end/tests/request_with_payload.cc',
                       'test/core/end2end/tests/resource_quota_server.cc',
+                      'test/core/end2end/tests/retry.cc',
+                      'test/core/end2end/tests/retry_cancellation.cc',
+                      'test/core/end2end/tests/retry_disabled.cc',
+                      'test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc',
+                      'test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc',
+                      'test/core/end2end/tests/retry_non_retriable_status.cc',
+                      'test/core/end2end/tests/retry_recv_initial_metadata.cc',
+                      'test/core/end2end/tests/retry_recv_message.cc',
+                      'test/core/end2end/tests/retry_server_pushback_delay.cc',
+                      'test/core/end2end/tests/retry_server_pushback_disabled.cc',
+                      'test/core/end2end/tests/retry_streaming.cc',
+                      'test/core/end2end/tests/retry_streaming_after_commit.cc',
+                      'test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc',
+                      'test/core/end2end/tests/retry_throttled.cc',
+                      'test/core/end2end/tests/retry_too_many_attempts.cc',
                       'test/core/end2end/tests/server_finishes_request.cc',
                       'test/core/end2end/tests/shutdown_finishes_calls.cc',
                       'test/core/end2end/tests/shutdown_finishes_tags.cc',
diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec
index a0375bd..149687e 100644
--- a/gRPC-ProtoRPC.podspec
+++ b/gRPC-ProtoRPC.podspec
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.10.0-dev'
+  version = '1.11.0-dev'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'https://grpc.io'
diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec
index 280aafe..2497174 100644
--- a/gRPC-RxLibrary.podspec
+++ b/gRPC-RxLibrary.podspec
@@ -21,7 +21,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.10.0-dev'
+  version = '1.11.0-dev'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'https://grpc.io'
diff --git a/gRPC.podspec b/gRPC.podspec
index 930d991..68e06b5 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -20,7 +20,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.10.0-dev'
+  version = '1.11.0-dev'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'https://grpc.io'
diff --git a/grpc.gemspec b/grpc.gemspec
index ac901da..3df7cea 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -83,7 +83,6 @@
   s.files += %w( src/core/lib/gpr/spinlock.h )
   s.files += %w( src/core/lib/gpr/string.h )
   s.files += %w( src/core/lib/gpr/string_windows.h )
-  s.files += %w( src/core/lib/gpr/thd.h )
   s.files += %w( src/core/lib/gpr/time_precise.h )
   s.files += %w( src/core/lib/gpr/tls.h )
   s.files += %w( src/core/lib/gpr/tls_gcc.h )
@@ -97,6 +96,7 @@
   s.files += %w( src/core/lib/gprpp/atomic_with_std.h )
   s.files += %w( src/core/lib/gprpp/manual_constructor.h )
   s.files += %w( src/core/lib/gprpp/memory.h )
+  s.files += %w( src/core/lib/gprpp/thd.h )
   s.files += %w( src/core/lib/profiling/timers.h )
   s.files += %w( src/core/lib/gpr/alloc.cc )
   s.files += %w( src/core/lib/gpr/arena.cc )
@@ -124,9 +124,6 @@
   s.files += %w( src/core/lib/gpr/sync.cc )
   s.files += %w( src/core/lib/gpr/sync_posix.cc )
   s.files += %w( src/core/lib/gpr/sync_windows.cc )
-  s.files += %w( src/core/lib/gpr/thd.cc )
-  s.files += %w( src/core/lib/gpr/thd_posix.cc )
-  s.files += %w( src/core/lib/gpr/thd_windows.cc )
   s.files += %w( src/core/lib/gpr/time.cc )
   s.files += %w( src/core/lib/gpr/time_posix.cc )
   s.files += %w( src/core/lib/gpr/time_precise.cc )
@@ -136,6 +133,8 @@
   s.files += %w( src/core/lib/gpr/tmpfile_posix.cc )
   s.files += %w( src/core/lib/gpr/tmpfile_windows.cc )
   s.files += %w( src/core/lib/gpr/wrap_memcpy.cc )
+  s.files += %w( src/core/lib/gprpp/thd_posix.cc )
+  s.files += %w( src/core/lib/gprpp/thd_windows.cc )
   s.files += %w( src/core/lib/profiling/basic_timers.cc )
   s.files += %w( src/core/lib/profiling/stap_timers.cc )
   s.files += %w( include/grpc/impl/codegen/byte_buffer.h )
@@ -198,6 +197,7 @@
   s.files += %w( src/core/ext/filters/http/message_compress/message_compress_filter.h )
   s.files += %w( src/core/ext/filters/http/server/http_server_filter.h )
   s.files += %w( src/core/lib/security/context/security_context.h )
+  s.files += %w( src/core/lib/security/credentials/alts/alts_credentials.h )
   s.files += %w( src/core/lib/security/credentials/composite/composite_credentials.h )
   s.files += %w( src/core/lib/security/credentials/credentials.h )
   s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.h )
@@ -209,22 +209,47 @@
   s.files += %w( src/core/lib/security/credentials/oauth2/oauth2_credentials.h )
   s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.h )
   s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.h )
+  s.files += %w( src/core/lib/security/security_connector/alts_security_connector.h )
+  s.files += %w( src/core/lib/security/security_connector/security_connector.h )
   s.files += %w( src/core/lib/security/transport/auth_filters.h )
-  s.files += %w( src/core/lib/security/transport/lb_targets_info.h )
   s.files += %w( src/core/lib/security/transport/secure_endpoint.h )
-  s.files += %w( src/core/lib/security/transport/security_connector.h )
   s.files += %w( src/core/lib/security/transport/security_handshaker.h )
+  s.files += %w( src/core/lib/security/transport/target_authority_table.h )
   s.files += %w( src/core/lib/security/transport/tsi_error.h )
   s.files += %w( src/core/lib/security/util/json_util.h )
-  s.files += %w( src/core/tsi/alts_transport_security.h )
-  s.files += %w( src/core/tsi/fake_transport_security.h )
-  s.files += %w( src/core/tsi/ssl_transport_security.h )
-  s.files += %w( src/core/tsi/ssl_types.h )
-  s.files += %w( src/core/tsi/transport_security_grpc.h )
+  s.files += %w( src/core/tsi/alts/crypt/gsec.h )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_counter.h )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_crypter.h )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_frame_protector.h )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h )
+  s.files += %w( src/core/tsi/alts/frame_protector/frame_handler.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_client.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_event.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_handshaker.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h )
+  s.files += %w( src/core/lib/security/credentials/alts/check_gcp_environment.h )
+  s.files += %w( src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_service_api.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_utils.h )
+  s.files += %w( src/core/tsi/alts/handshaker/transport_security_common_api.h )
+  s.files += %w( src/core/tsi/alts/handshaker/altscontext.pb.h )
+  s.files += %w( src/core/tsi/alts/handshaker/handshaker.pb.h )
+  s.files += %w( src/core/tsi/alts/handshaker/transport_security_common.pb.h )
+  s.files += %w( third_party/nanopb/pb.h )
+  s.files += %w( third_party/nanopb/pb_common.h )
+  s.files += %w( third_party/nanopb/pb_decode.h )
+  s.files += %w( third_party/nanopb/pb_encode.h )
   s.files += %w( src/core/tsi/transport_security.h )
   s.files += %w( src/core/tsi/transport_security_adapter.h )
   s.files += %w( src/core/tsi/transport_security_interface.h )
-  s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.h )
+  s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.h )
   s.files += %w( src/core/ext/filters/client_channel/backup_poller.h )
   s.files += %w( src/core/ext/filters/client_channel/client_channel.h )
   s.files += %w( src/core/ext/filters/client_channel/client_channel_factory.h )
@@ -234,6 +259,7 @@
   s.files += %w( src/core/ext/filters/client_channel/lb_policy.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy_factory.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.h )
+  s.files += %w( src/core/ext/filters/client_channel/method_params.h )
   s.files += %w( src/core/ext/filters/client_channel/parse_address.h )
   s.files += %w( src/core/ext/filters/client_channel/proxy_mapper.h )
   s.files += %w( src/core/ext/filters/client_channel/proxy_mapper_registry.h )
@@ -241,11 +267,17 @@
   s.files += %w( src/core/ext/filters/client_channel/resolver_factory.h )
   s.files += %w( src/core/ext/filters/client_channel/resolver_registry.h )
   s.files += %w( src/core/ext/filters/client_channel/retry_throttle.h )
+  s.files += %w( src/core/ext/filters/client_channel/status_util.h )
   s.files += %w( src/core/ext/filters/client_channel/subchannel.h )
   s.files += %w( src/core/ext/filters/client_channel/subchannel_index.h )
   s.files += %w( src/core/ext/filters/client_channel/uri_parser.h )
   s.files += %w( src/core/ext/filters/deadline/deadline_filter.h )
-  s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.h )
+  s.files += %w( src/core/tsi/alts_transport_security.h )
+  s.files += %w( src/core/tsi/fake_transport_security.h )
+  s.files += %w( src/core/tsi/ssl_transport_security.h )
+  s.files += %w( src/core/tsi/ssl_types.h )
+  s.files += %w( src/core/tsi/transport_security_grpc.h )
+  s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.h )
   s.files += %w( src/core/ext/transport/inproc/inproc_transport.h )
   s.files += %w( src/core/lib/avl/avl.h )
   s.files += %w( src/core/lib/backoff/backoff.h )
@@ -345,6 +377,7 @@
   s.files += %w( src/core/lib/slice/slice_hash_table.h )
   s.files += %w( src/core/lib/slice/slice_internal.h )
   s.files += %w( src/core/lib/slice/slice_string_helpers.h )
+  s.files += %w( src/core/lib/slice/slice_weak_hash_table.h )
   s.files += %w( src/core/lib/surface/api_trace.h )
   s.files += %w( src/core/lib/surface/call.h )
   s.files += %w( src/core/lib/surface/call_test_only.h )
@@ -369,20 +402,16 @@
   s.files += %w( src/core/lib/transport/service_config.h )
   s.files += %w( src/core/lib/transport/static_metadata.h )
   s.files += %w( src/core/lib/transport/status_conversion.h )
+  s.files += %w( src/core/lib/transport/status_metadata.h )
   s.files += %w( src/core/lib/transport/timeout_encoding.h )
   s.files += %w( src/core/lib/transport/transport.h )
   s.files += %w( src/core/lib/transport/transport_impl.h )
   s.files += %w( src/core/lib/debug/trace.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h )
-  s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h )
-  s.files += %w( third_party/nanopb/pb.h )
-  s.files += %w( third_party/nanopb/pb_common.h )
-  s.files += %w( third_party/nanopb/pb_decode.h )
-  s.files += %w( third_party/nanopb/pb_encode.h )
   s.files += %w( src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/subchannel_list.h )
   s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h )
@@ -494,7 +523,6 @@
   s.files += %w( src/core/lib/slice/percent_encoding.cc )
   s.files += %w( src/core/lib/slice/slice.cc )
   s.files += %w( src/core/lib/slice/slice_buffer.cc )
-  s.files += %w( src/core/lib/slice/slice_hash_table.cc )
   s.files += %w( src/core/lib/slice/slice_intern.cc )
   s.files += %w( src/core/lib/slice/slice_string_helpers.cc )
   s.files += %w( src/core/lib/surface/api_trace.cc )
@@ -525,6 +553,7 @@
   s.files += %w( src/core/lib/transport/service_config.cc )
   s.files += %w( src/core/lib/transport/static_metadata.cc )
   s.files += %w( src/core/lib/transport/status_conversion.cc )
+  s.files += %w( src/core/lib/transport/status_metadata.cc )
   s.files += %w( src/core/lib/transport/timeout_encoding.cc )
   s.files += %w( src/core/lib/transport/transport.cc )
   s.files += %w( src/core/lib/transport/transport_op_string.cc )
@@ -559,6 +588,7 @@
   s.files += %w( src/core/ext/filters/http/server/http_server_filter.cc )
   s.files += %w( src/core/lib/http/httpcli_security_connector.cc )
   s.files += %w( src/core/lib/security/context/security_context.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/alts_credentials.cc )
   s.files += %w( src/core/lib/security/credentials/composite/composite_credentials.cc )
   s.files += %w( src/core/lib/security/credentials/credentials.cc )
   s.files += %w( src/core/lib/security/credentials/credentials_metadata.cc )
@@ -572,23 +602,55 @@
   s.files += %w( src/core/lib/security/credentials/oauth2/oauth2_credentials.cc )
   s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.cc )
   s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.cc )
+  s.files += %w( src/core/lib/security/security_connector/alts_security_connector.cc )
+  s.files += %w( src/core/lib/security/security_connector/security_connector.cc )
   s.files += %w( src/core/lib/security/transport/client_auth_filter.cc )
-  s.files += %w( src/core/lib/security/transport/lb_targets_info.cc )
   s.files += %w( src/core/lib/security/transport/secure_endpoint.cc )
-  s.files += %w( src/core/lib/security/transport/security_connector.cc )
   s.files += %w( src/core/lib/security/transport/security_handshaker.cc )
   s.files += %w( src/core/lib/security/transport/server_auth_filter.cc )
+  s.files += %w( src/core/lib/security/transport/target_authority_table.cc )
   s.files += %w( src/core/lib/security/transport/tsi_error.cc )
   s.files += %w( src/core/lib/security/util/json_util.cc )
   s.files += %w( src/core/lib/surface/init_secure.cc )
-  s.files += %w( src/core/tsi/alts_transport_security.cc )
-  s.files += %w( src/core/tsi/fake_transport_security.cc )
-  s.files += %w( src/core/tsi/ssl_transport_security.cc )
-  s.files += %w( src/core/tsi/transport_security_grpc.cc )
+  s.files += %w( src/core/tsi/alts/crypt/aes_gcm.cc )
+  s.files += %w( src/core/tsi/alts/crypt/gsec.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_counter.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_crypter.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_frame_protector.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc )
+  s.files += %w( src/core/tsi/alts/frame_protector/frame_handler.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_client.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_event.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc )
+  s.files += %w( src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/check_gcp_environment.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc )
+  s.files += %w( src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/alts_tsi_utils.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/transport_security_common_api.cc )
+  s.files += %w( src/core/tsi/alts/handshaker/altscontext.pb.c )
+  s.files += %w( src/core/tsi/alts/handshaker/handshaker.pb.c )
+  s.files += %w( src/core/tsi/alts/handshaker/transport_security_common.pb.c )
+  s.files += %w( third_party/nanopb/pb_common.c )
+  s.files += %w( third_party/nanopb/pb_decode.c )
+  s.files += %w( third_party/nanopb/pb_encode.c )
   s.files += %w( src/core/tsi/transport_security.cc )
   s.files += %w( src/core/tsi/transport_security_adapter.cc )
-  s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.cc )
-  s.files += %w( src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc )
+  s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.cc )
+  s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc )
+  s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.cc )
   s.files += %w( src/core/ext/filters/client_channel/backup_poller.cc )
   s.files += %w( src/core/ext/filters/client_channel/channel_connectivity.cc )
   s.files += %w( src/core/ext/filters/client_channel/client_channel.cc )
@@ -600,21 +662,26 @@
   s.files += %w( src/core/ext/filters/client_channel/lb_policy.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy_factory.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.cc )
+  s.files += %w( src/core/ext/filters/client_channel/method_params.cc )
   s.files += %w( src/core/ext/filters/client_channel/parse_address.cc )
   s.files += %w( src/core/ext/filters/client_channel/proxy_mapper.cc )
   s.files += %w( src/core/ext/filters/client_channel/proxy_mapper_registry.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver.cc )
   s.files += %w( src/core/ext/filters/client_channel/resolver_registry.cc )
   s.files += %w( src/core/ext/filters/client_channel/retry_throttle.cc )
+  s.files += %w( src/core/ext/filters/client_channel/status_util.cc )
   s.files += %w( src/core/ext/filters/client_channel/subchannel.cc )
   s.files += %w( src/core/ext/filters/client_channel/subchannel_index.cc )
   s.files += %w( src/core/ext/filters/client_channel/uri_parser.cc )
   s.files += %w( src/core/ext/filters/deadline/deadline_filter.cc )
-  s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.cc )
+  s.files += %w( src/core/tsi/alts_transport_security.cc )
+  s.files += %w( src/core/tsi/fake_transport_security.cc )
+  s.files += %w( src/core/tsi/ssl_transport_security.cc )
+  s.files += %w( src/core/tsi/transport_security_grpc.cc )
+  s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.cc )
+  s.files += %w( src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc )
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc )
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc )
-  s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.cc )
-  s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc )
   s.files += %w( src/core/ext/transport/inproc/inproc_plugin.cc )
   s.files += %w( src/core/ext/transport/inproc/inproc_transport.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc )
@@ -623,9 +690,6 @@
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c )
-  s.files += %w( third_party/nanopb/pb_common.c )
-  s.files += %w( third_party/nanopb/pb_decode.c )
-  s.files += %w( third_party/nanopb/pb_encode.c )
   s.files += %w( src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc )
@@ -653,20 +717,80 @@
   s.files += %w( third_party/boringssl/crypto/curve25519/internal.h )
   s.files += %w( third_party/boringssl/crypto/err/internal.h )
   s.files += %w( third_party/boringssl/crypto/evp/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/aes/aes.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/aes/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/aes/key_wrap.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/aes/mode_wrappers.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/add.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/asm/x86_64-gcc.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/bn.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/bytes.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/cmp.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/ctx.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/div.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/exponentiation.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/gcd.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/generic.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/jacobi.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/montgomery.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/montgomery_inv.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/mul.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/prime.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/random.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/rsaz_exp.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/rsaz_exp.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/shift.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/bn/sqrt.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/cipher/aead.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/cipher/cipher.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/cipher/e_aes.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/cipher/e_des.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/cipher/internal.h )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/delocate.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/des/des.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/des/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/digest/digest.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/digest/digests.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/digest/internal.h )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/digest/md32_common.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/ec.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/ec_key.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/ec_montgomery.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/oct.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/p224-64.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/p256-64.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64-table.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/simple.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/util-64.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ec/wnaf.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/ecdsa/ecdsa.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/hmac/hmac.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/md4/md4.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/md5/md5.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/cbc.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/cfb.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/ctr.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/gcm.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/ofb.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/modes/polyval.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rand/ctrdrbg.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/rand/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rand/rand.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rand/urandom.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rsa/blinding.c )
   s.files += %w( third_party/boringssl/crypto/fipsmodule/rsa/internal.h )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rsa/padding.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rsa/rsa.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/rsa/rsa_impl.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/sha/sha1-altivec.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/sha/sha1.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/sha/sha256.c )
+  s.files += %w( third_party/boringssl/crypto/fipsmodule/sha/sha512.c )
   s.files += %w( third_party/boringssl/crypto/internal.h )
   s.files += %w( third_party/boringssl/crypto/obj/obj_dat.h )
   s.files += %w( third_party/boringssl/crypto/pkcs7/internal.h )
diff --git a/grpc.gyp b/grpc.gyp
index 44bc7e8..38597a5 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -64,11 +64,11 @@
     ],
     'cflags_c': [
       '-Werror',
-      '-std=c99'
+      '-std=c99',
     ],
     'cflags_cc': [
       '-Werror',
-      '-std=c++11'
+      '-std=c++11',
     ],
     'include_dirs': [
       '.',
@@ -148,7 +148,7 @@
             '-Wno-deprecated-declarations',
             '-stdlib=libc++',
             '-std=c++11',
-            '-Wno-error=deprecated-declarations'
+            '-Wno-error=deprecated-declarations',
           ],
         },
       }]
@@ -156,6 +156,17 @@
   },
   'targets': [
     {
+      'target_name': 'alts_test_util',
+      'type': 'static_library',
+      'dependencies': [
+        'grpc',
+      ],
+      'sources': [
+        'test/core/tsi/alts/crypt/gsec_test_util.cc',
+        'test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc',
+      ],
+    },
+    {
       'target_name': 'gpr',
       'type': 'static_library',
       'dependencies': [
@@ -187,9 +198,6 @@
         'src/core/lib/gpr/sync.cc',
         'src/core/lib/gpr/sync_posix.cc',
         'src/core/lib/gpr/sync_windows.cc',
-        'src/core/lib/gpr/thd.cc',
-        'src/core/lib/gpr/thd_posix.cc',
-        'src/core/lib/gpr/thd_windows.cc',
         'src/core/lib/gpr/time.cc',
         'src/core/lib/gpr/time_posix.cc',
         'src/core/lib/gpr/time_precise.cc',
@@ -199,6 +207,8 @@
         'src/core/lib/gpr/tmpfile_posix.cc',
         'src/core/lib/gpr/tmpfile_windows.cc',
         'src/core/lib/gpr/wrap_memcpy.cc',
+        'src/core/lib/gprpp/thd_posix.cc',
+        'src/core/lib/gprpp/thd_windows.cc',
         'src/core/lib/profiling/basic_timers.cc',
         'src/core/lib/profiling/stap_timers.cc',
       ],
@@ -321,7 +331,6 @@
         'src/core/lib/slice/percent_encoding.cc',
         'src/core/lib/slice/slice.cc',
         'src/core/lib/slice/slice_buffer.cc',
-        'src/core/lib/slice/slice_hash_table.cc',
         'src/core/lib/slice/slice_intern.cc',
         'src/core/lib/slice/slice_string_helpers.cc',
         'src/core/lib/surface/api_trace.cc',
@@ -352,6 +361,7 @@
         'src/core/lib/transport/service_config.cc',
         'src/core/lib/transport/static_metadata.cc',
         'src/core/lib/transport/status_conversion.cc',
+        'src/core/lib/transport/status_metadata.cc',
         'src/core/lib/transport/timeout_encoding.cc',
         'src/core/lib/transport/transport.cc',
         'src/core/lib/transport/transport_op_string.cc',
@@ -386,6 +396,7 @@
         'src/core/ext/filters/http/server/http_server_filter.cc',
         'src/core/lib/http/httpcli_security_connector.cc',
         'src/core/lib/security/context/security_context.cc',
+        'src/core/lib/security/credentials/alts/alts_credentials.cc',
         'src/core/lib/security/credentials/composite/composite_credentials.cc',
         'src/core/lib/security/credentials/credentials.cc',
         'src/core/lib/security/credentials/credentials_metadata.cc',
@@ -399,23 +410,55 @@
         'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc',
         'src/core/lib/security/credentials/plugin/plugin_credentials.cc',
         'src/core/lib/security/credentials/ssl/ssl_credentials.cc',
+        'src/core/lib/security/security_connector/alts_security_connector.cc',
+        'src/core/lib/security/security_connector/security_connector.cc',
         'src/core/lib/security/transport/client_auth_filter.cc',
-        'src/core/lib/security/transport/lb_targets_info.cc',
         'src/core/lib/security/transport/secure_endpoint.cc',
-        'src/core/lib/security/transport/security_connector.cc',
         'src/core/lib/security/transport/security_handshaker.cc',
         'src/core/lib/security/transport/server_auth_filter.cc',
+        'src/core/lib/security/transport/target_authority_table.cc',
         'src/core/lib/security/transport/tsi_error.cc',
         'src/core/lib/security/util/json_util.cc',
         'src/core/lib/surface/init_secure.cc',
-        'src/core/tsi/alts_transport_security.cc',
-        'src/core/tsi/fake_transport_security.cc',
-        'src/core/tsi/ssl_transport_security.cc',
-        'src/core/tsi/transport_security_grpc.cc',
+        'src/core/tsi/alts/crypt/aes_gcm.cc',
+        'src/core/tsi/alts/crypt/gsec.cc',
+        'src/core/tsi/alts/frame_protector/alts_counter.cc',
+        'src/core/tsi/alts/frame_protector/alts_crypter.cc',
+        'src/core/tsi/alts/frame_protector/alts_frame_protector.cc',
+        'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc',
+        'src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc',
+        'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc',
+        'src/core/tsi/alts/frame_protector/frame_handler.cc',
+        'src/core/tsi/alts/handshaker/alts_handshaker_client.cc',
+        'src/core/tsi/alts/handshaker/alts_tsi_event.cc',
+        'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc',
+        'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc',
+        'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc',
+        'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc',
+        'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc',
+        'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc',
+        'src/core/lib/security/credentials/alts/check_gcp_environment.cc',
+        'src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc',
+        'src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc',
+        'src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc',
+        'src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc',
+        'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc',
+        'src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc',
+        'src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc',
+        'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc',
+        'src/core/tsi/alts/handshaker/alts_tsi_utils.cc',
+        'src/core/tsi/alts/handshaker/transport_security_common_api.cc',
+        'src/core/tsi/alts/handshaker/altscontext.pb.c',
+        'src/core/tsi/alts/handshaker/handshaker.pb.c',
+        'src/core/tsi/alts/handshaker/transport_security_common.pb.c',
+        'third_party/nanopb/pb_common.c',
+        'third_party/nanopb/pb_decode.c',
+        'third_party/nanopb/pb_encode.c',
         'src/core/tsi/transport_security.cc',
         'src/core/tsi/transport_security_adapter.cc',
-        'src/core/ext/transport/chttp2/server/chttp2_server.cc',
-        'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc',
+        'src/core/ext/transport/chttp2/client/insecure/channel_create.cc',
+        'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc',
+        'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
         'src/core/ext/filters/client_channel/backup_poller.cc',
         'src/core/ext/filters/client_channel/channel_connectivity.cc',
         'src/core/ext/filters/client_channel/client_channel.cc',
@@ -427,21 +470,26 @@
         'src/core/ext/filters/client_channel/lb_policy.cc',
         'src/core/ext/filters/client_channel/lb_policy_factory.cc',
         'src/core/ext/filters/client_channel/lb_policy_registry.cc',
+        'src/core/ext/filters/client_channel/method_params.cc',
         'src/core/ext/filters/client_channel/parse_address.cc',
         'src/core/ext/filters/client_channel/proxy_mapper.cc',
         'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
         'src/core/ext/filters/client_channel/resolver.cc',
         'src/core/ext/filters/client_channel/resolver_registry.cc',
         'src/core/ext/filters/client_channel/retry_throttle.cc',
+        'src/core/ext/filters/client_channel/status_util.cc',
         'src/core/ext/filters/client_channel/subchannel.cc',
         'src/core/ext/filters/client_channel/subchannel_index.cc',
         'src/core/ext/filters/client_channel/uri_parser.cc',
         'src/core/ext/filters/deadline/deadline_filter.cc',
-        'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
+        'src/core/tsi/alts_transport_security.cc',
+        'src/core/tsi/fake_transport_security.cc',
+        'src/core/tsi/ssl_transport_security.cc',
+        'src/core/tsi/transport_security_grpc.cc',
+        'src/core/ext/transport/chttp2/server/chttp2_server.cc',
+        'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc',
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc',
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc',
-        'src/core/ext/transport/chttp2/client/insecure/channel_create.cc',
-        'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc',
         'src/core/ext/transport/inproc/inproc_plugin.cc',
         'src/core/ext/transport/inproc/inproc_transport.cc',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc',
@@ -450,9 +498,6 @@
         'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
-        'third_party/nanopb/pb_common.c',
-        'third_party/nanopb/pb_decode.c',
-        'third_party/nanopb/pb_encode.c',
         'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc',
         'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc',
         'src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc',
@@ -618,7 +663,6 @@
         'src/core/lib/slice/percent_encoding.cc',
         'src/core/lib/slice/slice.cc',
         'src/core/lib/slice/slice_buffer.cc',
-        'src/core/lib/slice/slice_hash_table.cc',
         'src/core/lib/slice/slice_intern.cc',
         'src/core/lib/slice/slice_string_helpers.cc',
         'src/core/lib/surface/api_trace.cc',
@@ -649,6 +693,7 @@
         'src/core/lib/transport/service_config.cc',
         'src/core/lib/transport/static_metadata.cc',
         'src/core/lib/transport/status_conversion.cc',
+        'src/core/lib/transport/status_metadata.cc',
         'src/core/lib/transport/timeout_encoding.cc',
         'src/core/lib/transport/transport.cc',
         'src/core/lib/transport/transport_op_string.cc',
@@ -664,12 +709,14 @@
         'src/core/ext/filters/client_channel/lb_policy.cc',
         'src/core/ext/filters/client_channel/lb_policy_factory.cc',
         'src/core/ext/filters/client_channel/lb_policy_registry.cc',
+        'src/core/ext/filters/client_channel/method_params.cc',
         'src/core/ext/filters/client_channel/parse_address.cc',
         'src/core/ext/filters/client_channel/proxy_mapper.cc',
         'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
         'src/core/ext/filters/client_channel/resolver.cc',
         'src/core/ext/filters/client_channel/resolver_registry.cc',
         'src/core/ext/filters/client_channel/retry_throttle.cc',
+        'src/core/ext/filters/client_channel/status_util.cc',
         'src/core/ext/filters/client_channel/subchannel.cc',
         'src/core/ext/filters/client_channel/subchannel_index.cc',
         'src/core/ext/filters/client_channel/uri_parser.cc',
@@ -833,7 +880,6 @@
         'src/core/lib/slice/percent_encoding.cc',
         'src/core/lib/slice/slice.cc',
         'src/core/lib/slice/slice_buffer.cc',
-        'src/core/lib/slice/slice_hash_table.cc',
         'src/core/lib/slice/slice_intern.cc',
         'src/core/lib/slice/slice_string_helpers.cc',
         'src/core/lib/surface/api_trace.cc',
@@ -864,6 +910,7 @@
         'src/core/lib/transport/service_config.cc',
         'src/core/lib/transport/static_metadata.cc',
         'src/core/lib/transport/status_conversion.cc',
+        'src/core/lib/transport/status_metadata.cc',
         'src/core/lib/transport/timeout_encoding.cc',
         'src/core/lib/transport/transport.cc',
         'src/core/lib/transport/transport_op_string.cc',
@@ -879,12 +926,14 @@
         'src/core/ext/filters/client_channel/lb_policy.cc',
         'src/core/ext/filters/client_channel/lb_policy_factory.cc',
         'src/core/ext/filters/client_channel/lb_policy_registry.cc',
+        'src/core/ext/filters/client_channel/method_params.cc',
         'src/core/ext/filters/client_channel/parse_address.cc',
         'src/core/ext/filters/client_channel/proxy_mapper.cc',
         'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
         'src/core/ext/filters/client_channel/resolver.cc',
         'src/core/ext/filters/client_channel/resolver_registry.cc',
         'src/core/ext/filters/client_channel/retry_throttle.cc',
+        'src/core/ext/filters/client_channel/status_util.cc',
         'src/core/ext/filters/client_channel/subchannel.cc',
         'src/core/ext/filters/client_channel/subchannel_index.cc',
         'src/core/ext/filters/client_channel/uri_parser.cc',
@@ -1027,7 +1076,6 @@
         'src/core/lib/slice/percent_encoding.cc',
         'src/core/lib/slice/slice.cc',
         'src/core/lib/slice/slice_buffer.cc',
-        'src/core/lib/slice/slice_hash_table.cc',
         'src/core/lib/slice/slice_intern.cc',
         'src/core/lib/slice/slice_string_helpers.cc',
         'src/core/lib/surface/api_trace.cc',
@@ -1058,6 +1106,7 @@
         'src/core/lib/transport/service_config.cc',
         'src/core/lib/transport/static_metadata.cc',
         'src/core/lib/transport/status_conversion.cc',
+        'src/core/lib/transport/status_metadata.cc',
         'src/core/lib/transport/timeout_encoding.cc',
         'src/core/lib/transport/transport.cc',
         'src/core/lib/transport/transport_op_string.cc',
@@ -1106,12 +1155,14 @@
         'src/core/ext/filters/client_channel/lb_policy.cc',
         'src/core/ext/filters/client_channel/lb_policy_factory.cc',
         'src/core/ext/filters/client_channel/lb_policy_registry.cc',
+        'src/core/ext/filters/client_channel/method_params.cc',
         'src/core/ext/filters/client_channel/parse_address.cc',
         'src/core/ext/filters/client_channel/proxy_mapper.cc',
         'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
         'src/core/ext/filters/client_channel/resolver.cc',
         'src/core/ext/filters/client_channel/resolver_registry.cc',
         'src/core/ext/filters/client_channel/retry_throttle.cc',
+        'src/core/ext/filters/client_channel/status_util.cc',
         'src/core/ext/filters/client_channel/subchannel.cc',
         'src/core/ext/filters/client_channel/subchannel_index.cc',
         'src/core/ext/filters/client_channel/uri_parser.cc',
@@ -2482,6 +2533,21 @@
         'test/core/end2end/tests/request_with_flags.cc',
         'test/core/end2end/tests/request_with_payload.cc',
         'test/core/end2end/tests/resource_quota_server.cc',
+        'test/core/end2end/tests/retry.cc',
+        'test/core/end2end/tests/retry_cancellation.cc',
+        'test/core/end2end/tests/retry_disabled.cc',
+        'test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc',
+        'test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc',
+        'test/core/end2end/tests/retry_non_retriable_status.cc',
+        'test/core/end2end/tests/retry_recv_initial_metadata.cc',
+        'test/core/end2end/tests/retry_recv_message.cc',
+        'test/core/end2end/tests/retry_server_pushback_delay.cc',
+        'test/core/end2end/tests/retry_server_pushback_disabled.cc',
+        'test/core/end2end/tests/retry_streaming.cc',
+        'test/core/end2end/tests/retry_streaming_after_commit.cc',
+        'test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc',
+        'test/core/end2end/tests/retry_throttled.cc',
+        'test/core/end2end/tests/retry_too_many_attempts.cc',
         'test/core/end2end/tests/server_finishes_request.cc',
         'test/core/end2end/tests/shutdown_finishes_calls.cc',
         'test/core/end2end/tests/shutdown_finishes_tags.cc',
@@ -2555,6 +2621,21 @@
         'test/core/end2end/tests/request_with_flags.cc',
         'test/core/end2end/tests/request_with_payload.cc',
         'test/core/end2end/tests/resource_quota_server.cc',
+        'test/core/end2end/tests/retry.cc',
+        'test/core/end2end/tests/retry_cancellation.cc',
+        'test/core/end2end/tests/retry_disabled.cc',
+        'test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc',
+        'test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc',
+        'test/core/end2end/tests/retry_non_retriable_status.cc',
+        'test/core/end2end/tests/retry_recv_initial_metadata.cc',
+        'test/core/end2end/tests/retry_recv_message.cc',
+        'test/core/end2end/tests/retry_server_pushback_delay.cc',
+        'test/core/end2end/tests/retry_server_pushback_disabled.cc',
+        'test/core/end2end/tests/retry_streaming.cc',
+        'test/core/end2end/tests/retry_streaming_after_commit.cc',
+        'test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc',
+        'test/core/end2end/tests/retry_throttled.cc',
+        'test/core/end2end/tests/retry_too_many_attempts.cc',
         'test/core/end2end/tests/server_finishes_request.cc',
         'test/core/end2end/tests/shutdown_finishes_calls.cc',
         'test/core/end2end/tests/shutdown_finishes_tags.cc',
diff --git a/include/grpc++/alarm.h b/include/grpc++/alarm.h
index 37d4189..dce742e 100644
--- a/include/grpc++/alarm.h
+++ b/include/grpc++/alarm.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,72 +16,13 @@
  *
  */
 
-/// An Alarm posts the user provided tag to its associated completion queue upon
-/// expiry or cancellation.
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_ALARM_H
 #define GRPCXX_ALARM_H
 
-#include <grpc++/impl/codegen/completion_queue.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/codegen/grpc_library.h>
-#include <grpc++/impl/codegen/time.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc/grpc.h>
-
-namespace grpc {
-
-/// A thin wrapper around \a grpc_alarm (see / \a / src/core/surface/alarm.h).
-class Alarm : private GrpcLibraryCodegen {
- public:
-  /// Create an unset completion queue alarm
-  Alarm();
-
-  /// Destroy the given completion queue alarm, cancelling it in the process.
-  ~Alarm();
-
-  /// DEPRECATED: Create and set a completion queue alarm instance associated to
-  /// \a cq.
-  /// This form is deprecated because it is inherently racy.
-  /// \internal We rely on the presence of \a cq for grpc initialization. If \a
-  /// cq were ever to be removed, a reference to a static
-  /// internal::GrpcLibraryInitializer instance would need to be introduced
-  /// here. \endinternal.
-  template <typename T>
-  Alarm(CompletionQueue* cq, const T& deadline, void* tag) : Alarm() {
-    SetInternal(cq, TimePoint<T>(deadline).raw_time(), tag);
-  }
-
-  /// Trigger an alarm instance on completion queue \a cq at the specified time.
-  /// Once the alarm expires (at \a deadline) or it's cancelled (see \a Cancel),
-  /// an event with tag \a tag will be added to \a cq. If the alarm expired, the
-  /// event's success bit will be true, false otherwise (ie, upon cancellation).
-  template <typename T>
-  void Set(CompletionQueue* cq, const T& deadline, void* tag) {
-    SetInternal(cq, TimePoint<T>(deadline).raw_time(), tag);
-  }
-
-  /// Alarms aren't copyable.
-  Alarm(const Alarm&) = delete;
-  Alarm& operator=(const Alarm&) = delete;
-
-  /// Alarms are movable.
-  Alarm(Alarm&& rhs) : alarm_(rhs.alarm_) { rhs.alarm_ = nullptr; }
-  Alarm& operator=(Alarm&& rhs) {
-    alarm_ = rhs.alarm_;
-    rhs.alarm_ = nullptr;
-    return *this;
-  }
-
-  /// Cancel a completion queue alarm. Calling this function over an alarm that
-  /// has already fired has no effect.
-  void Cancel();
-
- private:
-  void SetInternal(CompletionQueue* cq, gpr_timespec deadline, void* tag);
-
-  internal::CompletionQueueTag* alarm_;
-};
-
-}  // namespace grpc
+#include <grpcpp/alarm.h>
 
 #endif  // GRPCXX_ALARM_H
diff --git a/include/grpc++/channel.h b/include/grpc++/channel.h
index e9fb5a5..b1154ce 100644
--- a/include/grpc++/channel.h
+++ b/include/grpc++/channel.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,63 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_CHANNEL_H
 #define GRPCXX_CHANNEL_H
 
-#include <memory>
-
-#include <grpc++/impl/call.h>
-#include <grpc++/impl/codegen/channel_interface.h>
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/grpc_library.h>
-#include <grpc/grpc.h>
-
-struct grpc_channel;
-
-namespace grpc {
-/// Channels represent a connection to an endpoint. Created by \a CreateChannel.
-class Channel final : public ChannelInterface,
-                      public internal::CallHook,
-                      public std::enable_shared_from_this<Channel>,
-                      private GrpcLibraryCodegen {
- public:
-  ~Channel();
-
-  /// Get the current channel state. If the channel is in IDLE and
-  /// \a try_to_connect is set to true, try to connect.
-  grpc_connectivity_state GetState(bool try_to_connect) override;
-
-  /// Returns the LB policy name, or the empty string if not yet available.
-  grpc::string GetLoadBalancingPolicyName() const;
-
-  /// Returns the service config in JSON form, or the empty string if
-  /// not available.
-  grpc::string GetServiceConfigJSON() const;
-
- private:
-  template <class InputMessage, class OutputMessage>
-  friend class internal::BlockingUnaryCallImpl;
-  friend std::shared_ptr<Channel> CreateChannelInternal(
-      const grpc::string& host, grpc_channel* c_channel);
-  Channel(const grpc::string& host, grpc_channel* c_channel);
-
-  internal::Call CreateCall(const internal::RpcMethod& method,
-                            ClientContext* context,
-                            CompletionQueue* cq) override;
-  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                        internal::Call* call) override;
-  void* RegisterMethod(const char* method) override;
-
-  void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
-                               gpr_timespec deadline, CompletionQueue* cq,
-                               void* tag) override;
-  bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
-                              gpr_timespec deadline) override;
-
-  const grpc::string host_;
-  grpc_channel* const c_channel_;  // owned
-};
-
-}  // namespace grpc
+#include <grpcpp/channel.h>
 
 #endif  // GRPCXX_CHANNEL_H
diff --git a/include/grpc++/client_context.h b/include/grpc++/client_context.h
index cf42a62..4b23644 100644
--- a/include/grpc++/client_context.h
+++ b/include/grpc++/client_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,24 +16,13 @@
  *
  */
 
-/// A ClientContext allows the person implementing a service client to:
-///
-/// - Add custom metadata key-value pairs that will propagated to the server
-/// side.
-/// - Control call settings such as compression and authentication.
-/// - Initial and trailing metadata coming from the server.
-/// - Get performance metrics (ie, census).
-///
-/// Context settings are only relevant to the call they are invoked with, that
-/// is to say, they aren't sticky. Some of these settings, such as the
-/// compression options, can be made persistant at channel construction time
-/// (see \a grpc::CreateCustomChannel).
-///
-/// \warning ClientContext instances should \em not be reused across rpcs.
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
 
 #ifndef GRPCXX_CLIENT_CONTEXT_H
 #define GRPCXX_CLIENT_CONTEXT_H
 
-#include <grpc++/impl/codegen/client_context.h>
+#include <grpcpp/client_context.h>
 
 #endif  // GRPCXX_CLIENT_CONTEXT_H
diff --git a/include/grpc++/completion_queue.h b/include/grpc++/completion_queue.h
index a71123e..98ef18f 100644
--- a/include/grpc++/completion_queue.h
+++ b/include/grpc++/completion_queue.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_COMPLETION_QUEUE_H
 #define GRPCXX_COMPLETION_QUEUE_H
 
-#include <grpc++/impl/codegen/completion_queue.h>
+#include <grpcpp/completion_queue.h>
 
 #endif  // GRPCXX_COMPLETION_QUEUE_H
diff --git a/include/grpc++/create_channel.h b/include/grpc++/create_channel.h
index 7ba1131..d95f3a9 100644
--- a/include/grpc++/create_channel.h
+++ b/include/grpc++/create_channel.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,43 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_CREATE_CHANNEL_H
 #define GRPCXX_CREATE_CHANNEL_H
 
-#include <memory>
-
-#include <grpc++/channel.h>
-#include <grpc++/security/credentials.h>
-#include <grpc++/support/channel_arguments.h>
-#include <grpc++/support/config.h>
-
-namespace grpc {
-
-/// Create a new \a Channel pointing to \a target.
-///
-/// \param target The URI of the endpoint to connect to.
-/// \param creds Credentials to use for the created channel. If it does not
-/// hold an object or is invalid, a lame channel (one on which all operations
-/// fail) is returned.
-std::shared_ptr<Channel> CreateChannel(
-    const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds);
-
-/// Create a new \em custom \a Channel pointing to \a target.
-///
-/// \warning For advanced use and testing ONLY. Override default channel
-/// arguments only if necessary.
-///
-/// \param target The URI of the endpoint to connect to.
-/// \param creds Credentials to use for the created channel. If it does not
-/// hold an object or is invalid, a lame channel (one on which all operations
-/// fail) is returned.
-/// \param args Options for channel creation.
-std::shared_ptr<Channel> CreateCustomChannel(
-    const grpc::string& target,
-    const std::shared_ptr<ChannelCredentials>& creds,
-    const ChannelArguments& args);
-
-}  // namespace grpc
+#include <grpcpp/create_channel.h>
 
 #endif  // GRPCXX_CREATE_CHANNEL_H
diff --git a/include/grpc++/create_channel_posix.h b/include/grpc++/create_channel_posix.h
index 10f7e4a..8c8983b 100644
--- a/include/grpc++/create_channel_posix.h
+++ b/include/grpc++/create_channel_posix.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,37 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_CREATE_CHANNEL_POSIX_H
 #define GRPCXX_CREATE_CHANNEL_POSIX_H
 
-#include <memory>
-
-#include <grpc++/channel.h>
-#include <grpc++/support/channel_arguments.h>
-#include <grpc/support/port_platform.h>
-
-namespace grpc {
-
-#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
-
-/// Create a new \a Channel communicating over the given file descriptor.
-///
-/// \param target The name of the target.
-/// \param fd The file descriptor representing a socket.
-std::shared_ptr<Channel> CreateInsecureChannelFromFd(const grpc::string& target,
-                                                     int fd);
-
-/// Create a new \a Channel communicating over given file descriptor with custom
-/// channel arguments.
-///
-/// \param target The name of the target.
-/// \param fd The file descriptor representing a socket.
-/// \param args Options for channel creation.
-std::shared_ptr<Channel> CreateCustomInsecureChannelFromFd(
-    const grpc::string& target, int fd, const ChannelArguments& args);
-
-#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
-
-}  // namespace grpc
+#include <grpcpp/create_channel_posix.h>
 
 #endif  // GRPCXX_CREATE_CHANNEL_POSIX_H
diff --git a/include/grpc++/ext/health_check_service_server_builder_option.h b/include/grpc++/ext/health_check_service_server_builder_option.h
index 89af294..cb82fc0 100644
--- a/include/grpc++/ext/health_check_service_server_builder_option.h
+++ b/include/grpc++/ext/health_check_service_server_builder_option.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,32 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
 #define GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
 
-#include <memory>
-
-#include <grpc++/health_check_service_interface.h>
-#include <grpc++/impl/server_builder_option.h>
-#include <grpc++/support/config.h>
-
-namespace grpc {
-
-class HealthCheckServiceServerBuilderOption : public ServerBuilderOption {
- public:
-  /// The ownership of \a hc will be taken and transferred to the grpc server.
-  /// To explicitly disable default service, pass in a nullptr.
-  explicit HealthCheckServiceServerBuilderOption(
-      std::unique_ptr<HealthCheckServiceInterface> hc);
-  ~HealthCheckServiceServerBuilderOption() override {}
-  void UpdateArguments(ChannelArguments* args) override;
-  void UpdatePlugins(
-      std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override;
-
- private:
-  std::unique_ptr<HealthCheckServiceInterface> hc_;
-};
-
-}  // namespace grpc
+#include <grpcpp/ext/health_check_service_server_builder_option.h>
 
 #endif  // GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
diff --git a/include/grpc++/ext/proto_server_reflection_plugin.h b/include/grpc++/ext/proto_server_reflection_plugin.h
index ee3fafd..02e21b9 100644
--- a/include/grpc++/ext/proto_server_reflection_plugin.h
+++ b/include/grpc++/ext/proto_server_reflection_plugin.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,39 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
 #define GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
 
-#include <grpc++/impl/server_builder_plugin.h>
-#include <grpc++/support/config.h>
-
-namespace grpc {
-class ServerInitializer;
-class ProtoServerReflection;
-}  // namespace grpc
-
-namespace grpc {
-namespace reflection {
-
-class ProtoServerReflectionPlugin : public ::grpc::ServerBuilderPlugin {
- public:
-  ProtoServerReflectionPlugin();
-  ::grpc::string name() override;
-  void InitServer(::grpc::ServerInitializer* si) override;
-  void Finish(::grpc::ServerInitializer* si) override;
-  void ChangeArguments(const ::grpc::string& name, void* value) override;
-  bool has_async_methods() const override;
-  bool has_sync_methods() const override;
-
- private:
-  std::shared_ptr<grpc::ProtoServerReflection> reflection_service_;
-};
-
-/// Add proto reflection plugin to \a ServerBuilder.
-/// This function should be called at the static initialization time.
-void InitProtoReflectionServerBuilderPlugin();
-
-}  // namespace reflection
-}  // namespace grpc
+#include <grpcpp/ext/proto_server_reflection_plugin.h>
 
 #endif  // GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
diff --git a/include/grpc++/generic/async_generic_service.h b/include/grpc++/generic/async_generic_service.h
index b1ea4f3..d3283fa 100644
--- a/include/grpc++/generic/async_generic_service.h
+++ b/include/grpc++/generic/async_generic_service.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,63 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H
 #define GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H
 
-#include <grpc++/support/async_stream.h>
-#include <grpc++/support/byte_buffer.h>
-
-struct grpc_server;
-
-namespace grpc {
-
-typedef ServerAsyncReaderWriter<ByteBuffer, ByteBuffer>
-    GenericServerAsyncReaderWriter;
-
-class GenericServerContext final : public ServerContext {
- public:
-  const grpc::string& method() const { return method_; }
-  const grpc::string& host() const { return host_; }
-
- private:
-  friend class Server;
-  friend class ServerInterface;
-
-  grpc::string method_;
-  grpc::string host_;
-};
-
-// A generic service at the server side accepts all RPC methods and hosts. It is
-// typically used in proxies. The generic service can be registered to a server
-// which also has other services.
-// Sample usage:
-//   ServerBuilder builder;
-//   auto cq = builder.AddCompletionQueue();
-//   AsyncGenericService generic_service;
-//   builder.RegisterAsyncGeneicService(&generic_service);
-//   auto server = builder.BuildAndStart();
-//
-//   // request a new call
-//   GenericServerContext context;
-//   GenericAsyncReaderWriter stream;
-//   generic_service.RequestCall(&context, &stream, cq.get(), cq.get(), tag);
-//
-// When tag is retrieved from cq->Next(), context.method() can be used to look
-// at the method and the RPC can be handled accordingly.
-class AsyncGenericService final {
- public:
-  AsyncGenericService() : server_(nullptr) {}
-
-  void RequestCall(GenericServerContext* ctx,
-                   GenericServerAsyncReaderWriter* reader_writer,
-                   CompletionQueue* call_cq,
-                   ServerCompletionQueue* notification_cq, void* tag);
-
- private:
-  friend class Server;
-  Server* server_;
-};
-
-}  // namespace grpc
+#include <grpcpp/generic/async_generic_service.h>
 
 #endif  // GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H
diff --git a/include/grpc++/generic/generic_stub.h b/include/grpc++/generic/generic_stub.h
index e72826b..502953b 100644
--- a/include/grpc++/generic/generic_stub.h
+++ b/include/grpc++/generic/generic_stub.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,56 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_GENERIC_GENERIC_STUB_H
 #define GRPCXX_GENERIC_GENERIC_STUB_H
 
-#include <grpc++/support/async_stream.h>
-#include <grpc++/support/async_unary_call.h>
-#include <grpc++/support/byte_buffer.h>
-
-namespace grpc {
-
-class CompletionQueue;
-typedef ClientAsyncReaderWriter<ByteBuffer, ByteBuffer>
-    GenericClientAsyncReaderWriter;
-typedef ClientAsyncResponseReader<ByteBuffer> GenericClientAsyncResponseReader;
-
-/// Generic stubs provide a type-unsafe interface to call gRPC methods
-/// by name.
-class GenericStub final {
- public:
-  explicit GenericStub(std::shared_ptr<ChannelInterface> channel)
-      : channel_(channel) {}
-
-  /// Setup a call to a named method \a method using \a context, but don't
-  /// start it. Let it be started explicitly with StartCall and a tag.
-  /// The return value only indicates whether or not registration of the call
-  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
-  std::unique_ptr<GenericClientAsyncReaderWriter> PrepareCall(
-      ClientContext* context, const grpc::string& method, CompletionQueue* cq);
-
-  /// Setup a unary call to a named method \a method using \a context, and don't
-  /// start it. Let it be started explicitly with StartCall.
-  /// The return value only indicates whether or not registration of the call
-  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
-  std::unique_ptr<GenericClientAsyncResponseReader> PrepareUnaryCall(
-      ClientContext* context, const grpc::string& method,
-      const ByteBuffer& request, CompletionQueue* cq);
-
-  /// DEPRECATED for multi-threaded use
-  /// Begin a call to a named method \a method using \a context.
-  /// A tag \a tag will be delivered to \a cq when the call has been started
-  /// (i.e, initial metadata has been sent).
-  /// The return value only indicates whether or not registration of the call
-  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
-  std::unique_ptr<GenericClientAsyncReaderWriter> Call(
-      ClientContext* context, const grpc::string& method, CompletionQueue* cq,
-      void* tag);
-
- private:
-  std::shared_ptr<ChannelInterface> channel_;
-};
-
-}  // namespace grpc
+#include <grpcpp/generic/generic_stub.h>
 
 #endif  // GRPCXX_GENERIC_GENERIC_STUB_H
diff --git a/include/grpc++/grpc++.h b/include/grpc++/grpc++.h
index 31ed436..9f1d7b1 100644
--- a/include/grpc++/grpc++.h
+++ b/include/grpc++/grpc++.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,53 +16,13 @@
  *
  */
 
-/// \mainpage gRPC C++ API
-///
-/// The gRPC C++ API mainly consists of the following classes:
-/// <br>
-/// - grpc::Channel, which represents the connection to an endpoint. See [the
-/// gRPC Concepts page](https://grpc.io/docs/guides/concepts.html) for more
-/// details. Channels are created by the factory function grpc::CreateChannel.
-///
-/// - grpc::CompletionQueue, the producer-consumer queue used for all
-/// asynchronous communication with the gRPC runtime.
-///
-/// - grpc::ClientContext and grpc::ServerContext, where optional configuration
-/// for an RPC can be set, such as setting custom metadata to be conveyed to the
-/// peer, compression settings, authentication, etc.
-///
-/// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder.
-///
-/// Streaming calls are handled with the streaming classes in
-/// \ref sync_stream.h and
-/// \ref async_stream.h.
-///
-/// Refer to the
-/// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp)
-/// for code putting these pieces into play.
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
 
 #ifndef GRPCXX_GRPCXX_H
 #define GRPCXX_GRPCXX_H
 
-// Pragma for http://include-what-you-use.org/ tool, tells that following
-// headers are not private for grpc++.h and are part of its interface.
-// IWYU pragma: begin_exports
-#include <grpc/grpc.h>
-
-#include <grpc++/channel.h>
-#include <grpc++/client_context.h>
-#include <grpc++/completion_queue.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/create_channel_posix.h>
-#include <grpc++/server.h>
-#include <grpc++/server_builder.h>
-#include <grpc++/server_context.h>
-#include <grpc++/server_posix.h>
-// IWYU pragma: end_exports
-
-namespace grpc {
-/// Return gRPC library version.
-grpc::string Version();
-}  // namespace grpc
+#include <grpcpp/grpcpp.h>
 
 #endif  // GRPCXX_GRPCXX_H
diff --git a/include/grpc++/health_check_service_interface.h b/include/grpc++/health_check_service_interface.h
index 7d4d36a..0cb0668 100644
--- a/include/grpc++/health_check_service_interface.h
+++ b/include/grpc++/health_check_service_interface.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,39 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H
 #define GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H
 
-#include <grpc++/support/config.h>
-
-namespace grpc {
-
-const char kHealthCheckServiceInterfaceArg[] =
-    "grpc.health_check_service_interface";
-
-/// The gRPC server uses this interface to expose the health checking service
-/// without depending on protobuf.
-class HealthCheckServiceInterface {
- public:
-  virtual ~HealthCheckServiceInterface() {}
-
-  /// Set or change the serving status of the given \a service_name.
-  virtual void SetServingStatus(const grpc::string& service_name,
-                                bool serving) = 0;
-  /// Apply to all registered service names.
-  virtual void SetServingStatus(bool serving) = 0;
-};
-
-/// Enable/disable the default health checking service. This applies to all C++
-/// servers created afterwards. For each server, user can override the default
-/// with a HealthCheckServiceServerBuilderOption.
-/// NOT thread safe.
-void EnableDefaultHealthCheckService(bool enable);
-
-/// Returns whether the default health checking service is enabled.
-/// NOT thread safe.
-bool DefaultHealthCheckServiceEnabled();
-
-}  // namespace grpc
+#include <grpcpp/health_check_service_interface.h>
 
 #endif  // GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H
diff --git a/include/grpc++/impl/call.h b/include/grpc++/impl/call.h
index fceaa3b..b1da2b6 100644
--- a/include/grpc++/impl/call.h
+++ b/include/grpc++/impl/call.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CALL_H
 #define GRPCXX_IMPL_CALL_H
 
-#include <grpc++/impl/codegen/call.h>
+#include <grpcpp/impl/call.h>
 
 #endif  // GRPCXX_IMPL_CALL_H
diff --git a/include/grpc++/impl/channel_argument_option.h b/include/grpc++/impl/channel_argument_option.h
index f157ec1..3468378 100644
--- a/include/grpc++/impl/channel_argument_option.h
+++ b/include/grpc++/impl/channel_argument_option.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2017 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,22 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H
 #define GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H
 
-#include <map>
-#include <memory>
-
-#include <grpc++/impl/server_builder_option.h>
-#include <grpc++/support/channel_arguments.h>
-
-namespace grpc {
-
-std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
-    const grpc::string& name, const grpc::string& value);
-std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
-    const grpc::string& name, int value);
-
-}  // namespace grpc
+#include <grpcpp/impl/channel_argument_option.h>
 
 #endif  // GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H
diff --git a/include/grpc++/impl/client_unary_call.h b/include/grpc++/impl/client_unary_call.h
index d679727..75e6560 100644
--- a/include/grpc++/impl/client_unary_call.h
+++ b/include/grpc++/impl/client_unary_call.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CLIENT_UNARY_CALL_H
 #define GRPCXX_IMPL_CLIENT_UNARY_CALL_H
 
-#include <grpc++/impl/codegen/client_unary_call.h>
+#include <grpcpp/impl/client_unary_call.h>
 
 #endif  // GRPCXX_IMPL_CLIENT_UNARY_CALL_H
diff --git a/include/grpc++/impl/codegen/async_stream.h b/include/grpc++/impl/codegen/async_stream.h
index 4476033..a034470 100644
--- a/include/grpc++/impl/codegen/async_stream.h
+++ b/include/grpc++/impl/codegen/async_stream.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,1053 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H
 #define GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H
 
-#include <grpc++/impl/codegen/call.h>
-#include <grpc++/impl/codegen/channel_interface.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/server_context.h>
-#include <grpc++/impl/codegen/service_type.h>
-#include <grpc++/impl/codegen/status.h>
-
-namespace grpc {
-
-class CompletionQueue;
-
-namespace internal {
-/// Common interface for all client side asynchronous streaming.
-class ClientAsyncStreamingInterface {
- public:
-  virtual ~ClientAsyncStreamingInterface() {}
-
-  /// Start the call that was set up by the constructor, but only if the
-  /// constructor was invoked through the "Prepare" API which doesn't actually
-  /// start the call
-  virtual void StartCall(void* tag) = 0;
-
-  /// Request notification of the reading of the initial metadata. Completion
-  /// will be notified by \a tag on the associated completion queue.
-  /// This call is optional, but if it is used, it cannot be used concurrently
-  /// with or after the \a AsyncReaderInterface::Read method.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  virtual void ReadInitialMetadata(void* tag) = 0;
-
-  /// Indicate that the stream is to be finished and request notification for
-  /// when the call has been ended.
-  /// Should not be used concurrently with other operations.
-  ///
-  /// It is appropriate to call this method when both:
-  ///   * the client side has no more message to send
-  ///     (this can be declared implicitly by calling this method, or
-  ///     explicitly through an earlier call to the <i>WritesDone</i> method
-  ///     of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or
-  ///     \a ClientAsyncReaderWriterInterface::WritesDone).
-  ///   * there are no more messages to be received from the server (this can
-  ///     be known implicitly by the calling code, or explicitly from an
-  ///     earlier call to \a AsyncReaderInterface::Read that yielded a failed
-  ///     result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
-  ///
-  /// This function will return when either:
-  /// - all incoming messages have been read and the server has returned
-  ///   a status.
-  /// - the server has returned a non-OK status.
-  /// - the call failed for some reason and the library generated a
-  ///   status.
-  ///
-  /// Note that implementations of this method attempt to receive initial
-  /// metadata from the server if initial metadata hasn't yet been received.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[out] status To be updated with the operation status.
-  virtual void Finish(Status* status, void* tag) = 0;
-};
-
-/// An interface that yields a sequence of messages of type \a R.
-template <class R>
-class AsyncReaderInterface {
- public:
-  virtual ~AsyncReaderInterface() {}
-
-  /// Read a message of type \a R into \a msg. Completion will be notified by \a
-  /// tag on the associated completion queue.
-  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
-  /// should not be called concurrently with other streaming APIs
-  /// on the same stream. It is not meaningful to call it concurrently
-  /// with another \a AsyncReaderInterface::Read on the same stream since reads
-  /// on the same stream are delivered in order.
-  ///
-  /// \param[out] msg Where to eventually store the read message.
-  /// \param[in] tag The tag identifying the operation.
-  ///
-  /// Side effect: note that this method attempt to receive initial metadata for
-  /// a stream if it hasn't yet been received.
-  virtual void Read(R* msg, void* tag) = 0;
-};
-
-/// An interface that can be fed a sequence of messages of type \a W.
-template <class W>
-class AsyncWriterInterface {
- public:
-  virtual ~AsyncWriterInterface() {}
-
-  /// Request the writing of \a msg with identifying tag \a tag.
-  ///
-  /// Only one write may be outstanding at any given time. This means that
-  /// after calling Write, one must wait to receive \a tag from the completion
-  /// queue BEFORE calling Write again.
-  /// This is thread-safe with respect to \a AsyncReaderInterface::Read
-  ///
-  /// \param[in] msg The message to be written.
-  /// \param[in] tag The tag identifying the operation.
-  virtual void Write(const W& msg, void* tag) = 0;
-
-  /// Request the writing of \a msg using WriteOptions \a options with
-  /// identifying tag \a tag.
-  ///
-  /// Only one write may be outstanding at any given time. This means that
-  /// after calling Write, one must wait to receive \a tag from the completion
-  /// queue BEFORE calling Write again.
-  /// WriteOptions \a options is used to set the write options of this message.
-  /// This is thread-safe with respect to \a AsyncReaderInterface::Read
-  ///
-  /// \param[in] msg The message to be written.
-  /// \param[in] options The WriteOptions to be used to write this message.
-  /// \param[in] tag The tag identifying the operation.
-  virtual void Write(const W& msg, WriteOptions options, void* tag) = 0;
-
-  /// Request the writing of \a msg and coalesce it with the writing
-  /// of trailing metadata, using WriteOptions \a options with
-  /// identifying tag \a tag.
-  ///
-  /// For client, WriteLast is equivalent of performing Write and
-  /// WritesDone in a single step.
-  /// For server, WriteLast buffers the \a msg. The writing of \a msg is held
-  /// until Finish is called, where \a msg and trailing metadata are coalesced
-  /// and write is initiated. Note that WriteLast can only buffer \a msg up to
-  /// the flow control window size. If \a msg size is larger than the window
-  /// size, it will be sent on wire without buffering.
-  ///
-  /// \param[in] msg The message to be written.
-  /// \param[in] options The WriteOptions to be used to write this message.
-  /// \param[in] tag The tag identifying the operation.
-  void WriteLast(const W& msg, WriteOptions options, void* tag) {
-    Write(msg, options.set_last_message(), tag);
-  }
-};
-
-}  // namespace internal
-
-template <class R>
-class ClientAsyncReaderInterface
-    : public internal::ClientAsyncStreamingInterface,
-      public internal::AsyncReaderInterface<R> {};
-
-namespace internal {
-template <class R>
-class ClientAsyncReaderFactory {
- public:
-  /// Create a stream object.
-  /// Write the first request out if \a start is set.
-  /// \a tag will be notified on \a cq when the call has been started and
-  /// \a request has been written out. If \a start is not set, \a tag must be
-  /// nullptr and the actual call must be initiated by StartCall
-  /// Note that \a context will be used to fill in custom initial metadata
-  /// used to send to the server when starting the call.
-  template <class W>
-  static ClientAsyncReader<R>* Create(ChannelInterface* channel,
-                                      CompletionQueue* cq,
-                                      const ::grpc::internal::RpcMethod& method,
-                                      ClientContext* context, const W& request,
-                                      bool start, void* tag) {
-    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
-    return new (g_core_codegen_interface->grpc_call_arena_alloc(
-        call.call(), sizeof(ClientAsyncReader<R>)))
-        ClientAsyncReader<R>(call, context, request, start, tag);
-  }
-};
-}  // namespace internal
-
-/// Async client-side API for doing server-streaming RPCs,
-/// where the incoming message stream coming from the server has
-/// messages of type \a R.
-template <class R>
-class ClientAsyncReader final : public ClientAsyncReaderInterface<R> {
- public:
-  // always allocated against a call arena, no memory free required
-  static void operator delete(void* ptr, std::size_t size) {
-    assert(size == sizeof(ClientAsyncReader));
-  }
-
-  void StartCall(void* tag) override {
-    assert(!started_);
-    started_ = true;
-    StartCallInternal(tag);
-  }
-
-  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata
-  /// method for semantics.
-  ///
-  /// Side effect:
-  ///   - upon receiving initial metadata from the server,
-  ///     the \a ClientContext associated with this call is updated, and the
-  ///     calling code can access the received metadata through the
-  ///     \a ClientContext.
-  void ReadInitialMetadata(void* tag) override {
-    assert(started_);
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    meta_ops_.set_output_tag(tag);
-    meta_ops_.RecvInitialMetadata(context_);
-    call_.PerformOps(&meta_ops_);
-  }
-
-  void Read(R* msg, void* tag) override {
-    assert(started_);
-    read_ops_.set_output_tag(tag);
-    if (!context_->initial_metadata_received_) {
-      read_ops_.RecvInitialMetadata(context_);
-    }
-    read_ops_.RecvMessage(msg);
-    call_.PerformOps(&read_ops_);
-  }
-
-  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
-  ///
-  /// Side effect:
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible initial and trailing metadata received from the server.
-  void Finish(Status* status, void* tag) override {
-    assert(started_);
-    finish_ops_.set_output_tag(tag);
-    if (!context_->initial_metadata_received_) {
-      finish_ops_.RecvInitialMetadata(context_);
-    }
-    finish_ops_.ClientRecvStatus(context_, status);
-    call_.PerformOps(&finish_ops_);
-  }
-
- private:
-  friend class internal::ClientAsyncReaderFactory<R>;
-  template <class W>
-  ClientAsyncReader(::grpc::internal::Call call, ClientContext* context,
-                    const W& request, bool start, void* tag)
-      : context_(context), call_(call), started_(start) {
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(init_ops_.SendMessage(request).ok());
-    init_ops_.ClientSendClose();
-    if (start) {
-      StartCallInternal(tag);
-    } else {
-      assert(tag == nullptr);
-    }
-  }
-
-  void StartCallInternal(void* tag) {
-    init_ops_.SendInitialMetadata(context_->send_initial_metadata_,
-                                  context_->initial_metadata_flags());
-    init_ops_.set_output_tag(tag);
-    call_.PerformOps(&init_ops_);
-  }
-
-  ClientContext* context_;
-  ::grpc::internal::Call call_;
-  bool started_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpClientSendClose>
-      init_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-      meta_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpRecvMessage<R>>
-      read_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpClientRecvStatus>
-      finish_ops_;
-};
-
-/// Common interface for client side asynchronous writing.
-template <class W>
-class ClientAsyncWriterInterface
-    : public internal::ClientAsyncStreamingInterface,
-      public internal::AsyncWriterInterface<W> {
- public:
-  /// Signal the client is done with the writes (half-close the client stream).
-  /// Thread-safe with respect to \a AsyncReaderInterface::Read
-  ///
-  /// \param[in] tag The tag identifying the operation.
-  virtual void WritesDone(void* tag) = 0;
-};
-
-namespace internal {
-template <class W>
-class ClientAsyncWriterFactory {
- public:
-  /// Create a stream object.
-  /// Start the RPC if \a start is set
-  /// \a tag will be notified on \a cq when the call has been started (i.e.
-  /// intitial metadata sent) and \a request has been written out.
-  /// If \a start is not set, \a tag must be nullptr and the actual call
-  /// must be initiated by StartCall
-  /// Note that \a context will be used to fill in custom initial metadata
-  /// used to send to the server when starting the call.
-  /// \a response will be filled in with the single expected response
-  /// message from the server upon a successful call to the \a Finish
-  /// method of this instance.
-  template <class R>
-  static ClientAsyncWriter<W>* Create(ChannelInterface* channel,
-                                      CompletionQueue* cq,
-                                      const ::grpc::internal::RpcMethod& method,
-                                      ClientContext* context, R* response,
-                                      bool start, void* tag) {
-    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
-    return new (g_core_codegen_interface->grpc_call_arena_alloc(
-        call.call(), sizeof(ClientAsyncWriter<W>)))
-        ClientAsyncWriter<W>(call, context, response, start, tag);
-  }
-};
-}  // namespace internal
-
-/// Async API on the client side for doing client-streaming RPCs,
-/// where the outgoing message stream going to the server contains
-/// messages of type \a W.
-template <class W>
-class ClientAsyncWriter final : public ClientAsyncWriterInterface<W> {
- public:
-  // always allocated against a call arena, no memory free required
-  static void operator delete(void* ptr, std::size_t size) {
-    assert(size == sizeof(ClientAsyncWriter));
-  }
-
-  void StartCall(void* tag) override {
-    assert(!started_);
-    started_ = true;
-    StartCallInternal(tag);
-  }
-
-  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for
-  /// semantics.
-  ///
-  /// Side effect:
-  ///   - upon receiving initial metadata from the server, the \a ClientContext
-  ///     associated with this call is updated, and the calling code can access
-  ///     the received metadata through the \a ClientContext.
-  void ReadInitialMetadata(void* tag) override {
-    assert(started_);
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    meta_ops_.set_output_tag(tag);
-    meta_ops_.RecvInitialMetadata(context_);
-    call_.PerformOps(&meta_ops_);
-  }
-
-  void Write(const W& msg, void* tag) override {
-    assert(started_);
-    write_ops_.set_output_tag(tag);
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  void Write(const W& msg, WriteOptions options, void* tag) override {
-    assert(started_);
-    write_ops_.set_output_tag(tag);
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-      write_ops_.ClientSendClose();
-    }
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  void WritesDone(void* tag) override {
-    assert(started_);
-    write_ops_.set_output_tag(tag);
-    write_ops_.ClientSendClose();
-    call_.PerformOps(&write_ops_);
-  }
-
-  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
-  ///
-  /// Side effect:
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible initial and trailing metadata received from the server.
-  ///   - attempts to fill in the \a response parameter passed to this class's
-  ///     constructor with the server's response message.
-  void Finish(Status* status, void* tag) override {
-    assert(started_);
-    finish_ops_.set_output_tag(tag);
-    if (!context_->initial_metadata_received_) {
-      finish_ops_.RecvInitialMetadata(context_);
-    }
-    finish_ops_.ClientRecvStatus(context_, status);
-    call_.PerformOps(&finish_ops_);
-  }
-
- private:
-  friend class internal::ClientAsyncWriterFactory<W>;
-  template <class R>
-  ClientAsyncWriter(::grpc::internal::Call call, ClientContext* context,
-                    R* response, bool start, void* tag)
-      : context_(context), call_(call), started_(start) {
-    finish_ops_.RecvMessage(response);
-    finish_ops_.AllowNoMessage();
-    if (start) {
-      StartCallInternal(tag);
-    } else {
-      assert(tag == nullptr);
-    }
-  }
-
-  void StartCallInternal(void* tag) {
-    write_ops_.SendInitialMetadata(context_->send_initial_metadata_,
-                                   context_->initial_metadata_flags());
-    // if corked bit is set in context, we just keep the initial metadata
-    // buffered up to coalesce with later message send. No op is performed.
-    if (!context_->initial_metadata_corked_) {
-      write_ops_.set_output_tag(tag);
-      call_.PerformOps(&write_ops_);
-    }
-  }
-
-  ClientContext* context_;
-  ::grpc::internal::Call call_;
-  bool started_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-      meta_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpClientSendClose>
-      write_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpGenericRecvMessage,
-                              ::grpc::internal::CallOpClientRecvStatus>
-      finish_ops_;
-};
-
-/// Async client-side interface for bi-directional streaming,
-/// where the client-to-server message stream has messages of type \a W,
-/// and the server-to-client message stream has messages of type \a R.
-template <class W, class R>
-class ClientAsyncReaderWriterInterface
-    : public internal::ClientAsyncStreamingInterface,
-      public internal::AsyncWriterInterface<W>,
-      public internal::AsyncReaderInterface<R> {
- public:
-  /// Signal the client is done with the writes (half-close the client stream).
-  /// Thread-safe with respect to \a AsyncReaderInterface::Read
-  ///
-  /// \param[in] tag The tag identifying the operation.
-  virtual void WritesDone(void* tag) = 0;
-};
-
-namespace internal {
-template <class W, class R>
-class ClientAsyncReaderWriterFactory {
- public:
-  /// Create a stream object.
-  /// Start the RPC request if \a start is set.
-  /// \a tag will be notified on \a cq when the call has been started (i.e.
-  /// intitial metadata sent). If \a start is not set, \a tag must be
-  /// nullptr and the actual call must be initiated by StartCall
-  /// Note that \a context will be used to fill in custom initial metadata
-  /// used to send to the server when starting the call.
-  static ClientAsyncReaderWriter<W, R>* Create(
-      ChannelInterface* channel, CompletionQueue* cq,
-      const ::grpc::internal::RpcMethod& method, ClientContext* context,
-      bool start, void* tag) {
-    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
-
-    return new (g_core_codegen_interface->grpc_call_arena_alloc(
-        call.call(), sizeof(ClientAsyncReaderWriter<W, R>)))
-        ClientAsyncReaderWriter<W, R>(call, context, start, tag);
-  }
-};
-}  // namespace internal
-
-/// Async client-side interface for bi-directional streaming,
-/// where the outgoing message stream going to the server
-/// has messages of type \a W,  and the incoming message stream coming
-/// from the server has messages of type \a R.
-template <class W, class R>
-class ClientAsyncReaderWriter final
-    : public ClientAsyncReaderWriterInterface<W, R> {
- public:
-  // always allocated against a call arena, no memory free required
-  static void operator delete(void* ptr, std::size_t size) {
-    assert(size == sizeof(ClientAsyncReaderWriter));
-  }
-
-  void StartCall(void* tag) override {
-    assert(!started_);
-    started_ = true;
-    StartCallInternal(tag);
-  }
-
-  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method
-  /// for semantics of this method.
-  ///
-  /// Side effect:
-  ///   - upon receiving initial metadata from the server, the \a ClientContext
-  ///     is updated with it, and then the receiving initial metadata can
-  ///     be accessed through this \a ClientContext.
-  void ReadInitialMetadata(void* tag) override {
-    assert(started_);
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    meta_ops_.set_output_tag(tag);
-    meta_ops_.RecvInitialMetadata(context_);
-    call_.PerformOps(&meta_ops_);
-  }
-
-  void Read(R* msg, void* tag) override {
-    assert(started_);
-    read_ops_.set_output_tag(tag);
-    if (!context_->initial_metadata_received_) {
-      read_ops_.RecvInitialMetadata(context_);
-    }
-    read_ops_.RecvMessage(msg);
-    call_.PerformOps(&read_ops_);
-  }
-
-  void Write(const W& msg, void* tag) override {
-    assert(started_);
-    write_ops_.set_output_tag(tag);
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  void Write(const W& msg, WriteOptions options, void* tag) override {
-    assert(started_);
-    write_ops_.set_output_tag(tag);
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-      write_ops_.ClientSendClose();
-    }
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  void WritesDone(void* tag) override {
-    assert(started_);
-    write_ops_.set_output_tag(tag);
-    write_ops_.ClientSendClose();
-    call_.PerformOps(&write_ops_);
-  }
-
-  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
-  /// Side effect
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible initial and trailing metadata sent from the server.
-  void Finish(Status* status, void* tag) override {
-    assert(started_);
-    finish_ops_.set_output_tag(tag);
-    if (!context_->initial_metadata_received_) {
-      finish_ops_.RecvInitialMetadata(context_);
-    }
-    finish_ops_.ClientRecvStatus(context_, status);
-    call_.PerformOps(&finish_ops_);
-  }
-
- private:
-  friend class internal::ClientAsyncReaderWriterFactory<W, R>;
-  ClientAsyncReaderWriter(::grpc::internal::Call call, ClientContext* context,
-                          bool start, void* tag)
-      : context_(context), call_(call), started_(start) {
-    if (start) {
-      StartCallInternal(tag);
-    } else {
-      assert(tag == nullptr);
-    }
-  }
-
-  void StartCallInternal(void* tag) {
-    write_ops_.SendInitialMetadata(context_->send_initial_metadata_,
-                                   context_->initial_metadata_flags());
-    // if corked bit is set in context, we just keep the initial metadata
-    // buffered up to coalesce with later message send. No op is performed.
-    if (!context_->initial_metadata_corked_) {
-      write_ops_.set_output_tag(tag);
-      call_.PerformOps(&write_ops_);
-    }
-  }
-
-  ClientContext* context_;
-  ::grpc::internal::Call call_;
-  bool started_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-      meta_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpRecvMessage<R>>
-      read_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpClientSendClose>
-      write_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpClientRecvStatus>
-      finish_ops_;
-};
-
-template <class W, class R>
-class ServerAsyncReaderInterface
-    : public internal::ServerAsyncStreamingInterface,
-      public internal::AsyncReaderInterface<R> {
- public:
-  /// Indicate that the stream is to be finished with a certain status code
-  /// and also send out \a msg response to the client.
-  /// Request notification for when the server has sent the response and the
-  /// appropriate signals to the client to end the call.
-  /// Should not be used concurrently with other operations.
-  ///
-  /// It is appropriate to call this method when:
-  ///   * all messages from the client have been received (either known
-  ///     implictly, or explicitly because a previous
-  ///     \a AsyncReaderInterface::Read operation with a non-ok result,
-  ///     e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
-  ///
-  /// This operation will end when the server has finished sending out initial
-  /// metadata (if not sent already), response message, and status, or if
-  /// some failure occurred when trying to do so.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[in] status To be sent to the client as the result of this call.
-  /// \param[in] msg To be sent to the client as the response for this call.
-  virtual void Finish(const W& msg, const Status& status, void* tag) = 0;
-
-  /// Indicate that the stream is to be finished with a certain
-  /// non-OK status code.
-  /// Request notification for when the server has sent the appropriate
-  /// signals to the client to end the call.
-  /// Should not be used concurrently with other operations.
-  ///
-  /// This call is meant to end the call with some error, and can be called at
-  /// any point that the server would like to "fail" the call (though note
-  /// this shouldn't be called concurrently with any other "sending" call, like
-  /// \a AsyncWriterInterface::Write).
-  ///
-  /// This operation will end when the server has finished sending out initial
-  /// metadata (if not sent already), and status, or if some failure occurred
-  /// when trying to do so.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[in] status To be sent to the client as the result of this call.
-  ///     - Note: \a status must have a non-OK code.
-  virtual void FinishWithError(const Status& status, void* tag) = 0;
-};
-
-/// Async server-side API for doing client-streaming RPCs,
-/// where the incoming message stream from the client has messages of type \a R,
-/// and the single response message sent from the server is type \a W.
-template <class W, class R>
-class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
- public:
-  explicit ServerAsyncReader(ServerContext* ctx)
-      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
-
-  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - The initial metadata that will be sent to the client from this op will
-  ///     be taken from the \a ServerContext associated with the call.
-  void SendInitialMetadata(void* tag) override {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    meta_ops_.set_output_tag(tag);
-    meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                  ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      meta_ops_.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_.PerformOps(&meta_ops_);
-  }
-
-  void Read(R* msg, void* tag) override {
-    read_ops_.set_output_tag(tag);
-    read_ops_.RecvMessage(msg);
-    call_.PerformOps(&read_ops_);
-  }
-
-  /// See the \a ServerAsyncReaderInterface.Read method for semantics
-  ///
-  /// Side effect:
-  ///   - also sends initial metadata if not alreay sent.
-  ///   - uses the \a ServerContext associated with this call to send possible
-  ///     initial and trailing metadata.
-  ///
-  /// Note: \a msg is not sent if \a status has a non-OK code.
-  void Finish(const W& msg, const Status& status, void* tag) override {
-    finish_ops_.set_output_tag(tag);
-    if (!ctx_->sent_initial_metadata_) {
-      finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                      ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        finish_ops_.set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-    // The response is dropped if the status is not OK.
-    if (status.ok()) {
-      finish_ops_.ServerSendStatus(ctx_->trailing_metadata_,
-                                   finish_ops_.SendMessage(msg));
-    } else {
-      finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    }
-    call_.PerformOps(&finish_ops_);
-  }
-
-  /// See the \a ServerAsyncReaderInterface.Read method for semantics
-  ///
-  /// Side effect:
-  ///   - also sends initial metadata if not alreay sent.
-  ///   - uses the \a ServerContext associated with this call to send possible
-  ///     initial and trailing metadata.
-  void FinishWithError(const Status& status, void* tag) override {
-    GPR_CODEGEN_ASSERT(!status.ok());
-    finish_ops_.set_output_tag(tag);
-    if (!ctx_->sent_initial_metadata_) {
-      finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                      ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        finish_ops_.set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-    finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    call_.PerformOps(&finish_ops_);
-  }
-
- private:
-  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
-
-  ::grpc::internal::Call call_;
-  ServerContext* ctx_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
-      meta_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>> read_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpServerSendStatus>
-      finish_ops_;
-};
-
-template <class W>
-class ServerAsyncWriterInterface
-    : public internal::ServerAsyncStreamingInterface,
-      public internal::AsyncWriterInterface<W> {
- public:
-  /// Indicate that the stream is to be finished with a certain status code.
-  /// Request notification for when the server has sent the appropriate
-  /// signals to the client to end the call.
-  /// Should not be used concurrently with other operations.
-  ///
-  /// It is appropriate to call this method when either:
-  ///   * all messages from the client have been received (either known
-  ///     implictly, or explicitly because a previous \a
-  ///     AsyncReaderInterface::Read operation with a non-ok
-  ///     result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'.
-  ///   * it is desired to end the call early with some non-OK status code.
-  ///
-  /// This operation will end when the server has finished sending out initial
-  /// metadata (if not sent already), response message, and status, or if
-  /// some failure occurred when trying to do so.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[in] status To be sent to the client as the result of this call.
-  virtual void Finish(const Status& status, void* tag) = 0;
-
-  /// Request the writing of \a msg and coalesce it with trailing metadata which
-  /// contains \a status, using WriteOptions options with
-  /// identifying tag \a tag.
-  ///
-  /// WriteAndFinish is equivalent of performing WriteLast and Finish
-  /// in a single step.
-  ///
-  /// \param[in] msg The message to be written.
-  /// \param[in] options The WriteOptions to be used to write this message.
-  /// \param[in] status The Status that server returns to client.
-  /// \param[in] tag The tag identifying the operation.
-  virtual void WriteAndFinish(const W& msg, WriteOptions options,
-                              const Status& status, void* tag) = 0;
-};
-
-/// Async server-side API for doing server streaming RPCs,
-/// where the outgoing message stream from the server has messages of type \a W.
-template <class W>
-class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
- public:
-  explicit ServerAsyncWriter(ServerContext* ctx)
-      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
-
-  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - The initial metadata that will be sent to the client from this op will
-  ///     be taken from the \a ServerContext associated with the call.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  void SendInitialMetadata(void* tag) override {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    meta_ops_.set_output_tag(tag);
-    meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                  ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      meta_ops_.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_.PerformOps(&meta_ops_);
-  }
-
-  void Write(const W& msg, void* tag) override {
-    write_ops_.set_output_tag(tag);
-    EnsureInitialMetadataSent(&write_ops_);
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  void Write(const W& msg, WriteOptions options, void* tag) override {
-    write_ops_.set_output_tag(tag);
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-    }
-
-    EnsureInitialMetadataSent(&write_ops_);
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  /// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - the \a ServerContext associated with this call is used
-  ///     for sending trailing (and initial) metadata to the client.
-  ///
-  /// Note: \a status must have an OK code.
-  void WriteAndFinish(const W& msg, WriteOptions options, const Status& status,
-                      void* tag) override {
-    write_ops_.set_output_tag(tag);
-    EnsureInitialMetadataSent(&write_ops_);
-    options.set_buffer_hint();
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
-    write_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    call_.PerformOps(&write_ops_);
-  }
-
-  /// See the \a ServerAsyncWriterInterface.Finish method for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - the \a ServerContext associated with this call is used for sending
-  ///     trailing (and initial if not already sent) metadata to the client.
-  ///
-  /// Note: there are no restrictions are the code of
-  /// \a status,it may be non-OK
-  void Finish(const Status& status, void* tag) override {
-    finish_ops_.set_output_tag(tag);
-    EnsureInitialMetadataSent(&finish_ops_);
-    finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    call_.PerformOps(&finish_ops_);
-  }
-
- private:
-  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
-
-  template <class T>
-  void EnsureInitialMetadataSent(T* ops) {
-    if (!ctx_->sent_initial_metadata_) {
-      ops->SendInitialMetadata(ctx_->initial_metadata_,
-                               ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        ops->set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-  }
-
-  ::grpc::internal::Call call_;
-  ServerContext* ctx_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
-      meta_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpServerSendStatus>
-      write_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpServerSendStatus>
-      finish_ops_;
-};
-
-/// Server-side interface for asynchronous bi-directional streaming.
-template <class W, class R>
-class ServerAsyncReaderWriterInterface
-    : public internal::ServerAsyncStreamingInterface,
-      public internal::AsyncWriterInterface<W>,
-      public internal::AsyncReaderInterface<R> {
- public:
-  /// Indicate that the stream is to be finished with a certain status code.
-  /// Request notification for when the server has sent the appropriate
-  /// signals to the client to end the call.
-  /// Should not be used concurrently with other operations.
-  ///
-  /// It is appropriate to call this method when either:
-  ///   * all messages from the client have been received (either known
-  ///     implictly, or explicitly because a previous \a
-  ///     AsyncReaderInterface::Read operation
-  ///     with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok'
-  ///     with 'false'.
-  ///   * it is desired to end the call early with some non-OK status code.
-  ///
-  /// This operation will end when the server has finished sending out initial
-  /// metadata (if not sent already), response message, and status, or if some
-  /// failure occurred when trying to do so.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[in] status To be sent to the client as the result of this call.
-  virtual void Finish(const Status& status, void* tag) = 0;
-
-  /// Request the writing of \a msg and coalesce it with trailing metadata which
-  /// contains \a status, using WriteOptions options with
-  /// identifying tag \a tag.
-  ///
-  /// WriteAndFinish is equivalent of performing WriteLast and Finish in a
-  /// single step.
-  ///
-  /// \param[in] msg The message to be written.
-  /// \param[in] options The WriteOptions to be used to write this message.
-  /// \param[in] status The Status that server returns to client.
-  /// \param[in] tag The tag identifying the operation.
-  virtual void WriteAndFinish(const W& msg, WriteOptions options,
-                              const Status& status, void* tag) = 0;
-};
-
-/// Async server-side API for doing bidirectional streaming RPCs,
-/// where the incoming message stream coming from the client has messages of
-/// type \a R, and the outgoing message stream coming from the server has
-/// messages of type \a W.
-template <class W, class R>
-class ServerAsyncReaderWriter final
-    : public ServerAsyncReaderWriterInterface<W, R> {
- public:
-  explicit ServerAsyncReaderWriter(ServerContext* ctx)
-      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
-
-  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - The initial metadata that will be sent to the client from this op will
-  ///     be taken from the \a ServerContext associated with the call.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  void SendInitialMetadata(void* tag) override {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    meta_ops_.set_output_tag(tag);
-    meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                  ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      meta_ops_.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_.PerformOps(&meta_ops_);
-  }
-
-  void Read(R* msg, void* tag) override {
-    read_ops_.set_output_tag(tag);
-    read_ops_.RecvMessage(msg);
-    call_.PerformOps(&read_ops_);
-  }
-
-  void Write(const W& msg, void* tag) override {
-    write_ops_.set_output_tag(tag);
-    EnsureInitialMetadataSent(&write_ops_);
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  void Write(const W& msg, WriteOptions options, void* tag) override {
-    write_ops_.set_output_tag(tag);
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-    }
-    EnsureInitialMetadataSent(&write_ops_);
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
-    call_.PerformOps(&write_ops_);
-  }
-
-  /// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish
-  /// method for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - the \a ServerContext associated with this call is used
-  ///     for sending trailing (and initial) metadata to the client.
-  ///
-  /// Note: \a status must have an OK code.
-  void WriteAndFinish(const W& msg, WriteOptions options, const Status& status,
-                      void* tag) override {
-    write_ops_.set_output_tag(tag);
-    EnsureInitialMetadataSent(&write_ops_);
-    options.set_buffer_hint();
-    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
-    write_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    call_.PerformOps(&write_ops_);
-  }
-
-  /// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics.
-  ///
-  /// Implicit input parameter:
-  ///   - the \a ServerContext associated with this call is used for sending
-  ///     trailing (and initial if not already sent) metadata to the client.
-  ///
-  /// Note: there are no restrictions are the code of \a status,
-  /// it may be non-OK
-  void Finish(const Status& status, void* tag) override {
-    finish_ops_.set_output_tag(tag);
-    EnsureInitialMetadataSent(&finish_ops_);
-
-    finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    call_.PerformOps(&finish_ops_);
-  }
-
- private:
-  friend class ::grpc::Server;
-
-  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
-
-  template <class T>
-  void EnsureInitialMetadataSent(T* ops) {
-    if (!ctx_->sent_initial_metadata_) {
-      ops->SendInitialMetadata(ctx_->initial_metadata_,
-                               ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        ops->set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-  }
-
-  ::grpc::internal::Call call_;
-  ServerContext* ctx_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
-      meta_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>> read_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpServerSendStatus>
-      write_ops_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpServerSendStatus>
-      finish_ops_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/async_stream.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H
diff --git a/include/grpc++/impl/codegen/async_unary_call.h b/include/grpc++/impl/codegen/async_unary_call.h
index fb57300..2b08920 100644
--- a/include/grpc++/impl/codegen/async_unary_call.h
+++ b/include/grpc++/impl/codegen/async_unary_call.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,294 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
 #define GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
 
-#include <assert.h>
-#include <grpc++/impl/codegen/call.h>
-#include <grpc++/impl/codegen/channel_interface.h>
-#include <grpc++/impl/codegen/client_context.h>
-#include <grpc++/impl/codegen/server_context.h>
-#include <grpc++/impl/codegen/service_type.h>
-#include <grpc++/impl/codegen/status.h>
-
-namespace grpc {
-
-class CompletionQueue;
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-/// An interface relevant for async client side unary RPCs (which send
-/// one request message to a server and receive one response message).
-template <class R>
-class ClientAsyncResponseReaderInterface {
- public:
-  virtual ~ClientAsyncResponseReaderInterface() {}
-
-  /// Start the call that was set up by the constructor, but only if the
-  /// constructor was invoked through the "Prepare" API which doesn't actually
-  /// start the call
-  virtual void StartCall() = 0;
-
-  /// Request notification of the reading of initial metadata. Completion
-  /// will be notified by \a tag on the associated completion queue.
-  /// This call is optional, but if it is used, it cannot be used concurrently
-  /// with or after the \a Finish method.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  virtual void ReadInitialMetadata(void* tag) = 0;
-
-  /// Request to receive the server's response \a msg and final \a status for
-  /// the call, and to notify \a tag on this call's completion queue when
-  /// finished.
-  ///
-  /// This function will return when either:
-  /// - when the server's response message and status have been received.
-  /// - when the server has returned a non-OK status (no message expected in
-  ///   this case).
-  /// - when the call failed for some reason and the library generated a
-  ///   non-OK status.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[out] status To be updated with the operation status.
-  /// \param[out] msg To be filled in with the server's response message.
-  virtual void Finish(R* msg, Status* status, void* tag) = 0;
-};
-
-namespace internal {
-template <class R>
-class ClientAsyncResponseReaderFactory {
- public:
-  /// Start a call and write the request out if \a start is set.
-  /// \a tag will be notified on \a cq when the call has been started (i.e.
-  /// intitial metadata sent) and \a request has been written out.
-  /// If \a start is not set, the actual call must be initiated by StartCall
-  /// Note that \a context will be used to fill in custom initial metadata
-  /// used to send to the server when starting the call.
-  template <class W>
-  static ClientAsyncResponseReader<R>* Create(
-      ChannelInterface* channel, CompletionQueue* cq,
-      const ::grpc::internal::RpcMethod& method, ClientContext* context,
-      const W& request, bool start) {
-    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
-    return new (g_core_codegen_interface->grpc_call_arena_alloc(
-        call.call(), sizeof(ClientAsyncResponseReader<R>)))
-        ClientAsyncResponseReader<R>(call, context, request, start);
-  }
-};
-}  // namespace internal
-
-/// Async API for client-side unary RPCs, where the message response
-/// received from the server is of type \a R.
-template <class R>
-class ClientAsyncResponseReader final
-    : public ClientAsyncResponseReaderInterface<R> {
- public:
-  // always allocated against a call arena, no memory free required
-  static void operator delete(void* ptr, std::size_t size) {
-    assert(size == sizeof(ClientAsyncResponseReader));
-  }
-
-  // This operator should never be called as the memory should be freed as part
-  // of the arena destruction. It only exists to provide a matching operator
-  // delete to the operator new so that some compilers will not complain (see
-  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
-  // there are no tests catching the compiler warning.
-  static void operator delete(void*, void*) { assert(0); }
-
-  void StartCall() override {
-    assert(!started_);
-    started_ = true;
-    StartCallInternal();
-  }
-
-  /// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for
-  /// semantics.
-  ///
-  /// Side effect:
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible initial and trailing metadata sent from the server.
-  void ReadInitialMetadata(void* tag) override {
-    assert(started_);
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    meta_buf.set_output_tag(tag);
-    meta_buf.RecvInitialMetadata(context_);
-    call_.PerformOps(&meta_buf);
-  }
-
-  /// See \a ClientAysncResponseReaderInterface::Finish for semantics.
-  ///
-  /// Side effect:
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible initial and trailing metadata sent from the server.
-  void Finish(R* msg, Status* status, void* tag) override {
-    assert(started_);
-    finish_buf.set_output_tag(tag);
-    if (!context_->initial_metadata_received_) {
-      finish_buf.RecvInitialMetadata(context_);
-    }
-    finish_buf.RecvMessage(msg);
-    finish_buf.AllowNoMessage();
-    finish_buf.ClientRecvStatus(context_, status);
-    call_.PerformOps(&finish_buf);
-  }
-
- private:
-  friend class internal::ClientAsyncResponseReaderFactory<R>;
-  ClientContext* const context_;
-  ::grpc::internal::Call call_;
-  bool started_;
-
-  template <class W>
-  ClientAsyncResponseReader(::grpc::internal::Call call, ClientContext* context,
-                            const W& request, bool start)
-      : context_(context), call_(call), started_(start) {
-    // Bind the metadata at time of StartCallInternal but set up the rest here
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(init_buf.SendMessage(request).ok());
-    init_buf.ClientSendClose();
-    if (start) StartCallInternal();
-  }
-
-  void StartCallInternal() {
-    init_buf.SendInitialMetadata(context_->send_initial_metadata_,
-                                 context_->initial_metadata_flags());
-    call_.PerformOps(&init_buf);
-  }
-
-  // disable operator new
-  static void* operator new(std::size_t size);
-  static void* operator new(std::size_t size, void* p) { return p; }
-
-  ::grpc::internal::SneakyCallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                                    ::grpc::internal::CallOpSendMessage,
-                                    ::grpc::internal::CallOpClientSendClose>
-      init_buf;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-      meta_buf;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpRecvMessage<R>,
-                              ::grpc::internal::CallOpClientRecvStatus>
-      finish_buf;
-};
-
-/// Async server-side API for handling unary calls, where the single
-/// response message sent to the client is of type \a W.
-template <class W>
-class ServerAsyncResponseWriter final
-    : public internal::ServerAsyncStreamingInterface {
- public:
-  explicit ServerAsyncResponseWriter(ServerContext* ctx)
-      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
-
-  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
-  ///
-  /// Side effect:
-  ///   The initial metadata that will be sent to the client from this op will
-  ///   be taken from the \a ServerContext associated with the call.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  void SendInitialMetadata(void* tag) override {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    meta_buf_.set_output_tag(tag);
-    meta_buf_.SendInitialMetadata(ctx_->initial_metadata_,
-                                  ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      meta_buf_.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_.PerformOps(&meta_buf_);
-  }
-
-  /// Indicate that the stream is to be finished and request notification
-  /// when the server has sent the appropriate signals to the client to
-  /// end the call. Should not be used concurrently with other operations.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[in] status To be sent to the client as the result of the call.
-  /// \param[in] msg Message to be sent to the client.
-  ///
-  /// Side effect:
-  ///   - also sends initial metadata if not already sent (using the
-  ///     \a ServerContext associated with this call).
-  ///
-  /// Note: if \a status has a non-OK code, then \a msg will not be sent,
-  /// and the client will receive only the status with possible trailing
-  /// metadata.
-  void Finish(const W& msg, const Status& status, void* tag) {
-    finish_buf_.set_output_tag(tag);
-    if (!ctx_->sent_initial_metadata_) {
-      finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
-                                      ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        finish_buf_.set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-    // The response is dropped if the status is not OK.
-    if (status.ok()) {
-      finish_buf_.ServerSendStatus(ctx_->trailing_metadata_,
-                                   finish_buf_.SendMessage(msg));
-    } else {
-      finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    }
-    call_.PerformOps(&finish_buf_);
-  }
-
-  /// Indicate that the stream is to be finished with a non-OK status,
-  /// and request notification for when the server has finished sending the
-  /// appropriate signals to the client to end the call.
-  /// Should not be used concurrently with other operations.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  /// \param[in] status To be sent to the client as the result of the call.
-  ///   - Note: \a status must have a non-OK code.
-  ///
-  /// Side effect:
-  ///   - also sends initial metadata if not already sent (using the
-  ///     \a ServerContext associated with this call).
-  void FinishWithError(const Status& status, void* tag) {
-    GPR_CODEGEN_ASSERT(!status.ok());
-    finish_buf_.set_output_tag(tag);
-    if (!ctx_->sent_initial_metadata_) {
-      finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
-                                      ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        finish_buf_.set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-    finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
-    call_.PerformOps(&finish_buf_);
-  }
-
- private:
-  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
-
-  ::grpc::internal::Call call_;
-  ServerContext* ctx_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
-      meta_buf_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                              ::grpc::internal::CallOpSendMessage,
-                              ::grpc::internal::CallOpServerSendStatus>
-      finish_buf_;
-};
-
-}  // namespace grpc
-
-namespace std {
-template <class R>
-class default_delete<grpc::ClientAsyncResponseReader<R>> {
- public:
-  void operator()(void* p) {}
-};
-template <class R>
-class default_delete<grpc::ClientAsyncResponseReaderInterface<R>> {
- public:
-  void operator()(void* p) {}
-};
-}  // namespace std
+#include <grpcpp/impl/codegen/async_unary_call.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
diff --git a/include/grpc++/impl/codegen/byte_buffer.h b/include/grpc++/impl/codegen/byte_buffer.h
index fe73ce7..b754fa2 100644
--- a/include/grpc++/impl/codegen/byte_buffer.h
+++ b/include/grpc++/impl/codegen/byte_buffer.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2017 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,142 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
 #define GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
 
-#include <grpc/impl/codegen/byte_buffer.h>
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/serialization_traits.h>
-#include <grpc++/impl/codegen/slice.h>
-#include <grpc++/impl/codegen/status.h>
-
-#include <vector>
-
-namespace grpc {
-
-namespace internal {
-class CallOpSendMessage;
-template <class R>
-class CallOpRecvMessage;
-class CallOpGenericRecvMessage;
-class MethodHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class RpcMethodHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ServerStreamingHandler;
-template <class R>
-class DeserializeFuncType;
-}  // namespace internal
-/// A sequence of bytes.
-class ByteBuffer final {
- public:
-  /// Constuct an empty buffer.
-  ByteBuffer() : buffer_(nullptr) {}
-
-  /// Construct buffer from \a slices, of which there are \a nslices.
-  ByteBuffer(const Slice* slices, size_t nslices);
-
-  /// Constuct a byte buffer by referencing elements of existing buffer
-  /// \a buf. Wrapper of core function grpc_byte_buffer_copy
-  ByteBuffer(const ByteBuffer& buf);
-
-  ~ByteBuffer() {
-    if (buffer_) {
-      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
-    }
-  }
-
-  ByteBuffer& operator=(const ByteBuffer&);
-
-  /// Dump (read) the buffer contents into \a slices.
-  Status Dump(std::vector<Slice>* slices) const;
-
-  /// Remove all data.
-  void Clear() {
-    if (buffer_) {
-      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
-      buffer_ = nullptr;
-    }
-  }
-
-  /// Make a duplicate copy of the internals of this byte
-  /// buffer so that we have our own owned version of it.
-  /// bbuf.Duplicate(); is equivalent to bbuf=bbuf; but is actually readable
-  void Duplicate() {
-    buffer_ = g_core_codegen_interface->grpc_byte_buffer_copy(buffer_);
-  }
-
-  /// Forget underlying byte buffer without destroying
-  /// Use this only for un-owned byte buffers
-  void Release() { buffer_ = nullptr; }
-
-  /// Buffer size in bytes.
-  size_t Length() const;
-
-  /// Swap the state of *this and *other.
-  void Swap(ByteBuffer* other);
-
-  /// Is this ByteBuffer valid?
-  bool Valid() const { return (buffer_ != nullptr); }
-
- private:
-  friend class SerializationTraits<ByteBuffer, void>;
-  friend class internal::CallOpSendMessage;
-  template <class R>
-  friend class internal::CallOpRecvMessage;
-  friend class internal::CallOpGenericRecvMessage;
-  friend class internal::MethodHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class internal::RpcMethodHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class internal::ServerStreamingHandler;
-  template <class R>
-  friend class internal::DeserializeFuncType;
-
-  grpc_byte_buffer* buffer_;
-
-  // takes ownership
-  void set_buffer(grpc_byte_buffer* buf) {
-    if (buffer_) {
-      Clear();
-    }
-    buffer_ = buf;
-  }
-
-  grpc_byte_buffer* c_buffer() { return buffer_; }
-  grpc_byte_buffer** c_buffer_ptr() { return &buffer_; }
-
-  class ByteBufferPointer {
-   public:
-    ByteBufferPointer(const ByteBuffer* b)
-        : bbuf_(const_cast<ByteBuffer*>(b)) {}
-    operator ByteBuffer*() { return bbuf_; }
-    operator grpc_byte_buffer*() { return bbuf_->buffer_; }
-    operator grpc_byte_buffer**() { return &bbuf_->buffer_; }
-
-   private:
-    ByteBuffer* bbuf_;
-  };
-  ByteBufferPointer bbuf_ptr() const { return ByteBufferPointer(this); }
-};
-
-template <>
-class SerializationTraits<ByteBuffer, void> {
- public:
-  static Status Deserialize(ByteBuffer* byte_buffer, ByteBuffer* dest) {
-    dest->set_buffer(byte_buffer->buffer_);
-    return Status::OK;
-  }
-  static Status Serialize(const ByteBuffer& source, ByteBuffer* buffer,
-                          bool* own_buffer) {
-    *buffer = source;
-    *own_buffer = true;
-    return Status::OK;
-  }
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/byte_buffer.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index c04526c..dadab54 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,694 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CALL_H
 #define GRPCXX_IMPL_CODEGEN_CALL_H
 
-#include <assert.h>
-#include <cstring>
-#include <functional>
-#include <map>
-#include <memory>
-
-#include <grpc++/impl/codegen/byte_buffer.h>
-#include <grpc++/impl/codegen/call_hook.h>
-#include <grpc++/impl/codegen/client_context.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/serialization_traits.h>
-#include <grpc++/impl/codegen/slice.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc++/impl/codegen/string_ref.h>
-
-#include <grpc/impl/codegen/atm.h>
-#include <grpc/impl/codegen/compression_types.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-namespace grpc {
-
-class ByteBuffer;
-class CompletionQueue;
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-namespace internal {
-class Call;
-class CallHook;
-
-const char kBinaryErrorDetailsKey[] = "grpc-status-details-bin";
-
-// TODO(yangg) if the map is changed before we send, the pointers will be a
-// mess. Make sure it does not happen.
-inline grpc_metadata* FillMetadataArray(
-    const std::multimap<grpc::string, grpc::string>& metadata,
-    size_t* metadata_count, const grpc::string& optional_error_details) {
-  *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1);
-  if (*metadata_count == 0) {
-    return nullptr;
-  }
-  grpc_metadata* metadata_array =
-      (grpc_metadata*)(g_core_codegen_interface->gpr_malloc(
-          (*metadata_count) * sizeof(grpc_metadata)));
-  size_t i = 0;
-  for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
-    metadata_array[i].key = SliceReferencingString(iter->first);
-    metadata_array[i].value = SliceReferencingString(iter->second);
-  }
-  if (!optional_error_details.empty()) {
-    metadata_array[i].key =
-        g_core_codegen_interface->grpc_slice_from_static_buffer(
-            kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1);
-    metadata_array[i].value = SliceReferencingString(optional_error_details);
-  }
-  return metadata_array;
-}
-}  // namespace internal
-
-/// Per-message write options.
-class WriteOptions {
- public:
-  WriteOptions() : flags_(0), last_message_(false) {}
-  WriteOptions(const WriteOptions& other)
-      : flags_(other.flags_), last_message_(other.last_message_) {}
-
-  /// Clear all flags.
-  inline void Clear() { flags_ = 0; }
-
-  /// Returns raw flags bitset.
-  inline uint32_t flags() const { return flags_; }
-
-  /// Sets flag for the disabling of compression for the next message write.
-  ///
-  /// \sa GRPC_WRITE_NO_COMPRESS
-  inline WriteOptions& set_no_compression() {
-    SetBit(GRPC_WRITE_NO_COMPRESS);
-    return *this;
-  }
-
-  /// Clears flag for the disabling of compression for the next message write.
-  ///
-  /// \sa GRPC_WRITE_NO_COMPRESS
-  inline WriteOptions& clear_no_compression() {
-    ClearBit(GRPC_WRITE_NO_COMPRESS);
-    return *this;
-  }
-
-  /// Get value for the flag indicating whether compression for the next
-  /// message write is forcefully disabled.
-  ///
-  /// \sa GRPC_WRITE_NO_COMPRESS
-  inline bool get_no_compression() const {
-    return GetBit(GRPC_WRITE_NO_COMPRESS);
-  }
-
-  /// Sets flag indicating that the write may be buffered and need not go out on
-  /// the wire immediately.
-  ///
-  /// \sa GRPC_WRITE_BUFFER_HINT
-  inline WriteOptions& set_buffer_hint() {
-    SetBit(GRPC_WRITE_BUFFER_HINT);
-    return *this;
-  }
-
-  /// Clears flag indicating that the write may be buffered and need not go out
-  /// on the wire immediately.
-  ///
-  /// \sa GRPC_WRITE_BUFFER_HINT
-  inline WriteOptions& clear_buffer_hint() {
-    ClearBit(GRPC_WRITE_BUFFER_HINT);
-    return *this;
-  }
-
-  /// Get value for the flag indicating that the write may be buffered and need
-  /// not go out on the wire immediately.
-  ///
-  /// \sa GRPC_WRITE_BUFFER_HINT
-  inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
-
-  /// corked bit: aliases set_buffer_hint currently, with the intent that
-  /// set_buffer_hint will be removed in the future
-  inline WriteOptions& set_corked() {
-    SetBit(GRPC_WRITE_BUFFER_HINT);
-    return *this;
-  }
-
-  inline WriteOptions& clear_corked() {
-    ClearBit(GRPC_WRITE_BUFFER_HINT);
-    return *this;
-  }
-
-  inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
-
-  /// last-message bit: indicates this is the last message in a stream
-  /// client-side:  makes Write the equivalent of performing Write, WritesDone
-  /// in a single step
-  /// server-side:  hold the Write until the service handler returns (sync api)
-  /// or until Finish is called (async api)
-  inline WriteOptions& set_last_message() {
-    last_message_ = true;
-    return *this;
-  }
-
-  /// Clears flag indicating that this is the last message in a stream,
-  /// disabling coalescing.
-  inline WriteOptions& clear_last_message() {
-    last_message_ = false;
-    return *this;
-  }
-
-  /// Guarantee that all bytes have been written to the wire before completing
-  /// this write (usually writes are completed when they pass flow control)
-  inline WriteOptions& set_write_through() {
-    SetBit(GRPC_WRITE_THROUGH);
-    return *this;
-  }
-
-  inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); }
-
-  /// Get value for the flag indicating that this is the last message, and
-  /// should be coalesced with trailing metadata.
-  ///
-  /// \sa GRPC_WRITE_LAST_MESSAGE
-  bool is_last_message() const { return last_message_; }
-
-  WriteOptions& operator=(const WriteOptions& rhs) {
-    flags_ = rhs.flags_;
-    return *this;
-  }
-
- private:
-  void SetBit(const uint32_t mask) { flags_ |= mask; }
-
-  void ClearBit(const uint32_t mask) { flags_ &= ~mask; }
-
-  bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; }
-
-  uint32_t flags_;
-  bool last_message_;
-};
-
-namespace internal {
-/// Default argument for CallOpSet. I is unused by the class, but can be
-/// used for generating multiple names for the same thing.
-template <int I>
-class CallNoOp {
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {}
-  void FinishOp(bool* status) {}
-};
-
-class CallOpSendInitialMetadata {
- public:
-  CallOpSendInitialMetadata() : send_(false) {
-    maybe_compression_level_.is_set = false;
-  }
-
-  void SendInitialMetadata(
-      const std::multimap<grpc::string, grpc::string>& metadata,
-      uint32_t flags) {
-    maybe_compression_level_.is_set = false;
-    send_ = true;
-    flags_ = flags;
-    initial_metadata_ =
-        FillMetadataArray(metadata, &initial_metadata_count_, "");
-  }
-
-  void set_compression_level(grpc_compression_level level) {
-    maybe_compression_level_.is_set = true;
-    maybe_compression_level_.level = level;
-  }
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (!send_) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_SEND_INITIAL_METADATA;
-    op->flags = flags_;
-    op->reserved = NULL;
-    op->data.send_initial_metadata.count = initial_metadata_count_;
-    op->data.send_initial_metadata.metadata = initial_metadata_;
-    op->data.send_initial_metadata.maybe_compression_level.is_set =
-        maybe_compression_level_.is_set;
-    if (maybe_compression_level_.is_set) {
-      op->data.send_initial_metadata.maybe_compression_level.level =
-          maybe_compression_level_.level;
-    }
-  }
-  void FinishOp(bool* status) {
-    if (!send_) return;
-    g_core_codegen_interface->gpr_free(initial_metadata_);
-    send_ = false;
-  }
-
-  bool send_;
-  uint32_t flags_;
-  size_t initial_metadata_count_;
-  grpc_metadata* initial_metadata_;
-  struct {
-    bool is_set;
-    grpc_compression_level level;
-  } maybe_compression_level_;
-};
-
-class CallOpSendMessage {
- public:
-  CallOpSendMessage() : send_buf_() {}
-
-  /// Send \a message using \a options for the write. The \a options are cleared
-  /// after use.
-  template <class M>
-  Status SendMessage(const M& message,
-                     WriteOptions options) GRPC_MUST_USE_RESULT;
-
-  template <class M>
-  Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (!send_buf_.Valid()) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_SEND_MESSAGE;
-    op->flags = write_options_.flags();
-    op->reserved = NULL;
-    op->data.send_message.send_message = send_buf_.c_buffer();
-    // Flags are per-message: clear them after use.
-    write_options_.Clear();
-  }
-  void FinishOp(bool* status) { send_buf_.Clear(); }
-
- private:
-  ByteBuffer send_buf_;
-  WriteOptions write_options_;
-};
-
-template <class M>
-Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
-  write_options_ = options;
-  bool own_buf;
-  // TODO(vjpai): Remove the void below when possible
-  // The void in the template parameter below should not be needed
-  // (since it should be implicit) but is needed due to an observed
-  // difference in behavior between clang and gcc for certain internal users
-  Status result = SerializationTraits<M, void>::Serialize(
-      message, send_buf_.bbuf_ptr(), &own_buf);
-  if (!own_buf) {
-    send_buf_.Duplicate();
-  }
-  return result;
-}
-
-template <class M>
-Status CallOpSendMessage::SendMessage(const M& message) {
-  return SendMessage(message, WriteOptions());
-}
-
-template <class R>
-class CallOpRecvMessage {
- public:
-  CallOpRecvMessage()
-      : got_message(false),
-        message_(nullptr),
-        allow_not_getting_message_(false) {}
-
-  void RecvMessage(R* message) { message_ = message; }
-
-  // Do not change status if no message is received.
-  void AllowNoMessage() { allow_not_getting_message_ = true; }
-
-  bool got_message;
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (message_ == nullptr) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_RECV_MESSAGE;
-    op->flags = 0;
-    op->reserved = NULL;
-    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
-  }
-
-  void FinishOp(bool* status) {
-    if (message_ == nullptr) return;
-    if (recv_buf_.Valid()) {
-      if (*status) {
-        got_message = *status =
-            SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
-                .ok();
-        recv_buf_.Release();
-      } else {
-        got_message = false;
-        recv_buf_.Clear();
-      }
-    } else {
-      got_message = false;
-      if (!allow_not_getting_message_) {
-        *status = false;
-      }
-    }
-    message_ = nullptr;
-  }
-
- private:
-  R* message_;
-  ByteBuffer recv_buf_;
-  bool allow_not_getting_message_;
-};
-
-class DeserializeFunc {
- public:
-  virtual Status Deserialize(ByteBuffer* buf) = 0;
-  virtual ~DeserializeFunc() {}
-};
-
-template <class R>
-class DeserializeFuncType final : public DeserializeFunc {
- public:
-  DeserializeFuncType(R* message) : message_(message) {}
-  Status Deserialize(ByteBuffer* buf) override {
-    return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
-  }
-
-  ~DeserializeFuncType() override {}
-
- private:
-  R* message_;  // Not a managed pointer because management is external to this
-};
-
-class CallOpGenericRecvMessage {
- public:
-  CallOpGenericRecvMessage()
-      : got_message(false), allow_not_getting_message_(false) {}
-
-  template <class R>
-  void RecvMessage(R* message) {
-    // Use an explicit base class pointer to avoid resolution error in the
-    // following unique_ptr::reset for some old implementations.
-    DeserializeFunc* func = new DeserializeFuncType<R>(message);
-    deserialize_.reset(func);
-  }
-
-  // Do not change status if no message is received.
-  void AllowNoMessage() { allow_not_getting_message_ = true; }
-
-  bool got_message;
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (!deserialize_) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_RECV_MESSAGE;
-    op->flags = 0;
-    op->reserved = NULL;
-    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
-  }
-
-  void FinishOp(bool* status) {
-    if (!deserialize_) return;
-    if (recv_buf_.Valid()) {
-      if (*status) {
-        got_message = true;
-        *status = deserialize_->Deserialize(&recv_buf_).ok();
-        recv_buf_.Release();
-      } else {
-        got_message = false;
-        recv_buf_.Clear();
-      }
-    } else {
-      got_message = false;
-      if (!allow_not_getting_message_) {
-        *status = false;
-      }
-    }
-    deserialize_.reset();
-  }
-
- private:
-  std::unique_ptr<DeserializeFunc> deserialize_;
-  ByteBuffer recv_buf_;
-  bool allow_not_getting_message_;
-};
-
-class CallOpClientSendClose {
- public:
-  CallOpClientSendClose() : send_(false) {}
-
-  void ClientSendClose() { send_ = true; }
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (!send_) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
-    op->flags = 0;
-    op->reserved = NULL;
-  }
-  void FinishOp(bool* status) { send_ = false; }
-
- private:
-  bool send_;
-};
-
-class CallOpServerSendStatus {
- public:
-  CallOpServerSendStatus() : send_status_available_(false) {}
-
-  void ServerSendStatus(
-      const std::multimap<grpc::string, grpc::string>& trailing_metadata,
-      const Status& status) {
-    send_error_details_ = status.error_details();
-    trailing_metadata_ = FillMetadataArray(
-        trailing_metadata, &trailing_metadata_count_, send_error_details_);
-    send_status_available_ = true;
-    send_status_code_ = static_cast<grpc_status_code>(status.error_code());
-    send_error_message_ = status.error_message();
-  }
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (!send_status_available_) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
-    op->data.send_status_from_server.trailing_metadata_count =
-        trailing_metadata_count_;
-    op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
-    op->data.send_status_from_server.status = send_status_code_;
-    error_message_slice_ = SliceReferencingString(send_error_message_);
-    op->data.send_status_from_server.status_details =
-        send_error_message_.empty() ? nullptr : &error_message_slice_;
-    op->flags = 0;
-    op->reserved = NULL;
-  }
-
-  void FinishOp(bool* status) {
-    if (!send_status_available_) return;
-    g_core_codegen_interface->gpr_free(trailing_metadata_);
-    send_status_available_ = false;
-  }
-
- private:
-  bool send_status_available_;
-  grpc_status_code send_status_code_;
-  grpc::string send_error_details_;
-  grpc::string send_error_message_;
-  size_t trailing_metadata_count_;
-  grpc_metadata* trailing_metadata_;
-  grpc_slice error_message_slice_;
-};
-
-class CallOpRecvInitialMetadata {
- public:
-  CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
-
-  void RecvInitialMetadata(ClientContext* context) {
-    context->initial_metadata_received_ = true;
-    metadata_map_ = &context->recv_initial_metadata_;
-  }
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (metadata_map_ == nullptr) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_RECV_INITIAL_METADATA;
-    op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
-    op->flags = 0;
-    op->reserved = NULL;
-  }
-
-  void FinishOp(bool* status) {
-    if (metadata_map_ == nullptr) return;
-    metadata_map_->FillMap();
-    metadata_map_ = nullptr;
-  }
-
- private:
-  MetadataMap* metadata_map_;
-};
-
-class CallOpClientRecvStatus {
- public:
-  CallOpClientRecvStatus()
-      : recv_status_(nullptr), debug_error_string_(nullptr) {}
-
-  void ClientRecvStatus(ClientContext* context, Status* status) {
-    client_context_ = context;
-    metadata_map_ = &client_context_->trailing_metadata_;
-    recv_status_ = status;
-    error_message_ = g_core_codegen_interface->grpc_empty_slice();
-  }
-
- protected:
-  void AddOp(grpc_op* ops, size_t* nops) {
-    if (recv_status_ == nullptr) return;
-    grpc_op* op = &ops[(*nops)++];
-    op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
-    op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr();
-    op->data.recv_status_on_client.status = &status_code_;
-    op->data.recv_status_on_client.status_details = &error_message_;
-    op->data.recv_status_on_client.error_string = &debug_error_string_;
-    op->flags = 0;
-    op->reserved = NULL;
-  }
-
-  void FinishOp(bool* status) {
-    if (recv_status_ == nullptr) return;
-    metadata_map_->FillMap();
-    grpc::string binary_error_details;
-    auto iter = metadata_map_->map()->find(kBinaryErrorDetailsKey);
-    if (iter != metadata_map_->map()->end()) {
-      binary_error_details =
-          grpc::string(iter->second.begin(), iter->second.length());
-    }
-    *recv_status_ = Status(static_cast<StatusCode>(status_code_),
-                           grpc::string(GRPC_SLICE_START_PTR(error_message_),
-                                        GRPC_SLICE_END_PTR(error_message_)),
-                           binary_error_details);
-    client_context_->set_debug_error_string(
-        debug_error_string_ != nullptr ? debug_error_string_ : "");
-    g_core_codegen_interface->grpc_slice_unref(error_message_);
-    if (debug_error_string_ != nullptr) {
-      g_core_codegen_interface->gpr_free((void*)debug_error_string_);
-    }
-    recv_status_ = nullptr;
-  }
-
- private:
-  ClientContext* client_context_;
-  MetadataMap* metadata_map_;
-  Status* recv_status_;
-  const char* debug_error_string_;
-  grpc_status_code status_code_;
-  grpc_slice error_message_;
-};
-
-/// An abstract collection of call ops, used to generate the
-/// grpc_call_op structure to pass down to the lower layers,
-/// and as it is-a CompletionQueueTag, also massages the final
-/// completion into the correct form for consumption in the C++
-/// API.
-class CallOpSetInterface : public CompletionQueueTag {
- public:
-  /// Fills in grpc_op, starting from ops[*nops] and moving
-  /// upwards.
-  virtual void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) = 0;
-};
-
-/// Primary implementation of CallOpSetInterface.
-/// Since we cannot use variadic templates, we declare slots up to
-/// the maximum count of ops we'll need in a set. We leverage the
-/// empty base class optimization to slim this class (especially
-/// when there are many unused slots used). To avoid duplicate base classes,
-/// the template parmeter for CallNoOp is varied by argument position.
-template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
-          class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
-          class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
-class CallOpSet : public CallOpSetInterface,
-                  public Op1,
-                  public Op2,
-                  public Op3,
-                  public Op4,
-                  public Op5,
-                  public Op6 {
- public:
-  CallOpSet() : return_tag_(this), call_(nullptr) {}
-  void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) override {
-    this->Op1::AddOp(ops, nops);
-    this->Op2::AddOp(ops, nops);
-    this->Op3::AddOp(ops, nops);
-    this->Op4::AddOp(ops, nops);
-    this->Op5::AddOp(ops, nops);
-    this->Op6::AddOp(ops, nops);
-    g_core_codegen_interface->grpc_call_ref(call);
-    call_ = call;
-  }
-
-  bool FinalizeResult(void** tag, bool* status) override {
-    this->Op1::FinishOp(status);
-    this->Op2::FinishOp(status);
-    this->Op3::FinishOp(status);
-    this->Op4::FinishOp(status);
-    this->Op5::FinishOp(status);
-    this->Op6::FinishOp(status);
-    *tag = return_tag_;
-
-    g_core_codegen_interface->grpc_call_unref(call_);
-    return true;
-  }
-
-  void set_output_tag(void* return_tag) { return_tag_ = return_tag; }
-
- private:
-  void* return_tag_;
-  grpc_call* call_;
-};
-
-/// A CallOpSet that does not post completions to the completion queue.
-///
-/// Allows hiding some completions that the C core must generate from
-/// C++ users.
-template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
-          class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
-          class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
-class SneakyCallOpSet : public CallOpSet<Op1, Op2, Op3, Op4, Op5, Op6> {
- public:
-  bool FinalizeResult(void** tag, bool* status) override {
-    typedef CallOpSet<Op1, Op2, Op3, Op4, Op5, Op6> Base;
-    return Base::FinalizeResult(tag, status) && false;
-  }
-};
-
-/// Straightforward wrapping of the C call object
-class Call final {
- public:
-  /** call is owned by the caller */
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
-      : call_hook_(call_hook),
-        cq_(cq),
-        call_(call),
-        max_receive_message_size_(-1) {}
-
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
-       int max_receive_message_size)
-      : call_hook_(call_hook),
-        cq_(cq),
-        call_(call),
-        max_receive_message_size_(max_receive_message_size) {}
-
-  void PerformOps(CallOpSetInterface* ops) {
-    call_hook_->PerformOpsOnCall(ops, this);
-  }
-
-  grpc_call* call() const { return call_; }
-  CompletionQueue* cq() const { return cq_; }
-
-  int max_receive_message_size() const { return max_receive_message_size_; }
-
- private:
-  CallHook* call_hook_;
-  CompletionQueue* cq_;
-  grpc_call* call_;
-  int max_receive_message_size_;
-};
-}  // namespace internal
-}  // namespace grpc
+#include <grpcpp/impl/codegen/call.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CALL_H
diff --git a/include/grpc++/impl/codegen/call_hook.h b/include/grpc++/impl/codegen/call_hook.h
index 44e9de2..cf5ed57 100644
--- a/include/grpc++/impl/codegen/call_hook.h
+++ b/include/grpc++/impl/codegen/call_hook.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,24 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CALL_HOOK_H
 #define GRPCXX_IMPL_CODEGEN_CALL_HOOK_H
 
-namespace grpc {
-
-namespace internal {
-class CallOpSetInterface;
-class Call;
-
-/// This is an interface that Channel and Server implement to allow them to hook
-/// performing ops.
-class CallHook {
- public:
-  virtual ~CallHook() {}
-  virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0;
-};
-}  // namespace internal
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/call_hook.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CALL_HOOK_H
diff --git a/include/grpc++/impl/codegen/channel_interface.h b/include/grpc++/impl/codegen/channel_interface.h
index 769f853..c6e782e 100644
--- a/include/grpc++/impl/codegen/channel_interface.h
+++ b/include/grpc++/impl/codegen/channel_interface.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,106 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H
 #define GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H
 
-#include <grpc++/impl/codegen/status.h>
-#include <grpc++/impl/codegen/time.h>
-#include <grpc/impl/codegen/connectivity_state.h>
-
-namespace grpc {
-class ChannelInterface;
-class ClientContext;
-class CompletionQueue;
-
-template <class R>
-class ClientReader;
-template <class W>
-class ClientWriter;
-template <class W, class R>
-class ClientReaderWriter;
-
-namespace internal {
-class Call;
-class CallOpSetInterface;
-class RpcMethod;
-template <class InputMessage, class OutputMessage>
-class BlockingUnaryCallImpl;
-template <class R>
-class ClientAsyncReaderFactory;
-template <class W>
-class ClientAsyncWriterFactory;
-template <class W, class R>
-class ClientAsyncReaderWriterFactory;
-template <class R>
-class ClientAsyncResponseReaderFactory;
-}  // namespace internal
-
-/// Codegen interface for \a grpc::Channel.
-class ChannelInterface {
- public:
-  virtual ~ChannelInterface() {}
-  /// Get the current channel state. If the channel is in IDLE and
-  /// \a try_to_connect is set to true, try to connect.
-  virtual grpc_connectivity_state GetState(bool try_to_connect) = 0;
-
-  /// Return the \a tag on \a cq when the channel state is changed or \a
-  /// deadline expires. \a GetState needs to called to get the current state.
-  template <typename T>
-  void NotifyOnStateChange(grpc_connectivity_state last_observed, T deadline,
-                           CompletionQueue* cq, void* tag) {
-    TimePoint<T> deadline_tp(deadline);
-    NotifyOnStateChangeImpl(last_observed, deadline_tp.raw_time(), cq, tag);
-  }
-
-  /// Blocking wait for channel state change or \a deadline expiration.
-  /// \a GetState needs to called to get the current state.
-  template <typename T>
-  bool WaitForStateChange(grpc_connectivity_state last_observed, T deadline) {
-    TimePoint<T> deadline_tp(deadline);
-    return WaitForStateChangeImpl(last_observed, deadline_tp.raw_time());
-  }
-
-  /// Wait for this channel to be connected
-  template <typename T>
-  bool WaitForConnected(T deadline) {
-    grpc_connectivity_state state;
-    while ((state = GetState(true)) != GRPC_CHANNEL_READY) {
-      if (!WaitForStateChange(state, deadline)) return false;
-    }
-    return true;
-  }
-
- private:
-  template <class R>
-  friend class ::grpc::ClientReader;
-  template <class W>
-  friend class ::grpc::ClientWriter;
-  template <class W, class R>
-  friend class ::grpc::ClientReaderWriter;
-  template <class R>
-  friend class ::grpc::internal::ClientAsyncReaderFactory;
-  template <class W>
-  friend class ::grpc::internal::ClientAsyncWriterFactory;
-  template <class W, class R>
-  friend class ::grpc::internal::ClientAsyncReaderWriterFactory;
-  template <class R>
-  friend class ::grpc::internal::ClientAsyncResponseReaderFactory;
-  template <class InputMessage, class OutputMessage>
-  friend class ::grpc::internal::BlockingUnaryCallImpl;
-  friend class ::grpc::internal::RpcMethod;
-  virtual internal::Call CreateCall(const internal::RpcMethod& method,
-                                    ClientContext* context,
-                                    CompletionQueue* cq) = 0;
-  virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                                internal::Call* call) = 0;
-  virtual void* RegisterMethod(const char* method) = 0;
-  virtual void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
-                                       gpr_timespec deadline,
-                                       CompletionQueue* cq, void* tag) = 0;
-  virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
-                                      gpr_timespec deadline) = 0;
-};
-}  // namespace grpc
+#include <grpcpp/impl/codegen/channel_interface.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H
diff --git a/include/grpc++/impl/codegen/client_context.h b/include/grpc++/impl/codegen/client_context.h
index 38cce27..107532c 100644
--- a/include/grpc++/impl/codegen/client_context.h
+++ b/include/grpc++/impl/codegen/client_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,427 +16,13 @@
  *
  */
 
-/// A ClientContext allows the person implementing a service client to:
-///
-/// - Add custom metadata key-value pairs that will propagated to the server
-/// side.
-/// - Control call settings such as compression and authentication.
-/// - Initial and trailing metadata coming from the server.
-/// - Get performance metrics (ie, census).
-///
-/// Context settings are only relevant to the call they are invoked with, that
-/// is to say, they aren't sticky. Some of these settings, such as the
-/// compression options, can be made persistent at channel construction time
-/// (see \a grpc::CreateCustomChannel).
-///
-/// \warning ClientContext instances should \em not be reused across rpcs.
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
 
 #ifndef GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H
 #define GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H
 
-#include <map>
-#include <memory>
-#include <mutex>
-#include <string>
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/create_auth_context.h>
-#include <grpc++/impl/codegen/metadata_map.h>
-#include <grpc++/impl/codegen/security/auth_context.h>
-#include <grpc++/impl/codegen/slice.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc++/impl/codegen/string_ref.h>
-#include <grpc++/impl/codegen/time.h>
-#include <grpc/impl/codegen/compression_types.h>
-#include <grpc/impl/codegen/propagation_bits.h>
-
-struct census_context;
-struct grpc_call;
-
-namespace grpc {
-
-class Channel;
-class ChannelInterface;
-class CompletionQueue;
-class CallCredentials;
-class ClientContext;
-
-namespace internal {
-class RpcMethod;
-class CallOpClientRecvStatus;
-class CallOpRecvInitialMetadata;
-template <class InputMessage, class OutputMessage>
-class BlockingUnaryCallImpl;
-}  // namespace internal
-
-template <class R>
-class ClientReader;
-template <class W>
-class ClientWriter;
-template <class W, class R>
-class ClientReaderWriter;
-template <class R>
-class ClientAsyncReader;
-template <class W>
-class ClientAsyncWriter;
-template <class W, class R>
-class ClientAsyncReaderWriter;
-template <class R>
-class ClientAsyncResponseReader;
-class ServerContext;
-
-/// Options for \a ClientContext::FromServerContext specifying which traits from
-/// the \a ServerContext to propagate (copy) from it into a new \a
-/// ClientContext.
-///
-/// \see ClientContext::FromServerContext
-class PropagationOptions {
- public:
-  PropagationOptions() : propagate_(GRPC_PROPAGATE_DEFAULTS) {}
-
-  PropagationOptions& enable_deadline_propagation() {
-    propagate_ |= GRPC_PROPAGATE_DEADLINE;
-    return *this;
-  }
-
-  PropagationOptions& disable_deadline_propagation() {
-    propagate_ &= ~GRPC_PROPAGATE_DEADLINE;
-    return *this;
-  }
-
-  PropagationOptions& enable_census_stats_propagation() {
-    propagate_ |= GRPC_PROPAGATE_CENSUS_STATS_CONTEXT;
-    return *this;
-  }
-
-  PropagationOptions& disable_census_stats_propagation() {
-    propagate_ &= ~GRPC_PROPAGATE_CENSUS_STATS_CONTEXT;
-    return *this;
-  }
-
-  PropagationOptions& enable_census_tracing_propagation() {
-    propagate_ |= GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT;
-    return *this;
-  }
-
-  PropagationOptions& disable_census_tracing_propagation() {
-    propagate_ &= ~GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT;
-    return *this;
-  }
-
-  PropagationOptions& enable_cancellation_propagation() {
-    propagate_ |= GRPC_PROPAGATE_CANCELLATION;
-    return *this;
-  }
-
-  PropagationOptions& disable_cancellation_propagation() {
-    propagate_ &= ~GRPC_PROPAGATE_CANCELLATION;
-    return *this;
-  }
-
-  uint32_t c_bitmask() const { return propagate_; }
-
- private:
-  uint32_t propagate_;
-};
-
-namespace testing {
-class InteropClientContextInspector;
-}  // namespace testing
-
-/// A ClientContext allows the person implementing a service client to:
-///
-/// - Add custom metadata key-value pairs that will propagated to the server
-///   side.
-/// - Control call settings such as compression and authentication.
-/// - Initial and trailing metadata coming from the server.
-/// - Get performance metrics (ie, census).
-///
-/// Context settings are only relevant to the call they are invoked with, that
-/// is to say, they aren't sticky. Some of these settings, such as the
-/// compression options, can be made persistent at channel construction time
-/// (see \a grpc::CreateCustomChannel).
-///
-/// \warning ClientContext instances should \em not be reused across rpcs.
-class ClientContext {
- public:
-  ClientContext();
-  ~ClientContext();
-
-  /// Create a new \a ClientContext as a child of an incoming server call,
-  /// according to \a options (\see PropagationOptions).
-  ///
-  /// \param server_context The source server context to use as the basis for
-  /// constructing the client context.
-  /// \param options The options controlling what to copy from the \a
-  /// server_context.
-  ///
-  /// \return A newly constructed \a ClientContext instance based on \a
-  /// server_context, with traits propagated (copied) according to \a options.
-  static std::unique_ptr<ClientContext> FromServerContext(
-      const ServerContext& server_context,
-      PropagationOptions options = PropagationOptions());
-
-  /// Add the (\a meta_key, \a meta_value) pair to the metadata associated with
-  /// a client call. These are made available at the server side by the \a
-  /// grpc::ServerContext::client_metadata() method.
-  ///
-  /// \warning This method should only be called before invoking the rpc.
-  ///
-  /// \param meta_key The metadata key. If \a meta_value is binary data, it must
-  /// end in "-bin".
-  /// \param meta_value The metadata value. If its value is binary, the key name
-  /// must end in "-bin".
-  void AddMetadata(const grpc::string& meta_key,
-                   const grpc::string& meta_value);
-
-  /// Return a collection of initial metadata key-value pairs. Note that keys
-  /// may happen more than once (ie, a \a std::multimap is returned).
-  ///
-  /// \warning This method should only be called after initial metadata has been
-  /// received. For streaming calls, see \a
-  /// ClientReaderInterface::WaitForInitialMetadata().
-  ///
-  /// \return A multimap of initial metadata key-value pairs from the server.
-  const std::multimap<grpc::string_ref, grpc::string_ref>&
-  GetServerInitialMetadata() const {
-    GPR_CODEGEN_ASSERT(initial_metadata_received_);
-    return *recv_initial_metadata_.map();
-  }
-
-  /// Return a collection of trailing metadata key-value pairs. Note that keys
-  /// may happen more than once (ie, a \a std::multimap is returned).
-  ///
-  /// \warning This method is only callable once the stream has finished.
-  ///
-  /// \return A multimap of metadata trailing key-value pairs from the server.
-  const std::multimap<grpc::string_ref, grpc::string_ref>&
-  GetServerTrailingMetadata() const {
-    // TODO(yangg) check finished
-    return *trailing_metadata_.map();
-  }
-
-  /// Set the deadline for the client call.
-  ///
-  /// \warning This method should only be called before invoking the rpc.
-  ///
-  /// \param deadline the deadline for the client call. Units are determined by
-  /// the type used.
-  template <typename T>
-  void set_deadline(const T& deadline) {
-    TimePoint<T> deadline_tp(deadline);
-    deadline_ = deadline_tp.raw_time();
-  }
-
-  /// EXPERIMENTAL: Indicate that this request is idempotent.
-  /// By default, RPCs are assumed to <i>not</i> be idempotent.
-  ///
-  /// If true, the gRPC library assumes that it's safe to initiate
-  /// this RPC multiple times.
-  void set_idempotent(bool idempotent) { idempotent_ = idempotent; }
-
-  /// EXPERIMENTAL: Set this request to be cacheable.
-  /// If set, grpc is free to use the HTTP GET verb for sending the request,
-  /// with the possibility of receiving a cached response.
-  void set_cacheable(bool cacheable) { cacheable_ = cacheable; }
-
-  /// EXPERIMENTAL: Trigger wait-for-ready or not on this request.
-  /// See https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
-  /// If set, if an RPC is made when a channel's connectivity state is
-  /// TRANSIENT_FAILURE or CONNECTING, the call will not "fail fast",
-  /// and the channel will wait until the channel is READY before making the
-  /// call.
-  void set_wait_for_ready(bool wait_for_ready) {
-    wait_for_ready_ = wait_for_ready;
-    wait_for_ready_explicitly_set_ = true;
-  }
-
-  /// DEPRECATED: Use set_wait_for_ready() instead.
-  void set_fail_fast(bool fail_fast) { set_wait_for_ready(!fail_fast); }
-
-  /// Return the deadline for the client call.
-  std::chrono::system_clock::time_point deadline() const {
-    return Timespec2Timepoint(deadline_);
-  }
-
-  /// Return a \a gpr_timespec representation of the client call's deadline.
-  gpr_timespec raw_deadline() const { return deadline_; }
-
-  /// Set the per call authority header (see
-  /// https://tools.ietf.org/html/rfc7540#section-8.1.2.3).
-  void set_authority(const grpc::string& authority) { authority_ = authority; }
-
-  /// Return the authentication context for this client call.
-  ///
-  /// \see grpc::AuthContext.
-  std::shared_ptr<const AuthContext> auth_context() const {
-    if (auth_context_.get() == nullptr) {
-      auth_context_ = CreateAuthContext(call_);
-    }
-    return auth_context_;
-  }
-
-  /// Set credentials for the client call.
-  ///
-  /// A credentials object encapsulates all the state needed by a client to
-  /// authenticate with a server and make various assertions, e.g., about the
-  /// client’s identity, role, or whether it is authorized to make a particular
-  /// call.
-  ///
-  /// \see  https://grpc.io/docs/guides/auth.html
-  void set_credentials(const std::shared_ptr<CallCredentials>& creds) {
-    creds_ = creds;
-  }
-
-  /// Return the compression algorithm the client call will request be used.
-  /// Note that the gRPC runtime may decide to ignore this request, for example,
-  /// due to resource constraints.
-  grpc_compression_algorithm compression_algorithm() const {
-    return compression_algorithm_;
-  }
-
-  /// Set \a algorithm to be the compression algorithm used for the client call.
-  ///
-  /// \param algorithm The compression algorithm used for the client call.
-  void set_compression_algorithm(grpc_compression_algorithm algorithm);
-
-  /// Flag whether the initial metadata should be \a corked
-  ///
-  /// If \a corked is true, then the initial metadata will be coalesced with the
-  /// write of first message in the stream. As a result, any tag set for the
-  /// initial metadata operation (starting a client-streaming or bidi-streaming
-  /// RPC) will not actually be sent to the completion queue or delivered
-  /// via Next.
-  ///
-  /// \param corked The flag indicating whether the initial metadata is to be
-  /// corked or not.
-  void set_initial_metadata_corked(bool corked) {
-    initial_metadata_corked_ = corked;
-  }
-
-  /// Return the peer uri in a string.
-  ///
-  /// \warning This value is never authenticated or subject to any security
-  /// related code. It must not be used for any authentication related
-  /// functionality. Instead, use auth_context.
-  ///
-  /// \return The call's peer URI.
-  grpc::string peer() const;
-
-  /// Get and set census context.
-  void set_census_context(struct census_context* ccp) { census_context_ = ccp; }
-  struct census_context* census_context() const {
-    return census_context_;
-  }
-
-  /// Send a best-effort out-of-band cancel on the call associated with
-  /// this client context.  The call could be in any stage; e.g., if it is
-  /// already finished, it may still return success.
-  ///
-  /// There is no guarantee the call will be cancelled.
-  ///
-  /// Note that TryCancel() does not change any of the tags that are pending
-  /// on the completion queue. All pending tags will still be delivered
-  /// (though their ok result may reflect the effect of cancellation).
-  void TryCancel();
-
-  /// Global Callbacks
-  ///
-  /// Can be set exactly once per application to install hooks whenever
-  /// a client context is constructed and destructed.
-  class GlobalCallbacks {
-   public:
-    virtual ~GlobalCallbacks() {}
-    virtual void DefaultConstructor(ClientContext* context) = 0;
-    virtual void Destructor(ClientContext* context) = 0;
-  };
-  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
-
-  /// Should be used for framework-level extensions only.
-  /// Applications never need to call this method.
-  grpc_call* c_call() { return call_; }
-
-  /// EXPERIMENTAL debugging API
-  ///
-  /// if status is not ok() for an RPC, this will return a detailed string
-  /// of the gRPC Core error that led to the failure. It should not be relied
-  /// upon for anything other than gaining more debug data in failure cases.
-  grpc::string debug_error_string() const { return debug_error_string_; }
-
- private:
-  // Disallow copy and assign.
-  ClientContext(const ClientContext&);
-  ClientContext& operator=(const ClientContext&);
-
-  friend class ::grpc::testing::InteropClientContextInspector;
-  friend class ::grpc::internal::CallOpClientRecvStatus;
-  friend class ::grpc::internal::CallOpRecvInitialMetadata;
-  friend class Channel;
-  template <class R>
-  friend class ::grpc::ClientReader;
-  template <class W>
-  friend class ::grpc::ClientWriter;
-  template <class W, class R>
-  friend class ::grpc::ClientReaderWriter;
-  template <class R>
-  friend class ::grpc::ClientAsyncReader;
-  template <class W>
-  friend class ::grpc::ClientAsyncWriter;
-  template <class W, class R>
-  friend class ::grpc::ClientAsyncReaderWriter;
-  template <class R>
-  friend class ::grpc::ClientAsyncResponseReader;
-  template <class InputMessage, class OutputMessage>
-  friend class ::grpc::internal::BlockingUnaryCallImpl;
-
-  // Used by friend class CallOpClientRecvStatus
-  void set_debug_error_string(const grpc::string& debug_error_string) {
-    debug_error_string_ = debug_error_string;
-  }
-
-  grpc_call* call() const { return call_; }
-  void set_call(grpc_call* call, const std::shared_ptr<Channel>& channel);
-
-  uint32_t initial_metadata_flags() const {
-    return (idempotent_ ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST : 0) |
-           (wait_for_ready_ ? GRPC_INITIAL_METADATA_WAIT_FOR_READY : 0) |
-           (cacheable_ ? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST : 0) |
-           (wait_for_ready_explicitly_set_
-                ? GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET
-                : 0) |
-           (initial_metadata_corked_ ? GRPC_INITIAL_METADATA_CORKED : 0);
-  }
-
-  grpc::string authority() { return authority_; }
-
-  bool initial_metadata_received_;
-  bool wait_for_ready_;
-  bool wait_for_ready_explicitly_set_;
-  bool idempotent_;
-  bool cacheable_;
-  std::shared_ptr<Channel> channel_;
-  std::mutex mu_;
-  grpc_call* call_;
-  bool call_canceled_;
-  gpr_timespec deadline_;
-  grpc::string authority_;
-  std::shared_ptr<CallCredentials> creds_;
-  mutable std::shared_ptr<const AuthContext> auth_context_;
-  struct census_context* census_context_;
-  std::multimap<grpc::string, grpc::string> send_initial_metadata_;
-  internal::MetadataMap recv_initial_metadata_;
-  internal::MetadataMap trailing_metadata_;
-
-  grpc_call* propagate_from_call_;
-  PropagationOptions propagation_options_;
-
-  grpc_compression_algorithm compression_algorithm_;
-  bool initial_metadata_corked_;
-
-  grpc::string debug_error_string_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/client_context.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H
diff --git a/include/grpc++/impl/codegen/client_unary_call.h b/include/grpc++/impl/codegen/client_unary_call.h
index 543e54b..f7dff1f 100644
--- a/include/grpc++/impl/codegen/client_unary_call.h
+++ b/include/grpc++/impl/codegen/client_unary_call.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,75 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
 #define GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
 
-#include <grpc++/impl/codegen/call.h>
-#include <grpc++/impl/codegen/channel_interface.h>
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/status.h>
-
-namespace grpc {
-
-class Channel;
-class ClientContext;
-class CompletionQueue;
-
-namespace internal {
-class RpcMethod;
-/// Wrapper that performs a blocking unary call
-template <class InputMessage, class OutputMessage>
-Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
-                         ClientContext* context, const InputMessage& request,
-                         OutputMessage* result) {
-  return BlockingUnaryCallImpl<InputMessage, OutputMessage>(
-             channel, method, context, request, result)
-      .status();
-}
-
-template <class InputMessage, class OutputMessage>
-class BlockingUnaryCallImpl {
- public:
-  BlockingUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method,
-                        ClientContext* context, const InputMessage& request,
-                        OutputMessage* result) {
-    CompletionQueue cq(grpc_completion_queue_attributes{
-        GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
-        GRPC_CQ_DEFAULT_POLLING});  // Pluckable completion queue
-    Call call(channel->CreateCall(method, context, &cq));
-    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
-              CallOpRecvInitialMetadata, CallOpRecvMessage<OutputMessage>,
-              CallOpClientSendClose, CallOpClientRecvStatus>
-        ops;
-    status_ = ops.SendMessage(request);
-    if (!status_.ok()) {
-      return;
-    }
-    ops.SendInitialMetadata(context->send_initial_metadata_,
-                            context->initial_metadata_flags());
-    ops.RecvInitialMetadata(context);
-    ops.RecvMessage(result);
-    ops.AllowNoMessage();
-    ops.ClientSendClose();
-    ops.ClientRecvStatus(context, &status_);
-    call.PerformOps(&ops);
-    if (cq.Pluck(&ops)) {
-      if (!ops.got_message && status_.ok()) {
-        status_ = Status(StatusCode::UNIMPLEMENTED,
-                         "No message returned for unary request");
-      }
-    } else {
-      GPR_CODEGEN_ASSERT(!status_.ok());
-    }
-  }
-  Status status() { return status_; }
-
- private:
-  Status status_;
-};
-
-}  // namespace internal
-}  // namespace grpc
+#include <grpcpp/impl/codegen/client_unary_call.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
diff --git a/include/grpc++/impl/codegen/completion_queue.h b/include/grpc++/impl/codegen/completion_queue.h
index 83477d0..1075495 100644
--- a/include/grpc++/impl/codegen/completion_queue.h
+++ b/include/grpc++/impl/codegen/completion_queue.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015-2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,368 +16,13 @@
  *
  */
 
-/// A completion queue implements a concurrent producer-consumer queue, with
-/// two main API-exposed methods: \a Next and \a AsyncNext. These
-/// methods are the essential component of the gRPC C++ asynchronous API.
-/// There is also a \a Shutdown method to indicate that a given completion queue
-/// will no longer have regular events. This must be called before the
-/// completion queue is destroyed.
-/// All completion queue APIs are thread-safe and may be used concurrently with
-/// any other completion queue API invocation; it is acceptable to have
-/// multiple threads calling \a Next or \a AsyncNext on the same or different
-/// completion queues, or to call these methods concurrently with a \a Shutdown
-/// elsewhere.
-/// \remark{All other API calls on completion queue should be completed before
-/// a completion queue destructor is called.}
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H
 #define GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H
 
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/grpc_library.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc++/impl/codegen/time.h>
-#include <grpc/impl/codegen/atm.h>
-
-struct grpc_completion_queue;
-
-namespace grpc {
-
-template <class R>
-class ClientReader;
-template <class W>
-class ClientWriter;
-template <class W, class R>
-class ClientReaderWriter;
-template <class R>
-class ServerReader;
-template <class W>
-class ServerWriter;
-namespace internal {
-template <class W, class R>
-class ServerReaderWriterBody;
-}  // namespace internal
-
-class Channel;
-class ChannelInterface;
-class ClientContext;
-class CompletionQueue;
-class Server;
-class ServerBuilder;
-class ServerContext;
-class ServerInterface;
-
-namespace internal {
-class CompletionQueueTag;
-class RpcMethod;
-template <class ServiceType, class RequestType, class ResponseType>
-class RpcMethodHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ClientStreamingHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ServerStreamingHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class BidiStreamingHandler;
-class UnknownMethodHandler;
-template <class Streamer, bool WriteNeeded>
-class TemplatedBidiStreamingHandler;
-template <class InputMessage, class OutputMessage>
-class BlockingUnaryCallImpl;
-}  // namespace internal
-
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-/// A thin wrapper around \ref grpc_completion_queue (see \ref
-/// src/core/lib/surface/completion_queue.h).
-/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
-/// performance servers.
-class CompletionQueue : private GrpcLibraryCodegen {
- public:
-  /// Default constructor. Implicitly creates a \a grpc_completion_queue
-  /// instance.
-  CompletionQueue()
-      : CompletionQueue(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING}) {}
-
-  /// Wrap \a take, taking ownership of the instance.
-  ///
-  /// \param take The completion queue instance to wrap. Ownership is taken.
-  explicit CompletionQueue(grpc_completion_queue* take);
-
-  /// Destructor. Destroys the owned wrapped completion queue / instance.
-  ~CompletionQueue() {
-    g_core_codegen_interface->grpc_completion_queue_destroy(cq_);
-  }
-
-  /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT.
-  enum NextStatus {
-    SHUTDOWN,   ///< The completion queue has been shutdown and fully-drained
-    GOT_EVENT,  ///< Got a new event; \a tag will be filled in with its
-                ///< associated value; \a ok indicating its success.
-    TIMEOUT     ///< deadline was reached.
-  };
-
-  /// Read from the queue, blocking until an event is available or the queue is
-  /// shutting down.
-  ///
-  /// \param tag[out] Updated to point to the read event's tag.
-  /// \param ok[out] true if read a successful event, false otherwise.
-  ///
-  /// Note that each tag sent to the completion queue (through RPC operations
-  /// or alarms) will be delivered out of the completion queue by a call to
-  /// Next (or a related method), regardless of whether the operation succeeded
-  /// or not. Success here means that this operation completed in the normal
-  /// valid manner.
-  ///
-  /// Server-side RPC request: \a ok indicates that the RPC has indeed
-  /// been started. If it is false, the server has been Shutdown
-  /// before this particular call got matched to an incoming RPC.
-  ///
-  /// Client-side StartCall/RPC invocation: \a ok indicates that the RPC is
-  /// going to go to the wire. If it is false, it not going to the wire. This
-  /// would happen if the channel is either permanently broken or
-  /// transiently broken but with the fail-fast option. (Note that async unary
-  /// RPCs don't post a CQ tag at this point, nor do client-streaming
-  /// or bidi-streaming RPCs that have the initial metadata corked option set.)
-  ///
-  /// Client-side Write, Client-side WritesDone, Server-side Write,
-  /// Server-side Finish, Server-side SendInitialMetadata (which is
-  /// typically included in Write or Finish when not done explicitly):
-  /// \a ok means that the data/metadata/status/etc is going to go to the
-  /// wire. If it is false, it not going to the wire because the call
-  /// is already dead (i.e., canceled, deadline expired, other side
-  /// dropped the channel, etc).
-  ///
-  /// Client-side Read, Server-side Read, Client-side
-  /// RecvInitialMetadata (which is typically included in Read if not
-  /// done explicitly): \a ok indicates whether there is a valid message
-  /// that got read. If not, you know that there are certainly no more
-  /// messages that can ever be read from this stream. For the client-side
-  /// operations, this only happens because the call is dead. For the
-  /// server-sider operation, though, this could happen because the client
-  /// has done a WritesDone already.
-  ///
-  /// Client-side Finish: \a ok should always be true
-  ///
-  /// Server-side AsyncNotifyWhenDone: \a ok should always be true
-  ///
-  /// Alarm: \a ok is true if it expired, false if it was canceled
-  ///
-  /// \return true if got an event, false if the queue is fully drained and
-  ///         shut down.
-  bool Next(void** tag, bool* ok) {
-    return (AsyncNextInternal(tag, ok,
-                              g_core_codegen_interface->gpr_inf_future(
-                                  GPR_CLOCK_REALTIME)) != SHUTDOWN);
-  }
-
-  /// Read from the queue, blocking up to \a deadline (or the queue's shutdown).
-  /// Both \a tag and \a ok are updated upon success (if an event is available
-  /// within the \a deadline).  A \a tag points to an arbitrary location usually
-  /// employed to uniquely identify an event.
-  ///
-  /// \param tag[out] Upon sucess, updated to point to the event's tag.
-  /// \param ok[out] Upon sucess, true if a successful event, false otherwise
-  ///        See documentation for CompletionQueue::Next for explanation of ok
-  /// \param deadline[in] How long to block in wait for an event.
-  ///
-  /// \return The type of event read.
-  template <typename T>
-  NextStatus AsyncNext(void** tag, bool* ok, const T& deadline) {
-    TimePoint<T> deadline_tp(deadline);
-    return AsyncNextInternal(tag, ok, deadline_tp.raw_time());
-  }
-
-  /// EXPERIMENTAL
-  /// First executes \a F, then reads from the queue, blocking up to
-  /// \a deadline (or the queue's shutdown).
-  /// Both \a tag and \a ok are updated upon success (if an event is available
-  /// within the \a deadline).  A \a tag points to an arbitrary location usually
-  /// employed to uniquely identify an event.
-  ///
-  /// \param F[in] Function to execute before calling AsyncNext on this queue.
-  /// \param tag[out] Upon sucess, updated to point to the event's tag.
-  /// \param ok[out] Upon sucess, true if read a regular event, false otherwise.
-  /// \param deadline[in] How long to block in wait for an event.
-  ///
-  /// \return The type of event read.
-  template <typename T, typename F>
-  NextStatus DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) {
-    CompletionQueueTLSCache cache = CompletionQueueTLSCache(this);
-    f();
-    if (cache.Flush(tag, ok)) {
-      return GOT_EVENT;
-    } else {
-      return AsyncNext(tag, ok, deadline);
-    }
-  }
-
-  /// Request the shutdown of the queue.
-  ///
-  /// \warning This method must be called at some point if this completion queue
-  /// is accessed with Next or AsyncNext. \a Next will not return false
-  /// until this method has been called and all pending tags have been drained.
-  /// (Likewise for \a AsyncNext returning \a NextStatus::SHUTDOWN .)
-  /// Only once either one of these methods does that (that is, once the queue
-  /// has been \em drained) can an instance of this class be destroyed.
-  /// Also note that applications must ensure that no work is enqueued on this
-  /// completion queue after this method is called.
-  void Shutdown();
-
-  /// Returns a \em raw pointer to the underlying \a grpc_completion_queue
-  /// instance.
-  ///
-  /// \warning Remember that the returned instance is owned. No transfer of
-  /// owership is performed.
-  grpc_completion_queue* cq() { return cq_; }
-
- protected:
-  /// Private constructor of CompletionQueue only visible to friend classes
-  CompletionQueue(const grpc_completion_queue_attributes& attributes) {
-    cq_ = g_core_codegen_interface->grpc_completion_queue_create(
-        g_core_codegen_interface->grpc_completion_queue_factory_lookup(
-            &attributes),
-        &attributes, NULL);
-    InitialAvalanching();  // reserve this for the future shutdown
-  }
-
- private:
-  // Friend synchronous wrappers so that they can access Pluck(), which is
-  // a semi-private API geared towards the synchronous implementation.
-  template <class R>
-  friend class ::grpc::ClientReader;
-  template <class W>
-  friend class ::grpc::ClientWriter;
-  template <class W, class R>
-  friend class ::grpc::ClientReaderWriter;
-  template <class R>
-  friend class ::grpc::ServerReader;
-  template <class W>
-  friend class ::grpc::ServerWriter;
-  template <class W, class R>
-  friend class ::grpc::internal::ServerReaderWriterBody;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::RpcMethodHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::ClientStreamingHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::ServerStreamingHandler;
-  template <class Streamer, bool WriteNeeded>
-  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
-  friend class ::grpc::internal::UnknownMethodHandler;
-  friend class ::grpc::Server;
-  friend class ::grpc::ServerContext;
-  friend class ::grpc::ServerInterface;
-  template <class InputMessage, class OutputMessage>
-  friend class ::grpc::internal::BlockingUnaryCallImpl;
-
-  /// EXPERIMENTAL
-  /// Creates a Thread Local cache to store the first event
-  /// On this completion queue queued from this thread.  Once
-  /// initialized, it must be flushed on the same thread.
-  class CompletionQueueTLSCache {
-   public:
-    CompletionQueueTLSCache(CompletionQueue* cq);
-    ~CompletionQueueTLSCache();
-    bool Flush(void** tag, bool* ok);
-
-   private:
-    CompletionQueue* cq_;
-    bool flushed_;
-  };
-
-  NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline);
-
-  /// Wraps \a grpc_completion_queue_pluck.
-  /// \warning Must not be mixed with calls to \a Next.
-  bool Pluck(internal::CompletionQueueTag* tag) {
-    auto deadline =
-        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME);
-    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
-        cq_, tag, deadline, nullptr);
-    bool ok = ev.success != 0;
-    void* ignored = tag;
-    GPR_CODEGEN_ASSERT(tag->FinalizeResult(&ignored, &ok));
-    GPR_CODEGEN_ASSERT(ignored == tag);
-    // Ignore mutations by FinalizeResult: Pluck returns the C API status
-    return ev.success != 0;
-  }
-
-  /// Performs a single polling pluck on \a tag.
-  /// \warning Must not be mixed with calls to \a Next.
-  ///
-  /// TODO: sreek - This calls tag->FinalizeResult() even if the cq_ is already
-  /// shutdown. This is most likely a bug and if it is a bug, then change this
-  /// implementation to simple call the other TryPluck function with a zero
-  /// timeout. i.e:
-  ///      TryPluck(tag, gpr_time_0(GPR_CLOCK_REALTIME))
-  void TryPluck(internal::CompletionQueueTag* tag) {
-    auto deadline = g_core_codegen_interface->gpr_time_0(GPR_CLOCK_REALTIME);
-    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
-        cq_, tag, deadline, nullptr);
-    if (ev.type == GRPC_QUEUE_TIMEOUT) return;
-    bool ok = ev.success != 0;
-    void* ignored = tag;
-    // the tag must be swallowed if using TryPluck
-    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
-  }
-
-  /// Performs a single polling pluck on \a tag. Calls tag->FinalizeResult if
-  /// the pluck() was successful and returned the tag.
-  ///
-  /// This exects tag->FinalizeResult (if called) to return 'false' i.e expects
-  /// that the tag is internal not something that is returned to the user.
-  void TryPluck(internal::CompletionQueueTag* tag, gpr_timespec deadline) {
-    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
-        cq_, tag, deadline, nullptr);
-    if (ev.type == GRPC_QUEUE_TIMEOUT || ev.type == GRPC_QUEUE_SHUTDOWN) {
-      return;
-    }
-
-    bool ok = ev.success != 0;
-    void* ignored = tag;
-    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
-  }
-
-  /// Manage state of avalanching operations : completion queue tags that
-  /// trigger other completion queue operations. The underlying core completion
-  /// queue should not really shutdown until all avalanching operations have
-  /// been finalized. Note that we maintain the requirement that an avalanche
-  /// registration must take place before CQ shutdown (which must be maintained
-  /// elsehwere)
-  void InitialAvalanching() {
-    gpr_atm_rel_store(&avalanches_in_flight_, static_cast<gpr_atm>(1));
-  }
-  void RegisterAvalanching() {
-    gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
-                                 static_cast<gpr_atm>(1));
-  }
-  void CompleteAvalanching();
-
-  grpc_completion_queue* cq_;  // owned
-
-  gpr_atm avalanches_in_flight_;
-};
-
-/// A specific type of completion queue used by the processing of notifications
-/// by servers. Instantiated by \a ServerBuilder.
-class ServerCompletionQueue : public CompletionQueue {
- public:
-  bool IsFrequentlyPolled() { return polling_type_ != GRPC_CQ_NON_LISTENING; }
-
- private:
-  grpc_cq_polling_type polling_type_;
-  friend class ServerBuilder;
-  /// \param is_frequently_polled Informs the GRPC library about whether the
-  /// server completion queue would be actively polled (by calling Next() or
-  /// AsyncNext()). By default all server completion queues are assumed to be
-  /// frequently polled.
-  ServerCompletionQueue(grpc_cq_polling_type polling_type)
-      : CompletionQueue(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, polling_type}),
-        polling_type_(polling_type) {}
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/completion_queue.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H
diff --git a/include/grpc++/impl/codegen/completion_queue_tag.h b/include/grpc++/impl/codegen/completion_queue_tag.h
index cb16bcf..994fa2a 100644
--- a/include/grpc++/impl/codegen/completion_queue_tag.h
+++ b/include/grpc++/impl/codegen/completion_queue_tag.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,24 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
 #define GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
 
-namespace grpc {
-
-namespace internal {
-/// An interface allowing implementors to process and filter event tags.
-class CompletionQueueTag {
- public:
-  virtual ~CompletionQueueTag() {}
-  /// Called prior to returning from Next(), return value is the status of the
-  /// operation (return status is the default thing to do). If this function
-  /// returns false, the tag is dropped and not returned from the completion
-  /// queue
-  virtual bool FinalizeResult(void** tag, bool* status) = 0;
-};
-}  // namespace internal
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
diff --git a/include/grpc++/impl/codegen/config.h b/include/grpc++/impl/codegen/config.h
index b5ac9a7..237bf38 100644
--- a/include/grpc++/impl/codegen/config.h
+++ b/include/grpc++/impl/codegen/config.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,26 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CONFIG_H
 #define GRPCXX_IMPL_CODEGEN_CONFIG_H
 
-#ifndef GRPC_CUSTOM_STRING
-#include <string>
-#define GRPC_CUSTOM_STRING std::string
-#endif
-
-/// The following macros are deprecated and appear only for users
-/// with PB files generated using gRPC 1.0.x plugins. They should
-/// not be used in new code
-#define GRPC_OVERRIDE override  // deprecated
-#define GRPC_FINAL final        // deprecated
-
-namespace grpc {
-
-typedef GRPC_CUSTOM_STRING string;
-
-using std::to_string;
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/config.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CONFIG_H
diff --git a/include/grpc++/impl/codegen/config_protobuf.h b/include/grpc++/impl/codegen/config_protobuf.h
index 7387fa2..debd74a 100644
--- a/include/grpc++/impl/codegen/config_protobuf.h
+++ b/include/grpc++/impl/codegen/config_protobuf.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,80 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
 #define GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
 
-#define GRPC_OPEN_SOURCE_PROTO
-
-#ifndef GRPC_CUSTOM_PROTOBUF_INT64
-#include <google/protobuf/stubs/common.h>
-#define GRPC_CUSTOM_PROTOBUF_INT64 ::google::protobuf::int64
-#endif
-
-#ifndef GRPC_CUSTOM_MESSAGE
-#ifdef GRPC_USE_PROTO_LITE
-#include <google/protobuf/message_lite.h>
-#define GRPC_CUSTOM_MESSAGE ::google::protobuf::MessageLite
-#else
-#include <google/protobuf/message.h>
-#define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message
-#endif
-#endif
-
-#ifndef GRPC_CUSTOM_DESCRIPTOR
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/descriptor.pb.h>
-#define GRPC_CUSTOM_DESCRIPTOR ::google::protobuf::Descriptor
-#define GRPC_CUSTOM_DESCRIPTORPOOL ::google::protobuf::DescriptorPool
-#define GRPC_CUSTOM_FIELDDESCRIPTOR ::google::protobuf::FieldDescriptor
-#define GRPC_CUSTOM_FILEDESCRIPTOR ::google::protobuf::FileDescriptor
-#define GRPC_CUSTOM_FILEDESCRIPTORPROTO ::google::protobuf::FileDescriptorProto
-#define GRPC_CUSTOM_METHODDESCRIPTOR ::google::protobuf::MethodDescriptor
-#define GRPC_CUSTOM_SERVICEDESCRIPTOR ::google::protobuf::ServiceDescriptor
-#define GRPC_CUSTOM_SOURCELOCATION ::google::protobuf::SourceLocation
-#endif
-
-#ifndef GRPC_CUSTOM_DESCRIPTORDATABASE
-#include <google/protobuf/descriptor_database.h>
-#define GRPC_CUSTOM_DESCRIPTORDATABASE ::google::protobuf::DescriptorDatabase
-#define GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE \
-  ::google::protobuf::SimpleDescriptorDatabase
-#endif
-
-#ifndef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream.h>
-#define GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM \
-  ::google::protobuf::io::ZeroCopyOutputStream
-#define GRPC_CUSTOM_ZEROCOPYINPUTSTREAM \
-  ::google::protobuf::io::ZeroCopyInputStream
-#define GRPC_CUSTOM_CODEDINPUTSTREAM ::google::protobuf::io::CodedInputStream
-#endif
-
-namespace grpc {
-namespace protobuf {
-
-typedef GRPC_CUSTOM_MESSAGE Message;
-typedef GRPC_CUSTOM_PROTOBUF_INT64 int64;
-
-typedef GRPC_CUSTOM_DESCRIPTOR Descriptor;
-typedef GRPC_CUSTOM_DESCRIPTORPOOL DescriptorPool;
-typedef GRPC_CUSTOM_DESCRIPTORDATABASE DescriptorDatabase;
-typedef GRPC_CUSTOM_FIELDDESCRIPTOR FieldDescriptor;
-typedef GRPC_CUSTOM_FILEDESCRIPTOR FileDescriptor;
-typedef GRPC_CUSTOM_FILEDESCRIPTORPROTO FileDescriptorProto;
-typedef GRPC_CUSTOM_METHODDESCRIPTOR MethodDescriptor;
-typedef GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor;
-typedef GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE SimpleDescriptorDatabase;
-typedef GRPC_CUSTOM_SOURCELOCATION SourceLocation;
-
-namespace io {
-typedef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM ZeroCopyOutputStream;
-typedef GRPC_CUSTOM_ZEROCOPYINPUTSTREAM ZeroCopyInputStream;
-typedef GRPC_CUSTOM_CODEDINPUTSTREAM CodedInputStream;
-}  // namespace io
-
-}  // namespace protobuf
-}  // namespace grpc
+#include <grpcpp/impl/codegen/config_protobuf.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
diff --git a/include/grpc++/impl/codegen/core_codegen.h b/include/grpc++/impl/codegen/core_codegen.h
index d7c57be..ee600a9 100644
--- a/include/grpc++/impl/codegen/core_codegen.h
+++ b/include/grpc++/impl/codegen/core_codegen.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,102 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
 #define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
 
-// This file should be compiled as part of grpc++.
-
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc/byte_buffer.h>
-#include <grpc/grpc.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-namespace grpc {
-
-/// Implementation of the core codegen interface.
-class CoreCodegen final : public CoreCodegenInterface {
- private:
-  virtual const grpc_completion_queue_factory*
-  grpc_completion_queue_factory_lookup(
-      const grpc_completion_queue_attributes* attributes) override;
-  virtual grpc_completion_queue* grpc_completion_queue_create(
-      const grpc_completion_queue_factory* factory,
-      const grpc_completion_queue_attributes* attributes,
-      void* reserved) override;
-  grpc_completion_queue* grpc_completion_queue_create_for_next(
-      void* reserved) override;
-  grpc_completion_queue* grpc_completion_queue_create_for_pluck(
-      void* reserved) override;
-  void grpc_completion_queue_destroy(grpc_completion_queue* cq) override;
-  grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, void* tag,
-                                         gpr_timespec deadline,
-                                         void* reserved) override;
-
-  void* gpr_malloc(size_t size) override;
-  void gpr_free(void* p) override;
-
-  void grpc_init() override;
-  void grpc_shutdown() override;
-
-  void gpr_mu_init(gpr_mu* mu) override;
-  void gpr_mu_destroy(gpr_mu* mu) override;
-  void gpr_mu_lock(gpr_mu* mu) override;
-  void gpr_mu_unlock(gpr_mu* mu) override;
-  void gpr_cv_init(gpr_cv* cv) override;
-  void gpr_cv_destroy(gpr_cv* cv) override;
-  int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) override;
-  void gpr_cv_signal(gpr_cv* cv) override;
-  void gpr_cv_broadcast(gpr_cv* cv) override;
-
-  grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
-                                               grpc_status_code status,
-                                               const char* description,
-                                               void* reserved) override;
-  void grpc_call_ref(grpc_call* call) override;
-  void grpc_call_unref(grpc_call* call) override;
-  virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) override;
-
-  grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) override;
-  void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) override;
-
-  int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
-                                   grpc_byte_buffer* buffer) override;
-  void grpc_byte_buffer_reader_destroy(
-      grpc_byte_buffer_reader* reader) override;
-  int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
-                                   grpc_slice* slice) override;
-
-  grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
-                                                size_t nslices) override;
-  grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
-                                           void (*destroy)(void*),
-                                           void* user_data) override;
-  grpc_slice grpc_empty_slice() override;
-  grpc_slice grpc_slice_malloc(size_t length) override;
-  void grpc_slice_unref(grpc_slice slice) override;
-  grpc_slice grpc_slice_ref(grpc_slice slice) override;
-  grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) override;
-  grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) override;
-  grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) override;
-  void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice slice) override;
-  void grpc_slice_buffer_pop(grpc_slice_buffer* sb) override;
-  grpc_slice grpc_slice_from_static_buffer(const void* buffer,
-                                           size_t length) override;
-  grpc_slice grpc_slice_from_copied_buffer(const void* buffer,
-                                           size_t length) override;
-  void grpc_metadata_array_init(grpc_metadata_array* array) override;
-  void grpc_metadata_array_destroy(grpc_metadata_array* array) override;
-
-  gpr_timespec gpr_inf_future(gpr_clock_type type) override;
-  gpr_timespec gpr_time_0(gpr_clock_type type) override;
-
-  virtual const Status& ok() override;
-  virtual const Status& cancelled() override;
-
-  void assert_fail(const char* failed_assertion, const char* file,
-                   int line) override;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/core_codegen.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
diff --git a/include/grpc++/impl/codegen/core_codegen_interface.h b/include/grpc++/impl/codegen/core_codegen_interface.h
index d7ad7a4..03b3f67 100644
--- a/include/grpc++/impl/codegen/core_codegen_interface.h
+++ b/include/grpc++/impl/codegen/core_codegen_interface.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,125 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
 #define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
 
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc/impl/codegen/byte_buffer_reader.h>
-#include <grpc/impl/codegen/grpc_types.h>
-#include <grpc/impl/codegen/sync.h>
-
-namespace grpc {
-
-/// Interface between the codegen library and the minimal subset of core
-/// features required by the generated code.
-///
-/// All undocumented methods are simply forwarding the call to their namesakes.
-/// Please refer to their corresponding documentation for details.
-///
-/// \warning This interface should be considered internal and private.
-class CoreCodegenInterface {
- public:
-  /// Upon a failed assertion, log the error.
-  virtual void assert_fail(const char* failed_assertion, const char* file,
-                           int line) = 0;
-
-  virtual const grpc_completion_queue_factory*
-  grpc_completion_queue_factory_lookup(
-      const grpc_completion_queue_attributes* attributes) = 0;
-  virtual grpc_completion_queue* grpc_completion_queue_create(
-      const grpc_completion_queue_factory* factory,
-      const grpc_completion_queue_attributes* attributes, void* reserved) = 0;
-  virtual grpc_completion_queue* grpc_completion_queue_create_for_next(
-      void* reserved) = 0;
-  virtual grpc_completion_queue* grpc_completion_queue_create_for_pluck(
-      void* reserved) = 0;
-  virtual void grpc_completion_queue_destroy(grpc_completion_queue* cq) = 0;
-  virtual grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq,
-                                                 void* tag,
-                                                 gpr_timespec deadline,
-                                                 void* reserved) = 0;
-
-  virtual void* gpr_malloc(size_t size) = 0;
-  virtual void gpr_free(void* p) = 0;
-
-  // These are only to be used to fix edge cases involving grpc_init and
-  // grpc_shutdown. Calling grpc_init from the codegen interface before
-  // the real grpc_init is called will cause a crash, so if you use this
-  // function, ensure that it is not the first call to grpc_init.
-  virtual void grpc_init() = 0;
-  virtual void grpc_shutdown() = 0;
-
-  virtual void gpr_mu_init(gpr_mu* mu) = 0;
-  virtual void gpr_mu_destroy(gpr_mu* mu) = 0;
-  virtual void gpr_mu_lock(gpr_mu* mu) = 0;
-  virtual void gpr_mu_unlock(gpr_mu* mu) = 0;
-  virtual void gpr_cv_init(gpr_cv* cv) = 0;
-  virtual void gpr_cv_destroy(gpr_cv* cv) = 0;
-  virtual int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu,
-                          gpr_timespec abs_deadline) = 0;
-  virtual void gpr_cv_signal(gpr_cv* cv) = 0;
-  virtual void gpr_cv_broadcast(gpr_cv* cv) = 0;
-
-  virtual grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) = 0;
-  virtual void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) = 0;
-
-  virtual int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
-                                           grpc_byte_buffer* buffer)
-      GRPC_MUST_USE_RESULT = 0;
-  virtual void grpc_byte_buffer_reader_destroy(
-      grpc_byte_buffer_reader* reader) = 0;
-  virtual int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
-                                           grpc_slice* slice) = 0;
-
-  virtual grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
-                                                        size_t nslices) = 0;
-  virtual grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
-                                                   void (*destroy)(void*),
-                                                   void* user_data) = 0;
-  virtual grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
-                                                       grpc_status_code status,
-                                                       const char* description,
-                                                       void* reserved) = 0;
-  virtual void grpc_call_ref(grpc_call* call) = 0;
-  virtual void grpc_call_unref(grpc_call* call) = 0;
-  virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) = 0;
-  virtual grpc_slice grpc_empty_slice() = 0;
-  virtual grpc_slice grpc_slice_malloc(size_t length) = 0;
-  virtual void grpc_slice_unref(grpc_slice slice) = 0;
-  virtual grpc_slice grpc_slice_ref(grpc_slice slice) = 0;
-  virtual grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) = 0;
-  virtual grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) = 0;
-  virtual grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) = 0;
-  virtual void grpc_slice_buffer_add(grpc_slice_buffer* sb,
-                                     grpc_slice slice) = 0;
-  virtual void grpc_slice_buffer_pop(grpc_slice_buffer* sb) = 0;
-  virtual grpc_slice grpc_slice_from_static_buffer(const void* buffer,
-                                                   size_t length) = 0;
-  virtual grpc_slice grpc_slice_from_copied_buffer(const void* buffer,
-                                                   size_t length) = 0;
-
-  virtual void grpc_metadata_array_init(grpc_metadata_array* array) = 0;
-  virtual void grpc_metadata_array_destroy(grpc_metadata_array* array) = 0;
-
-  virtual const Status& ok() = 0;
-  virtual const Status& cancelled() = 0;
-
-  virtual gpr_timespec gpr_inf_future(gpr_clock_type type) = 0;
-  virtual gpr_timespec gpr_time_0(gpr_clock_type type) = 0;
-};
-
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-/// Codegen specific version of \a GPR_ASSERT.
-#define GPR_CODEGEN_ASSERT(x)                                              \
-  do {                                                                     \
-    if (!(x)) {                                                            \
-      grpc::g_core_codegen_interface->assert_fail(#x, __FILE__, __LINE__); \
-    }                                                                      \
-  } while (0)
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
diff --git a/include/grpc++/impl/codegen/create_auth_context.h b/include/grpc++/impl/codegen/create_auth_context.h
index afa6302..ef89229 100644
--- a/include/grpc++/impl/codegen/create_auth_context.h
+++ b/include/grpc++/impl/codegen/create_auth_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,18 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
 #define GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
 
-#include <memory>
-
-#include <grpc++/impl/codegen/security/auth_context.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-namespace grpc {
-
-std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call);
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/create_auth_context.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
diff --git a/include/grpc++/impl/codegen/grpc_library.h b/include/grpc++/impl/codegen/grpc_library.h
index 6036ad7..33c3e25 100644
--- a/include/grpc++/impl/codegen/grpc_library.h
+++ b/include/grpc++/impl/codegen/grpc_library.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,48 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
 #define GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
 
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-
-namespace grpc {
-
-class GrpcLibraryInterface {
- public:
-  virtual void init() = 0;
-  virtual void shutdown() = 0;
-};
-
-/// Initialized by \a grpc::GrpcLibraryInitializer from
-/// <grpc++/impl/grpc_library.h>
-extern GrpcLibraryInterface* g_glip;
-
-/// Classes that require gRPC to be initialized should inherit from this class.
-class GrpcLibraryCodegen {
- public:
-  GrpcLibraryCodegen(bool call_grpc_init = true) : grpc_init_called_(false) {
-    if (call_grpc_init) {
-      GPR_CODEGEN_ASSERT(g_glip &&
-                         "gRPC library not initialized. See "
-                         "grpc::internal::GrpcLibraryInitializer.");
-      g_glip->init();
-      grpc_init_called_ = true;
-    }
-  }
-  virtual ~GrpcLibraryCodegen() {
-    if (grpc_init_called_) {
-      GPR_CODEGEN_ASSERT(g_glip &&
-                         "gRPC library not initialized. See "
-                         "grpc::internal::GrpcLibraryInitializer.");
-      g_glip->shutdown();
-    }
-  }
-
- private:
-  bool grpc_init_called_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/grpc_library.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
diff --git a/include/grpc++/impl/codegen/metadata_map.h b/include/grpc++/impl/codegen/metadata_map.h
index 8dc7211..41c5ad3 100644
--- a/include/grpc++/impl/codegen/metadata_map.h
+++ b/include/grpc++/impl/codegen/metadata_map.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,43 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
 #define GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
 
-#include <grpc++/impl/codegen/slice.h>
-
-namespace grpc {
-
-namespace internal {
-class MetadataMap {
- public:
-  MetadataMap() { memset(&arr_, 0, sizeof(arr_)); }
-
-  ~MetadataMap() {
-    g_core_codegen_interface->grpc_metadata_array_destroy(&arr_);
-  }
-
-  void FillMap() {
-    for (size_t i = 0; i < arr_.count; i++) {
-      // TODO(yangg) handle duplicates?
-      map_.insert(std::pair<grpc::string_ref, grpc::string_ref>(
-          StringRefFromSlice(&arr_.metadata[i].key),
-          StringRefFromSlice(&arr_.metadata[i].value)));
-    }
-  }
-
-  std::multimap<grpc::string_ref, grpc::string_ref>* map() { return &map_; }
-  const std::multimap<grpc::string_ref, grpc::string_ref>* map() const {
-    return &map_;
-  }
-  grpc_metadata_array* arr() { return &arr_; }
-
- private:
-  grpc_metadata_array arr_;
-  std::multimap<grpc::string_ref, grpc::string_ref> map_;
-};
-}  // namespace internal
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/metadata_map.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
diff --git a/include/grpc++/impl/codegen/method_handler_impl.h b/include/grpc++/impl/codegen/method_handler_impl.h
index daf090f..0bdb620 100644
--- a/include/grpc++/impl/codegen/method_handler_impl.h
+++ b/include/grpc++/impl/codegen/method_handler_impl.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,287 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
 #define GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
 
-#include <grpc++/impl/codegen/byte_buffer.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/rpc_service_method.h>
-#include <grpc++/impl/codegen/sync_stream.h>
-
-namespace grpc {
-
-namespace internal {
-
-// Invoke the method handler, fill in the status, and
-// return whether or not we finished safely (without an exception).
-// Note that exception handling is 0-cost in most compiler/library
-// implementations (except when an exception is actually thrown),
-// so this process doesn't require additional overhead in the common case.
-// Additionally, we don't need to return if we caught an exception or not;
-// the handling is the same in either case.
-template <class Callable>
-Status CatchingFunctionHandler(Callable&& handler) {
-#if GRPC_ALLOW_EXCEPTIONS
-  try {
-    return handler();
-  } catch (...) {
-    return Status(StatusCode::UNKNOWN, "Unexpected error in RPC handling");
-  }
-#else   // GRPC_ALLOW_EXCEPTIONS
-  return handler();
-#endif  // GRPC_ALLOW_EXCEPTIONS
-}
-
-/// A wrapper class of an application provided rpc method handler.
-template <class ServiceType, class RequestType, class ResponseType>
-class RpcMethodHandler : public MethodHandler {
- public:
-  RpcMethodHandler(std::function<Status(ServiceType*, ServerContext*,
-                                        const RequestType*, ResponseType*)>
-                       func,
-                   ServiceType* service)
-      : func_(func), service_(service) {}
-
-  void RunHandler(const HandlerParameter& param) final {
-    RequestType req;
-    Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request.bbuf_ptr(), &req);
-    ResponseType rsp;
-    if (status.ok()) {
-      status = CatchingFunctionHandler([this, &param, &req, &rsp] {
-        return func_(service_, param.server_context, &req, &rsp);
-      });
-    }
-
-    GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_);
-    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
-              CallOpServerSendStatus>
-        ops;
-    ops.SendInitialMetadata(param.server_context->initial_metadata_,
-                            param.server_context->initial_metadata_flags());
-    if (param.server_context->compression_level_set()) {
-      ops.set_compression_level(param.server_context->compression_level());
-    }
-    if (status.ok()) {
-      status = ops.SendMessage(rsp);
-    }
-    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
-    param.call->PerformOps(&ops);
-    param.call->cq()->Pluck(&ops);
-  }
-
- private:
-  /// Application provided rpc handler function.
-  std::function<Status(ServiceType*, ServerContext*, const RequestType*,
-                       ResponseType*)>
-      func_;
-  // The class the above handler function lives in.
-  ServiceType* service_;
-};
-
-/// A wrapper class of an application provided client streaming handler.
-template <class ServiceType, class RequestType, class ResponseType>
-class ClientStreamingHandler : public MethodHandler {
- public:
-  ClientStreamingHandler(
-      std::function<Status(ServiceType*, ServerContext*,
-                           ServerReader<RequestType>*, ResponseType*)>
-          func,
-      ServiceType* service)
-      : func_(func), service_(service) {}
-
-  void RunHandler(const HandlerParameter& param) final {
-    ServerReader<RequestType> reader(param.call, param.server_context);
-    ResponseType rsp;
-    Status status = CatchingFunctionHandler([this, &param, &reader, &rsp] {
-      return func_(service_, param.server_context, &reader, &rsp);
-    });
-
-    GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_);
-    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
-              CallOpServerSendStatus>
-        ops;
-    ops.SendInitialMetadata(param.server_context->initial_metadata_,
-                            param.server_context->initial_metadata_flags());
-    if (param.server_context->compression_level_set()) {
-      ops.set_compression_level(param.server_context->compression_level());
-    }
-    if (status.ok()) {
-      status = ops.SendMessage(rsp);
-    }
-    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
-    param.call->PerformOps(&ops);
-    param.call->cq()->Pluck(&ops);
-  }
-
- private:
-  std::function<Status(ServiceType*, ServerContext*, ServerReader<RequestType>*,
-                       ResponseType*)>
-      func_;
-  ServiceType* service_;
-};
-
-/// A wrapper class of an application provided server streaming handler.
-template <class ServiceType, class RequestType, class ResponseType>
-class ServerStreamingHandler : public MethodHandler {
- public:
-  ServerStreamingHandler(
-      std::function<Status(ServiceType*, ServerContext*, const RequestType*,
-                           ServerWriter<ResponseType>*)>
-          func,
-      ServiceType* service)
-      : func_(func), service_(service) {}
-
-  void RunHandler(const HandlerParameter& param) final {
-    RequestType req;
-    Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request.bbuf_ptr(), &req);
-
-    if (status.ok()) {
-      ServerWriter<ResponseType> writer(param.call, param.server_context);
-      status = CatchingFunctionHandler([this, &param, &req, &writer] {
-        return func_(service_, param.server_context, &req, &writer);
-      });
-    }
-
-    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
-    if (!param.server_context->sent_initial_metadata_) {
-      ops.SendInitialMetadata(param.server_context->initial_metadata_,
-                              param.server_context->initial_metadata_flags());
-      if (param.server_context->compression_level_set()) {
-        ops.set_compression_level(param.server_context->compression_level());
-      }
-    }
-    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
-    param.call->PerformOps(&ops);
-    if (param.server_context->has_pending_ops_) {
-      param.call->cq()->Pluck(&param.server_context->pending_ops_);
-    }
-    param.call->cq()->Pluck(&ops);
-  }
-
- private:
-  std::function<Status(ServiceType*, ServerContext*, const RequestType*,
-                       ServerWriter<ResponseType>*)>
-      func_;
-  ServiceType* service_;
-};
-
-/// A wrapper class of an application provided bidi-streaming handler.
-/// This also applies to server-streamed implementation of a unary method
-/// with the additional requirement that such methods must have done a
-/// write for status to be ok
-/// Since this is used by more than 1 class, the service is not passed in.
-/// Instead, it is expected to be an implicitly-captured argument of func
-/// (through bind or something along those lines)
-template <class Streamer, bool WriteNeeded>
-class TemplatedBidiStreamingHandler : public MethodHandler {
- public:
-  TemplatedBidiStreamingHandler(
-      std::function<Status(ServerContext*, Streamer*)> func)
-      : func_(func), write_needed_(WriteNeeded) {}
-
-  void RunHandler(const HandlerParameter& param) final {
-    Streamer stream(param.call, param.server_context);
-    Status status = CatchingFunctionHandler([this, &param, &stream] {
-      return func_(param.server_context, &stream);
-    });
-
-    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
-    if (!param.server_context->sent_initial_metadata_) {
-      ops.SendInitialMetadata(param.server_context->initial_metadata_,
-                              param.server_context->initial_metadata_flags());
-      if (param.server_context->compression_level_set()) {
-        ops.set_compression_level(param.server_context->compression_level());
-      }
-      if (write_needed_ && status.ok()) {
-        // If we needed a write but never did one, we need to mark the
-        // status as a fail
-        status = Status(StatusCode::INTERNAL,
-                        "Service did not provide response message");
-      }
-    }
-    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
-    param.call->PerformOps(&ops);
-    if (param.server_context->has_pending_ops_) {
-      param.call->cq()->Pluck(&param.server_context->pending_ops_);
-    }
-    param.call->cq()->Pluck(&ops);
-  }
-
- private:
-  std::function<Status(ServerContext*, Streamer*)> func_;
-  const bool write_needed_;
-};
-
-template <class ServiceType, class RequestType, class ResponseType>
-class BidiStreamingHandler
-    : public TemplatedBidiStreamingHandler<
-          ServerReaderWriter<ResponseType, RequestType>, false> {
- public:
-  BidiStreamingHandler(
-      std::function<Status(ServiceType*, ServerContext*,
-                           ServerReaderWriter<ResponseType, RequestType>*)>
-          func,
-      ServiceType* service)
-      : TemplatedBidiStreamingHandler<
-            ServerReaderWriter<ResponseType, RequestType>, false>(std::bind(
-            func, service, std::placeholders::_1, std::placeholders::_2)) {}
-};
-
-template <class RequestType, class ResponseType>
-class StreamedUnaryHandler
-    : public TemplatedBidiStreamingHandler<
-          ServerUnaryStreamer<RequestType, ResponseType>, true> {
- public:
-  explicit StreamedUnaryHandler(
-      std::function<Status(ServerContext*,
-                           ServerUnaryStreamer<RequestType, ResponseType>*)>
-          func)
-      : TemplatedBidiStreamingHandler<
-            ServerUnaryStreamer<RequestType, ResponseType>, true>(func) {}
-};
-
-template <class RequestType, class ResponseType>
-class SplitServerStreamingHandler
-    : public TemplatedBidiStreamingHandler<
-          ServerSplitStreamer<RequestType, ResponseType>, false> {
- public:
-  explicit SplitServerStreamingHandler(
-      std::function<Status(ServerContext*,
-                           ServerSplitStreamer<RequestType, ResponseType>*)>
-          func)
-      : TemplatedBidiStreamingHandler<
-            ServerSplitStreamer<RequestType, ResponseType>, false>(func) {}
-};
-
-/// Handle unknown method by returning UNIMPLEMENTED error.
-class UnknownMethodHandler : public MethodHandler {
- public:
-  template <class T>
-  static void FillOps(ServerContext* context, T* ops) {
-    Status status(StatusCode::UNIMPLEMENTED, "");
-    if (!context->sent_initial_metadata_) {
-      ops->SendInitialMetadata(context->initial_metadata_,
-                               context->initial_metadata_flags());
-      if (context->compression_level_set()) {
-        ops->set_compression_level(context->compression_level());
-      }
-      context->sent_initial_metadata_ = true;
-    }
-    ops->ServerSendStatus(context->trailing_metadata_, status);
-  }
-
-  void RunHandler(const HandlerParameter& param) final {
-    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
-    FillOps(param.server_context, &ops);
-    param.call->PerformOps(&ops);
-    param.call->cq()->Pluck(&ops);
-  }
-};
-
-}  // namespace internal
-}  // namespace grpc
+#include <grpcpp/impl/codegen/method_handler_impl.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
diff --git a/include/grpc++/impl/codegen/proto_utils.h b/include/grpc++/impl/codegen/proto_utils.h
index 209250b..1f47884 100644
--- a/include/grpc++/impl/codegen/proto_utils.h
+++ b/include/grpc++/impl/codegen/proto_utils.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,253 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H
 #define GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H
 
-#include <type_traits>
-
-#include <grpc++/impl/codegen/config_protobuf.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/serialization_traits.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc/impl/codegen/byte_buffer_reader.h>
-#include <grpc/impl/codegen/grpc_types.h>
-#include <grpc/impl/codegen/slice.h>
-
-namespace grpc {
-
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-namespace internal {
-
-class GrpcBufferWriterPeer;
-
-const int kGrpcBufferWriterMaxBufferLength = 1024 * 1024;
-
-class GrpcBufferWriter : public ::grpc::protobuf::io::ZeroCopyOutputStream {
- public:
-  GrpcBufferWriter(grpc_byte_buffer** bp, int block_size, int total_size)
-      : block_size_(block_size),
-        total_size_(total_size),
-        byte_count_(0),
-        have_backup_(false) {
-    *bp = g_core_codegen_interface->grpc_raw_byte_buffer_create(NULL, 0);
-    slice_buffer_ = &(*bp)->data.raw.slice_buffer;
-  }
-
-  ~GrpcBufferWriter() override {
-    if (have_backup_) {
-      g_core_codegen_interface->grpc_slice_unref(backup_slice_);
-    }
-  }
-
-  bool Next(void** data, int* size) override {
-    // Protobuf should not ask for more memory than total_size_.
-    GPR_CODEGEN_ASSERT(byte_count_ < total_size_);
-    size_t remain = total_size_ - byte_count_;
-    if (have_backup_) {
-      slice_ = backup_slice_;
-      have_backup_ = false;
-      if (GRPC_SLICE_LENGTH(slice_) > remain) {
-        GRPC_SLICE_SET_LENGTH(slice_, remain);
-      }
-    } else {
-      // When less than a whole block is needed, only allocate that much.
-      // But make sure the allocated slice is not inlined.
-      size_t allocate_length =
-          remain > static_cast<size_t>(block_size_) ? block_size_ : remain;
-      slice_ = g_core_codegen_interface->grpc_slice_malloc(
-          allocate_length > GRPC_SLICE_INLINED_SIZE
-              ? allocate_length
-              : GRPC_SLICE_INLINED_SIZE + 1);
-    }
-    *data = GRPC_SLICE_START_PTR(slice_);
-    // On win x64, int is only 32bit
-    GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
-    byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_);
-    g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_);
-    return true;
-  }
-
-  void BackUp(int count) override {
-    g_core_codegen_interface->grpc_slice_buffer_pop(slice_buffer_);
-    if ((size_t)count == GRPC_SLICE_LENGTH(slice_)) {
-      backup_slice_ = slice_;
-    } else {
-      backup_slice_ = g_core_codegen_interface->grpc_slice_split_tail(
-          &slice_, GRPC_SLICE_LENGTH(slice_) - count);
-      g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_);
-    }
-    // It's dangerous to keep an inlined grpc_slice as the backup slice, since
-    // on a following Next() call, a reference will be returned to this slice
-    // via GRPC_SLICE_START_PTR, which will not be an adddress held by
-    // slice_buffer_.
-    have_backup_ = backup_slice_.refcount != NULL;
-    byte_count_ -= count;
-  }
-
-  grpc::protobuf::int64 ByteCount() const override { return byte_count_; }
-
- protected:
-  friend class GrpcBufferWriterPeer;
-  const int block_size_;
-  const int total_size_;
-  int64_t byte_count_;
-  grpc_slice_buffer* slice_buffer_;
-  bool have_backup_;
-  grpc_slice backup_slice_;
-  grpc_slice slice_;
-};
-
-class GrpcBufferReader : public ::grpc::protobuf::io::ZeroCopyInputStream {
- public:
-  explicit GrpcBufferReader(grpc_byte_buffer* buffer)
-      : byte_count_(0), backup_count_(0), status_() {
-    if (!g_core_codegen_interface->grpc_byte_buffer_reader_init(&reader_,
-                                                                buffer)) {
-      status_ = Status(StatusCode::INTERNAL,
-                       "Couldn't initialize byte buffer reader");
-    }
-  }
-  ~GrpcBufferReader() override {
-    g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader_);
-  }
-
-  bool Next(const void** data, int* size) override {
-    if (!status_.ok()) {
-      return false;
-    }
-    if (backup_count_ > 0) {
-      *data = GRPC_SLICE_START_PTR(slice_) + GRPC_SLICE_LENGTH(slice_) -
-              backup_count_;
-      GPR_CODEGEN_ASSERT(backup_count_ <= INT_MAX);
-      *size = (int)backup_count_;
-      backup_count_ = 0;
-      return true;
-    }
-    if (!g_core_codegen_interface->grpc_byte_buffer_reader_next(&reader_,
-                                                                &slice_)) {
-      return false;
-    }
-    g_core_codegen_interface->grpc_slice_unref(slice_);
-    *data = GRPC_SLICE_START_PTR(slice_);
-    // On win x64, int is only 32bit
-    GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
-    byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_);
-    return true;
-  }
-
-  Status status() const { return status_; }
-
-  void BackUp(int count) override { backup_count_ = count; }
-
-  bool Skip(int count) override {
-    const void* data;
-    int size;
-    while (Next(&data, &size)) {
-      if (size >= count) {
-        BackUp(size - count);
-        return true;
-      }
-      // size < count;
-      count -= size;
-    }
-    // error or we have too large count;
-    return false;
-  }
-
-  grpc::protobuf::int64 ByteCount() const override {
-    return byte_count_ - backup_count_;
-  }
-
- protected:
-  int64_t byte_count_;
-  int64_t backup_count_;
-  grpc_byte_buffer_reader reader_;
-  grpc_slice slice_;
-  Status status_;
-};
-
-// BufferWriter must be a subclass of io::ZeroCopyOutputStream.
-template <class BufferWriter, class T>
-Status GenericSerialize(const grpc::protobuf::Message& msg,
-                        grpc_byte_buffer** bp, bool* own_buffer) {
-  static_assert(
-      std::is_base_of<protobuf::io::ZeroCopyOutputStream, BufferWriter>::value,
-      "BufferWriter must be a subclass of io::ZeroCopyOutputStream");
-  *own_buffer = true;
-  int byte_size = msg.ByteSize();
-  if ((size_t)byte_size <= GRPC_SLICE_INLINED_SIZE) {
-    grpc_slice slice = g_core_codegen_interface->grpc_slice_malloc(byte_size);
-    GPR_CODEGEN_ASSERT(
-        GRPC_SLICE_END_PTR(slice) ==
-        msg.SerializeWithCachedSizesToArray(GRPC_SLICE_START_PTR(slice)));
-    *bp = g_core_codegen_interface->grpc_raw_byte_buffer_create(&slice, 1);
-    g_core_codegen_interface->grpc_slice_unref(slice);
-
-    return g_core_codegen_interface->ok();
-  }
-  BufferWriter writer(bp, kGrpcBufferWriterMaxBufferLength, byte_size);
-  return msg.SerializeToZeroCopyStream(&writer)
-             ? g_core_codegen_interface->ok()
-             : Status(StatusCode::INTERNAL, "Failed to serialize message");
-}
-
-// BufferReader must be a subclass of io::ZeroCopyInputStream.
-template <class BufferReader, class T>
-Status GenericDeserialize(grpc_byte_buffer* buffer,
-                          grpc::protobuf::Message* msg) {
-  static_assert(
-      std::is_base_of<protobuf::io::ZeroCopyInputStream, BufferReader>::value,
-      "BufferReader must be a subclass of io::ZeroCopyInputStream");
-  if (buffer == nullptr) {
-    return Status(StatusCode::INTERNAL, "No payload");
-  }
-  Status result = g_core_codegen_interface->ok();
-  {
-    BufferReader reader(buffer);
-    if (!reader.status().ok()) {
-      return reader.status();
-    }
-    ::grpc::protobuf::io::CodedInputStream decoder(&reader);
-    decoder.SetTotalBytesLimit(INT_MAX, INT_MAX);
-    if (!msg->ParseFromCodedStream(&decoder)) {
-      result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
-    }
-    if (!decoder.ConsumedEntireMessage()) {
-      result = Status(StatusCode::INTERNAL, "Did not read entire message");
-    }
-  }
-  g_core_codegen_interface->grpc_byte_buffer_destroy(buffer);
-  return result;
-}
-
-}  // namespace internal
-
-// this is needed so the following class does not conflict with protobuf
-// serializers that utilize internal-only tools.
-#ifdef GRPC_OPEN_SOURCE_PROTO
-// This class provides a protobuf serializer. It translates between protobuf
-// objects and grpc_byte_buffers. More information about SerializationTraits can
-// be found in include/grpc++/impl/codegen/serialization_traits.h.
-template <class T>
-class SerializationTraits<T, typename std::enable_if<std::is_base_of<
-                                 grpc::protobuf::Message, T>::value>::type> {
- public:
-  static Status Serialize(const grpc::protobuf::Message& msg,
-                          grpc_byte_buffer** bp, bool* own_buffer) {
-    return internal::GenericSerialize<internal::GrpcBufferWriter, T>(
-        msg, bp, own_buffer);
-  }
-
-  static Status Deserialize(grpc_byte_buffer* buffer,
-                            grpc::protobuf::Message* msg) {
-    return internal::GenericDeserialize<internal::GrpcBufferReader, T>(buffer,
-                                                                       msg);
-  }
-};
-#endif
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/proto_utils.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H
diff --git a/include/grpc++/impl/codegen/rpc_method.h b/include/grpc++/impl/codegen/rpc_method.h
index 54e5236..2906c74 100644
--- a/include/grpc++/impl/codegen/rpc_method.h
+++ b/include/grpc++/impl/codegen/rpc_method.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,46 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_RPC_METHOD_H
 #define GRPCXX_IMPL_CODEGEN_RPC_METHOD_H
 
-#include <memory>
-
-#include <grpc++/impl/codegen/channel_interface.h>
-
-namespace grpc {
-namespace internal {
-/// Descriptor of an RPC method
-class RpcMethod {
- public:
-  enum RpcType {
-    NORMAL_RPC = 0,
-    CLIENT_STREAMING,  // request streaming
-    SERVER_STREAMING,  // response streaming
-    BIDI_STREAMING
-  };
-
-  RpcMethod(const char* name, RpcType type)
-      : name_(name), method_type_(type), channel_tag_(NULL) {}
-
-  RpcMethod(const char* name, RpcType type,
-            const std::shared_ptr<ChannelInterface>& channel)
-      : name_(name),
-        method_type_(type),
-        channel_tag_(channel->RegisterMethod(name)) {}
-
-  const char* name() const { return name_; }
-  RpcType method_type() const { return method_type_; }
-  void SetMethodType(RpcType type) { method_type_ = type; }
-  void* channel_tag() const { return channel_tag_; }
-
- private:
-  const char* const name_;
-  RpcType method_type_;
-  void* const channel_tag_;
-};
-
-}  // namespace internal
-}  // namespace grpc
+#include <grpcpp/impl/codegen/rpc_method.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_RPC_METHOD_H
diff --git a/include/grpc++/impl/codegen/rpc_service_method.h b/include/grpc++/impl/codegen/rpc_service_method.h
index 5ba11e8..999c0d5 100644
--- a/include/grpc++/impl/codegen/rpc_service_method.h
+++ b/include/grpc++/impl/codegen/rpc_service_method.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,63 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
 #define GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
 
-#include <climits>
-#include <functional>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <grpc++/impl/codegen/byte_buffer.h>
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/rpc_method.h>
-#include <grpc++/impl/codegen/status.h>
-
-namespace grpc {
-class ServerContext;
-
-namespace internal {
-/// Base class for running an RPC handler.
-class MethodHandler {
- public:
-  virtual ~MethodHandler() {}
-  struct HandlerParameter {
-    HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req)
-        : call(c), server_context(context) {
-      request.set_buffer(req);
-    }
-    ~HandlerParameter() { request.Release(); }
-    Call* call;
-    ServerContext* server_context;
-    // Handler required to destroy these contents
-    ByteBuffer request;
-  };
-  virtual void RunHandler(const HandlerParameter& param) = 0;
-};
-
-/// Server side rpc method class
-class RpcServiceMethod : public RpcMethod {
- public:
-  /// Takes ownership of the handler
-  RpcServiceMethod(const char* name, RpcMethod::RpcType type,
-                   MethodHandler* handler)
-      : RpcMethod(name, type), server_tag_(nullptr), handler_(handler) {}
-
-  void set_server_tag(void* tag) { server_tag_ = tag; }
-  void* server_tag() const { return server_tag_; }
-  /// if MethodHandler is nullptr, then this is an async method
-  MethodHandler* handler() const { return handler_.get(); }
-  void ResetHandler() { handler_.reset(); }
-  void SetHandler(MethodHandler* handler) { handler_.reset(handler); }
-
- private:
-  void* server_tag_;
-  std::unique_ptr<MethodHandler> handler_;
-};
-}  // namespace internal
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/rpc_service_method.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
diff --git a/include/grpc++/impl/codegen/security/auth_context.h b/include/grpc++/impl/codegen/security/auth_context.h
index 337da91..b466373 100644
--- a/include/grpc++/impl/codegen/security/auth_context.h
+++ b/include/grpc++/impl/codegen/security/auth_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,80 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
 #define GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
 
-#include <iterator>
-#include <vector>
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/string_ref.h>
-
-struct grpc_auth_context;
-struct grpc_auth_property;
-struct grpc_auth_property_iterator;
-
-namespace grpc {
-class SecureAuthContext;
-
-typedef std::pair<grpc::string_ref, grpc::string_ref> AuthProperty;
-
-class AuthPropertyIterator
-    : public std::iterator<std::input_iterator_tag, const AuthProperty> {
- public:
-  ~AuthPropertyIterator();
-  AuthPropertyIterator& operator++();
-  AuthPropertyIterator operator++(int);
-  bool operator==(const AuthPropertyIterator& rhs) const;
-  bool operator!=(const AuthPropertyIterator& rhs) const;
-  const AuthProperty operator*();
-
- protected:
-  AuthPropertyIterator();
-  AuthPropertyIterator(const grpc_auth_property* property,
-                       const grpc_auth_property_iterator* iter);
-
- private:
-  friend class SecureAuthContext;
-  const grpc_auth_property* property_;
-  // The following items form a grpc_auth_property_iterator.
-  const grpc_auth_context* ctx_;
-  size_t index_;
-  const char* name_;
-};
-
-/// Class encapsulating the Authentication Information.
-///
-/// It includes the secure identity of the peer, the type of secure transport
-/// used as well as any other properties required by the authorization layer.
-class AuthContext {
- public:
-  virtual ~AuthContext() {}
-
-  /// Returns true if the peer is authenticated.
-  virtual bool IsPeerAuthenticated() const = 0;
-
-  /// A peer identity.
-  ///
-  /// It is, in general, comprised of one or more properties (in which case they
-  /// have the same name).
-  virtual std::vector<grpc::string_ref> GetPeerIdentity() const = 0;
-  virtual grpc::string GetPeerIdentityPropertyName() const = 0;
-
-  /// Returns all the property values with the given name.
-  virtual std::vector<grpc::string_ref> FindPropertyValues(
-      const grpc::string& name) const = 0;
-
-  /// Iteration over all the properties.
-  virtual AuthPropertyIterator begin() const = 0;
-  virtual AuthPropertyIterator end() const = 0;
-
-  /// Mutation functions: should only be used by an AuthMetadataProcessor.
-  virtual void AddProperty(const grpc::string& key,
-                           const grpc::string_ref& value) = 0;
-  virtual bool SetPeerIdentityPropertyName(const grpc::string& name) = 0;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/security/auth_context.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
diff --git a/include/grpc++/impl/codegen/serialization_traits.h b/include/grpc++/impl/codegen/serialization_traits.h
index 4d91739..480575d 100644
--- a/include/grpc++/impl/codegen/serialization_traits.h
+++ b/include/grpc++/impl/codegen/serialization_traits.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,47 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
 #define GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
 
-namespace grpc {
-
-/// Defines how to serialize and deserialize some type.
-///
-/// Used for hooking different message serialization API's into GRPC.
-/// Each SerializationTraits<Message> implementation must provide the
-/// following functions:
-/// 1.  static Status Serialize(const Message& msg,
-///                             ByteBuffer* buffer,
-///                             bool* own_buffer);
-///     OR
-///     static Status Serialize(const Message& msg,
-///                             grpc_byte_buffer** buffer,
-///                             bool* own_buffer);
-///     The former is preferred; the latter is deprecated
-///
-/// 2.  static Status Deserialize(ByteBuffer* buffer,
-///                               Message* msg);
-///     OR
-///     static Status Deserialize(grpc_byte_buffer* buffer,
-///                               Message* msg);
-///     The former is preferred; the latter is deprecated
-///
-/// Serialize is required to convert message to a ByteBuffer, and
-/// return that byte buffer through *buffer. *own_buffer should
-/// be set to true if the caller owns said byte buffer, or false if
-/// ownership is retained elsewhere.
-///
-/// Deserialize is required to convert buffer into the message stored at
-/// msg. max_receive_message_size is passed in as a bound on the maximum
-/// number of message bytes Deserialize should accept.
-///
-/// Both functions return a Status, allowing them to explain what went
-/// wrong if required.
-template <class Message,
-          class UnusedButHereForPartialTemplateSpecialization = void>
-class SerializationTraits;
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/serialization_traits.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
diff --git a/include/grpc++/impl/codegen/server_context.h b/include/grpc++/impl/codegen/server_context.h
index 57347f4..1c3342d 100644
--- a/include/grpc++/impl/codegen/server_context.h
+++ b/include/grpc++/impl/codegen/server_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,294 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H
 #define GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H
 
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <grpc/impl/codegen/compression_types.h>
-
-#include <grpc++/impl/codegen/call.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/create_auth_context.h>
-#include <grpc++/impl/codegen/metadata_map.h>
-#include <grpc++/impl/codegen/security/auth_context.h>
-#include <grpc++/impl/codegen/string_ref.h>
-#include <grpc++/impl/codegen/time.h>
-
-struct grpc_metadata;
-struct grpc_call;
-struct census_context;
-
-namespace grpc {
-class ClientContext;
-template <class W, class R>
-class ServerAsyncReader;
-template <class W>
-class ServerAsyncWriter;
-template <class W>
-class ServerAsyncResponseWriter;
-template <class W, class R>
-class ServerAsyncReaderWriter;
-template <class R>
-class ServerReader;
-template <class W>
-class ServerWriter;
-namespace internal {
-template <class W, class R>
-class ServerReaderWriterBody;
-template <class ServiceType, class RequestType, class ResponseType>
-class RpcMethodHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ClientStreamingHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class ServerStreamingHandler;
-template <class ServiceType, class RequestType, class ResponseType>
-class BidiStreamingHandler;
-class UnknownMethodHandler;
-template <class Streamer, bool WriteNeeded>
-class TemplatedBidiStreamingHandler;
-class Call;
-}  // namespace internal
-
-class CompletionQueue;
-class Server;
-class ServerInterface;
-
-namespace testing {
-class InteropServerContextInspector;
-class ServerContextTestSpouse;
-}  // namespace testing
-
-/// A ServerContext allows the person implementing a service handler to:
-///
-/// - Add custom initial and trailing metadata key-value pairs that will
-///   propagated to the client side.
-/// - Control call settings such as compression and authentication.
-/// - Access metadata coming from the client.
-/// - Get performance metrics (ie, census).
-///
-/// Context settings are only relevant to the call handler they are supplied to,
-/// that is to say, they aren't sticky across multiple calls. Some of these
-/// settings, such as the compression options, can be made persistant at server
-/// construction time by specifying the approriate \a ChannelArguments
-/// to a \a grpc::ServerBuilder, via \a ServerBuilder::AddChannelArgument.
-///
-/// \warning ServerContext instances should \em not be reused across rpcs.
-class ServerContext {
- public:
-  ServerContext();  // for async calls
-  ~ServerContext();
-
-  /// Return the deadline for the server call.
-  std::chrono::system_clock::time_point deadline() const {
-    return Timespec2Timepoint(deadline_);
-  }
-
-  /// Return a \a gpr_timespec representation of the server call's deadline.
-  gpr_timespec raw_deadline() const { return deadline_; }
-
-  /// Add the (\a meta_key, \a meta_value) pair to the initial metadata
-  /// associated with a server call. These are made available at the client side
-  /// by the \a grpc::ClientContext::GetServerInitialMetadata() method.
-  ///
-  /// \warning This method should only be called before sending initial metadata
-  /// to the client (which can happen explicitly, or implicitly when sending a
-  /// a response message or status to the client).
-  ///
-  /// \param meta_key The metadata key. If \a meta_value is binary data, it must
-  /// end in "-bin".
-  /// \param meta_value The metadata value. If its value is binary, the key name
-  /// must end in "-bin".
-  void AddInitialMetadata(const grpc::string& key, const grpc::string& value);
-
-  /// Add the (\a meta_key, \a meta_value) pair to the initial metadata
-  /// associated with a server call. These are made available at the client
-  /// side by the \a grpc::ClientContext::GetServerTrailingMetadata() method.
-  ///
-  /// \warning This method should only be called before sending trailing
-  /// metadata to the client (which happens when the call is finished and a
-  /// status is sent to the client).
-  ///
-  /// \param meta_key The metadata key. If \a meta_value is binary data,
-  /// it must end in "-bin".
-  /// \param meta_value The metadata value. If its value is binary, the key name
-  /// must end in "-bin".
-  void AddTrailingMetadata(const grpc::string& key, const grpc::string& value);
-
-  /// IsCancelled is always safe to call when using sync API.
-  /// When using async API, it is only safe to call IsCancelled after
-  /// the AsyncNotifyWhenDone tag has been delivered.
-  bool IsCancelled() const;
-
-  /// Cancel the Call from the server. This is a best-effort API and
-  /// depending on when it is called, the RPC may still appear successful to
-  /// the client.
-  /// For example, if TryCancel() is called on a separate thread, it might race
-  /// with the server handler which might return success to the client before
-  /// TryCancel() was even started by the thread.
-  ///
-  /// It is the caller's responsibility to prevent such races and ensure that if
-  /// TryCancel() is called, the serverhandler must return Status::CANCELLED.
-  /// The only exception is that if the serverhandler is already returning an
-  /// error status code, it is ok to not return Status::CANCELLED even if
-  /// TryCancel() was called.
-  ///
-  /// Note that TryCancel() does not change any of the tags that are pending
-  /// on the completion queue. All pending tags will still be delivered
-  /// (though their ok result may reflect the effect of cancellation).
-  void TryCancel() const;
-
-  /// Return a collection of initial metadata key-value pairs sent from the
-  /// client. Note that keys may happen more than
-  /// once (ie, a \a std::multimap is returned).
-  ///
-  /// It is safe to use this method after initial metadata has been received,
-  /// Calls always begin with the client sending initial metadata, so this is
-  /// safe to access as soon as the call has begun on the server side.
-  ///
-  /// \return A multimap of initial metadata key-value pairs from the server.
-  const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata()
-      const {
-    return *client_metadata_.map();
-  }
-
-  /// Return the compression algorithm to be used by the server call.
-  grpc_compression_level compression_level() const {
-    return compression_level_;
-  }
-
-  /// Set \a algorithm to be the compression algorithm used for the server call.
-  ///
-  /// \param algorithm The compression algorithm used for the server call.
-  void set_compression_level(grpc_compression_level level) {
-    compression_level_set_ = true;
-    compression_level_ = level;
-  }
-
-  /// Return a bool indicating whether the compression level for this call
-  /// has been set (either implicitly or through a previous call to
-  /// \a set_compression_level.
-  bool compression_level_set() const { return compression_level_set_; }
-
-  /// Return the compression algorithm the server call will request be used.
-  /// Note that the gRPC runtime may decide to ignore this request, for example,
-  /// due to resource constraints, or if the server is aware the client doesn't
-  /// support the requested algorithm.
-  grpc_compression_algorithm compression_algorithm() const {
-    return compression_algorithm_;
-  }
-  /// Set \a algorithm to be the compression algorithm used for the server call.
-  ///
-  /// \param algorithm The compression algorithm used for the server call.
-  void set_compression_algorithm(grpc_compression_algorithm algorithm);
-
-  /// Set the load reporting costs in \a cost_data for the call.
-  void SetLoadReportingCosts(const std::vector<grpc::string>& cost_data);
-
-  /// Return the authentication context for this server call.
-  ///
-  /// \see grpc::AuthContext.
-  std::shared_ptr<const AuthContext> auth_context() const {
-    if (auth_context_.get() == nullptr) {
-      auth_context_ = CreateAuthContext(call_);
-    }
-    return auth_context_;
-  }
-
-  /// Return the peer uri in a string.
-  /// WARNING: this value is never authenticated or subject to any security
-  /// related code. It must not be used for any authentication related
-  /// functionality. Instead, use auth_context.
-  grpc::string peer() const;
-
-  /// Get the census context associated with this server call.
-  const struct census_context* census_context() const;
-
-  /// Async only. Has to be called before the rpc starts.
-  /// Returns the tag in completion queue when the rpc finishes.
-  /// IsCancelled() can then be called to check whether the rpc was cancelled.
-  void AsyncNotifyWhenDone(void* tag) {
-    has_notify_when_done_tag_ = true;
-    async_notify_when_done_tag_ = tag;
-  }
-
-  /// Should be used for framework-level extensions only.
-  /// Applications never need to call this method.
-  grpc_call* c_call() { return call_; }
-
- private:
-  friend class ::grpc::testing::InteropServerContextInspector;
-  friend class ::grpc::testing::ServerContextTestSpouse;
-  friend class ::grpc::ServerInterface;
-  friend class ::grpc::Server;
-  template <class W, class R>
-  friend class ::grpc::ServerAsyncReader;
-  template <class W>
-  friend class ::grpc::ServerAsyncWriter;
-  template <class W>
-  friend class ::grpc::ServerAsyncResponseWriter;
-  template <class W, class R>
-  friend class ::grpc::ServerAsyncReaderWriter;
-  template <class R>
-  friend class ::grpc::ServerReader;
-  template <class W>
-  friend class ::grpc::ServerWriter;
-  template <class W, class R>
-  friend class ::grpc::internal::ServerReaderWriterBody;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::RpcMethodHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::ClientStreamingHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class ::grpc::internal::ServerStreamingHandler;
-  template <class Streamer, bool WriteNeeded>
-  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
-  friend class ::grpc::internal::UnknownMethodHandler;
-  friend class ::grpc::ClientContext;
-
-  /// Prevent copying.
-  ServerContext(const ServerContext&);
-  ServerContext& operator=(const ServerContext&);
-
-  class CompletionOp;
-
-  void BeginCompletionOp(internal::Call* call);
-  /// Return the tag queued by BeginCompletionOp()
-  internal::CompletionQueueTag* GetCompletionOpTag();
-
-  ServerContext(gpr_timespec deadline, grpc_metadata_array* arr);
-
-  void set_call(grpc_call* call) { call_ = call; }
-
-  uint32_t initial_metadata_flags() const { return 0; }
-
-  CompletionOp* completion_op_;
-  bool has_notify_when_done_tag_;
-  void* async_notify_when_done_tag_;
-
-  gpr_timespec deadline_;
-  grpc_call* call_;
-  CompletionQueue* cq_;
-  bool sent_initial_metadata_;
-  mutable std::shared_ptr<const AuthContext> auth_context_;
-  internal::MetadataMap client_metadata_;
-  std::multimap<grpc::string, grpc::string> initial_metadata_;
-  std::multimap<grpc::string, grpc::string> trailing_metadata_;
-
-  bool compression_level_set_;
-  grpc_compression_level compression_level_;
-  grpc_compression_algorithm compression_algorithm_;
-
-  internal::CallOpSet<internal::CallOpSendInitialMetadata,
-                      internal::CallOpSendMessage>
-      pending_ops_;
-  bool has_pending_ops_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/server_context.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H
diff --git a/include/grpc++/impl/codegen/server_interface.h b/include/grpc++/impl/codegen/server_interface.h
index 3bcf4c8..ceea44c 100644
--- a/include/grpc++/impl/codegen/server_interface.h
+++ b/include/grpc++/impl/codegen/server_interface.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,259 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H
 #define GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H
 
-#include <grpc++/impl/codegen/call_hook.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/rpc_service_method.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-namespace grpc {
-
-class AsyncGenericService;
-class Channel;
-class GenericServerContext;
-class ServerCompletionQueue;
-class ServerContext;
-class ServerCredentials;
-class Service;
-
-extern CoreCodegenInterface* g_core_codegen_interface;
-
-/// Models a gRPC server.
-///
-/// Servers are configured and started via \a grpc::ServerBuilder.
-namespace internal {
-class ServerAsyncStreamingInterface;
-}  // namespace internal
-
-class ServerInterface : public internal::CallHook {
- public:
-  virtual ~ServerInterface() {}
-
-  /// Shutdown the server, blocking until all rpc processing finishes.
-  /// Forcefully terminate pending calls after \a deadline expires.
-  ///
-  /// All completion queue associated with the server (for example, for async
-  /// serving) must be shutdown *after* this method has returned:
-  /// See \a ServerBuilder::AddCompletionQueue for details.
-  ///
-  /// \param deadline How long to wait until pending rpcs are forcefully
-  /// terminated.
-  template <class T>
-  void Shutdown(const T& deadline) {
-    ShutdownInternal(TimePoint<T>(deadline).raw_time());
-  }
-
-  /// Shutdown the server, waiting for all rpc processing to finish.
-  ///
-  /// All completion queue associated with the server (for example, for async
-  /// serving) must be shutdown *after* this method has returned:
-  /// See \a ServerBuilder::AddCompletionQueue for details.
-  void Shutdown() {
-    ShutdownInternal(
-        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_MONOTONIC));
-  }
-
-  /// Block waiting for all work to complete.
-  ///
-  /// \warning The server must be either shutting down or some other thread must
-  /// call \a Shutdown for this function to ever return.
-  virtual void Wait() = 0;
-
- protected:
-  friend class ::grpc::Service;
-
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the Server instance.
-  virtual bool RegisterService(const grpc::string* host, Service* service) = 0;
-
-  /// Register a generic service. This call does not take ownership of the
-  /// service. The service must exist for the lifetime of the Server instance.
-  virtual void RegisterAsyncGenericService(AsyncGenericService* service) = 0;
-
-  /// Tries to bind \a server to the given \a addr.
-  ///
-  /// It can be invoked multiple times.
-  ///
-  /// \param addr The address to try to bind to the server (eg, localhost:1234,
-  /// 192.168.1.1:31416, [::1]:27182, etc.).
-  /// \params creds The credentials associated with the server.
-  ///
-  /// \return bound port number on sucess, 0 on failure.
-  ///
-  /// \warning It's an error to call this method on an already started server.
-  virtual int AddListeningPort(const grpc::string& addr,
-                               ServerCredentials* creds) = 0;
-
-  /// Start the server.
-  ///
-  /// \param cqs Completion queues for handling asynchronous services. The
-  /// caller is required to keep all completion queues live until the server is
-  /// destroyed.
-  /// \param num_cqs How many completion queues does \a cqs hold.
-  virtual void Start(ServerCompletionQueue** cqs, size_t num_cqs) = 0;
-
-  virtual void ShutdownInternal(gpr_timespec deadline) = 0;
-
-  virtual int max_receive_message_size() const = 0;
-
-  virtual grpc_server* server() = 0;
-
-  virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                                internal::Call* call) = 0;
-
-  class BaseAsyncRequest : public internal::CompletionQueueTag {
-   public:
-    BaseAsyncRequest(ServerInterface* server, ServerContext* context,
-                     internal::ServerAsyncStreamingInterface* stream,
-                     CompletionQueue* call_cq, void* tag,
-                     bool delete_on_finalize);
-    virtual ~BaseAsyncRequest();
-
-    bool FinalizeResult(void** tag, bool* status) override;
-
-   protected:
-    ServerInterface* const server_;
-    ServerContext* const context_;
-    internal::ServerAsyncStreamingInterface* const stream_;
-    CompletionQueue* const call_cq_;
-    void* const tag_;
-    const bool delete_on_finalize_;
-    grpc_call* call_;
-  };
-
-  class RegisteredAsyncRequest : public BaseAsyncRequest {
-   public:
-    RegisteredAsyncRequest(ServerInterface* server, ServerContext* context,
-                           internal::ServerAsyncStreamingInterface* stream,
-                           CompletionQueue* call_cq, void* tag);
-
-    // uses BaseAsyncRequest::FinalizeResult
-
-   protected:
-    void IssueRequest(void* registered_method, grpc_byte_buffer** payload,
-                      ServerCompletionQueue* notification_cq);
-  };
-
-  class NoPayloadAsyncRequest final : public RegisteredAsyncRequest {
-   public:
-    NoPayloadAsyncRequest(void* registered_method, ServerInterface* server,
-                          ServerContext* context,
-                          internal::ServerAsyncStreamingInterface* stream,
-                          CompletionQueue* call_cq,
-                          ServerCompletionQueue* notification_cq, void* tag)
-        : RegisteredAsyncRequest(server, context, stream, call_cq, tag) {
-      IssueRequest(registered_method, nullptr, notification_cq);
-    }
-
-    // uses RegisteredAsyncRequest::FinalizeResult
-  };
-
-  template <class Message>
-  class PayloadAsyncRequest final : public RegisteredAsyncRequest {
-   public:
-    PayloadAsyncRequest(void* registered_method, ServerInterface* server,
-                        ServerContext* context,
-                        internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag,
-                        Message* request)
-        : RegisteredAsyncRequest(server, context, stream, call_cq, tag),
-          registered_method_(registered_method),
-          server_(server),
-          context_(context),
-          stream_(stream),
-          call_cq_(call_cq),
-          notification_cq_(notification_cq),
-          tag_(tag),
-          request_(request) {
-      IssueRequest(registered_method, &payload_, notification_cq);
-    }
-
-    bool FinalizeResult(void** tag, bool* status) override {
-      if (*status) {
-        if (payload_ == nullptr ||
-            !SerializationTraits<Message>::Deserialize(payload_, request_)
-                 .ok()) {
-          // If deserialization fails, we cancel the call and instantiate
-          // a new instance of ourselves to request another call.  We then
-          // return false, which prevents the call from being returned to
-          // the application.
-          g_core_codegen_interface->grpc_call_cancel_with_status(
-              call_, GRPC_STATUS_INTERNAL, "Unable to parse request", nullptr);
-          g_core_codegen_interface->grpc_call_unref(call_);
-          new PayloadAsyncRequest(registered_method_, server_, context_,
-                                  stream_, call_cq_, notification_cq_, tag_,
-                                  request_);
-          delete this;
-          return false;
-        }
-      }
-      return RegisteredAsyncRequest::FinalizeResult(tag, status);
-    }
-
-   private:
-    void* const registered_method_;
-    ServerInterface* const server_;
-    ServerContext* const context_;
-    internal::ServerAsyncStreamingInterface* const stream_;
-    CompletionQueue* const call_cq_;
-    ServerCompletionQueue* const notification_cq_;
-    void* const tag_;
-    Message* const request_;
-    grpc_byte_buffer* payload_;
-  };
-
-  class GenericAsyncRequest : public BaseAsyncRequest {
-   public:
-    GenericAsyncRequest(ServerInterface* server, GenericServerContext* context,
-                        internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag,
-                        bool delete_on_finalize);
-
-    bool FinalizeResult(void** tag, bool* status) override;
-
-   private:
-    grpc_call_details call_details_;
-  };
-
-  template <class Message>
-  void RequestAsyncCall(internal::RpcServiceMethod* method,
-                        ServerContext* context,
-                        internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag,
-                        Message* message) {
-    GPR_CODEGEN_ASSERT(method);
-    new PayloadAsyncRequest<Message>(method->server_tag(), this, context,
-                                     stream, call_cq, notification_cq, tag,
-                                     message);
-  }
-
-  void RequestAsyncCall(internal::RpcServiceMethod* method,
-                        ServerContext* context,
-                        internal::ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* call_cq,
-                        ServerCompletionQueue* notification_cq, void* tag) {
-    GPR_CODEGEN_ASSERT(method);
-    new NoPayloadAsyncRequest(method->server_tag(), this, context, stream,
-                              call_cq, notification_cq, tag);
-  }
-
-  void RequestAsyncGenericCall(GenericServerContext* context,
-                               internal::ServerAsyncStreamingInterface* stream,
-                               CompletionQueue* call_cq,
-                               ServerCompletionQueue* notification_cq,
-                               void* tag) {
-    new GenericAsyncRequest(this, context, stream, call_cq, notification_cq,
-                            tag, true);
-  }
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/server_interface.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H
diff --git a/include/grpc++/impl/codegen/service_type.h b/include/grpc++/impl/codegen/service_type.h
index 71c3d99..be02b75 100644
--- a/include/grpc++/impl/codegen/service_type.h
+++ b/include/grpc++/impl/codegen/service_type.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,148 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H
 #define GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H
 
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/rpc_service_method.h>
-#include <grpc++/impl/codegen/serialization_traits.h>
-#include <grpc++/impl/codegen/server_interface.h>
-#include <grpc++/impl/codegen/status.h>
-
-namespace grpc {
-
-class CompletionQueue;
-class Server;
-class ServerInterface;
-class ServerCompletionQueue;
-class ServerContext;
-
-namespace internal {
-class Call;
-class ServerAsyncStreamingInterface {
- public:
-  virtual ~ServerAsyncStreamingInterface() {}
-
-  /// Request notification of the sending of initial metadata to the client.
-  /// Completion will be notified by \a tag on the associated completion
-  /// queue. This call is optional, but if it is used, it cannot be used
-  /// concurrently with or after the \a Finish method.
-  ///
-  /// \param[in] tag Tag identifying this request.
-  virtual void SendInitialMetadata(void* tag) = 0;
-
- private:
-  friend class ::grpc::ServerInterface;
-  virtual void BindCall(Call* call) = 0;
-};
-}  // namespace internal
-
-/// Desriptor of an RPC service and its various RPC methods
-class Service {
- public:
-  Service() : server_(nullptr) {}
-  virtual ~Service() {}
-
-  bool has_async_methods() const {
-    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
-      if (*it && (*it)->handler() == nullptr) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  bool has_synchronous_methods() const {
-    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
-      if (*it && (*it)->handler() != nullptr) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  bool has_generic_methods() const {
-    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
-      if (it->get() == nullptr) {
-        return true;
-      }
-    }
-    return false;
-  }
-
- protected:
-  template <class Message>
-  void RequestAsyncUnary(int index, ServerContext* context, Message* request,
-                         internal::ServerAsyncStreamingInterface* stream,
-                         CompletionQueue* call_cq,
-                         ServerCompletionQueue* notification_cq, void* tag) {
-    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
-                              notification_cq, tag, request);
-  }
-  void RequestAsyncClientStreaming(
-      int index, ServerContext* context,
-      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-      ServerCompletionQueue* notification_cq, void* tag) {
-    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
-                              notification_cq, tag);
-  }
-  template <class Message>
-  void RequestAsyncServerStreaming(
-      int index, ServerContext* context, Message* request,
-      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-      ServerCompletionQueue* notification_cq, void* tag) {
-    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
-                              notification_cq, tag, request);
-  }
-  void RequestAsyncBidiStreaming(
-      int index, ServerContext* context,
-      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-      ServerCompletionQueue* notification_cq, void* tag) {
-    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
-                              notification_cq, tag);
-  }
-
-  void AddMethod(internal::RpcServiceMethod* method) {
-    methods_.emplace_back(method);
-  }
-
-  void MarkMethodAsync(int index) {
-    GPR_CODEGEN_ASSERT(
-        methods_[index].get() != nullptr &&
-        "Cannot mark the method as 'async' because it has already been "
-        "marked as 'generic'.");
-    methods_[index]->ResetHandler();
-  }
-
-  void MarkMethodGeneric(int index) {
-    GPR_CODEGEN_ASSERT(
-        methods_[index]->handler() != nullptr &&
-        "Cannot mark the method as 'generic' because it has already been "
-        "marked as 'async'.");
-    methods_[index].reset();
-  }
-
-  void MarkMethodStreamed(int index, internal::MethodHandler* streamed_method) {
-    GPR_CODEGEN_ASSERT(methods_[index] && methods_[index]->handler() &&
-                       "Cannot mark an async or generic method Streamed");
-    methods_[index]->SetHandler(streamed_method);
-
-    // From the server's point of view, streamed unary is a special
-    // case of BIDI_STREAMING that has 1 read and 1 write, in that order,
-    // and split server-side streaming is BIDI_STREAMING with 1 read and
-    // any number of writes, in that order.
-    methods_[index]->SetMethodType(internal::RpcMethod::BIDI_STREAMING);
-  }
-
- private:
-  friend class Server;
-  friend class ServerInterface;
-  ServerInterface* server_;
-  std::vector<std::unique_ptr<internal::RpcServiceMethod>> methods_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/service_type.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H
diff --git a/include/grpc++/impl/codegen/slice.h b/include/grpc++/impl/codegen/slice.h
index c185bf4..6714bad 100644
--- a/include/grpc++/impl/codegen/slice.h
+++ b/include/grpc++/impl/codegen/slice.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,113 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SLICE_H
 #define GRPCXX_IMPL_CODEGEN_SLICE_H
 
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/string_ref.h>
-
-#include <grpc/impl/codegen/slice.h>
-
-namespace grpc {
-
-/// A wrapper around \a grpc_slice.
-///
-/// A slice represents a contiguous reference counted array of bytes.
-/// It is cheap to take references to a slice, and it is cheap to create a
-/// slice pointing to a subset of another slice.
-class Slice final {
- public:
-  /// Construct an empty slice.
-  Slice();
-  /// Destructor - drops one reference.
-  ~Slice();
-
-  enum AddRef { ADD_REF };
-  /// Construct a slice from \a slice, adding a reference.
-  Slice(grpc_slice slice, AddRef);
-
-  enum StealRef { STEAL_REF };
-  /// Construct a slice from \a slice, stealing a reference.
-  Slice(grpc_slice slice, StealRef);
-
-  /// Allocate a slice of specified size
-  Slice(size_t len);
-
-  /// Construct a slice from a copied buffer
-  Slice(const void* buf, size_t len);
-
-  /// Construct a slice from a copied string
-  Slice(const grpc::string& str);
-
-  enum StaticSlice { STATIC_SLICE };
-
-  /// Construct a slice from a static buffer
-  Slice(const void* buf, size_t len, StaticSlice);
-
-  /// Copy constructor, adds a reference.
-  Slice(const Slice& other);
-
-  /// Assignment, reference count is unchanged.
-  Slice& operator=(Slice other) {
-    std::swap(slice_, other.slice_);
-    return *this;
-  }
-
-  /// Create a slice pointing at some data. Calls malloc to allocate a refcount
-  /// for the object, and arranges that destroy will be called with the
-  /// user data pointer passed in at destruction. Can be the same as buf or
-  /// different (e.g., if data is part of a larger structure that must be
-  /// destroyed when the data is no longer needed)
-  Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data);
-
-  /// Specialization of above for common case where buf == user_data
-  Slice(void* buf, size_t len, void (*destroy)(void*))
-      : Slice(buf, len, destroy, buf) {}
-
-  /// Similar to the above but has a destroy that also takes slice length
-  Slice(void* buf, size_t len, void (*destroy)(void*, size_t));
-
-  /// Byte size.
-  size_t size() const { return GRPC_SLICE_LENGTH(slice_); }
-
-  /// Raw pointer to the beginning (first element) of the slice.
-  const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); }
-
-  /// Raw pointer to the end (one byte \em past the last element) of the slice.
-  const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
-
-  /// Raw C slice. Caller needs to call grpc_slice_unref when done.
-  grpc_slice c_slice() const;
-
- private:
-  friend class ByteBuffer;
-
-  grpc_slice slice_;
-};
-
-inline grpc::string_ref StringRefFromSlice(const grpc_slice* slice) {
-  return grpc::string_ref(
-      reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(*slice)),
-      GRPC_SLICE_LENGTH(*slice));
-}
-
-inline grpc::string StringFromCopiedSlice(grpc_slice slice) {
-  return grpc::string(reinterpret_cast<char*>(GRPC_SLICE_START_PTR(slice)),
-                      GRPC_SLICE_LENGTH(slice));
-}
-
-inline grpc_slice SliceReferencingString(const grpc::string& str) {
-  return g_core_codegen_interface->grpc_slice_from_static_buffer(str.data(),
-                                                                 str.length());
-}
-
-inline grpc_slice SliceFromCopiedString(const grpc::string& str) {
-  return g_core_codegen_interface->grpc_slice_from_copied_buffer(str.data(),
-                                                                 str.length());
-}
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/slice.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SLICE_H
diff --git a/include/grpc++/impl/codegen/status.h b/include/grpc++/impl/codegen/status.h
index 6f013cf..6cf9459 100644
--- a/include/grpc++/impl/codegen/status.h
+++ b/include/grpc++/impl/codegen/status.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,64 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_STATUS_H
 #define GRPCXX_IMPL_CODEGEN_STATUS_H
 
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/status_code_enum.h>
-
-namespace grpc {
-
-/// Did it work? If it didn't, why?
-///
-/// See \a grpc::StatusCode for details on the available code and their meaning.
-class Status {
- public:
-  /// Construct an OK instance.
-  Status() : code_(StatusCode::OK) {}
-
-  /// Construct an instance with associated \a code and \a error_message.
-  /// It is an error to construct an OK status with non-empty \a error_message.
-  Status(StatusCode code, const grpc::string& error_message)
-      : code_(code), error_message_(error_message) {}
-
-  /// Construct an instance with \a code,  \a error_message and
-  /// \a error_details. It is an error to construct an OK status with non-empty
-  /// \a error_message and/or \a error_details.
-  Status(StatusCode code, const grpc::string& error_message,
-         const grpc::string& error_details)
-      : code_(code),
-        error_message_(error_message),
-        binary_error_details_(error_details) {}
-
-  // Pre-defined special status objects.
-  /// An OK pre-defined instance.
-  static const Status& OK;
-  /// A CANCELLED pre-defined instance.
-  static const Status& CANCELLED;
-
-  /// Return the instance's error code.
-  StatusCode error_code() const { return code_; }
-  /// Return the instance's error message.
-  grpc::string error_message() const { return error_message_; }
-  /// Return the (binary) error details.
-  // Usually it contains a serialized google.rpc.Status proto.
-  grpc::string error_details() const { return binary_error_details_; }
-
-  /// Is the status OK?
-  bool ok() const { return code_ == StatusCode::OK; }
-
-  // Ignores any errors. This method does nothing except potentially suppress
-  // complaints from any tools that are checking that errors are not dropped on
-  // the floor.
-  void IgnoreError() const {}
-
- private:
-  StatusCode code_;
-  grpc::string error_message_;
-  grpc::string binary_error_details_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/status.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_STATUS_H
diff --git a/include/grpc++/impl/codegen/status_code_enum.h b/include/grpc++/impl/codegen/status_code_enum.h
index 68da185..7503eae 100644
--- a/include/grpc++/impl/codegen/status_code_enum.h
+++ b/include/grpc++/impl/codegen/status_code_enum.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,127 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H
 #define GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H
 
-namespace grpc {
-
-enum StatusCode {
-  /// Not an error; returned on success.
-  OK = 0,
-
-  /// The operation was cancelled (typically by the caller).
-  CANCELLED = 1,
-
-  /// Unknown error. An example of where this error may be returned is if a
-  /// Status value received from another address space belongs to an error-space
-  /// that is not known in this address space. Also errors raised by APIs that
-  /// do not return enough error information may be converted to this error.
-  UNKNOWN = 2,
-
-  /// Client specified an invalid argument. Note that this differs from
-  /// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
-  /// problematic regardless of the state of the system (e.g., a malformed file
-  /// name).
-  INVALID_ARGUMENT = 3,
-
-  /// Deadline expired before operation could complete. For operations that
-  /// change the state of the system, this error may be returned even if the
-  /// operation has completed successfully. For example, a successful response
-  /// from a server could have been delayed long enough for the deadline to
-  /// expire.
-  DEADLINE_EXCEEDED = 4,
-
-  /// Some requested entity (e.g., file or directory) was not found.
-  NOT_FOUND = 5,
-
-  /// Some entity that we attempted to create (e.g., file or directory) already
-  /// exists.
-  ALREADY_EXISTS = 6,
-
-  /// The caller does not have permission to execute the specified operation.
-  /// PERMISSION_DENIED must not be used for rejections caused by exhausting
-  /// some resource (use RESOURCE_EXHAUSTED instead for those errors).
-  /// PERMISSION_DENIED must not be used if the caller can not be identified
-  /// (use UNAUTHENTICATED instead for those errors).
-  PERMISSION_DENIED = 7,
-
-  /// The request does not have valid authentication credentials for the
-  /// operation.
-  UNAUTHENTICATED = 16,
-
-  /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
-  /// entire file system is out of space.
-  RESOURCE_EXHAUSTED = 8,
-
-  /// Operation was rejected because the system is not in a state required for
-  /// the operation's execution. For example, directory to be deleted may be
-  /// non-empty, an rmdir operation is applied to a non-directory, etc.
-  ///
-  /// A litmus test that may help a service implementor in deciding
-  /// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
-  ///  (a) Use UNAVAILABLE if the client can retry just the failing call.
-  ///  (b) Use ABORTED if the client should retry at a higher-level
-  ///      (e.g., restarting a read-modify-write sequence).
-  ///  (c) Use FAILED_PRECONDITION if the client should not retry until
-  ///      the system state has been explicitly fixed. E.g., if an "rmdir"
-  ///      fails because the directory is non-empty, FAILED_PRECONDITION
-  ///      should be returned since the client should not retry unless
-  ///      they have first fixed up the directory by deleting files from it.
-  ///  (d) Use FAILED_PRECONDITION if the client performs conditional
-  ///      REST Get/Update/Delete on a resource and the resource on the
-  ///      server does not match the condition. E.g., conflicting
-  ///      read-modify-write on the same resource.
-  FAILED_PRECONDITION = 9,
-
-  /// The operation was aborted, typically due to a concurrency issue like
-  /// sequencer check failures, transaction aborts, etc.
-  ///
-  /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
-  /// and UNAVAILABLE.
-  ABORTED = 10,
-
-  /// Operation was attempted past the valid range. E.g., seeking or reading
-  /// past end of file.
-  ///
-  /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed
-  /// if the system state changes. For example, a 32-bit file system will
-  /// generate INVALID_ARGUMENT if asked to read at an offset that is not in the
-  /// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from
-  /// an offset past the current file size.
-  ///
-  /// There is a fair bit of overlap between FAILED_PRECONDITION and
-  /// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
-  /// when it applies so that callers who are iterating through a space can
-  /// easily look for an OUT_OF_RANGE error to detect when they are done.
-  OUT_OF_RANGE = 11,
-
-  /// Operation is not implemented or not supported/enabled in this service.
-  UNIMPLEMENTED = 12,
-
-  /// Internal errors. Means some invariants expected by underlying System has
-  /// been broken. If you see one of these errors, Something is very broken.
-  INTERNAL = 13,
-
-  /// The service is currently unavailable. This is a most likely a transient
-  /// condition and may be corrected by retrying with a backoff.
-  ///
-  /// \warning Although data MIGHT not have been transmitted when this
-  /// status occurs, there is NOT A GUARANTEE that the server has not seen
-  /// anything. So in general it is unsafe to retry on this status code
-  /// if the call is non-idempotent.
-  ///
-  /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
-  /// and UNAVAILABLE.
-  UNAVAILABLE = 14,
-
-  /// Unrecoverable data loss or corruption.
-  DATA_LOSS = 15,
-
-  /// Force users to include a default branch:
-  DO_NOT_USE = -1
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/status_code_enum.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H
diff --git a/include/grpc++/impl/codegen/string_ref.h b/include/grpc++/impl/codegen/string_ref.h
index dbe3f19..66e250e 100644
--- a/include/grpc++/impl/codegen/string_ref.h
+++ b/include/grpc++/impl/codegen/string_ref.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,131 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_STRING_REF_H
 #define GRPCXX_IMPL_CODEGEN_STRING_REF_H
 
-#include <string.h>
-
-#include <algorithm>
-#include <iosfwd>
-#include <iostream>
-#include <iterator>
-
-#include <grpc++/impl/codegen/config.h>
-
-namespace grpc {
-
-/// This class is a non owning reference to a string.
-///
-/// It should be a strict subset of the upcoming std::string_ref.
-///
-/// \see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html
-///
-/// The constexpr is dropped or replaced with const for legacy compiler
-/// compatibility.
-class string_ref {
- public:
-  /// types
-  typedef const char* const_iterator;
-  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
-  /// constants
-  const static size_t npos;
-
-  /// construct/copy.
-  string_ref() : data_(nullptr), length_(0) {}
-  string_ref(const string_ref& other)
-      : data_(other.data_), length_(other.length_) {}
-  string_ref& operator=(const string_ref& rhs) {
-    data_ = rhs.data_;
-    length_ = rhs.length_;
-    return *this;
-  }
-
-  string_ref(const char* s) : data_(s), length_(strlen(s)) {}
-  string_ref(const char* s, size_t l) : data_(s), length_(l) {}
-  string_ref(const grpc::string& s) : data_(s.data()), length_(s.length()) {}
-
-  /// iterators
-  const_iterator begin() const { return data_; }
-  const_iterator end() const { return data_ + length_; }
-  const_iterator cbegin() const { return data_; }
-  const_iterator cend() const { return data_ + length_; }
-  const_reverse_iterator rbegin() const {
-    return const_reverse_iterator(end());
-  }
-  const_reverse_iterator rend() const {
-    return const_reverse_iterator(begin());
-  }
-  const_reverse_iterator crbegin() const {
-    return const_reverse_iterator(end());
-  }
-  const_reverse_iterator crend() const {
-    return const_reverse_iterator(begin());
-  }
-
-  /// capacity
-  size_t size() const { return length_; }
-  size_t length() const { return length_; }
-  size_t max_size() const { return length_; }
-  bool empty() const { return length_ == 0; }
-
-  /// element access
-  const char* data() const { return data_; }
-
-  /// string operations
-  int compare(string_ref x) const {
-    size_t min_size = length_ < x.length_ ? length_ : x.length_;
-    int r = memcmp(data_, x.data_, min_size);
-    if (r < 0) return -1;
-    if (r > 0) return 1;
-    if (length_ < x.length_) return -1;
-    if (length_ > x.length_) return 1;
-    return 0;
-  }
-
-  bool starts_with(string_ref x) const {
-    return length_ >= x.length_ && (memcmp(data_, x.data_, x.length_) == 0);
-  }
-
-  bool ends_with(string_ref x) const {
-    return length_ >= x.length_ &&
-           (memcmp(data_ + (length_ - x.length_), x.data_, x.length_) == 0);
-  }
-
-  size_t find(string_ref s) const {
-    auto it = std::search(cbegin(), cend(), s.cbegin(), s.cend());
-    return it == cend() ? npos : std::distance(cbegin(), it);
-  }
-
-  size_t find(char c) const {
-    auto it = std::find(cbegin(), cend(), c);
-    return it == cend() ? npos : std::distance(cbegin(), it);
-  }
-
-  string_ref substr(size_t pos, size_t n = npos) const {
-    if (pos > length_) pos = length_;
-    if (n > (length_ - pos)) n = length_ - pos;
-    return string_ref(data_ + pos, n);
-  }
-
- private:
-  const char* data_;
-  size_t length_;
-};
-
-/// Comparison operators
-inline bool operator==(string_ref x, string_ref y) { return x.compare(y) == 0; }
-inline bool operator!=(string_ref x, string_ref y) { return x.compare(y) != 0; }
-inline bool operator<(string_ref x, string_ref y) { return x.compare(y) < 0; }
-inline bool operator<=(string_ref x, string_ref y) { return x.compare(y) <= 0; }
-inline bool operator>(string_ref x, string_ref y) { return x.compare(y) > 0; }
-inline bool operator>=(string_ref x, string_ref y) { return x.compare(y) >= 0; }
-
-inline std::ostream& operator<<(std::ostream& out, const string_ref& string) {
-  return out << grpc::string(string.begin(), string.end());
-}
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/string_ref.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_STRING_REF_H
diff --git a/include/grpc++/impl/codegen/stub_options.h b/include/grpc++/impl/codegen/stub_options.h
index 380d052..07cb441 100644
--- a/include/grpc++/impl/codegen/stub_options.h
+++ b/include/grpc++/impl/codegen/stub_options.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,14 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H
 #define GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H
 
-namespace grpc {
-
-/// Useful interface for generated stubs
-class StubOptions {};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/stub_options.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H
diff --git a/include/grpc++/impl/codegen/sync_stream.h b/include/grpc++/impl/codegen/sync_stream.h
index a6dd26f..1e6ba27 100644
--- a/include/grpc++/impl/codegen/sync_stream.h
+++ b/include/grpc++/impl/codegen/sync_stream.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,919 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H
 #define GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H
 
-#include <grpc++/impl/codegen/call.h>
-#include <grpc++/impl/codegen/channel_interface.h>
-#include <grpc++/impl/codegen/client_context.h>
-#include <grpc++/impl/codegen/completion_queue.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/server_context.h>
-#include <grpc++/impl/codegen/service_type.h>
-#include <grpc++/impl/codegen/status.h>
-
-namespace grpc {
-
-namespace internal {
-/// Common interface for all synchronous client side streaming.
-class ClientStreamingInterface {
- public:
-  virtual ~ClientStreamingInterface() {}
-
-  /// Block waiting until the stream finishes and a final status of the call is
-  /// available.
-  ///
-  /// It is appropriate to call this method when both:
-  ///   * the calling code (client-side) has no more message to send
-  ///     (this can be declared implicitly by calling this method, or
-  ///     explicitly through an earlier call to <i>WritesDone</i> method of the
-  ///     class in use, e.g. \a ClientWriterInterface::WritesDone or
-  ///     \a ClientReaderWriterInterface::WritesDone).
-  ///   * there are no more messages to be received from the server (which can
-  ///     be known implicitly, or explicitly from an earlier call to \a
-  ///     ReaderInterface::Read that returned "false").
-  ///
-  /// This function will return either:
-  /// - when all incoming messages have been read and the server has
-  ///   returned status.
-  /// - when the server has returned a non-OK status.
-  /// - OR when the call failed for some reason and the library generated a
-  ///   status.
-  ///
-  /// Return values:
-  ///   - \a Status contains the status code, message and details for the call
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible trailing metadata sent from the server.
-  virtual Status Finish() = 0;
-};
-
-/// Common interface for all synchronous server side streaming.
-class ServerStreamingInterface {
- public:
-  virtual ~ServerStreamingInterface() {}
-
-  /// Block to send initial metadata to client.
-  /// This call is optional, but if it is used, it cannot be used concurrently
-  /// with or after the \a Finish method.
-  ///
-  /// The initial metadata that will be sent to the client will be
-  /// taken from the \a ServerContext associated with the call.
-  virtual void SendInitialMetadata() = 0;
-};
-
-/// An interface that yields a sequence of messages of type \a R.
-template <class R>
-class ReaderInterface {
- public:
-  virtual ~ReaderInterface() {}
-
-  /// Get an upper bound on the next message size available for reading on this
-  /// stream.
-  virtual bool NextMessageSize(uint32_t* sz) = 0;
-
-  /// Block to read a message and parse to \a msg. Returns \a true on success.
-  /// This is thread-safe with respect to \a Write or \WritesDone methods on
-  /// the same stream. It should not be called concurrently with another \a
-  /// Read on the same stream as the order of delivery will not be defined.
-  ///
-  /// \param[out] msg The read message.
-  ///
-  /// \return \a false when there will be no more incoming messages, either
-  /// because the other side has called \a WritesDone() or the stream has failed
-  /// (or been cancelled).
-  virtual bool Read(R* msg) = 0;
-};
-
-/// An interface that can be fed a sequence of messages of type \a W.
-template <class W>
-class WriterInterface {
- public:
-  virtual ~WriterInterface() {}
-
-  /// Block to write \a msg to the stream with WriteOptions \a options.
-  /// This is thread-safe with respect to \a ReaderInterface::Read
-  ///
-  /// \param msg The message to be written to the stream.
-  /// \param options The WriteOptions affecting the write operation.
-  ///
-  /// \return \a true on success, \a false when the stream has been closed.
-  virtual bool Write(const W& msg, WriteOptions options) = 0;
-
-  /// Block to write \a msg to the stream with default write options.
-  /// This is thread-safe with respect to \a ReaderInterface::Read
-  ///
-  /// \param msg The message to be written to the stream.
-  ///
-  /// \return \a true on success, \a false when the stream has been closed.
-  inline bool Write(const W& msg) { return Write(msg, WriteOptions()); }
-
-  /// Write \a msg and coalesce it with the writing of trailing metadata, using
-  /// WriteOptions \a options.
-  ///
-  /// For client, WriteLast is equivalent of performing Write and WritesDone in
-  /// a single step. \a msg and trailing metadata are coalesced and sent on wire
-  /// by calling this function. For server, WriteLast buffers the \a msg.
-  /// The writing of \a msg is held until the service handler returns,
-  /// where \a msg and trailing metadata are coalesced and sent on wire.
-  /// Note that WriteLast can only buffer \a msg up to the flow control window
-  /// size. If \a msg size is larger than the window size, it will be sent on
-  /// wire without buffering.
-  ///
-  /// \param[in] msg The message to be written to the stream.
-  /// \param[in] options The WriteOptions to be used to write this message.
-  void WriteLast(const W& msg, WriteOptions options) {
-    Write(msg, options.set_last_message());
-  }
-};
-
-}  // namespace internal
-
-/// Client-side interface for streaming reads of message of type \a R.
-template <class R>
-class ClientReaderInterface : public internal::ClientStreamingInterface,
-                              public internal::ReaderInterface<R> {
- public:
-  /// Block to wait for initial metadata from server. The received metadata
-  /// can only be accessed after this call returns. Should only be called before
-  /// the first read. Calling this method is optional, and if it is not called
-  /// the metadata will be available in ClientContext after the first read.
-  virtual void WaitForInitialMetadata() = 0;
-};
-
-namespace internal {
-template <class R>
-class ClientReaderFactory {
- public:
-  template <class W>
-  static ClientReader<R>* Create(ChannelInterface* channel,
-                                 const ::grpc::internal::RpcMethod& method,
-                                 ClientContext* context, const W& request) {
-    return new ClientReader<R>(channel, method, context, request);
-  }
-};
-}  // namespace internal
-
-/// Synchronous (blocking) client-side API for doing server-streaming RPCs,
-/// where the stream of messages coming from the server has messages
-/// of type \a R.
-template <class R>
-class ClientReader final : public ClientReaderInterface<R> {
- public:
-  /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
-  /// semantics.
-  ///
-  //  Side effect:
-  ///   Once complete, the initial metadata read from
-  ///   the server will be accessable through the \a ClientContext used to
-  ///   construct this object.
-  void WaitForInitialMetadata() override {
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-        ops;
-    ops.RecvInitialMetadata(context_);
-    call_.PerformOps(&ops);
-    cq_.Pluck(&ops);  /// status ignored
-  }
-
-  bool NextMessageSize(uint32_t* sz) override {
-    *sz = call_.max_receive_message_size();
-    return true;
-  }
-
-  /// See the \a ReaderInterface.Read method for semantics.
-  /// Side effect:
-  ///   This also receives initial metadata from the server, if not
-  ///   already received (if initial metadata is received, it can be then
-  ///   accessed through the \a ClientContext associated with this call).
-  bool Read(R* msg) override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                                ::grpc::internal::CallOpRecvMessage<R>>
-        ops;
-    if (!context_->initial_metadata_received_) {
-      ops.RecvInitialMetadata(context_);
-    }
-    ops.RecvMessage(msg);
-    call_.PerformOps(&ops);
-    return cq_.Pluck(&ops) && ops.got_message;
-  }
-
-  /// See the \a ClientStreamingInterface.Finish method for semantics.
-  ///
-  /// Side effect:
-  ///   The \a ClientContext associated with this call is updated with
-  ///   possible metadata received from the server.
-  Status Finish() override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientRecvStatus> ops;
-    Status status;
-    ops.ClientRecvStatus(context_, &status);
-    call_.PerformOps(&ops);
-    GPR_CODEGEN_ASSERT(cq_.Pluck(&ops));
-    return status;
-  }
-
- private:
-  friend class internal::ClientReaderFactory<R>;
-  ClientContext* context_;
-  CompletionQueue cq_;
-  ::grpc::internal::Call call_;
-
-  /// Block to create a stream and write the initial metadata and \a request
-  /// out. Note that \a context will be used to fill in custom initial
-  /// metadata used to send to the server when starting the call.
-  template <class W>
-  ClientReader(::grpc::ChannelInterface* channel,
-               const ::grpc::internal::RpcMethod& method,
-               ClientContext* context, const W& request)
-      : context_(context),
-        cq_(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
-            GRPC_CQ_DEFAULT_POLLING}),  // Pluckable cq
-        call_(channel->CreateCall(method, context, &cq_)) {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                                ::grpc::internal::CallOpSendMessage,
-                                ::grpc::internal::CallOpClientSendClose>
-        ops;
-    ops.SendInitialMetadata(context->send_initial_metadata_,
-                            context->initial_metadata_flags());
-    // TODO(ctiller): don't assert
-    GPR_CODEGEN_ASSERT(ops.SendMessage(request).ok());
-    ops.ClientSendClose();
-    call_.PerformOps(&ops);
-    cq_.Pluck(&ops);
-  }
-};
-
-/// Client-side interface for streaming writes of message type \a W.
-template <class W>
-class ClientWriterInterface : public internal::ClientStreamingInterface,
-                              public internal::WriterInterface<W> {
- public:
-  /// Half close writing from the client. (signal that the stream of messages
-  /// coming from the client is complete).
-  /// Blocks until currently-pending writes are completed.
-  /// Thread safe with respect to \a ReaderInterface::Read operations only
-  ///
-  /// \return Whether the writes were successful.
-  virtual bool WritesDone() = 0;
-};
-
-namespace internal {
-template <class W>
-class ClientWriterFactory {
- public:
-  template <class R>
-  static ClientWriter<W>* Create(::grpc::ChannelInterface* channel,
-                                 const ::grpc::internal::RpcMethod& method,
-                                 ClientContext* context, R* response) {
-    return new ClientWriter<W>(channel, method, context, response);
-  }
-};
-}  // namespace internal
-
-/// Synchronous (blocking) client-side API for doing client-streaming RPCs,
-/// where the outgoing message stream coming from the client has messages of
-/// type \a W.
-template <class W>
-class ClientWriter : public ClientWriterInterface<W> {
- public:
-  /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
-  /// semantics.
-  ///
-  //  Side effect:
-  ///   Once complete, the initial metadata read from the server will be
-  ///   accessable through the \a ClientContext used to construct this object.
-  void WaitForInitialMetadata() {
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-        ops;
-    ops.RecvInitialMetadata(context_);
-    call_.PerformOps(&ops);
-    cq_.Pluck(&ops);  // status ignored
-  }
-
-  /// See the WriterInterface.Write(const W& msg, WriteOptions options) method
-  /// for semantics.
-  ///
-  /// Side effect:
-  ///   Also sends initial metadata if not already sent (using the
-  ///   \a ClientContext associated with this call).
-  using ::grpc::internal::WriterInterface<W>::Write;
-  bool Write(const W& msg, WriteOptions options) override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                                ::grpc::internal::CallOpSendMessage,
-                                ::grpc::internal::CallOpClientSendClose>
-        ops;
-
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-      ops.ClientSendClose();
-    }
-    if (context_->initial_metadata_corked_) {
-      ops.SendInitialMetadata(context_->send_initial_metadata_,
-                              context_->initial_metadata_flags());
-      context_->set_initial_metadata_corked(false);
-    }
-    if (!ops.SendMessage(msg, options).ok()) {
-      return false;
-    }
-
-    call_.PerformOps(&ops);
-    return cq_.Pluck(&ops);
-  }
-
-  bool WritesDone() override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops;
-    ops.ClientSendClose();
-    call_.PerformOps(&ops);
-    return cq_.Pluck(&ops);
-  }
-
-  /// See the ClientStreamingInterface.Finish method for semantics.
-  /// Side effects:
-  ///   - Also receives initial metadata if not already received.
-  ///   - Attempts to fill in the \a response parameter passed
-  ///     to the constructor of this instance with the response
-  ///     message from the server.
-  Status Finish() override {
-    Status status;
-    if (!context_->initial_metadata_received_) {
-      finish_ops_.RecvInitialMetadata(context_);
-    }
-    finish_ops_.ClientRecvStatus(context_, &status);
-    call_.PerformOps(&finish_ops_);
-    GPR_CODEGEN_ASSERT(cq_.Pluck(&finish_ops_));
-    return status;
-  }
-
- private:
-  friend class internal::ClientWriterFactory<W>;
-
-  /// Block to create a stream (i.e. send request headers and other initial
-  /// metadata to the server). Note that \a context will be used to fill
-  /// in custom initial metadata. \a response will be filled in with the
-  /// single expected response message from the server upon a successful
-  /// call to the \a Finish method of this instance.
-  template <class R>
-  ClientWriter(ChannelInterface* channel,
-               const ::grpc::internal::RpcMethod& method,
-               ClientContext* context, R* response)
-      : context_(context),
-        cq_(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
-            GRPC_CQ_DEFAULT_POLLING}),  // Pluckable cq
-        call_(channel->CreateCall(method, context, &cq_)) {
-    finish_ops_.RecvMessage(response);
-    finish_ops_.AllowNoMessage();
-
-    if (!context_->initial_metadata_corked_) {
-      ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
-          ops;
-      ops.SendInitialMetadata(context->send_initial_metadata_,
-                              context->initial_metadata_flags());
-      call_.PerformOps(&ops);
-      cq_.Pluck(&ops);
-    }
-  }
-
-  ClientContext* context_;
-  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                              ::grpc::internal::CallOpGenericRecvMessage,
-                              ::grpc::internal::CallOpClientRecvStatus>
-      finish_ops_;
-  CompletionQueue cq_;
-  ::grpc::internal::Call call_;
-};
-
-/// Client-side interface for bi-directional streaming with
-/// client-to-server stream messages of type \a W and
-/// server-to-client stream messages of type \a R.
-template <class W, class R>
-class ClientReaderWriterInterface : public internal::ClientStreamingInterface,
-                                    public internal::WriterInterface<W>,
-                                    public internal::ReaderInterface<R> {
- public:
-  /// Block to wait for initial metadata from server. The received metadata
-  /// can only be accessed after this call returns. Should only be called before
-  /// the first read. Calling this method is optional, and if it is not called
-  /// the metadata will be available in ClientContext after the first read.
-  virtual void WaitForInitialMetadata() = 0;
-
-  /// Half close writing from the client. (signal that the stream of messages
-  /// coming from the clinet is complete).
-  /// Blocks until currently-pending writes are completed.
-  /// Thread-safe with respect to \a ReaderInterface::Read
-  ///
-  /// \return Whether the writes were successful.
-  virtual bool WritesDone() = 0;
-};
-
-namespace internal {
-template <class W, class R>
-class ClientReaderWriterFactory {
- public:
-  static ClientReaderWriter<W, R>* Create(
-      ::grpc::ChannelInterface* channel,
-      const ::grpc::internal::RpcMethod& method, ClientContext* context) {
-    return new ClientReaderWriter<W, R>(channel, method, context);
-  }
-};
-}  // namespace internal
-
-/// Synchronous (blocking) client-side API for bi-directional streaming RPCs,
-/// where the outgoing message stream coming from the client has messages of
-/// type \a W, and the incoming messages stream coming from the server has
-/// messages of type \a R.
-template <class W, class R>
-class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
- public:
-  /// Block waiting to read initial metadata from the server.
-  /// This call is optional, but if it is used, it cannot be used concurrently
-  /// with or after the \a Finish method.
-  ///
-  /// Once complete, the initial metadata read from the server will be
-  /// accessable through the \a ClientContext used to construct this object.
-  void WaitForInitialMetadata() override {
-    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
-
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
-        ops;
-    ops.RecvInitialMetadata(context_);
-    call_.PerformOps(&ops);
-    cq_.Pluck(&ops);  // status ignored
-  }
-
-  bool NextMessageSize(uint32_t* sz) override {
-    *sz = call_.max_receive_message_size();
-    return true;
-  }
-
-  /// See the \a ReaderInterface.Read method for semantics.
-  /// Side effect:
-  ///   Also receives initial metadata if not already received (updates the \a
-  ///   ClientContext associated with this call in that case).
-  bool Read(R* msg) override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                                ::grpc::internal::CallOpRecvMessage<R>>
-        ops;
-    if (!context_->initial_metadata_received_) {
-      ops.RecvInitialMetadata(context_);
-    }
-    ops.RecvMessage(msg);
-    call_.PerformOps(&ops);
-    return cq_.Pluck(&ops) && ops.got_message;
-  }
-
-  /// See the \a WriterInterface.Write method for semantics.
-  ///
-  /// Side effect:
-  ///   Also sends initial metadata if not already sent (using the
-  ///   \a ClientContext associated with this call to fill in values).
-  using ::grpc::internal::WriterInterface<W>::Write;
-  bool Write(const W& msg, WriteOptions options) override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
-                                ::grpc::internal::CallOpSendMessage,
-                                ::grpc::internal::CallOpClientSendClose>
-        ops;
-
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-      ops.ClientSendClose();
-    }
-    if (context_->initial_metadata_corked_) {
-      ops.SendInitialMetadata(context_->send_initial_metadata_,
-                              context_->initial_metadata_flags());
-      context_->set_initial_metadata_corked(false);
-    }
-    if (!ops.SendMessage(msg, options).ok()) {
-      return false;
-    }
-
-    call_.PerformOps(&ops);
-    return cq_.Pluck(&ops);
-  }
-
-  bool WritesDone() override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops;
-    ops.ClientSendClose();
-    call_.PerformOps(&ops);
-    return cq_.Pluck(&ops);
-  }
-
-  /// See the ClientStreamingInterface.Finish method for semantics.
-  ///
-  /// Side effect:
-  ///   - the \a ClientContext associated with this call is updated with
-  ///     possible trailing metadata sent from the server.
-  Status Finish() override {
-    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
-                                ::grpc::internal::CallOpClientRecvStatus>
-        ops;
-    if (!context_->initial_metadata_received_) {
-      ops.RecvInitialMetadata(context_);
-    }
-    Status status;
-    ops.ClientRecvStatus(context_, &status);
-    call_.PerformOps(&ops);
-    GPR_CODEGEN_ASSERT(cq_.Pluck(&ops));
-    return status;
-  }
-
- private:
-  friend class internal::ClientReaderWriterFactory<W, R>;
-
-  ClientContext* context_;
-  CompletionQueue cq_;
-  ::grpc::internal::Call call_;
-
-  /// Block to create a stream and write the initial metadata and \a request
-  /// out. Note that \a context will be used to fill in custom initial metadata
-  /// used to send to the server when starting the call.
-  ClientReaderWriter(::grpc::ChannelInterface* channel,
-                     const ::grpc::internal::RpcMethod& method,
-                     ClientContext* context)
-      : context_(context),
-        cq_(grpc_completion_queue_attributes{
-            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
-            GRPC_CQ_DEFAULT_POLLING}),  // Pluckable cq
-        call_(channel->CreateCall(method, context, &cq_)) {
-    if (!context_->initial_metadata_corked_) {
-      ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
-          ops;
-      ops.SendInitialMetadata(context->send_initial_metadata_,
-                              context->initial_metadata_flags());
-      call_.PerformOps(&ops);
-      cq_.Pluck(&ops);
-    }
-  }
-};
-
-/// Server-side interface for streaming reads of message of type \a R.
-template <class R>
-class ServerReaderInterface : public internal::ServerStreamingInterface,
-                              public internal::ReaderInterface<R> {};
-
-/// Synchronous (blocking) server-side API for doing client-streaming RPCs,
-/// where the incoming message stream coming from the client has messages of
-/// type \a R.
-template <class R>
-class ServerReader final : public ServerReaderInterface<R> {
- public:
-  /// See the \a ServerStreamingInterface.SendInitialMetadata method
-  /// for semantics. Note that initial metadata will be affected by the
-  /// \a ServerContext associated with this call.
-  void SendInitialMetadata() override {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
-    ops.SendInitialMetadata(ctx_->initial_metadata_,
-                            ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      ops.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_->PerformOps(&ops);
-    call_->cq()->Pluck(&ops);
-  }
-
-  bool NextMessageSize(uint32_t* sz) override {
-    *sz = call_->max_receive_message_size();
-    return true;
-  }
-
-  bool Read(R* msg) override {
-    internal::CallOpSet<internal::CallOpRecvMessage<R>> ops;
-    ops.RecvMessage(msg);
-    call_->PerformOps(&ops);
-    return call_->cq()->Pluck(&ops) && ops.got_message;
-  }
-
- private:
-  internal::Call* const call_;
-  ServerContext* const ctx_;
-
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class internal::ClientStreamingHandler;
-
-  ServerReader(internal::Call* call, ServerContext* ctx)
-      : call_(call), ctx_(ctx) {}
-};
-
-/// Server-side interface for streaming writes of message of type \a W.
-template <class W>
-class ServerWriterInterface : public internal::ServerStreamingInterface,
-                              public internal::WriterInterface<W> {};
-
-/// Synchronous (blocking) server-side API for doing for doing a
-/// server-streaming RPCs, where the outgoing message stream coming from the
-/// server has messages of type \a W.
-template <class W>
-class ServerWriter final : public ServerWriterInterface<W> {
- public:
-  /// See the \a ServerStreamingInterface.SendInitialMetadata method
-  /// for semantics.
-  /// Note that initial metadata will be affected by the
-  /// \a ServerContext associated with this call.
-  void SendInitialMetadata() override {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
-    ops.SendInitialMetadata(ctx_->initial_metadata_,
-                            ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      ops.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_->PerformOps(&ops);
-    call_->cq()->Pluck(&ops);
-  }
-
-  /// See the \a WriterInterface.Write method for semantics.
-  ///
-  /// Side effect:
-  ///   Also sends initial metadata if not already sent (using the
-  ///   \a ClientContext associated with this call to fill in values).
-  using internal::WriterInterface<W>::Write;
-  bool Write(const W& msg, WriteOptions options) override {
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-    }
-
-    if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) {
-      return false;
-    }
-    if (!ctx_->sent_initial_metadata_) {
-      ctx_->pending_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                             ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-    call_->PerformOps(&ctx_->pending_ops_);
-    // if this is the last message we defer the pluck until AFTER we start
-    // the trailing md op. This prevents hangs. See
-    // https://github.com/grpc/grpc/issues/11546
-    if (options.is_last_message()) {
-      ctx_->has_pending_ops_ = true;
-      return true;
-    }
-    ctx_->has_pending_ops_ = false;
-    return call_->cq()->Pluck(&ctx_->pending_ops_);
-  }
-
- private:
-  internal::Call* const call_;
-  ServerContext* const ctx_;
-
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class internal::ServerStreamingHandler;
-
-  ServerWriter(internal::Call* call, ServerContext* ctx)
-      : call_(call), ctx_(ctx) {}
-};
-
-/// Server-side interface for bi-directional streaming.
-template <class W, class R>
-class ServerReaderWriterInterface : public internal::ServerStreamingInterface,
-                                    public internal::WriterInterface<W>,
-                                    public internal::ReaderInterface<R> {};
-
-/// Actual implementation of bi-directional streaming
-namespace internal {
-template <class W, class R>
-class ServerReaderWriterBody final {
- public:
-  ServerReaderWriterBody(Call* call, ServerContext* ctx)
-      : call_(call), ctx_(ctx) {}
-
-  void SendInitialMetadata() {
-    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
-
-    CallOpSet<CallOpSendInitialMetadata> ops;
-    ops.SendInitialMetadata(ctx_->initial_metadata_,
-                            ctx_->initial_metadata_flags());
-    if (ctx_->compression_level_set()) {
-      ops.set_compression_level(ctx_->compression_level());
-    }
-    ctx_->sent_initial_metadata_ = true;
-    call_->PerformOps(&ops);
-    call_->cq()->Pluck(&ops);
-  }
-
-  bool NextMessageSize(uint32_t* sz) {
-    *sz = call_->max_receive_message_size();
-    return true;
-  }
-
-  bool Read(R* msg) {
-    CallOpSet<CallOpRecvMessage<R>> ops;
-    ops.RecvMessage(msg);
-    call_->PerformOps(&ops);
-    return call_->cq()->Pluck(&ops) && ops.got_message;
-  }
-
-  bool Write(const W& msg, WriteOptions options) {
-    if (options.is_last_message()) {
-      options.set_buffer_hint();
-    }
-    if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) {
-      return false;
-    }
-    if (!ctx_->sent_initial_metadata_) {
-      ctx_->pending_ops_.SendInitialMetadata(ctx_->initial_metadata_,
-                                             ctx_->initial_metadata_flags());
-      if (ctx_->compression_level_set()) {
-        ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
-      }
-      ctx_->sent_initial_metadata_ = true;
-    }
-    call_->PerformOps(&ctx_->pending_ops_);
-    // if this is the last message we defer the pluck until AFTER we start
-    // the trailing md op. This prevents hangs. See
-    // https://github.com/grpc/grpc/issues/11546
-    if (options.is_last_message()) {
-      ctx_->has_pending_ops_ = true;
-      return true;
-    }
-    ctx_->has_pending_ops_ = false;
-    return call_->cq()->Pluck(&ctx_->pending_ops_);
-  }
-
- private:
-  Call* const call_;
-  ServerContext* const ctx_;
-};
-
-}  // namespace internal
-
-/// Synchronous (blocking) server-side API for a bidirectional
-/// streaming call, where the incoming message stream coming from the client has
-/// messages of type \a R, and the outgoing message streaming coming from
-/// the server has messages of type \a W.
-template <class W, class R>
-class ServerReaderWriter final : public ServerReaderWriterInterface<W, R> {
- public:
-  /// See the \a ServerStreamingInterface.SendInitialMetadata method
-  /// for semantics. Note that initial metadata will be affected by the
-  /// \a ServerContext associated with this call.
-  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
-
-  bool NextMessageSize(uint32_t* sz) override {
-    return body_.NextMessageSize(sz);
-  }
-
-  bool Read(R* msg) override { return body_.Read(msg); }
-
-  /// See the \a WriterInterface.Write(const W& msg, WriteOptions options)
-  /// method for semantics.
-  /// Side effect:
-  ///   Also sends initial metadata if not already sent (using the \a
-  ///   ServerContext associated with this call).
-  using internal::WriterInterface<W>::Write;
-  bool Write(const W& msg, WriteOptions options) override {
-    return body_.Write(msg, options);
-  }
-
- private:
-  internal::ServerReaderWriterBody<W, R> body_;
-
-  friend class internal::TemplatedBidiStreamingHandler<ServerReaderWriter<W, R>,
-                                                       false>;
-  ServerReaderWriter(internal::Call* call, ServerContext* ctx)
-      : body_(call, ctx) {}
-};
-
-/// A class to represent a flow-controlled unary call. This is something
-/// of a hybrid between conventional unary and streaming. This is invoked
-/// through a unary call on the client side, but the server responds to it
-/// as though it were a single-ping-pong streaming call. The server can use
-/// the \a NextMessageSize method to determine an upper-bound on the size of
-/// the message. A key difference relative to streaming: ServerUnaryStreamer
-/// must have exactly 1 Read and exactly 1 Write, in that order, to function
-/// correctly. Otherwise, the RPC is in error.
-template <class RequestType, class ResponseType>
-class ServerUnaryStreamer final
-    : public ServerReaderWriterInterface<ResponseType, RequestType> {
- public:
-  /// Block to send initial metadata to client.
-  /// Implicit input parameter:
-  ///    - the \a ServerContext associated with this call will be used for
-  ///      sending initial metadata.
-  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
-
-  /// Get an upper bound on the request message size from the client.
-  bool NextMessageSize(uint32_t* sz) override {
-    return body_.NextMessageSize(sz);
-  }
-
-  /// Read a message of type \a R into \a msg. Completion will be notified by \a
-  /// tag on the associated completion queue.
-  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
-  /// should not be called concurrently with other streaming APIs
-  /// on the same stream. It is not meaningful to call it concurrently
-  /// with another \a ReaderInterface::Read on the same stream since reads on
-  /// the same stream are delivered in order.
-  ///
-  /// \param[out] msg Where to eventually store the read message.
-  /// \param[in] tag The tag identifying the operation.
-  bool Read(RequestType* request) override {
-    if (read_done_) {
-      return false;
-    }
-    read_done_ = true;
-    return body_.Read(request);
-  }
-
-  /// Block to write \a msg to the stream with WriteOptions \a options.
-  /// This is thread-safe with respect to \a ReaderInterface::Read
-  ///
-  /// \param msg The message to be written to the stream.
-  /// \param options The WriteOptions affecting the write operation.
-  ///
-  /// \return \a true on success, \a false when the stream has been closed.
-  using internal::WriterInterface<ResponseType>::Write;
-  bool Write(const ResponseType& response, WriteOptions options) override {
-    if (write_done_ || !read_done_) {
-      return false;
-    }
-    write_done_ = true;
-    return body_.Write(response, options);
-  }
-
- private:
-  internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
-  bool read_done_;
-  bool write_done_;
-
-  friend class internal::TemplatedBidiStreamingHandler<
-      ServerUnaryStreamer<RequestType, ResponseType>, true>;
-  ServerUnaryStreamer(internal::Call* call, ServerContext* ctx)
-      : body_(call, ctx), read_done_(false), write_done_(false) {}
-};
-
-/// A class to represent a flow-controlled server-side streaming call.
-/// This is something of a hybrid between server-side and bidi streaming.
-/// This is invoked through a server-side streaming call on the client side,
-/// but the server responds to it as though it were a bidi streaming call that
-/// must first have exactly 1 Read and then any number of Writes.
-template <class RequestType, class ResponseType>
-class ServerSplitStreamer final
-    : public ServerReaderWriterInterface<ResponseType, RequestType> {
- public:
-  /// Block to send initial metadata to client.
-  /// Implicit input parameter:
-  ///    - the \a ServerContext associated with this call will be used for
-  ///      sending initial metadata.
-  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
-
-  /// Get an upper bound on the request message size from the client.
-  bool NextMessageSize(uint32_t* sz) override {
-    return body_.NextMessageSize(sz);
-  }
-
-  /// Read a message of type \a R into \a msg. Completion will be notified by \a
-  /// tag on the associated completion queue.
-  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
-  /// should not be called concurrently with other streaming APIs
-  /// on the same stream. It is not meaningful to call it concurrently
-  /// with another \a ReaderInterface::Read on the same stream since reads on
-  /// the same stream are delivered in order.
-  ///
-  /// \param[out] msg Where to eventually store the read message.
-  /// \param[in] tag The tag identifying the operation.
-  bool Read(RequestType* request) override {
-    if (read_done_) {
-      return false;
-    }
-    read_done_ = true;
-    return body_.Read(request);
-  }
-
-  /// Block to write \a msg to the stream with WriteOptions \a options.
-  /// This is thread-safe with respect to \a ReaderInterface::Read
-  ///
-  /// \param msg The message to be written to the stream.
-  /// \param options The WriteOptions affecting the write operation.
-  ///
-  /// \return \a true on success, \a false when the stream has been closed.
-  using internal::WriterInterface<ResponseType>::Write;
-  bool Write(const ResponseType& response, WriteOptions options) override {
-    return read_done_ && body_.Write(response, options);
-  }
-
- private:
-  internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
-  bool read_done_;
-
-  friend class internal::TemplatedBidiStreamingHandler<
-      ServerSplitStreamer<RequestType, ResponseType>, false>;
-  ServerSplitStreamer(internal::Call* call, ServerContext* ctx)
-      : body_(call, ctx), read_done_(false) {}
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/sync_stream.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H
diff --git a/include/grpc++/impl/codegen/time.h b/include/grpc++/impl/codegen/time.h
index d464d6e..f9b70f8 100644
--- a/include/grpc++/impl/codegen/time.h
+++ b/include/grpc++/impl/codegen/time.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,74 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_CODEGEN_TIME_H
 #define GRPCXX_IMPL_CODEGEN_TIME_H
 
-#include <chrono>
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-namespace grpc {
-
-/** If you are trying to use CompletionQueue::AsyncNext with a time class that
-    isn't either gpr_timespec or std::chrono::system_clock::time_point, you
-    will most likely be looking at this comment as your compiler will have
-    fired an error below. In order to fix this issue, you have two potential
-    solutions:
-
-      1. Use gpr_timespec or std::chrono::system_clock::time_point instead
-      2. Specialize the TimePoint class with whichever time class that you
-         want to use here. See below for two examples of how to do this.
- */
-template <typename T>
-class TimePoint {
- public:
-  TimePoint(const T& time) { you_need_a_specialization_of_TimePoint(); }
-  gpr_timespec raw_time() {
-    gpr_timespec t;
-    return t;
-  }
-
- private:
-  void you_need_a_specialization_of_TimePoint();
-};
-
-template <>
-class TimePoint<gpr_timespec> {
- public:
-  TimePoint(const gpr_timespec& time) : time_(time) {}
-  gpr_timespec raw_time() { return time_; }
-
- private:
-  gpr_timespec time_;
-};
-
-}  // namespace grpc
-
-namespace grpc {
-
-// from and to should be absolute time.
-void Timepoint2Timespec(const std::chrono::system_clock::time_point& from,
-                        gpr_timespec* to);
-void TimepointHR2Timespec(
-    const std::chrono::high_resolution_clock::time_point& from,
-    gpr_timespec* to);
-
-std::chrono::system_clock::time_point Timespec2Timepoint(gpr_timespec t);
-
-template <>
-class TimePoint<std::chrono::system_clock::time_point> {
- public:
-  TimePoint(const std::chrono::system_clock::time_point& time) {
-    Timepoint2Timespec(time, &time_);
-  }
-  gpr_timespec raw_time() const { return time_; }
-
- private:
-  gpr_timespec time_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/codegen/time.h>
 
 #endif  // GRPCXX_IMPL_CODEGEN_TIME_H
diff --git a/include/grpc++/impl/grpc_library.h b/include/grpc++/impl/grpc_library.h
index 55c867d..f34a281 100644
--- a/include/grpc++/impl/grpc_library.h
+++ b/include/grpc++/impl/grpc_library.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,46 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_GRPC_LIBRARY_H
 #define GRPCXX_IMPL_GRPC_LIBRARY_H
 
-#include <iostream>
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen.h>
-#include <grpc++/impl/codegen/grpc_library.h>
-#include <grpc/grpc.h>
-
-namespace grpc {
-
-namespace internal {
-class GrpcLibrary final : public GrpcLibraryInterface {
- public:
-  void init() override { grpc_init(); }
-  void shutdown() override { grpc_shutdown(); }
-};
-
-static GrpcLibrary g_gli;
-static CoreCodegen g_core_codegen;
-
-/// Instantiating this class ensures the proper initialization of gRPC.
-class GrpcLibraryInitializer final {
- public:
-  GrpcLibraryInitializer() {
-    if (grpc::g_glip == nullptr) {
-      grpc::g_glip = &g_gli;
-    }
-    if (grpc::g_core_codegen_interface == nullptr) {
-      grpc::g_core_codegen_interface = &g_core_codegen;
-    }
-  }
-
-  /// A no-op method to force the linker to reference this class, which will
-  /// take care of initializing and shutting down the gRPC runtime.
-  int summon() { return 0; }
-};
-
-}  // namespace internal
-}  // namespace grpc
+#include <grpcpp/impl/grpc_library.h>
 
 #endif  // GRPCXX_IMPL_GRPC_LIBRARY_H
diff --git a/include/grpc++/impl/method_handler_impl.h b/include/grpc++/impl/method_handler_impl.h
index 0b1ab02..3840f48 100644
--- a/include/grpc++/impl/method_handler_impl.h
+++ b/include/grpc++/impl/method_handler_impl.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
 #define GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
 
-#include <grpc++/impl/codegen/method_handler_impl.h>
+#include <grpcpp/impl/method_handler_impl.h>
 
 #endif  // GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
diff --git a/include/grpc++/impl/rpc_method.h b/include/grpc++/impl/rpc_method.h
index 51e95bb..7cba7c4 100644
--- a/include/grpc++/impl/rpc_method.h
+++ b/include/grpc++/impl/rpc_method.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_RPC_METHOD_H
 #define GRPCXX_IMPL_RPC_METHOD_H
 
-#include <grpc++/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/rpc_method.h>
 
 #endif  // GRPCXX_IMPL_RPC_METHOD_H
diff --git a/include/grpc++/impl/rpc_service_method.h b/include/grpc++/impl/rpc_service_method.h
index efde374..2c75087 100644
--- a/include/grpc++/impl/rpc_service_method.h
+++ b/include/grpc++/impl/rpc_service_method.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_RPC_SERVICE_METHOD_H
 #define GRPCXX_IMPL_RPC_SERVICE_METHOD_H
 
-#include <grpc++/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/rpc_service_method.h>
 
 #endif  // GRPCXX_IMPL_RPC_SERVICE_METHOD_H
diff --git a/include/grpc++/impl/serialization_traits.h b/include/grpc++/impl/serialization_traits.h
index 91e894c..33b3a0b 100644
--- a/include/grpc++/impl/serialization_traits.h
+++ b/include/grpc++/impl/serialization_traits.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SERIALIZATION_TRAITS_H
 #define GRPCXX_IMPL_SERIALIZATION_TRAITS_H
 
-#include <grpc++/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/serialization_traits.h>
 
 #endif  // GRPCXX_IMPL_SERIALIZATION_TRAITS_H
diff --git a/include/grpc++/impl/server_builder_option.h b/include/grpc++/impl/server_builder_option.h
index ab04a1c..833f8db 100644
--- a/include/grpc++/impl/server_builder_option.h
+++ b/include/grpc++/impl/server_builder_option.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,28 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SERVER_BUILDER_OPTION_H
 #define GRPCXX_IMPL_SERVER_BUILDER_OPTION_H
 
-#include <map>
-#include <memory>
-
-#include <grpc++/impl/server_builder_plugin.h>
-#include <grpc++/support/channel_arguments.h>
-
-namespace grpc {
-
-/// Interface to pass an option to a \a ServerBuilder.
-class ServerBuilderOption {
- public:
-  virtual ~ServerBuilderOption() {}
-  /// Alter the \a ChannelArguments used to create the gRPC server.
-  virtual void UpdateArguments(ChannelArguments* args) = 0;
-  /// Alter the ServerBuilderPlugin map that will be added into ServerBuilder.
-  virtual void UpdatePlugins(
-      std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) = 0;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/server_builder_option.h>
 
 #endif  // GRPCXX_IMPL_SERVER_BUILDER_OPTION_H
diff --git a/include/grpc++/impl/server_builder_plugin.h b/include/grpc++/impl/server_builder_plugin.h
index e15cd7b..844d32c 100644
--- a/include/grpc++/impl/server_builder_plugin.h
+++ b/include/grpc++/impl/server_builder_plugin.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,50 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H
 #define GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H
 
-#include <memory>
-
-#include <grpc++/support/config.h>
-
-namespace grpc {
-
-class ServerBuilder;
-class ServerInitializer;
-class ChannelArguments;
-
-/// This interface is meant for internal usage only. Implementations of this
-/// interface should add themselves to a \a ServerBuilder instance through the
-/// \a InternalAddPluginFactory method.
-class ServerBuilderPlugin {
- public:
-  virtual ~ServerBuilderPlugin() {}
-  virtual grpc::string name() = 0;
-
-  /// UpdateServerBuilder will be called at the beginning of
-  /// \a ServerBuilder::BuildAndStart().
-  virtual void UpdateServerBuilder(ServerBuilder* builder) {}
-
-  /// InitServer will be called in ServerBuilder::BuildAndStart(), after the
-  /// Server instance is created.
-  virtual void InitServer(ServerInitializer* si) = 0;
-
-  /// Finish will be called at the end of ServerBuilder::BuildAndStart().
-  virtual void Finish(ServerInitializer* si) = 0;
-
-  /// ChangeArguments is an interface that can be used in
-  /// ServerBuilderOption::UpdatePlugins
-  virtual void ChangeArguments(const grpc::string& name, void* value) = 0;
-
-  /// UpdateChannelArguments will be called in ServerBuilder::BuildAndStart(),
-  /// before the Server instance is created.
-  virtual void UpdateChannelArguments(ChannelArguments* args) {}
-
-  virtual bool has_sync_methods() const { return false; }
-  virtual bool has_async_methods() const { return false; }
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/server_builder_plugin.h>
 
 #endif  // GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H
diff --git a/include/grpc++/impl/server_initializer.h b/include/grpc++/impl/server_initializer.h
index 873c46f..6a1669c 100644
--- a/include/grpc++/impl/server_initializer.h
+++ b/include/grpc++/impl/server_initializer.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,40 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SERVER_INITIALIZER_H
 #define GRPCXX_IMPL_SERVER_INITIALIZER_H
 
-#include <memory>
-#include <vector>
-
-#include <grpc++/server.h>
-
-namespace grpc {
-
-class Server;
-class Service;
-
-class ServerInitializer {
- public:
-  ServerInitializer(Server* server) : server_(server) {}
-
-  bool RegisterService(std::shared_ptr<Service> service) {
-    if (!server_->RegisterService(nullptr, service.get())) {
-      return false;
-    }
-    default_services_.push_back(service);
-    return true;
-  }
-
-  const std::vector<grpc::string>* GetServiceList() {
-    return &server_->services_;
-  }
-
- private:
-  Server* server_;
-  std::vector<std::shared_ptr<Service> > default_services_;
-};
-
-}  // namespace grpc
+#include <grpcpp/impl/server_initializer.h>
 
 #endif  // GRPCXX_IMPL_SERVER_INITIALIZER_H
diff --git a/include/grpc++/impl/service_type.h b/include/grpc++/impl/service_type.h
index 6a9e90a..8642216 100644
--- a/include/grpc++/impl/service_type.h
+++ b/include/grpc++/impl/service_type.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SERVICE_TYPE_H
 #define GRPCXX_IMPL_SERVICE_TYPE_H
 
-#include <grpc++/impl/codegen/service_type.h>
+#include <grpcpp/impl/service_type.h>
 
 #endif  // GRPCXX_IMPL_SERVICE_TYPE_H
diff --git a/include/grpc++/impl/sync_cxx11.h b/include/grpc++/impl/sync_cxx11.h
index 64d467f..8bcfb2c 100644
--- a/include/grpc++/impl/sync_cxx11.h
+++ b/include/grpc++/impl/sync_cxx11.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SYNC_CXX11_H
 #define GRPCXX_IMPL_SYNC_CXX11_H
 
-#include <grpc++/impl/codegen/sync_cxx11.h>
+#include <grpcpp/impl/sync_cxx11.h>
 
 #endif  // GRPCXX_IMPL_SYNC_CXX11_H
diff --git a/include/grpc++/impl/sync_no_cxx11.h b/include/grpc++/impl/sync_no_cxx11.h
index cc0177e..5264567 100644
--- a/include/grpc++/impl/sync_no_cxx11.h
+++ b/include/grpc++/impl/sync_no_cxx11.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_IMPL_SYNC_NO_CXX11_H
 #define GRPCXX_IMPL_SYNC_NO_CXX11_H
 
-#include <grpc++/impl/codegen/sync_no_cxx11.h>
+#include <grpcpp/impl/sync_no_cxx11.h>
 
 #endif  // GRPCXX_IMPL_SYNC_NO_CXX11_H
diff --git a/include/grpc++/resource_quota.h b/include/grpc++/resource_quota.h
index ef214db..aad1b56 100644
--- a/include/grpc++/resource_quota.h
+++ b/include/grpc++/resource_quota.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,43 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_RESOURCE_QUOTA_H
 #define GRPCXX_RESOURCE_QUOTA_H
 
-struct grpc_resource_quota;
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/grpc_library.h>
-
-namespace grpc {
-
-/// ResourceQuota represents a bound on memory usage by the gRPC library.
-/// A ResourceQuota can be attached to a server (via \a ServerBuilder),
-/// or a client channel (via \a ChannelArguments).
-/// gRPC will attempt to keep memory used by all attached entities
-/// below the ResourceQuota bound.
-class ResourceQuota final : private GrpcLibraryCodegen {
- public:
-  /// \param name - a unique name for this ResourceQuota.
-  explicit ResourceQuota(const grpc::string& name);
-  ResourceQuota();
-  ~ResourceQuota();
-
-  /// Resize this \a ResourceQuota to a new size. If \a new_size is smaller
-  /// than the current size of the pool, memory usage will be monotonically
-  /// decreased until it falls under \a new_size.
-  /// No time bound is given for this to occur however.
-  ResourceQuota& Resize(size_t new_size);
-
-  grpc_resource_quota* c_resource_quota() const { return impl_; }
-
- private:
-  ResourceQuota(const ResourceQuota& rhs);
-  ResourceQuota& operator=(const ResourceQuota& rhs);
-
-  grpc_resource_quota* const impl_;
-};
-
-}  // namespace grpc
+#include <grpcpp/resource_quota.h>
 
 #endif  // GRPCXX_RESOURCE_QUOTA_H
diff --git a/include/grpc++/security/auth_context.h b/include/grpc++/security/auth_context.h
index 71f5d6e..9fe59d4 100644
--- a/include/grpc++/security/auth_context.h
+++ b/include/grpc++/security/auth_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SECURITY_AUTH_CONTEXT_H
 #define GRPCXX_SECURITY_AUTH_CONTEXT_H
 
-#include <grpc++/impl/codegen/security/auth_context.h>
+#include <grpcpp/security/auth_context.h>
 
 #endif  // GRPCXX_SECURITY_AUTH_CONTEXT_H
diff --git a/include/grpc++/security/auth_metadata_processor.h b/include/grpc++/security/auth_metadata_processor.h
index a49e30f..d045313 100644
--- a/include/grpc++/security/auth_metadata_processor.h
+++ b/include/grpc++/security/auth_metadata_processor.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,46 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H
 #define GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H
 
-#include <map>
-
-#include <grpc++/security/auth_context.h>
-#include <grpc++/support/status.h>
-#include <grpc++/support/string_ref.h>
-
-namespace grpc {
-
-/// Interface allowing custom server-side authorization based on credentials
-/// encoded in metadata.  Objects of this type can be passed to
-/// \a ServerCredentials::SetAuthMetadataProcessor().
-class AuthMetadataProcessor {
- public:
-  typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
-  typedef std::multimap<grpc::string, grpc::string> OutputMetadata;
-
-  virtual ~AuthMetadataProcessor() {}
-
-  /// If this method returns true, the \a Process function will be scheduled in
-  /// a different thread from the one processing the call.
-  virtual bool IsBlocking() const { return true; }
-
-  /// context is read/write: it contains the properties of the channel peer and
-  /// it is the job of the Process method to augment it with properties derived
-  /// from the passed-in auth_metadata.
-  /// consumed_auth_metadata needs to be filled with metadata that has been
-  /// consumed by the processor and will be removed from the call.
-  /// response_metadata is the metadata that will be sent as part of the
-  /// response.
-  /// If the return value is not Status::OK, the rpc call will be aborted with
-  /// the error code and error message sent back to the client.
-  virtual Status Process(const InputMetadata& auth_metadata,
-                         AuthContext* context,
-                         OutputMetadata* consumed_auth_metadata,
-                         OutputMetadata* response_metadata) = 0;
-};
-
-}  // namespace grpc
+#include <grpcpp/security/auth_metadata_processor.h>
 
 #endif  // GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H
diff --git a/include/grpc++/security/credentials.h b/include/grpc++/security/credentials.h
index 92330d4..9404418 100644
--- a/include/grpc++/security/credentials.h
+++ b/include/grpc++/security/credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,209 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SECURITY_CREDENTIALS_H
 #define GRPCXX_SECURITY_CREDENTIALS_H
 
-#include <map>
-#include <memory>
-
-#include <grpc++/impl/codegen/grpc_library.h>
-#include <grpc++/security/auth_context.h>
-#include <grpc++/support/status.h>
-#include <grpc++/support/string_ref.h>
-
-struct grpc_call;
-
-namespace grpc {
-class ChannelArguments;
-class Channel;
-class SecureChannelCredentials;
-class CallCredentials;
-class SecureCallCredentials;
-
-/// A channel credentials object encapsulates all the state needed by a client
-/// to authenticate with a server for a given channel.
-/// It can make various assertions, e.g., about the client’s identity, role
-/// for all the calls on that channel.
-///
-/// \see https://grpc.io/docs/guides/auth.html
-class ChannelCredentials : private GrpcLibraryCodegen {
- public:
-  ChannelCredentials();
-  ~ChannelCredentials();
-
- protected:
-  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
-      const std::shared_ptr<ChannelCredentials>& channel_creds,
-      const std::shared_ptr<CallCredentials>& call_creds);
-
-  virtual SecureChannelCredentials* AsSecureCredentials() = 0;
-
- private:
-  friend std::shared_ptr<Channel> CreateCustomChannel(
-      const grpc::string& target,
-      const std::shared_ptr<ChannelCredentials>& creds,
-      const ChannelArguments& args);
-
-  virtual std::shared_ptr<Channel> CreateChannel(
-      const grpc::string& target, const ChannelArguments& args) = 0;
-};
-
-/// A call credentials object encapsulates the state needed by a client to
-/// authenticate with a server for a given call on a channel.
-///
-/// \see https://grpc.io/docs/guides/auth.html
-class CallCredentials : private GrpcLibraryCodegen {
- public:
-  CallCredentials();
-  ~CallCredentials();
-
-  /// Apply this instance's credentials to \a call.
-  virtual bool ApplyToCall(grpc_call* call) = 0;
-
- protected:
-  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
-      const std::shared_ptr<ChannelCredentials>& channel_creds,
-      const std::shared_ptr<CallCredentials>& call_creds);
-
-  friend std::shared_ptr<CallCredentials> CompositeCallCredentials(
-      const std::shared_ptr<CallCredentials>& creds1,
-      const std::shared_ptr<CallCredentials>& creds2);
-
-  virtual SecureCallCredentials* AsSecureCredentials() = 0;
-};
-
-/// Options used to build SslCredentials.
-struct SslCredentialsOptions {
-  /// The buffer containing the PEM encoding of the server root certificates. If
-  /// this parameter is empty, the default roots will be used.  The default
-  /// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
-  /// environment variable pointing to a file on the file system containing the
-  /// roots.
-  grpc::string pem_root_certs;
-
-  /// The buffer containing the PEM encoding of the client's private key. This
-  /// parameter can be empty if the client does not have a private key.
-  grpc::string pem_private_key;
-
-  /// The buffer containing the PEM encoding of the client's certificate chain.
-  /// This parameter can be empty if the client does not have a certificate
-  /// chain.
-  grpc::string pem_cert_chain;
-};
-
-// Factories for building different types of Credentials The functions may
-// return empty shared_ptr when credentials cannot be created. If a
-// Credentials pointer is returned, it can still be invalid when used to create
-// a channel. A lame channel will be created then and all rpcs will fail on it.
-
-/// Builds credentials with reasonable defaults.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials();
-
-/// Builds SSL Credentials given SSL specific options
-std::shared_ptr<ChannelCredentials> SslCredentials(
-    const SslCredentialsOptions& options);
-
-/// Builds credentials for use when running in GCE
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials();
-
-/// Constant for maximum auth token lifetime.
-constexpr long kMaxAuthTokenLifetimeSecs = 3600;
-
-/// Builds Service Account JWT Access credentials.
-/// json_key is the JSON key string containing the client's private key.
-/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token
-/// (JWT) created with this credentials. It should not exceed
-/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value.
-std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
-    const grpc::string& json_key,
-    long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs);
-
-/// Builds refresh token credentials.
-/// json_refresh_token is the JSON string containing the refresh token along
-/// with a client_id and client_secret.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
-    const grpc::string& json_refresh_token);
-
-/// Builds access token credentials.
-/// access_token is an oauth2 access token that was fetched using an out of band
-/// mechanism.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> AccessTokenCredentials(
-    const grpc::string& access_token);
-
-/// Builds IAM credentials.
-///
-/// \warning Only use these credentials when connecting to a Google endpoint.
-/// Using these credentials to connect to any other service may result in this
-/// service being able to impersonate your client for requests to Google
-/// services.
-std::shared_ptr<CallCredentials> GoogleIAMCredentials(
-    const grpc::string& authorization_token,
-    const grpc::string& authority_selector);
-
-/// Combines a channel credentials and a call credentials into a composite
-/// channel credentials.
-std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
-    const std::shared_ptr<ChannelCredentials>& channel_creds,
-    const std::shared_ptr<CallCredentials>& call_creds);
-
-/// Combines two call credentials objects into a composite call credentials.
-std::shared_ptr<CallCredentials> CompositeCallCredentials(
-    const std::shared_ptr<CallCredentials>& creds1,
-    const std::shared_ptr<CallCredentials>& creds2);
-
-/// Credentials for an unencrypted, unauthenticated channel
-std::shared_ptr<ChannelCredentials> InsecureChannelCredentials();
-
-/// Credentials for a channel using Cronet.
-std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine);
-
-/// User defined metadata credentials.
-class MetadataCredentialsPlugin {
- public:
-  virtual ~MetadataCredentialsPlugin() {}
-
-  /// If this method returns true, the Process function will be scheduled in
-  /// a different thread from the one processing the call.
-  virtual bool IsBlocking() const { return true; }
-
-  /// Type of credentials this plugin is implementing.
-  virtual const char* GetType() const { return ""; }
-
-  /// Gets the auth metatada produced by this plugin.
-  /// The fully qualified method name is:
-  /// service_url + "/" + method_name.
-  /// The channel_auth_context contains (among other things), the identity of
-  /// the server.
-  virtual Status GetMetadata(
-      grpc::string_ref service_url, grpc::string_ref method_name,
-      const AuthContext& channel_auth_context,
-      std::multimap<grpc::string, grpc::string>* metadata) = 0;
-};
-
-std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
-    std::unique_ptr<MetadataCredentialsPlugin> plugin);
-
-}  // namespace grpc
+#include <grpcpp/security/credentials.h>
 
 #endif  // GRPCXX_SECURITY_CREDENTIALS_H
diff --git a/include/grpc++/security/server_credentials.h b/include/grpc++/security/server_credentials.h
index 74a61b5..c6d1c4f 100644
--- a/include/grpc++/security/server_credentials.h
+++ b/include/grpc++/security/server_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,76 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SECURITY_SERVER_CREDENTIALS_H
 #define GRPCXX_SECURITY_SERVER_CREDENTIALS_H
 
-#include <memory>
-#include <vector>
-
-#include <grpc++/security/auth_metadata_processor.h>
-#include <grpc++/support/config.h>
-#include <grpc/grpc_security_constants.h>
-
-struct grpc_server;
-
-namespace grpc {
-class Server;
-
-/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
-class ServerCredentials {
- public:
-  virtual ~ServerCredentials();
-
-  /// This method is not thread-safe and has to be called before the server is
-  /// started. The last call to this function wins.
-  virtual void SetAuthMetadataProcessor(
-      const std::shared_ptr<AuthMetadataProcessor>& processor) = 0;
-
- private:
-  friend class ::grpc::Server;
-
-  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
-  /// 192.168.1.1:31416, [::1]:27182, etc.)
-  ///
-  /// \return bound port number on sucess, 0 on failure.
-  // TODO(dgq): the "port" part seems to be a misnomer.
-  virtual int AddPortToServer(const grpc::string& addr,
-                              grpc_server* server) = 0;
-};
-
-/// Options to create ServerCredentials with SSL
-struct SslServerCredentialsOptions {
-  /// \warning Deprecated
-  SslServerCredentialsOptions()
-      : force_client_auth(false),
-        client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {}
-  SslServerCredentialsOptions(
-      grpc_ssl_client_certificate_request_type request_type)
-      : force_client_auth(false), client_certificate_request(request_type) {}
-
-  struct PemKeyCertPair {
-    grpc::string private_key;
-    grpc::string cert_chain;
-  };
-  grpc::string pem_root_certs;
-  std::vector<PemKeyCertPair> pem_key_cert_pairs;
-  /// \warning Deprecated
-  bool force_client_auth;
-
-  /// If both \a force_client_auth and \a client_certificate_request
-  /// fields are set, \a force_client_auth takes effect, i.e.
-  /// \a REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
-  /// will be enforced.
-  grpc_ssl_client_certificate_request_type client_certificate_request;
-};
-
-/// Builds SSL ServerCredentials given SSL specific options
-std::shared_ptr<ServerCredentials> SslServerCredentials(
-    const SslServerCredentialsOptions& options);
-
-/// Builds insecure server credentials.
-std::shared_ptr<ServerCredentials> InsecureServerCredentials();
-
-}  // namespace grpc
+#include <grpcpp/security/server_credentials.h>
 
 #endif  // GRPCXX_SECURITY_SERVER_CREDENTIALS_H
diff --git a/include/grpc++/server.h b/include/grpc++/server.h
index 01c4a60..086c24c 100644
--- a/include/grpc++/server.h
+++ b/include/grpc++/server.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,212 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SERVER_H
 #define GRPCXX_SERVER_H
 
-#include <condition_variable>
-#include <list>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include <grpc++/completion_queue.h>
-#include <grpc++/impl/call.h>
-#include <grpc++/impl/codegen/grpc_library.h>
-#include <grpc++/impl/codegen/server_interface.h>
-#include <grpc++/impl/rpc_service_method.h>
-#include <grpc++/security/server_credentials.h>
-#include <grpc++/support/channel_arguments.h>
-#include <grpc++/support/config.h>
-#include <grpc++/support/status.h>
-#include <grpc/compression.h>
-
-struct grpc_server;
-
-namespace grpc {
-
-class AsyncGenericService;
-class HealthCheckServiceInterface;
-class ServerContext;
-class ServerInitializer;
-
-/// Represents a gRPC server.
-///
-/// Use a \a grpc::ServerBuilder to create, configure, and start
-/// \a Server instances.
-class Server final : public ServerInterface, private GrpcLibraryCodegen {
- public:
-  ~Server();
-
-  /// Block until the server shuts down.
-  ///
-  /// \warning The server must be either shutting down or some other thread must
-  /// call \a Shutdown for this function to ever return.
-  void Wait() override;
-
-  /// Global callbacks are a set of hooks that are called when server
-  /// events occur.  \a SetGlobalCallbacks method is used to register
-  /// the hooks with gRPC.  Note that
-  /// the \a GlobalCallbacks instance will be shared among all
-  /// \a Server instances in an application and can be set exactly
-  /// once per application.
-  class GlobalCallbacks {
-   public:
-    virtual ~GlobalCallbacks() {}
-    /// Called before server is created.
-    virtual void UpdateArguments(ChannelArguments* args) {}
-    /// Called before application callback for each synchronous server request
-    virtual void PreSynchronousRequest(ServerContext* context) = 0;
-    /// Called after application callback for each synchronous server request
-    virtual void PostSynchronousRequest(ServerContext* context) = 0;
-    /// Called before server is started.
-    virtual void PreServerStart(Server* server) {}
-    /// Called after a server port is added.
-    virtual void AddPort(Server* server, const grpc::string& addr,
-                         ServerCredentials* creds, int port) {}
-  };
-  /// Set the global callback object. Can only be called once per application.
-  /// Does not take ownership of callbacks, and expects the pointed to object
-  /// to be alive until all server objects in the process have been destroyed.
-  /// The same \a GlobalCallbacks object will be used throughout the
-  /// application and is shared among all \a Server objects.
-  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
-
-  // Returns a \em raw pointer to the underlying \a grpc_server instance.
-  grpc_server* c_server();
-
-  /// Returns the health check service.
-  HealthCheckServiceInterface* GetHealthCheckService() const {
-    return health_check_service_.get();
-  }
-
-  /// Establish a channel for in-process communication
-  std::shared_ptr<Channel> InProcessChannel(const ChannelArguments& args);
-
- private:
-  friend class AsyncGenericService;
-  friend class ServerBuilder;
-  friend class ServerInitializer;
-
-  class SyncRequest;
-  class AsyncRequest;
-  class ShutdownRequest;
-
-  /// SyncRequestThreadManager is an implementation of ThreadManager. This class
-  /// is responsible for polling for incoming RPCs and calling the RPC handlers.
-  /// This is only used in case of a Sync server (i.e a server exposing a sync
-  /// interface)
-  class SyncRequestThreadManager;
-
-  class UnimplementedAsyncRequestContext;
-  class UnimplementedAsyncRequest;
-  class UnimplementedAsyncResponse;
-
-  /// Server constructors. To be used by \a ServerBuilder only.
-  ///
-  /// \param max_message_size Maximum message length that the channel can
-  /// receive.
-  ///
-  /// \param args The channel args
-  ///
-  /// \param sync_server_cqs The completion queues to use if the server is a
-  /// synchronous server (or a hybrid server). The server polls for new RPCs on
-  /// these queues
-  ///
-  /// \param min_pollers The minimum number of polling threads per server
-  /// completion queue (in param sync_server_cqs) to use for listening to
-  /// incoming requests (used only in case of sync server)
-  ///
-  /// \param max_pollers The maximum number of polling threads per server
-  /// completion queue (in param sync_server_cqs) to use for listening to
-  /// incoming requests (used only in case of sync server)
-  ///
-  /// \param sync_cq_timeout_msec The timeout to use when calling AsyncNext() on
-  /// server completion queues passed via sync_server_cqs param.
-  Server(int max_message_size, ChannelArguments* args,
-         std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
-             sync_server_cqs,
-         int min_pollers, int max_pollers, int sync_cq_timeout_msec);
-
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the Server instance.
-  bool RegisterService(const grpc::string* host, Service* service) override;
-
-  /// Register a generic service. This call does not take ownership of the
-  /// service. The service must exist for the lifetime of the Server instance.
-  void RegisterAsyncGenericService(AsyncGenericService* service) override;
-
-  /// Try binding the server to the given \a addr endpoint
-  /// (port, and optionally including IP address to bind to).
-  ///
-  /// It can be invoked multiple times. Should be used before
-  /// starting the server.
-  ///
-  /// \param addr The address to try to bind to the server (eg, localhost:1234,
-  /// 192.168.1.1:31416, [::1]:27182, etc.).
-  /// \param creds The credentials associated with the server.
-  ///
-  /// \return bound port number on success, 0 on failure.
-  ///
-  /// \warning It is an error to call this method on an already started server.
-  int AddListeningPort(const grpc::string& addr,
-                       ServerCredentials* creds) override;
-
-  /// Start the server.
-  ///
-  /// \param cqs Completion queues for handling asynchronous services. The
-  /// caller is required to keep all completion queues live until the server is
-  /// destroyed.
-  /// \param num_cqs How many completion queues does \a cqs hold.
-  void Start(ServerCompletionQueue** cqs, size_t num_cqs) override;
-
-  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
-                        internal::Call* call) override;
-
-  void ShutdownInternal(gpr_timespec deadline) override;
-
-  int max_receive_message_size() const override {
-    return max_receive_message_size_;
-  };
-
-  grpc_server* server() override { return server_; };
-
-  ServerInitializer* initializer();
-
-  const int max_receive_message_size_;
-
-  /// The following completion queues are ONLY used in case of Sync API
-  /// i.e. if the server has any services with sync methods. The server uses
-  /// these completion queues to poll for new RPCs
-  std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
-      sync_server_cqs_;
-
-  /// List of \a ThreadManager instances (one for each cq in
-  /// the \a sync_server_cqs)
-  std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
-
-  // Sever status
-  std::mutex mu_;
-  bool started_;
-  bool shutdown_;
-  bool shutdown_notified_;  // Was notify called on the shutdown_cv_
-
-  std::condition_variable shutdown_cv_;
-
-  std::shared_ptr<GlobalCallbacks> global_callbacks_;
-
-  std::vector<grpc::string> services_;
-  bool has_generic_service_;
-
-  // Pointer to the wrapped grpc_server.
-  grpc_server* server_;
-
-  std::unique_ptr<ServerInitializer> server_initializer_;
-
-  std::unique_ptr<HealthCheckServiceInterface> health_check_service_;
-  bool health_check_service_disabled_;
-};
-
-}  // namespace grpc
+#include <grpcpp/server.h>
 
 #endif  // GRPCXX_SERVER_H
diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h
index ea9834c..2c6dab4 100644
--- a/include/grpc++/server_builder.h
+++ b/include/grpc++/server_builder.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015-2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,263 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SERVER_BUILDER_H
 #define GRPCXX_SERVER_BUILDER_H
 
-#include <climits>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <grpc++/impl/channel_argument_option.h>
-#include <grpc++/impl/server_builder_option.h>
-#include <grpc++/impl/server_builder_plugin.h>
-#include <grpc++/support/config.h>
-#include <grpc/compression.h>
-#include <grpc/support/cpu.h>
-#include <grpc/support/workaround_list.h>
-
-struct grpc_resource_quota;
-
-namespace grpc {
-
-class AsyncGenericService;
-class ResourceQuota;
-class CompletionQueue;
-class Server;
-class ServerCompletionQueue;
-class ServerCredentials;
-class Service;
-
-namespace testing {
-class ServerBuilderPluginTest;
-}  // namespace testing
-
-/// A builder class for the creation and startup of \a grpc::Server instances.
-class ServerBuilder {
- public:
-  ServerBuilder();
-  ~ServerBuilder();
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Primary API's
-
-  /// Return a running server which is ready for processing calls.
-  /// Before calling, one typically needs to ensure that:
-  ///  1. a service is registered - so that the server knows what to serve
-  ///     (via RegisterService, or RegisterAsyncGenericService)
-  ///  2. a listening port has been added - so the server knows where to receive
-  ///     traffic (via AddListeningPort)
-  ///  3. [for async api only] completion queues have been added via
-  ///     AddCompletionQueue
-  std::unique_ptr<Server> BuildAndStart();
-
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the \a Server instance returned
-  /// by \a BuildAndStart().
-  /// Matches requests with any :authority
-  ServerBuilder& RegisterService(Service* service);
-
-  /// Enlists an endpoint \a addr (port with an optional IP address) to
-  /// bind the \a grpc::Server object to be created to.
-  ///
-  /// It can be invoked multiple times.
-  ///
-  /// \param addr_uri The address to try to bind to the server in URI form. If
-  /// the scheme name is omitted, "dns:///" is assumed. To bind to any address,
-  /// please use IPv6 any, i.e., [::]:<port>, which also accepts IPv4
-  /// connections.  Valid values include dns:///localhost:1234, /
-  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.).
-  /// \param creds The credentials associated with the server.
-  /// \param selected_port[out] If not `nullptr`, gets populated with the port
-  /// number bound to the \a grpc::Server for the corresponding endpoint after
-  /// it is successfully bound, 0 otherwise.
-  ///
-  ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
-                                  std::shared_ptr<ServerCredentials> creds,
-                                  int* selected_port = nullptr);
-
-  /// Add a completion queue for handling asynchronous services.
-  ///
-  /// Best performance is typically obtained by using one thread per polling
-  /// completion queue.
-  ///
-  /// Caller is required to shutdown the server prior to shutting down the
-  /// returned completion queue. Caller is also required to drain the
-  /// completion queue after shutting it down. A typical usage scenario:
-  ///
-  /// // While building the server:
-  /// ServerBuilder builder;
-  /// ...
-  /// cq_ = builder.AddCompletionQueue();
-  /// server_ = builder.BuildAndStart();
-  ///
-  /// // While shutting down the server;
-  /// server_->Shutdown();
-  /// cq_->Shutdown();  // Always *after* the associated server's Shutdown()!
-  /// // Drain the cq_ that was created
-  /// void* ignored_tag;
-  /// bool ignored_ok;
-  /// while (cq_->Next(&ignored_tag, &ignored_ok)) { }
-  ///
-  /// \param is_frequently_polled This is an optional parameter to inform gRPC
-  /// library about whether this completion queue would be frequently polled
-  /// (i.e. by calling \a Next() or \a AsyncNext()). The default value is
-  /// 'true' and is the recommended setting. Setting this to 'false' (i.e.
-  /// not polling the completion queue frequently) will have a significantly
-  /// negative performance impact and hence should not be used in production
-  /// use cases.
-  std::unique_ptr<ServerCompletionQueue> AddCompletionQueue(
-      bool is_frequently_polled = true);
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Less commonly used RegisterService variants
-
-  /// Register a service. This call does not take ownership of the service.
-  /// The service must exist for the lifetime of the \a Server instance returned
-  /// by \a BuildAndStart().
-  /// Only matches requests with :authority \a host
-  ServerBuilder& RegisterService(const grpc::string& host, Service* service);
-
-  /// Register a generic service.
-  /// Matches requests with any :authority
-  /// This is mostly useful for writing generic gRPC Proxies where the exact
-  /// serialization format is unknown
-  ServerBuilder& RegisterAsyncGenericService(AsyncGenericService* service);
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Fine control knobs
-
-  /// Set max receive message size in bytes.
-  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
-    max_receive_message_size_ = max_receive_message_size;
-    return *this;
-  }
-
-  /// Set max send message size in bytes.
-  ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) {
-    max_send_message_size_ = max_send_message_size;
-    return *this;
-  }
-
-  /// \deprecated For backward compatibility.
-  ServerBuilder& SetMaxMessageSize(int max_message_size) {
-    return SetMaxReceiveMessageSize(max_message_size);
-  }
-
-  /// Set the support status for compression algorithms. All algorithms are
-  /// enabled by default.
-  ///
-  /// Incoming calls compressed with an unsupported algorithm will fail with
-  /// \a GRPC_STATUS_UNIMPLEMENTED.
-  ServerBuilder& SetCompressionAlgorithmSupportStatus(
-      grpc_compression_algorithm algorithm, bool enabled);
-
-  /// The default compression level to use for all channel calls in the
-  /// absence of a call-specific level.
-  ServerBuilder& SetDefaultCompressionLevel(grpc_compression_level level);
-
-  /// The default compression algorithm to use for all channel calls in the
-  /// absence of a call-specific level. Note that it overrides any compression
-  /// level set by \a SetDefaultCompressionLevel.
-  ServerBuilder& SetDefaultCompressionAlgorithm(
-      grpc_compression_algorithm algorithm);
-
-  /// Set the attached buffer pool for this server
-  ServerBuilder& SetResourceQuota(const ResourceQuota& resource_quota);
-
-  ServerBuilder& SetOption(std::unique_ptr<ServerBuilderOption> option);
-
-  /// Options for synchronous servers.
-  enum SyncServerOption {
-    NUM_CQS,         ///< Number of completion queues.
-    MIN_POLLERS,     ///< Minimum number of polling threads.
-    MAX_POLLERS,     ///< Maximum number of polling threads.
-    CQ_TIMEOUT_MSEC  ///< Completion queue timeout in milliseconds.
-  };
-
-  /// Only useful if this is a Synchronous server.
-  ServerBuilder& SetSyncServerOption(SyncServerOption option, int value);
-
-  /// Add a channel argument (an escape hatch to tuning core library parameters
-  /// directly)
-  template <class T>
-  ServerBuilder& AddChannelArgument(const grpc::string& arg, const T& value) {
-    return SetOption(MakeChannelArgumentOption(arg, value));
-  }
-
-  /// For internal use only: Register a ServerBuilderPlugin factory function.
-  static void InternalAddPluginFactory(
-      std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)());
-
-  /// Enable a server workaround. Do not use unless you know what the workaround
-  /// does. For explanation and detailed descriptions of workarounds, see
-  /// doc/workarounds.md.
-  ServerBuilder& EnableWorkaround(grpc_workaround_list id);
-
- private:
-  friend class ::grpc::testing::ServerBuilderPluginTest;
-
-  struct Port {
-    grpc::string addr;
-    std::shared_ptr<ServerCredentials> creds;
-    int* selected_port;
-  };
-
-  struct SyncServerSettings {
-    SyncServerSettings()
-        : num_cqs(1), min_pollers(1), max_pollers(2), cq_timeout_msec(10000) {}
-
-    /// Number of server completion queues to create to listen to incoming RPCs.
-    int num_cqs;
-
-    /// Minimum number of threads per completion queue that should be listening
-    /// to incoming RPCs.
-    int min_pollers;
-
-    /// Maximum number of threads per completion queue that can be listening to
-    /// incoming RPCs.
-    int max_pollers;
-
-    /// The timeout for server completion queue's AsyncNext call.
-    int cq_timeout_msec;
-  };
-
-  typedef std::unique_ptr<grpc::string> HostString;
-  struct NamedService {
-    explicit NamedService(Service* s) : service(s) {}
-    NamedService(const grpc::string& h, Service* s)
-        : host(new grpc::string(h)), service(s) {}
-    HostString host;
-    Service* service;
-  };
-
-  int max_receive_message_size_;
-  int max_send_message_size_;
-  std::vector<std::unique_ptr<ServerBuilderOption>> options_;
-  std::vector<std::unique_ptr<NamedService>> services_;
-  std::vector<Port> ports_;
-
-  SyncServerSettings sync_server_settings_;
-
-  /// List of completion queues added via \a AddCompletionQueue method.
-  std::vector<ServerCompletionQueue*> cqs_;
-
-  std::shared_ptr<ServerCredentials> creds_;
-  std::vector<std::unique_ptr<ServerBuilderPlugin>> plugins_;
-  grpc_resource_quota* resource_quota_;
-  AsyncGenericService* generic_service_;
-  struct {
-    bool is_set;
-    grpc_compression_level level;
-  } maybe_default_compression_level_;
-  struct {
-    bool is_set;
-    grpc_compression_algorithm algorithm;
-  } maybe_default_compression_algorithm_;
-  uint32_t enabled_compression_algorithms_bitset_;
-};
-
-}  // namespace grpc
+#include <grpcpp/server_builder.h>
 
 #endif  // GRPCXX_SERVER_BUILDER_H
diff --git a/include/grpc++/server_context.h b/include/grpc++/server_context.h
index f9b98e1..672ccdc 100644
--- a/include/grpc++/server_context.h
+++ b/include/grpc++/server_context.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SERVER_CONTEXT_H
 #define GRPCXX_SERVER_CONTEXT_H
 
-#include <grpc++/impl/codegen/server_context.h>
+#include <grpcpp/server_context.h>
 
 #endif  // GRPCXX_SERVER_CONTEXT_H
diff --git a/include/grpc++/server_posix.h b/include/grpc++/server_posix.h
index 6cafcff..d2866d9 100644
--- a/include/grpc++/server_posix.h
+++ b/include/grpc++/server_posix.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,27 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SERVER_POSIX_H
 #define GRPCXX_SERVER_POSIX_H
 
-#include <memory>
-
-#include <grpc++/server.h>
-#include <grpc/support/port_platform.h>
-
-namespace grpc {
-
-#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
-
-/// Add a new client to a \a Server communicating over the given
-/// file descriptor.
-///
-/// \param server The server to add the client to.
-/// \param fd The file descriptor representing a socket.
-void AddInsecureChannelFromFd(Server* server, int fd);
-
-#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
-
-}  // namespace grpc
+#include <grpcpp/server_posix.h>
 
 #endif  // GRPCXX_SERVER_POSIX_H
diff --git a/include/grpc++/support/async_stream.h b/include/grpc++/support/async_stream.h
index f2cab84..9bb2b72 100644
--- a/include/grpc++/support/async_stream.h
+++ b/include/grpc++/support/async_stream.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_ASYNC_STREAM_H
 #define GRPCXX_SUPPORT_ASYNC_STREAM_H
 
-#include <grpc++/impl/codegen/async_stream.h>
+#include <grpcpp/support/async_stream.h>
 
 #endif  // GRPCXX_SUPPORT_ASYNC_STREAM_H
diff --git a/include/grpc++/support/async_unary_call.h b/include/grpc++/support/async_unary_call.h
index 4947c44..56fbf31 100644
--- a/include/grpc++/support/async_unary_call.h
+++ b/include/grpc++/support/async_unary_call.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H
 #define GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H
 
-#include <grpc++/impl/codegen/async_unary_call.h>
+#include <grpcpp/support/async_unary_call.h>
 
 #endif  // GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index 81fa3b0..ec607ee 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,16 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_BYTE_BUFFER_H
 #define GRPCXX_SUPPORT_BYTE_BUFFER_H
 
-#include <grpc++/impl/codegen/byte_buffer.h>
-#include <grpc++/impl/serialization_traits.h>
-#include <grpc++/support/config.h>
-#include <grpc++/support/slice.h>
-#include <grpc++/support/status.h>
-#include <grpc/byte_buffer.h>
-#include <grpc/grpc.h>
-#include <grpc/support/log.h>
+#include <grpcpp/support/byte_buffer.h>
 
 #endif  // GRPCXX_SUPPORT_BYTE_BUFFER_H
diff --git a/include/grpc++/support/channel_arguments.h b/include/grpc++/support/channel_arguments.h
index c9879d8..6d5300c 100644
--- a/include/grpc++/support/channel_arguments.h
+++ b/include/grpc++/support/channel_arguments.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,127 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H
 #define GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H
 
-#include <list>
-#include <vector>
-
-#include <grpc++/support/config.h>
-#include <grpc/compression.h>
-#include <grpc/grpc.h>
-
-namespace grpc {
-namespace testing {
-class ChannelArgumentsTest;
-}  // namespace testing
-
-class ResourceQuota;
-
-/// Options for channel creation. The user can use generic setters to pass
-/// key value pairs down to C channel creation code. For gRPC related options,
-/// concrete setters are provided.
-class ChannelArguments {
- public:
-  ChannelArguments();
-  ~ChannelArguments();
-
-  ChannelArguments(const ChannelArguments& other);
-  ChannelArguments& operator=(ChannelArguments other) {
-    Swap(other);
-    return *this;
-  }
-
-  void Swap(ChannelArguments& other);
-
-  /// Dump arguments in this instance to \a channel_args. Does not take
-  /// ownership of \a channel_args.
-  ///
-  /// Note that the underlying arguments are shared. Changes made to either \a
-  /// channel_args or this instance would be reflected on both.
-  void SetChannelArgs(grpc_channel_args* channel_args) const;
-
-  // gRPC specific channel argument setters
-  /// Set target name override for SSL host name checking. This option is for
-  /// testing only and should never be used in production.
-  void SetSslTargetNameOverride(const grpc::string& name);
-  // TODO(yangg) add flow control options
-  /// Set the compression algorithm for the channel.
-  void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
-
-  /// Set the grpclb fallback timeout (in ms) for the channel. If this amount
-  /// of time has passed but we have not gotten any non-empty \a serverlist from
-  /// the balancer, we will fall back to use the backend address(es) returned by
-  /// the resolver.
-  void SetGrpclbFallbackTimeout(int fallback_timeout);
-
-  /// Set the socket mutator for the channel.
-  void SetSocketMutator(grpc_socket_mutator* mutator);
-
-  /// Set the string to prepend to the user agent.
-  void SetUserAgentPrefix(const grpc::string& user_agent_prefix);
-
-  /// Set the buffer pool to be attached to the constructed channel.
-  void SetResourceQuota(const ResourceQuota& resource_quota);
-
-  /// Set the max receive and send message sizes.
-  void SetMaxReceiveMessageSize(int size);
-  void SetMaxSendMessageSize(int size);
-
-  /// Set LB policy name.
-  /// Note that if the name resolver returns only balancer addresses, the
-  /// grpclb LB policy will be used, regardless of what is specified here.
-  void SetLoadBalancingPolicyName(const grpc::string& lb_policy_name);
-
-  /// Set service config in JSON form.
-  /// Primarily meant for use in unit tests.
-  void SetServiceConfigJSON(const grpc::string& service_config_json);
-
-  // Generic channel argument setters. Only for advanced use cases.
-  /// Set an integer argument \a value under \a key.
-  void SetInt(const grpc::string& key, int value);
-
-  // Generic channel argument setter. Only for advanced use cases.
-  /// Set a pointer argument \a value under \a key. Owership is not transferred.
-  void SetPointer(const grpc::string& key, void* value);
-
-  void SetPointerWithVtable(const grpc::string& key, void* value,
-                            const grpc_arg_pointer_vtable* vtable);
-
-  /// Set a textual argument \a value under \a key.
-  void SetString(const grpc::string& key, const grpc::string& value);
-
-  /// Return (by value) a C \a grpc_channel_args structure which points to
-  /// arguments owned by this \a ChannelArguments instance
-  grpc_channel_args c_channel_args() const {
-    grpc_channel_args out;
-    out.num_args = args_.size();
-    out.args = args_.empty() ? NULL : const_cast<grpc_arg*>(&args_[0]);
-    return out;
-  }
-
- private:
-  friend class SecureChannelCredentials;
-  friend class testing::ChannelArgumentsTest;
-
-  /// Default pointer argument operations.
-  struct PointerVtableMembers {
-    static void* Copy(void* in) { return in; }
-    static void Destroy(void* in) {}
-    static int Compare(void* a, void* b) {
-      if (a < b) return -1;
-      if (a > b) return 1;
-      return 0;
-    }
-  };
-
-  // Returns empty string when it is not set.
-  grpc::string GetSslTargetNameOverride() const;
-
-  std::vector<grpc_arg> args_;
-  std::list<grpc::string> strings_;
-};
-
-}  // namespace grpc
+#include <grpcpp/support/channel_arguments.h>
 
 #endif  // GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H
diff --git a/include/grpc++/support/config.h b/include/grpc++/support/config.h
index b65af31..f8eee50 100644
--- a/include/grpc++/support/config.h
+++ b/include/grpc++/support/config.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_CONFIG_H
 #define GRPCXX_SUPPORT_CONFIG_H
 
-#include <grpc++/impl/codegen/config.h>
+#include <grpcpp/support/config.h>
 
 #endif  // GRPCXX_SUPPORT_CONFIG_H
diff --git a/include/grpc++/support/error_details.h b/include/grpc++/support/error_details.h
index 8925fa8..7ace308 100644
--- a/include/grpc++/support/error_details.h
+++ b/include/grpc++/support/error_details.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2017 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,31 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_ERROR_DETAILS_H
 #define GRPCXX_SUPPORT_ERROR_DETAILS_H
 
-#include <grpc++/support/status.h>
-
-namespace google {
-namespace rpc {
-class Status;
-}  // namespace rpc
-}  // namespace google
-
-namespace grpc {
-
-/// Map a \a grpc::Status to a \a google::rpc::Status.
-/// The given \a to object will be cleared.
-/// On success, returns status with OK.
-/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
-/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
-Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to);
-
-/// Map \a google::rpc::Status to a \a grpc::Status.
-/// Returns OK on success.
-/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
-Status SetErrorDetails(const ::google::rpc::Status& from, Status* to);
-
-}  // namespace grpc
+#include <grpcpp/support/error_details.h>
 
 #endif  // GRPCXX_SUPPORT_ERROR_DETAILS_H
diff --git a/include/grpc++/support/slice.h b/include/grpc++/support/slice.h
index 10db10d..b02b1a9 100644
--- a/include/grpc++/support/slice.h
+++ b/include/grpc++/support/slice.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,11 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_SLICE_H
 #define GRPCXX_SUPPORT_SLICE_H
 
-#include <grpc++/impl/codegen/slice.h>
-#include <grpc++/support/config.h>
-#include <grpc/slice.h>
+#include <grpcpp/support/slice.h>
 
 #endif  // GRPCXX_SUPPORT_SLICE_H
diff --git a/include/grpc++/support/status.h b/include/grpc++/support/status.h
index 1fa910a..e58a18b 100644
--- a/include/grpc++/support/status.h
+++ b/include/grpc++/support/status.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_STATUS_H
 #define GRPCXX_SUPPORT_STATUS_H
 
-#include <grpc++/impl/codegen/status.h>
+#include <grpcpp/support/status.h>
 
 #endif  // GRPCXX_SUPPORT_STATUS_H
diff --git a/include/grpc++/support/status_code_enum.h b/include/grpc++/support/status_code_enum.h
index d320c0c..c278add 100644
--- a/include/grpc++/support/status_code_enum.h
+++ b/include/grpc++/support/status_code_enum.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_STATUS_CODE_ENUM_H
 #define GRPCXX_SUPPORT_STATUS_CODE_ENUM_H
 
-#include <grpc++/impl/codegen/status_code_enum.h>
+#include <grpcpp/support/status_code_enum.h>
 
 #endif  // GRPCXX_SUPPORT_STATUS_CODE_ENUM_H
diff --git a/include/grpc++/support/string_ref.h b/include/grpc++/support/string_ref.h
index 873be63..49de6da 100644
--- a/include/grpc++/support/string_ref.h
+++ b/include/grpc++/support/string_ref.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_STRING_REF_H
 #define GRPCXX_SUPPORT_STRING_REF_H
 
-#include <grpc++/impl/codegen/string_ref.h>
+#include <grpcpp/support/string_ref.h>
 
 #endif  // GRPCXX_SUPPORT_STRING_REF_H
diff --git a/include/grpc++/support/stub_options.h b/include/grpc++/support/stub_options.h
index 87843cf..a712ce8 100644
--- a/include/grpc++/support/stub_options.h
+++ b/include/grpc++/support/stub_options.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_STUB_OPTIONS_H
 #define GRPCXX_SUPPORT_STUB_OPTIONS_H
 
-#include <grpc++/impl/codegen/stub_options.h>
+#include <grpcpp/support/stub_options.h>
 
 #endif  // GRPCXX_SUPPORT_STUB_OPTIONS_H
diff --git a/include/grpc++/support/sync_stream.h b/include/grpc++/support/sync_stream.h
index 6a6e0c7..c118df9 100644
--- a/include/grpc++/support/sync_stream.h
+++ b/include/grpc++/support/sync_stream.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_SYNC_STREAM_H
 #define GRPCXX_SUPPORT_SYNC_STREAM_H
 
-#include <grpc++/impl/codegen/sync_stream.h>
+#include <grpcpp/support/sync_stream.h>
 
 #endif  // GRPCXX_SUPPORT_SYNC_STREAM_H
diff --git a/include/grpc++/support/time.h b/include/grpc++/support/time.h
index e47a593..d356b91 100644
--- a/include/grpc++/support/time.h
+++ b/include/grpc++/support/time.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_SUPPORT_TIME_H
 #define GRPCXX_SUPPORT_TIME_H
 
-#include <grpc++/impl/codegen/time.h>
+#include <grpcpp/support/time.h>
 
 #endif  // GRPCXX_SUPPORT_TIME_H
diff --git a/include/grpc++/test/mock_stream.h b/include/grpc++/test/mock_stream.h
index f8f7824..a29345b 100644
--- a/include/grpc++/test/mock_stream.h
+++ b/include/grpc++/test/mock_stream.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2017 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,133 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_TEST_MOCK_STREAM_H
 #define GRPCXX_TEST_MOCK_STREAM_H
 
-#include <stdint.h>
-
-#include <gmock/gmock.h>
-#include <grpc++/impl/codegen/call.h>
-#include <grpc++/support/async_stream.h>
-#include <grpc++/support/async_unary_call.h>
-#include <grpc++/support/sync_stream.h>
-
-namespace grpc {
-namespace testing {
-
-template <class R>
-class MockClientReader : public ClientReaderInterface<R> {
- public:
-  MockClientReader() = default;
-
-  /// ClientStreamingInterface
-  MOCK_METHOD0_T(Finish, Status());
-
-  /// ReaderInterface
-  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
-  MOCK_METHOD1_T(Read, bool(R*));
-
-  /// ClientReaderInterface
-  MOCK_METHOD0_T(WaitForInitialMetadata, void());
-};
-
-template <class W>
-class MockClientWriter : public ClientWriterInterface<W> {
- public:
-  MockClientWriter() = default;
-
-  /// ClientStreamingInterface
-  MOCK_METHOD0_T(Finish, Status());
-
-  /// WriterInterface
-  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
-
-  /// ClientWriterInterface
-  MOCK_METHOD0_T(WritesDone, bool());
-};
-
-template <class W, class R>
-class MockClientReaderWriter : public ClientReaderWriterInterface<W, R> {
- public:
-  MockClientReaderWriter() = default;
-
-  /// ClientStreamingInterface
-  MOCK_METHOD0_T(Finish, Status());
-
-  /// ReaderInterface
-  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
-  MOCK_METHOD1_T(Read, bool(R*));
-
-  /// WriterInterface
-  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
-
-  /// ClientReaderWriterInterface
-  MOCK_METHOD0_T(WaitForInitialMetadata, void());
-  MOCK_METHOD0_T(WritesDone, bool());
-};
-
-/// TODO: We do not support mocking an async RPC for now.
-
-template <class R>
-class MockClientAsyncResponseReader
-    : public ClientAsyncResponseReaderInterface<R> {
- public:
-  MockClientAsyncResponseReader() = default;
-
-  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
-  MOCK_METHOD3_T(Finish, void(R*, Status*, void*));
-};
-
-template <class R>
-class MockClientAsyncReader : public ClientAsyncReaderInterface<R> {
- public:
-  MockClientAsyncReader() = default;
-
-  /// ClientAsyncStreamingInterface
-  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
-  MOCK_METHOD2_T(Finish, void(Status*, void*));
-
-  /// AsyncReaderInterface
-  MOCK_METHOD2_T(Read, void(R*, void*));
-};
-
-template <class W>
-class MockClientAsyncWriter : public ClientAsyncWriterInterface<W> {
- public:
-  MockClientAsyncWriter() = default;
-
-  /// ClientAsyncStreamingInterface
-  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
-  MOCK_METHOD2_T(Finish, void(Status*, void*));
-
-  /// AsyncWriterInterface
-  MOCK_METHOD2_T(Write, void(const W&, void*));
-
-  /// ClientAsyncWriterInterface
-  MOCK_METHOD1_T(WritesDone, void(void*));
-};
-
-template <class W, class R>
-class MockClientAsyncReaderWriter
-    : public ClientAsyncReaderWriterInterface<W, R> {
- public:
-  MockClientAsyncReaderWriter() = default;
-
-  /// ClientAsyncStreamingInterface
-  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
-  MOCK_METHOD2_T(Finish, void(Status*, void*));
-
-  /// AsyncWriterInterface
-  MOCK_METHOD2_T(Write, void(const W&, void*));
-
-  /// AsyncReaderInterface
-  MOCK_METHOD2_T(Read, void(R*, void*));
-
-  /// ClientAsyncReaderWriterInterface
-  MOCK_METHOD1_T(WritesDone, void(void*));
-};
-
-}  // namespace testing
-}  // namespace grpc
+#include <grpcpp/test/mock_stream.h>
 
 #endif  // GRPCXX_TEST_MOCK_STREAM_H
diff --git a/include/grpc++/test/server_context_test_spouse.h b/include/grpc++/test/server_context_test_spouse.h
index b1a7f87..48a4838 100644
--- a/include/grpc++/test/server_context_test_spouse.h
+++ b/include/grpc++/test/server_context_test_spouse.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2016 gRPC authors.
+ * Copyright 2018 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,50 +16,13 @@
  *
  */
 
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
 #ifndef GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
 #define GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
 
-#include <map>
-
-#include <grpc++/server_context.h>
-
-namespace grpc {
-namespace testing {
-
-/// A test-only class to access private members and methods of ServerContext.
-class ServerContextTestSpouse {
- public:
-  explicit ServerContextTestSpouse(ServerContext* ctx) : ctx_(ctx) {}
-
-  /// Inject client metadata to the ServerContext for the test. The test spouse
-  /// must be alive when \a ServerContext::client_metadata is called.
-  void AddClientMetadata(const grpc::string& key, const grpc::string& value) {
-    client_metadata_storage_.insert(
-        std::pair<grpc::string, grpc::string>(key, value));
-    ctx_->client_metadata_.map()->clear();
-    for (auto iter = client_metadata_storage_.begin();
-         iter != client_metadata_storage_.end(); ++iter) {
-      ctx_->client_metadata_.map()->insert(
-          std::pair<grpc::string_ref, grpc::string_ref>(
-              iter->first.c_str(),
-              grpc::string_ref(iter->second.data(), iter->second.size())));
-    }
-  }
-
-  std::multimap<grpc::string, grpc::string> GetInitialMetadata() const {
-    return ctx_->initial_metadata_;
-  }
-
-  std::multimap<grpc::string, grpc::string> GetTrailingMetadata() const {
-    return ctx_->trailing_metadata_;
-  }
-
- private:
-  ServerContext* ctx_;  // not owned
-  std::multimap<grpc::string, grpc::string> client_metadata_storage_;
-};
-
-}  // namespace testing
-}  // namespace grpc
+#include <grpcpp/test/server_context_test_spouse.h>
 
 #endif  // GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index 7669582..ee740f4 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_BYTE_BUFFER_H
 #define GRPC_BYTE_BUFFER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/byte_buffer.h>
 #include <grpc/slice_buffer.h>
 
diff --git a/include/grpc/byte_buffer_reader.h b/include/grpc/byte_buffer_reader.h
index 6bd0784..15e06ca 100644
--- a/include/grpc/byte_buffer_reader.h
+++ b/include/grpc/byte_buffer_reader.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_BYTE_BUFFER_READER_H
 #define GRPC_BYTE_BUFFER_READER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/byte_buffer_reader.h>
 
 #endif /* GRPC_BYTE_BUFFER_READER_H */
diff --git a/include/grpc/census.h b/include/grpc/census.h
index 2258af8..4894f1c 100644
--- a/include/grpc/census.h
+++ b/include/grpc/census.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CENSUS_H
 #define GRPC_CENSUS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #ifdef __cplusplus
diff --git a/include/grpc/fork.h b/include/grpc/fork.h
index ca45e11..26f9df9 100644
--- a/include/grpc/fork.h
+++ b/include/grpc/fork.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_FORK_H
 #define GRPC_FORK_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/fork.h>
 
 #endif /* GRPC_FORK_H */
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index aec78be..c129a66 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_GRPC_H
 #define GRPC_GRPC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/status.h>
 
 #include <grpc/byte_buffer.h>
diff --git a/include/grpc/grpc_cronet.h b/include/grpc/grpc_cronet.h
index 127d5d0..289cfcd 100644
--- a/include/grpc/grpc_cronet.h
+++ b/include/grpc/grpc_cronet.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_GRPC_CRONET_H
 #define GRPC_GRPC_CRONET_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #ifdef __cplusplus
diff --git a/include/grpc/grpc_posix.h b/include/grpc/grpc_posix.h
index fa7ebce..5f1ada5 100644
--- a/include/grpc/grpc_posix.h
+++ b/include/grpc/grpc_posix.h
@@ -19,9 +19,10 @@
 #ifndef GRPC_GRPC_POSIX_H
 #define GRPC_GRPC_POSIX_H
 
-#include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/port_platform.h>
 
+#include <grpc/impl/codegen/grpc_types.h>
+
 #include <stddef.h>
 
 #ifdef __cplusplus
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 0877633..abc591f 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_GRPC_SECURITY_H
 #define GRPC_GRPC_SECURITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/grpc_security_constants.h>
 #include <grpc/status.h>
diff --git a/include/grpc/impl/codegen/byte_buffer.h b/include/grpc/impl/codegen/byte_buffer.h
index f8dfbd1..774655e 100644
--- a/include/grpc/impl/codegen/byte_buffer.h
+++ b/include/grpc/impl/codegen/byte_buffer.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
 #define GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #ifdef __cplusplus
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index a372dbb..dcce2e7 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -301,7 +301,7 @@
 #define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_call_timeout_ms"
 /* Timeout in milliseconds to wait for the serverlist from the grpclb load
    balancer before using fallback backend addresses from the resolver.
-   If 0, fallback will never be used. */
+   If 0, fallback will never be used. Default value is 10000. */
 #define GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS "grpc.grpclb_fallback_timeout_ms"
 /** If non-zero, grpc server's cronet compression workaround will be enabled */
 #define GRPC_ARG_WORKAROUND_CRONET_COMPRESSION \
@@ -314,6 +314,17 @@
     Defaults to "blend". In the current implementation "blend" is equivalent to
     "latency". */
 #define GRPC_ARG_OPTIMIZATION_TARGET "grpc.optimization_target"
+/** If set to zero, disables retry behavior. Otherwise, transparent retries
+    are enabled for all RPCs, and configurable retries are enabled when they
+    are configured via the service config. For details, see:
+      https://github.com/grpc/proposal/blob/master/A6-client-retries.md
+ */
+#define GRPC_ARG_ENABLE_RETRIES "grpc.enable_retries"
+/** Per-RPC retry buffer size, in bytes. Default is 256 KiB. */
+#define GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE "grpc.per_rpc_retry_buffer_size"
+/** Channel arg that carries the bridged objective c object for custom metrics
+ * logging filter. */
+#define GRPC_ARG_MOBILE_LOG_CONFIG "grpc.mobile_log_config"
 /** \} */
 
 /** Result of a grpc call. If the caller satisfies the prerequisites of a
@@ -551,6 +562,8 @@
     } recv_initial_metadata;
     /** ownership of the byte buffer is moved to the caller; the caller must
         call grpc_byte_buffer_destroy on this value, or reuse it in a future op.
+        The returned byte buffer will be NULL if trailing metadata was
+        received instead of a message.
        */
     struct grpc_op_recv_message {
       struct grpc_byte_buffer** recv_message;
diff --git a/include/grpc/impl/codegen/sync.h b/include/grpc/impl/codegen/sync.h
index 6cdb0c5..3df68c6 100644
--- a/include/grpc/impl/codegen/sync.h
+++ b/include/grpc/impl/codegen/sync.h
@@ -43,6 +43,7 @@
 
 /* Platform-specific type declarations of gpr_mu and gpr_cv.   */
 #include <grpc/impl/codegen/port_platform.h>
+
 #include <grpc/impl/codegen/sync_generic.h>
 
 #if defined(GPR_POSIX_SYNC)
diff --git a/include/grpc/impl/codegen/sync_custom.h b/include/grpc/impl/codegen/sync_custom.h
index 0840ad2..69b1bf6 100644
--- a/include/grpc/impl/codegen/sync_custom.h
+++ b/include/grpc/impl/codegen/sync_custom.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H
 #define GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #include <grpc/impl/codegen/sync_generic.h>
 
 /* Users defining GPR_CUSTOM_SYNC need to define the following macros. */
diff --git a/include/grpc/impl/codegen/sync_generic.h b/include/grpc/impl/codegen/sync_generic.h
index 83f905e..d64db58 100644
--- a/include/grpc/impl/codegen/sync_generic.h
+++ b/include/grpc/impl/codegen/sync_generic.h
@@ -20,6 +20,8 @@
 #define GRPC_IMPL_CODEGEN_SYNC_GENERIC_H
 /* Generic type defintions for gpr_sync. */
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #include <grpc/impl/codegen/atm.h>
 
 /* gpr_event */
diff --git a/include/grpc/impl/codegen/sync_posix.h b/include/grpc/impl/codegen/sync_posix.h
index 6a3aed9..d927046 100644
--- a/include/grpc/impl/codegen/sync_posix.h
+++ b/include/grpc/impl/codegen/sync_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_IMPL_CODEGEN_SYNC_POSIX_H
 #define GRPC_IMPL_CODEGEN_SYNC_POSIX_H
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #include <grpc/impl/codegen/sync_generic.h>
 
 #include <pthread.h>
diff --git a/include/grpc/impl/codegen/sync_windows.h b/include/grpc/impl/codegen/sync_windows.h
index 39b1276..ba5d5ae 100644
--- a/include/grpc/impl/codegen/sync_windows.h
+++ b/include/grpc/impl/codegen/sync_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H
 #define GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #include <grpc/impl/codegen/sync_generic.h>
 
 typedef struct {
diff --git a/include/grpc/slice.h b/include/grpc/slice.h
index 10b6a62..ce48292 100644
--- a/include/grpc/slice.h
+++ b/include/grpc/slice.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SLICE_H
 #define GRPC_SLICE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/slice.h>
 #include <grpc/support/sync.h>
 
diff --git a/include/grpc/slice_buffer.h b/include/grpc/slice_buffer.h
index 30833d0..3260019 100644
--- a/include/grpc/slice_buffer.h
+++ b/include/grpc/slice_buffer.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SLICE_BUFFER_H
 #define GRPC_SLICE_BUFFER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 
 #ifdef __cplusplus
diff --git a/include/grpc/status.h b/include/grpc/status.h
index 9d8f50b..ecb9668 100644
--- a/include/grpc/status.h
+++ b/include/grpc/status.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_STATUS_H
 #define GRPC_STATUS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/status.h>
 
 #endif /* GRPC_STATUS_H */
diff --git a/include/grpc/support/alloc.h b/include/grpc/support/alloc.h
index 577d4f0..8bd940b 100644
--- a/include/grpc/support/alloc.h
+++ b/include/grpc/support/alloc.h
@@ -19,9 +19,9 @@
 #ifndef GRPC_SUPPORT_ALLOC_H
 #define GRPC_SUPPORT_ALLOC_H
 
-#include <stddef.h>
+#include <grpc/support/port_platform.h>
 
-#include <grpc/impl/codegen/port_platform.h>
+#include <stddef.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/grpc/support/atm.h b/include/grpc/support/atm.h
index b3afa52..073b0a6 100644
--- a/include/grpc/support/atm.h
+++ b/include/grpc/support/atm.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_ATM_H
 #define GRPC_SUPPORT_ATM_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/atm.h>
 
 #endif /* GRPC_SUPPORT_ATM_H */
diff --git a/include/grpc/support/atm_gcc_atomic.h b/include/grpc/support/atm_gcc_atomic.h
index e7b5ec4..ae603db 100644
--- a/include/grpc/support/atm_gcc_atomic.h
+++ b/include/grpc/support/atm_gcc_atomic.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_ATM_GCC_ATOMIC_H
 #define GRPC_SUPPORT_ATM_GCC_ATOMIC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/atm_gcc_atomic.h>
 
 #endif /* GRPC_SUPPORT_ATM_GCC_ATOMIC_H */
diff --git a/include/grpc/support/atm_gcc_sync.h b/include/grpc/support/atm_gcc_sync.h
index 7284897..6f51fdb 100644
--- a/include/grpc/support/atm_gcc_sync.h
+++ b/include/grpc/support/atm_gcc_sync.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_ATM_GCC_SYNC_H
 #define GRPC_SUPPORT_ATM_GCC_SYNC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/atm_gcc_sync.h>
 
 #endif /* GRPC_SUPPORT_ATM_GCC_SYNC_H */
diff --git a/include/grpc/support/atm_windows.h b/include/grpc/support/atm_windows.h
index 554c59a..36955e4 100644
--- a/include/grpc/support/atm_windows.h
+++ b/include/grpc/support/atm_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_ATM_WINDOWS_H
 #define GRPC_SUPPORT_ATM_WINDOWS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/atm_windows.h>
 
 #endif /* GRPC_SUPPORT_ATM_WINDOWS_H */
diff --git a/include/grpc/support/log.h b/include/grpc/support/log.h
index a8371cb..ccb4b30 100644
--- a/include/grpc/support/log.h
+++ b/include/grpc/support/log.h
@@ -19,12 +19,12 @@
 #ifndef GRPC_SUPPORT_LOG_H
 #define GRPC_SUPPORT_LOG_H
 
+#include <grpc/impl/codegen/port_platform.h>
+
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdlib.h> /* for abort() */
 
-#include <grpc/impl/codegen/port_platform.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/include/grpc/support/sync.h b/include/grpc/support/sync.h
index 7519267..91d1fa7 100644
--- a/include/grpc/support/sync.h
+++ b/include/grpc/support/sync.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_SYNC_H
 #define GRPC_SUPPORT_SYNC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/gpr_types.h> /* for gpr_timespec */
 #include <grpc/impl/codegen/sync.h>
 
diff --git a/include/grpc/support/sync_custom.h b/include/grpc/support/sync_custom.h
index b575f5e..27cf0e0 100644
--- a/include/grpc/support/sync_custom.h
+++ b/include/grpc/support/sync_custom.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_SYNC_CUSTOM_H
 #define GRPC_SUPPORT_SYNC_CUSTOM_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/sync_custom.h>
 
 #endif /* GRPC_SUPPORT_SYNC_CUSTOM_H */
diff --git a/include/grpc/support/sync_generic.h b/include/grpc/support/sync_generic.h
index 970b7a5..93028c4 100644
--- a/include/grpc/support/sync_generic.h
+++ b/include/grpc/support/sync_generic.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_SYNC_GENERIC_H
 #define GRPC_SUPPORT_SYNC_GENERIC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/sync_generic.h>
 
 #endif /* GRPC_SUPPORT_SYNC_GENERIC_H */
diff --git a/include/grpc/support/sync_posix.h b/include/grpc/support/sync_posix.h
index 482a600..3dce7ee 100644
--- a/include/grpc/support/sync_posix.h
+++ b/include/grpc/support/sync_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_SYNC_POSIX_H
 #define GRPC_SUPPORT_SYNC_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/sync_posix.h>
 
 #endif /* GRPC_SUPPORT_SYNC_POSIX_H */
diff --git a/include/grpc/support/sync_windows.h b/include/grpc/support/sync_windows.h
index 90ce8b7..a493c86 100644
--- a/include/grpc/support/sync_windows.h
+++ b/include/grpc/support/sync_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_SYNC_WINDOWS_H
 #define GRPC_SUPPORT_SYNC_WINDOWS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/sync_windows.h>
 
 #endif /* GRPC_SUPPORT_SYNC_WINDOWS_H */
diff --git a/include/grpc/support/time.h b/include/grpc/support/time.h
index 62d354a..550ffc2 100644
--- a/include/grpc/support/time.h
+++ b/include/grpc/support/time.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_SUPPORT_TIME_H
 #define GRPC_SUPPORT_TIME_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/gpr_types.h>
 
 #include <stddef.h>
diff --git a/include/grpcpp/alarm.h b/include/grpcpp/alarm.h
new file mode 100644
index 0000000..f484610
--- /dev/null
+++ b/include/grpcpp/alarm.h
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/// An Alarm posts the user provided tag to its associated completion queue upon
+/// expiry or cancellation.
+#ifndef GRPCPP_ALARM_H
+#define GRPCPP_ALARM_H
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/codegen/completion_queue.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/time.h>
+#include <grpcpp/impl/grpc_library.h>
+
+namespace grpc {
+
+/// A thin wrapper around \a grpc_alarm (see / \a / src/core/surface/alarm.h).
+class Alarm : private GrpcLibraryCodegen {
+ public:
+  /// Create an unset completion queue alarm
+  Alarm();
+
+  /// Destroy the given completion queue alarm, cancelling it in the process.
+  ~Alarm();
+
+  /// DEPRECATED: Create and set a completion queue alarm instance associated to
+  /// \a cq.
+  /// This form is deprecated because it is inherently racy.
+  /// \internal We rely on the presence of \a cq for grpc initialization. If \a
+  /// cq were ever to be removed, a reference to a static
+  /// internal::GrpcLibraryInitializer instance would need to be introduced
+  /// here. \endinternal.
+  template <typename T>
+  Alarm(CompletionQueue* cq, const T& deadline, void* tag) : Alarm() {
+    SetInternal(cq, TimePoint<T>(deadline).raw_time(), tag);
+  }
+
+  /// Trigger an alarm instance on completion queue \a cq at the specified time.
+  /// Once the alarm expires (at \a deadline) or it's cancelled (see \a Cancel),
+  /// an event with tag \a tag will be added to \a cq. If the alarm expired, the
+  /// event's success bit will be true, false otherwise (ie, upon cancellation).
+  template <typename T>
+  void Set(CompletionQueue* cq, const T& deadline, void* tag) {
+    SetInternal(cq, TimePoint<T>(deadline).raw_time(), tag);
+  }
+
+  /// Alarms aren't copyable.
+  Alarm(const Alarm&) = delete;
+  Alarm& operator=(const Alarm&) = delete;
+
+  /// Alarms are movable.
+  Alarm(Alarm&& rhs) : alarm_(rhs.alarm_) { rhs.alarm_ = nullptr; }
+  Alarm& operator=(Alarm&& rhs) {
+    alarm_ = rhs.alarm_;
+    rhs.alarm_ = nullptr;
+    return *this;
+  }
+
+  /// Cancel a completion queue alarm. Calling this function over an alarm that
+  /// has already fired has no effect.
+  void Cancel();
+
+ private:
+  void SetInternal(CompletionQueue* cq, gpr_timespec deadline, void* tag);
+
+  internal::CompletionQueueTag* alarm_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_ALARM_H
diff --git a/include/grpcpp/channel.h b/include/grpcpp/channel.h
new file mode 100644
index 0000000..4b45d53
--- /dev/null
+++ b/include/grpcpp/channel.h
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_CHANNEL_H
+#define GRPCPP_CHANNEL_H
+
+#include <memory>
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+struct grpc_channel;
+
+namespace grpc {
+/// Channels represent a connection to an endpoint. Created by \a CreateChannel.
+class Channel final : public ChannelInterface,
+                      public internal::CallHook,
+                      public std::enable_shared_from_this<Channel>,
+                      private GrpcLibraryCodegen {
+ public:
+  ~Channel();
+
+  /// Get the current channel state. If the channel is in IDLE and
+  /// \a try_to_connect is set to true, try to connect.
+  grpc_connectivity_state GetState(bool try_to_connect) override;
+
+  /// Returns the LB policy name, or the empty string if not yet available.
+  grpc::string GetLoadBalancingPolicyName() const;
+
+  /// Returns the service config in JSON form, or the empty string if
+  /// not available.
+  grpc::string GetServiceConfigJSON() const;
+
+ private:
+  template <class InputMessage, class OutputMessage>
+  friend class internal::BlockingUnaryCallImpl;
+  friend std::shared_ptr<Channel> CreateChannelInternal(
+      const grpc::string& host, grpc_channel* c_channel);
+  Channel(const grpc::string& host, grpc_channel* c_channel);
+
+  internal::Call CreateCall(const internal::RpcMethod& method,
+                            ClientContext* context,
+                            CompletionQueue* cq) override;
+  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                        internal::Call* call) override;
+  void* RegisterMethod(const char* method) override;
+
+  void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+                               gpr_timespec deadline, CompletionQueue* cq,
+                               void* tag) override;
+  bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                              gpr_timespec deadline) override;
+
+  const grpc::string host_;
+  grpc_channel* const c_channel_;  // owned
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_CHANNEL_H
diff --git a/include/grpcpp/client_context.h b/include/grpcpp/client_context.h
new file mode 100644
index 0000000..8c12577
--- /dev/null
+++ b/include/grpcpp/client_context.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/// A ClientContext allows the person implementing a service client to:
+///
+/// - Add custom metadata key-value pairs that will propagated to the server
+/// side.
+/// - Control call settings such as compression and authentication.
+/// - Initial and trailing metadata coming from the server.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call they are invoked with, that
+/// is to say, they aren't sticky. Some of these settings, such as the
+/// compression options, can be made persistant at channel construction time
+/// (see \a grpc::CreateCustomChannel).
+///
+/// \warning ClientContext instances should \em not be reused across rpcs.
+
+#ifndef GRPCPP_CLIENT_CONTEXT_H
+#define GRPCPP_CLIENT_CONTEXT_H
+
+#include <grpcpp/impl/codegen/client_context.h>
+
+#endif  // GRPCPP_CLIENT_CONTEXT_H
diff --git a/include/grpcpp/completion_queue.h b/include/grpcpp/completion_queue.h
new file mode 100644
index 0000000..123b277
--- /dev/null
+++ b/include/grpcpp/completion_queue.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_COMPLETION_QUEUE_H
+#define GRPCPP_COMPLETION_QUEUE_H
+
+#include <grpcpp/impl/codegen/completion_queue.h>
+
+#endif  // GRPCPP_COMPLETION_QUEUE_H
diff --git a/include/grpcpp/create_channel.h b/include/grpcpp/create_channel.h
new file mode 100644
index 0000000..7a505a7
--- /dev/null
+++ b/include/grpcpp/create_channel.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_CREATE_CHANNEL_H
+#define GRPCPP_CREATE_CHANNEL_H
+
+#include <memory>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+/// Create a new \a Channel pointing to \a target.
+///
+/// \param target The URI of the endpoint to connect to.
+/// \param creds Credentials to use for the created channel. If it does not
+/// hold an object or is invalid, a lame channel (one on which all operations
+/// fail) is returned.
+std::shared_ptr<Channel> CreateChannel(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds);
+
+/// Create a new \em custom \a Channel pointing to \a target.
+///
+/// \warning For advanced use and testing ONLY. Override default channel
+/// arguments only if necessary.
+///
+/// \param target The URI of the endpoint to connect to.
+/// \param creds Credentials to use for the created channel. If it does not
+/// hold an object or is invalid, a lame channel (one on which all operations
+/// fail) is returned.
+/// \param args Options for channel creation.
+std::shared_ptr<Channel> CreateCustomChannel(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const ChannelArguments& args);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_CREATE_CHANNEL_H
diff --git a/include/grpcpp/create_channel_posix.h b/include/grpcpp/create_channel_posix.h
new file mode 100644
index 0000000..9bf5acc
--- /dev/null
+++ b/include/grpcpp/create_channel_posix.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_CREATE_CHANNEL_POSIX_H
+#define GRPCPP_CREATE_CHANNEL_POSIX_H
+
+#include <memory>
+
+#include <grpc/support/port_platform.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/support/channel_arguments.h>
+
+namespace grpc {
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+/// Create a new \a Channel communicating over the given file descriptor.
+///
+/// \param target The name of the target.
+/// \param fd The file descriptor representing a socket.
+std::shared_ptr<Channel> CreateInsecureChannelFromFd(const grpc::string& target,
+                                                     int fd);
+
+/// Create a new \a Channel communicating over given file descriptor with custom
+/// channel arguments.
+///
+/// \param target The name of the target.
+/// \param fd The file descriptor representing a socket.
+/// \param args Options for channel creation.
+std::shared_ptr<Channel> CreateCustomInsecureChannelFromFd(
+    const grpc::string& target, int fd, const ChannelArguments& args);
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
+
+}  // namespace grpc
+
+#endif  // GRPCPP_CREATE_CHANNEL_POSIX_H
diff --git a/include/grpcpp/ext/health_check_service_server_builder_option.h b/include/grpcpp/ext/health_check_service_server_builder_option.h
new file mode 100644
index 0000000..dd43b05
--- /dev/null
+++ b/include/grpcpp/ext/health_check_service_server_builder_option.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
+#define GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
+
+#include <memory>
+
+#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+class HealthCheckServiceServerBuilderOption : public ServerBuilderOption {
+ public:
+  /// The ownership of \a hc will be taken and transferred to the grpc server.
+  /// To explicitly disable default service, pass in a nullptr.
+  explicit HealthCheckServiceServerBuilderOption(
+      std::unique_ptr<HealthCheckServiceInterface> hc);
+  ~HealthCheckServiceServerBuilderOption() override {}
+  void UpdateArguments(ChannelArguments* args) override;
+  void UpdatePlugins(
+      std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override;
+
+ private:
+  std::unique_ptr<HealthCheckServiceInterface> hc_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
diff --git a/include/grpcpp/ext/proto_server_reflection_plugin.h b/include/grpcpp/ext/proto_server_reflection_plugin.h
new file mode 100644
index 0000000..1cfdc1b
--- /dev/null
+++ b/include/grpcpp/ext/proto_server_reflection_plugin.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
+#define GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
+
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+class ServerInitializer;
+class ProtoServerReflection;
+}  // namespace grpc
+
+namespace grpc {
+namespace reflection {
+
+class ProtoServerReflectionPlugin : public ::grpc::ServerBuilderPlugin {
+ public:
+  ProtoServerReflectionPlugin();
+  ::grpc::string name() override;
+  void InitServer(::grpc::ServerInitializer* si) override;
+  void Finish(::grpc::ServerInitializer* si) override;
+  void ChangeArguments(const ::grpc::string& name, void* value) override;
+  bool has_async_methods() const override;
+  bool has_sync_methods() const override;
+
+ private:
+  std::shared_ptr<grpc::ProtoServerReflection> reflection_service_;
+};
+
+/// Add proto reflection plugin to \a ServerBuilder.
+/// This function should be called at the static initialization time.
+void InitProtoReflectionServerBuilderPlugin();
+
+}  // namespace reflection
+}  // namespace grpc
+
+#endif  // GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
diff --git a/include/grpcpp/generic/async_generic_service.h b/include/grpcpp/generic/async_generic_service.h
new file mode 100644
index 0000000..7eaa541
--- /dev/null
+++ b/include/grpcpp/generic/async_generic_service.h
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H
+#define GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H
+
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/byte_buffer.h>
+
+struct grpc_server;
+
+namespace grpc {
+
+typedef ServerAsyncReaderWriter<ByteBuffer, ByteBuffer>
+    GenericServerAsyncReaderWriter;
+
+class GenericServerContext final : public ServerContext {
+ public:
+  const grpc::string& method() const { return method_; }
+  const grpc::string& host() const { return host_; }
+
+ private:
+  friend class Server;
+  friend class ServerInterface;
+
+  grpc::string method_;
+  grpc::string host_;
+};
+
+// A generic service at the server side accepts all RPC methods and hosts. It is
+// typically used in proxies. The generic service can be registered to a server
+// which also has other services.
+// Sample usage:
+//   ServerBuilder builder;
+//   auto cq = builder.AddCompletionQueue();
+//   AsyncGenericService generic_service;
+//   builder.RegisterAsyncGeneicService(&generic_service);
+//   auto server = builder.BuildAndStart();
+//
+//   // request a new call
+//   GenericServerContext context;
+//   GenericAsyncReaderWriter stream;
+//   generic_service.RequestCall(&context, &stream, cq.get(), cq.get(), tag);
+//
+// When tag is retrieved from cq->Next(), context.method() can be used to look
+// at the method and the RPC can be handled accordingly.
+class AsyncGenericService final {
+ public:
+  AsyncGenericService() : server_(nullptr) {}
+
+  void RequestCall(GenericServerContext* ctx,
+                   GenericServerAsyncReaderWriter* reader_writer,
+                   CompletionQueue* call_cq,
+                   ServerCompletionQueue* notification_cq, void* tag);
+
+ private:
+  friend class Server;
+  Server* server_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H
diff --git a/include/grpcpp/generic/generic_stub.h b/include/grpcpp/generic/generic_stub.h
new file mode 100644
index 0000000..92405a4
--- /dev/null
+++ b/include/grpcpp/generic/generic_stub.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_GENERIC_GENERIC_STUB_H
+#define GRPCPP_GENERIC_GENERIC_STUB_H
+
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/support/byte_buffer.h>
+
+namespace grpc {
+
+class CompletionQueue;
+typedef ClientAsyncReaderWriter<ByteBuffer, ByteBuffer>
+    GenericClientAsyncReaderWriter;
+typedef ClientAsyncResponseReader<ByteBuffer> GenericClientAsyncResponseReader;
+
+/// Generic stubs provide a type-unsafe interface to call gRPC methods
+/// by name.
+class GenericStub final {
+ public:
+  explicit GenericStub(std::shared_ptr<ChannelInterface> channel)
+      : channel_(channel) {}
+
+  /// Setup a call to a named method \a method using \a context, but don't
+  /// start it. Let it be started explicitly with StartCall and a tag.
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncReaderWriter> PrepareCall(
+      ClientContext* context, const grpc::string& method, CompletionQueue* cq);
+
+  /// Setup a unary call to a named method \a method using \a context, and don't
+  /// start it. Let it be started explicitly with StartCall.
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncResponseReader> PrepareUnaryCall(
+      ClientContext* context, const grpc::string& method,
+      const ByteBuffer& request, CompletionQueue* cq);
+
+  /// DEPRECATED for multi-threaded use
+  /// Begin a call to a named method \a method using \a context.
+  /// A tag \a tag will be delivered to \a cq when the call has been started
+  /// (i.e, initial metadata has been sent).
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncReaderWriter> Call(
+      ClientContext* context, const grpc::string& method, CompletionQueue* cq,
+      void* tag);
+
+ private:
+  std::shared_ptr<ChannelInterface> channel_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_GENERIC_GENERIC_STUB_H
diff --git a/include/grpcpp/grpcpp.h b/include/grpcpp/grpcpp.h
new file mode 100644
index 0000000..aa8a242
--- /dev/null
+++ b/include/grpcpp/grpcpp.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/// \mainpage gRPC C++ API
+///
+/// The gRPC C++ API mainly consists of the following classes:
+/// <br>
+/// - grpc::Channel, which represents the connection to an endpoint. See [the
+/// gRPC Concepts page](https://grpc.io/docs/guides/concepts.html) for more
+/// details. Channels are created by the factory function grpc::CreateChannel.
+///
+/// - grpc::CompletionQueue, the producer-consumer queue used for all
+/// asynchronous communication with the gRPC runtime.
+///
+/// - grpc::ClientContext and grpc::ServerContext, where optional configuration
+/// for an RPC can be set, such as setting custom metadata to be conveyed to the
+/// peer, compression settings, authentication, etc.
+///
+/// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder.
+///
+/// Streaming calls are handled with the streaming classes in
+/// \ref sync_stream.h and
+/// \ref async_stream.h.
+///
+/// Refer to the
+/// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp)
+/// for code putting these pieces into play.
+
+#ifndef GRPCPP_GRPCPP_H
+#define GRPCPP_GRPCPP_H
+
+// Pragma for http://include-what-you-use.org/ tool, tells that following
+// headers are not private for grpcpp.h and are part of its interface.
+// IWYU pragma: begin_exports
+#include <grpc/grpc.h>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/create_channel_posix.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/server_posix.h>
+// IWYU pragma: end_exports
+
+namespace grpc {
+/// Return gRPC library version.
+grpc::string Version();
+}  // namespace grpc
+
+#endif  // GRPCPP_GRPCPP_H
diff --git a/include/grpcpp/health_check_service_interface.h b/include/grpcpp/health_check_service_interface.h
new file mode 100644
index 0000000..b45a699
--- /dev/null
+++ b/include/grpcpp/health_check_service_interface.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
+#define GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
+
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+const char kHealthCheckServiceInterfaceArg[] =
+    "grpc.health_check_service_interface";
+
+/// The gRPC server uses this interface to expose the health checking service
+/// without depending on protobuf.
+class HealthCheckServiceInterface {
+ public:
+  virtual ~HealthCheckServiceInterface() {}
+
+  /// Set or change the serving status of the given \a service_name.
+  virtual void SetServingStatus(const grpc::string& service_name,
+                                bool serving) = 0;
+  /// Apply to all registered service names.
+  virtual void SetServingStatus(bool serving) = 0;
+};
+
+/// Enable/disable the default health checking service. This applies to all C++
+/// servers created afterwards. For each server, user can override the default
+/// with a HealthCheckServiceServerBuilderOption.
+/// NOT thread safe.
+void EnableDefaultHealthCheckService(bool enable);
+
+/// Returns whether the default health checking service is enabled.
+/// NOT thread safe.
+bool DefaultHealthCheckServiceEnabled();
+
+}  // namespace grpc
+
+#endif  // GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
diff --git a/include/grpc++/impl/README.md b/include/grpcpp/impl/README.md
similarity index 100%
rename from include/grpc++/impl/README.md
rename to include/grpcpp/impl/README.md
diff --git a/include/grpcpp/impl/call.h b/include/grpcpp/impl/call.h
new file mode 100644
index 0000000..a6b1312
--- /dev/null
+++ b/include/grpcpp/impl/call.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CALL_H
+#define GRPCPP_IMPL_CALL_H
+
+#include <grpcpp/impl/codegen/call.h>
+
+#endif  // GRPCPP_IMPL_CALL_H
diff --git a/include/grpcpp/impl/channel_argument_option.h b/include/grpcpp/impl/channel_argument_option.h
new file mode 100644
index 0000000..0c48824
--- /dev/null
+++ b/include/grpcpp/impl/channel_argument_option.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H
+#define GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H
+
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/support/channel_arguments.h>
+
+namespace grpc {
+
+std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
+    const grpc::string& name, const grpc::string& value);
+std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
+    const grpc::string& name, int value);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H
diff --git a/include/grpcpp/impl/client_unary_call.h b/include/grpcpp/impl/client_unary_call.h
new file mode 100644
index 0000000..378482c
--- /dev/null
+++ b/include/grpcpp/impl/client_unary_call.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CLIENT_UNARY_CALL_H
+#define GRPCPP_IMPL_CLIENT_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/client_unary_call.h>
+
+#endif  // GRPCPP_IMPL_CLIENT_UNARY_CALL_H
diff --git a/include/grpcpp/impl/codegen/async_stream.h b/include/grpcpp/impl/codegen/async_stream.h
new file mode 100644
index 0000000..b213459
--- /dev/null
+++ b/include/grpcpp/impl/codegen/async_stream.h
@@ -0,0 +1,1068 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H
+#define GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+
+namespace internal {
+/// Common interface for all client side asynchronous streaming.
+class ClientAsyncStreamingInterface {
+ public:
+  virtual ~ClientAsyncStreamingInterface() {}
+
+  /// Start the call that was set up by the constructor, but only if the
+  /// constructor was invoked through the "Prepare" API which doesn't actually
+  /// start the call
+  virtual void StartCall(void* tag) = 0;
+
+  /// Request notification of the reading of the initial metadata. Completion
+  /// will be notified by \a tag on the associated completion queue.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a AsyncReaderInterface::Read method.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  virtual void ReadInitialMetadata(void* tag) = 0;
+
+  /// Indicate that the stream is to be finished and request notification for
+  /// when the call has been ended.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when both:
+  ///   * the client side has no more message to send
+  ///     (this can be declared implicitly by calling this method, or
+  ///     explicitly through an earlier call to the <i>WritesDone</i> method
+  ///     of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or
+  ///     \a ClientAsyncReaderWriterInterface::WritesDone).
+  ///   * there are no more messages to be received from the server (this can
+  ///     be known implicitly by the calling code, or explicitly from an
+  ///     earlier call to \a AsyncReaderInterface::Read that yielded a failed
+  ///     result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
+  ///
+  /// This function will return when either:
+  /// - all incoming messages have been read and the server has returned
+  ///   a status.
+  /// - the server has returned a non-OK status.
+  /// - the call failed for some reason and the library generated a
+  ///   status.
+  ///
+  /// Note that implementations of this method attempt to receive initial
+  /// metadata from the server if initial metadata hasn't yet been received.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[out] status To be updated with the operation status.
+  virtual void Finish(Status* status, void* tag) = 0;
+};
+
+/// An interface that yields a sequence of messages of type \a R.
+template <class R>
+class AsyncReaderInterface {
+ public:
+  virtual ~AsyncReaderInterface() {}
+
+  /// Read a message of type \a R into \a msg. Completion will be notified by \a
+  /// tag on the associated completion queue.
+  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
+  /// should not be called concurrently with other streaming APIs
+  /// on the same stream. It is not meaningful to call it concurrently
+  /// with another \a AsyncReaderInterface::Read on the same stream since reads
+  /// on the same stream are delivered in order.
+  ///
+  /// \param[out] msg Where to eventually store the read message.
+  /// \param[in] tag The tag identifying the operation.
+  ///
+  /// Side effect: note that this method attempt to receive initial metadata for
+  /// a stream if it hasn't yet been received.
+  virtual void Read(R* msg, void* tag) = 0;
+};
+
+/// An interface that can be fed a sequence of messages of type \a W.
+template <class W>
+class AsyncWriterInterface {
+ public:
+  virtual ~AsyncWriterInterface() {}
+
+  /// Request the writing of \a msg with identifying tag \a tag.
+  ///
+  /// Only one write may be outstanding at any given time. This means that
+  /// after calling Write, one must wait to receive \a tag from the completion
+  /// queue BEFORE calling Write again.
+  /// This is thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void Write(const W& msg, void* tag) = 0;
+
+  /// Request the writing of \a msg using WriteOptions \a options with
+  /// identifying tag \a tag.
+  ///
+  /// Only one write may be outstanding at any given time. This means that
+  /// after calling Write, one must wait to receive \a tag from the completion
+  /// queue BEFORE calling Write again.
+  /// WriteOptions \a options is used to set the write options of this message.
+  /// This is thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void Write(const W& msg, WriteOptions options, void* tag) = 0;
+
+  /// Request the writing of \a msg and coalesce it with the writing
+  /// of trailing metadata, using WriteOptions \a options with
+  /// identifying tag \a tag.
+  ///
+  /// For client, WriteLast is equivalent of performing Write and
+  /// WritesDone in a single step.
+  /// For server, WriteLast buffers the \a msg. The writing of \a msg is held
+  /// until Finish is called, where \a msg and trailing metadata are coalesced
+  /// and write is initiated. Note that WriteLast can only buffer \a msg up to
+  /// the flow control window size. If \a msg size is larger than the window
+  /// size, it will be sent on wire without buffering.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] tag The tag identifying the operation.
+  void WriteLast(const W& msg, WriteOptions options, void* tag) {
+    Write(msg, options.set_last_message(), tag);
+  }
+};
+
+}  // namespace internal
+
+template <class R>
+class ClientAsyncReaderInterface
+    : public internal::ClientAsyncStreamingInterface,
+      public internal::AsyncReaderInterface<R> {};
+
+namespace internal {
+template <class R>
+class ClientAsyncReaderFactory {
+ public:
+  /// Create a stream object.
+  /// Write the first request out if \a start is set.
+  /// \a tag will be notified on \a cq when the call has been started and
+  /// \a request has been written out. If \a start is not set, \a tag must be
+  /// nullptr and the actual call must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  template <class W>
+  static ClientAsyncReader<R>* Create(ChannelInterface* channel,
+                                      CompletionQueue* cq,
+                                      const ::grpc::internal::RpcMethod& method,
+                                      ClientContext* context, const W& request,
+                                      bool start, void* tag) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncReader<R>)))
+        ClientAsyncReader<R>(call, context, request, start, tag);
+  }
+};
+}  // namespace internal
+
+/// Async client-side API for doing server-streaming RPCs,
+/// where the incoming message stream coming from the server has
+/// messages of type \a R.
+template <class R>
+class ClientAsyncReader final : public ClientAsyncReaderInterface<R> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncReader));
+  }
+
+  void StartCall(void* tag) override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal(tag);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata
+  /// method for semantics.
+  ///
+  /// Side effect:
+  ///   - upon receiving initial metadata from the server,
+  ///     the \a ClientContext associated with this call is updated, and the
+  ///     calling code can access the received metadata through the
+  ///     \a ClientContext.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    assert(started_);
+    read_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      read_ops_.RecvInitialMetadata(context_);
+    }
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata received from the server.
+  void Finish(Status* status, void* tag) override {
+    assert(started_);
+    finish_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class internal::ClientAsyncReaderFactory<R>;
+  template <class W>
+  ClientAsyncReader(::grpc::internal::Call call, ClientContext* context,
+                    const W& request, bool start, void* tag)
+      : context_(context), call_(call), started_(start) {
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(init_ops_.SendMessage(request).ok());
+    init_ops_.ClientSendClose();
+    if (start) {
+      StartCallInternal(tag);
+    } else {
+      assert(tag == nullptr);
+    }
+  }
+
+  void StartCallInternal(void* tag) {
+    init_ops_.SendInitialMetadata(context_->send_initial_metadata_,
+                                  context_->initial_metadata_flags());
+    init_ops_.set_output_tag(tag);
+    call_.PerformOps(&init_ops_);
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose>
+      init_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpRecvMessage<R>>
+      read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+};
+
+/// Common interface for client side asynchronous writing.
+template <class W>
+class ClientAsyncWriterInterface
+    : public internal::ClientAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W> {
+ public:
+  /// Signal the client is done with the writes (half-close the client stream).
+  /// Thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WritesDone(void* tag) = 0;
+};
+
+namespace internal {
+template <class W>
+class ClientAsyncWriterFactory {
+ public:
+  /// Create a stream object.
+  /// Start the RPC if \a start is set
+  /// \a tag will be notified on \a cq when the call has been started (i.e.
+  /// intitial metadata sent) and \a request has been written out.
+  /// If \a start is not set, \a tag must be nullptr and the actual call
+  /// must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  /// \a response will be filled in with the single expected response
+  /// message from the server upon a successful call to the \a Finish
+  /// method of this instance.
+  template <class R>
+  static ClientAsyncWriter<W>* Create(ChannelInterface* channel,
+                                      CompletionQueue* cq,
+                                      const ::grpc::internal::RpcMethod& method,
+                                      ClientContext* context, R* response,
+                                      bool start, void* tag) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncWriter<W>)))
+        ClientAsyncWriter<W>(call, context, response, start, tag);
+  }
+};
+}  // namespace internal
+
+/// Async API on the client side for doing client-streaming RPCs,
+/// where the outgoing message stream going to the server contains
+/// messages of type \a W.
+template <class W>
+class ClientAsyncWriter final : public ClientAsyncWriterInterface<W> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncWriter));
+  }
+
+  void StartCall(void* tag) override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal(tag);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for
+  /// semantics.
+  ///
+  /// Side effect:
+  ///   - upon receiving initial metadata from the server, the \a ClientContext
+  ///     associated with this call is updated, and the calling code can access
+  ///     the received metadata through the \a ClientContext.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      write_ops_.ClientSendClose();
+    }
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void WritesDone(void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    write_ops_.ClientSendClose();
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata received from the server.
+  ///   - attempts to fill in the \a response parameter passed to this class's
+  ///     constructor with the server's response message.
+  void Finish(Status* status, void* tag) override {
+    assert(started_);
+    finish_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class internal::ClientAsyncWriterFactory<W>;
+  template <class R>
+  ClientAsyncWriter(::grpc::internal::Call call, ClientContext* context,
+                    R* response, bool start, void* tag)
+      : context_(context), call_(call), started_(start) {
+    finish_ops_.RecvMessage(response);
+    finish_ops_.AllowNoMessage();
+    if (start) {
+      StartCallInternal(tag);
+    } else {
+      assert(tag == nullptr);
+    }
+  }
+
+  void StartCallInternal(void* tag) {
+    write_ops_.SendInitialMetadata(context_->send_initial_metadata_,
+                                   context_->initial_metadata_flags());
+    // if corked bit is set in context, we just keep the initial metadata
+    // buffered up to coalesce with later message send. No op is performed.
+    if (!context_->initial_metadata_corked_) {
+      write_ops_.set_output_tag(tag);
+      call_.PerformOps(&write_ops_);
+    }
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpGenericRecvMessage,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+};
+
+/// Async client-side interface for bi-directional streaming,
+/// where the client-to-server message stream has messages of type \a W,
+/// and the server-to-client message stream has messages of type \a R.
+template <class W, class R>
+class ClientAsyncReaderWriterInterface
+    : public internal::ClientAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W>,
+      public internal::AsyncReaderInterface<R> {
+ public:
+  /// Signal the client is done with the writes (half-close the client stream).
+  /// Thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WritesDone(void* tag) = 0;
+};
+
+namespace internal {
+template <class W, class R>
+class ClientAsyncReaderWriterFactory {
+ public:
+  /// Create a stream object.
+  /// Start the RPC request if \a start is set.
+  /// \a tag will be notified on \a cq when the call has been started (i.e.
+  /// intitial metadata sent). If \a start is not set, \a tag must be
+  /// nullptr and the actual call must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  static ClientAsyncReaderWriter<W, R>* Create(
+      ChannelInterface* channel, CompletionQueue* cq,
+      const ::grpc::internal::RpcMethod& method, ClientContext* context,
+      bool start, void* tag) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncReaderWriter<W, R>)))
+        ClientAsyncReaderWriter<W, R>(call, context, start, tag);
+  }
+};
+}  // namespace internal
+
+/// Async client-side interface for bi-directional streaming,
+/// where the outgoing message stream going to the server
+/// has messages of type \a W,  and the incoming message stream coming
+/// from the server has messages of type \a R.
+template <class W, class R>
+class ClientAsyncReaderWriter final
+    : public ClientAsyncReaderWriterInterface<W, R> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncReaderWriter));
+  }
+
+  void StartCall(void* tag) override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal(tag);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method
+  /// for semantics of this method.
+  ///
+  /// Side effect:
+  ///   - upon receiving initial metadata from the server, the \a ClientContext
+  ///     is updated with it, and then the receiving initial metadata can
+  ///     be accessed through this \a ClientContext.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    assert(started_);
+    read_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      read_ops_.RecvInitialMetadata(context_);
+    }
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      write_ops_.ClientSendClose();
+    }
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void WritesDone(void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    write_ops_.ClientSendClose();
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
+  /// Side effect
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata sent from the server.
+  void Finish(Status* status, void* tag) override {
+    assert(started_);
+    finish_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class internal::ClientAsyncReaderWriterFactory<W, R>;
+  ClientAsyncReaderWriter(::grpc::internal::Call call, ClientContext* context,
+                          bool start, void* tag)
+      : context_(context), call_(call), started_(start) {
+    if (start) {
+      StartCallInternal(tag);
+    } else {
+      assert(tag == nullptr);
+    }
+  }
+
+  void StartCallInternal(void* tag) {
+    write_ops_.SendInitialMetadata(context_->send_initial_metadata_,
+                                   context_->initial_metadata_flags());
+    // if corked bit is set in context, we just keep the initial metadata
+    // buffered up to coalesce with later message send. No op is performed.
+    if (!context_->initial_metadata_corked_) {
+      write_ops_.set_output_tag(tag);
+      call_.PerformOps(&write_ops_);
+    }
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpRecvMessage<R>>
+      read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+};
+
+template <class W, class R>
+class ServerAsyncReaderInterface
+    : public internal::ServerAsyncStreamingInterface,
+      public internal::AsyncReaderInterface<R> {
+ public:
+  /// Indicate that the stream is to be finished with a certain status code
+  /// and also send out \a msg response to the client.
+  /// Request notification for when the server has sent the response and the
+  /// appropriate signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when:
+  ///   * all messages from the client have been received (either known
+  ///     implictly, or explicitly because a previous
+  ///     \a AsyncReaderInterface::Read operation with a non-ok result,
+  ///     e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), response message, and status, or if
+  /// some failure occurred when trying to do so.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  /// \param[in] msg To be sent to the client as the response for this call.
+  virtual void Finish(const W& msg, const Status& status, void* tag) = 0;
+
+  /// Indicate that the stream is to be finished with a certain
+  /// non-OK status code.
+  /// Request notification for when the server has sent the appropriate
+  /// signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// This call is meant to end the call with some error, and can be called at
+  /// any point that the server would like to "fail" the call (though note
+  /// this shouldn't be called concurrently with any other "sending" call, like
+  /// \a AsyncWriterInterface::Write).
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), and status, or if some failure occurred
+  /// when trying to do so.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  ///     - Note: \a status must have a non-OK code.
+  virtual void FinishWithError(const Status& status, void* tag) = 0;
+};
+
+/// Async server-side API for doing client-streaming RPCs,
+/// where the incoming message stream from the client has messages of type \a R,
+/// and the single response message sent from the server is type \a W.
+template <class W, class R>
+class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
+ public:
+  explicit ServerAsyncReader(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - The initial metadata that will be sent to the client from this op will
+  ///     be taken from the \a ServerContext associated with the call.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    read_ops_.set_output_tag(tag);
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderInterface.Read method for semantics
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not alreay sent.
+  ///   - uses the \a ServerContext associated with this call to send possible
+  ///     initial and trailing metadata.
+  ///
+  /// Note: \a msg is not sent if \a status has a non-OK code.
+  void Finish(const W& msg, const Status& status, void* tag) override {
+    finish_ops_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    // The response is dropped if the status is not OK.
+    if (status.ok()) {
+      finish_ops_.ServerSendStatus(ctx_->trailing_metadata_,
+                                   finish_ops_.SendMessage(msg));
+    } else {
+      finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    }
+    call_.PerformOps(&finish_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderInterface.Read method for semantics
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not alreay sent.
+  ///   - uses the \a ServerContext associated with this call to send possible
+  ///     initial and trailing metadata.
+  void FinishWithError(const Status& status, void* tag) override {
+    GPR_CODEGEN_ASSERT(!status.ok());
+    finish_ops_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>> read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_ops_;
+};
+
+template <class W>
+class ServerAsyncWriterInterface
+    : public internal::ServerAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W> {
+ public:
+  /// Indicate that the stream is to be finished with a certain status code.
+  /// Request notification for when the server has sent the appropriate
+  /// signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when either:
+  ///   * all messages from the client have been received (either known
+  ///     implictly, or explicitly because a previous \a
+  ///     AsyncReaderInterface::Read operation with a non-ok
+  ///     result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'.
+  ///   * it is desired to end the call early with some non-OK status code.
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), response message, and status, or if
+  /// some failure occurred when trying to do so.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  virtual void Finish(const Status& status, void* tag) = 0;
+
+  /// Request the writing of \a msg and coalesce it with trailing metadata which
+  /// contains \a status, using WriteOptions options with
+  /// identifying tag \a tag.
+  ///
+  /// WriteAndFinish is equivalent of performing WriteLast and Finish
+  /// in a single step.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] status The Status that server returns to client.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WriteAndFinish(const W& msg, WriteOptions options,
+                              const Status& status, void* tag) = 0;
+};
+
+/// Async server-side API for doing server streaming RPCs,
+/// where the outgoing message stream from the server has messages of type \a W.
+template <class W>
+class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
+ public:
+  explicit ServerAsyncWriter(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - The initial metadata that will be sent to the client from this op will
+  ///     be taken from the \a ServerContext associated with the call.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+
+    EnsureInitialMetadataSent(&write_ops_);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used
+  ///     for sending trailing (and initial) metadata to the client.
+  ///
+  /// Note: \a status must have an OK code.
+  void WriteAndFinish(const W& msg, WriteOptions options, const Status& status,
+                      void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    options.set_buffer_hint();
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    write_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncWriterInterface.Finish method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used for sending
+  ///     trailing (and initial if not already sent) metadata to the client.
+  ///
+  /// Note: there are no restrictions are the code of
+  /// \a status,it may be non-OK
+  void Finish(const Status& status, void* tag) override {
+    finish_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&finish_ops_);
+    finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  template <class T>
+  void EnsureInitialMetadataSent(T* ops) {
+    if (!ctx_->sent_initial_metadata_) {
+      ops->SendInitialMetadata(ctx_->initial_metadata_,
+                               ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ops->set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+  }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_ops_;
+};
+
+/// Server-side interface for asynchronous bi-directional streaming.
+template <class W, class R>
+class ServerAsyncReaderWriterInterface
+    : public internal::ServerAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W>,
+      public internal::AsyncReaderInterface<R> {
+ public:
+  /// Indicate that the stream is to be finished with a certain status code.
+  /// Request notification for when the server has sent the appropriate
+  /// signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when either:
+  ///   * all messages from the client have been received (either known
+  ///     implictly, or explicitly because a previous \a
+  ///     AsyncReaderInterface::Read operation
+  ///     with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok'
+  ///     with 'false'.
+  ///   * it is desired to end the call early with some non-OK status code.
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), response message, and status, or if some
+  /// failure occurred when trying to do so.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  virtual void Finish(const Status& status, void* tag) = 0;
+
+  /// Request the writing of \a msg and coalesce it with trailing metadata which
+  /// contains \a status, using WriteOptions options with
+  /// identifying tag \a tag.
+  ///
+  /// WriteAndFinish is equivalent of performing WriteLast and Finish in a
+  /// single step.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] status The Status that server returns to client.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WriteAndFinish(const W& msg, WriteOptions options,
+                              const Status& status, void* tag) = 0;
+};
+
+/// Async server-side API for doing bidirectional streaming RPCs,
+/// where the incoming message stream coming from the client has messages of
+/// type \a R, and the outgoing message stream coming from the server has
+/// messages of type \a W.
+template <class W, class R>
+class ServerAsyncReaderWriter final
+    : public ServerAsyncReaderWriterInterface<W, R> {
+ public:
+  explicit ServerAsyncReaderWriter(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - The initial metadata that will be sent to the client from this op will
+  ///     be taken from the \a ServerContext associated with the call.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    read_ops_.set_output_tag(tag);
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+    EnsureInitialMetadataSent(&write_ops_);
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish
+  /// method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used
+  ///     for sending trailing (and initial) metadata to the client.
+  ///
+  /// Note: \a status must have an OK code.
+  void WriteAndFinish(const W& msg, WriteOptions options, const Status& status,
+                      void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    options.set_buffer_hint();
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    write_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used for sending
+  ///     trailing (and initial if not already sent) metadata to the client.
+  ///
+  /// Note: there are no restrictions are the code of \a status,
+  /// it may be non-OK
+  void Finish(const Status& status, void* tag) override {
+    finish_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&finish_ops_);
+
+    finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class ::grpc::Server;
+
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  template <class T>
+  void EnsureInitialMetadataSent(T* ops) {
+    if (!ctx_->sent_initial_metadata_) {
+      ops->SendInitialMetadata(ctx_->initial_metadata_,
+                               ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ops->set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+  }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>> read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_ops_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H
diff --git a/include/grpcpp/impl/codegen/async_unary_call.h b/include/grpcpp/impl/codegen/async_unary_call.h
new file mode 100644
index 0000000..255f874
--- /dev/null
+++ b/include/grpcpp/impl/codegen/async_unary_call.h
@@ -0,0 +1,309 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
+#define GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
+
+#include <assert.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// An interface relevant for async client side unary RPCs (which send
+/// one request message to a server and receive one response message).
+template <class R>
+class ClientAsyncResponseReaderInterface {
+ public:
+  virtual ~ClientAsyncResponseReaderInterface() {}
+
+  /// Start the call that was set up by the constructor, but only if the
+  /// constructor was invoked through the "Prepare" API which doesn't actually
+  /// start the call
+  virtual void StartCall() = 0;
+
+  /// Request notification of the reading of initial metadata. Completion
+  /// will be notified by \a tag on the associated completion queue.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a Finish method.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  virtual void ReadInitialMetadata(void* tag) = 0;
+
+  /// Request to receive the server's response \a msg and final \a status for
+  /// the call, and to notify \a tag on this call's completion queue when
+  /// finished.
+  ///
+  /// This function will return when either:
+  /// - when the server's response message and status have been received.
+  /// - when the server has returned a non-OK status (no message expected in
+  ///   this case).
+  /// - when the call failed for some reason and the library generated a
+  ///   non-OK status.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[out] status To be updated with the operation status.
+  /// \param[out] msg To be filled in with the server's response message.
+  virtual void Finish(R* msg, Status* status, void* tag) = 0;
+};
+
+namespace internal {
+template <class R>
+class ClientAsyncResponseReaderFactory {
+ public:
+  /// Start a call and write the request out if \a start is set.
+  /// \a tag will be notified on \a cq when the call has been started (i.e.
+  /// intitial metadata sent) and \a request has been written out.
+  /// If \a start is not set, the actual call must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  template <class W>
+  static ClientAsyncResponseReader<R>* Create(
+      ChannelInterface* channel, CompletionQueue* cq,
+      const ::grpc::internal::RpcMethod& method, ClientContext* context,
+      const W& request, bool start) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncResponseReader<R>)))
+        ClientAsyncResponseReader<R>(call, context, request, start);
+  }
+};
+}  // namespace internal
+
+/// Async API for client-side unary RPCs, where the message response
+/// received from the server is of type \a R.
+template <class R>
+class ClientAsyncResponseReader final
+    : public ClientAsyncResponseReaderInterface<R> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncResponseReader));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void StartCall() override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal();
+  }
+
+  /// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for
+  /// semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata sent from the server.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_buf.set_output_tag(tag);
+    meta_buf.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_buf);
+  }
+
+  /// See \a ClientAysncResponseReaderInterface::Finish for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata sent from the server.
+  void Finish(R* msg, Status* status, void* tag) override {
+    assert(started_);
+    finish_buf.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_buf.RecvInitialMetadata(context_);
+    }
+    finish_buf.RecvMessage(msg);
+    finish_buf.AllowNoMessage();
+    finish_buf.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_buf);
+  }
+
+ private:
+  friend class internal::ClientAsyncResponseReaderFactory<R>;
+  ClientContext* const context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+
+  template <class W>
+  ClientAsyncResponseReader(::grpc::internal::Call call, ClientContext* context,
+                            const W& request, bool start)
+      : context_(context), call_(call), started_(start) {
+    // Bind the metadata at time of StartCallInternal but set up the rest here
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(init_buf.SendMessage(request).ok());
+    init_buf.ClientSendClose();
+    if (start) StartCallInternal();
+  }
+
+  void StartCallInternal() {
+    init_buf.SendInitialMetadata(context_->send_initial_metadata_,
+                                 context_->initial_metadata_flags());
+    call_.PerformOps(&init_buf);
+  }
+
+  // disable operator new
+  static void* operator new(std::size_t size);
+  static void* operator new(std::size_t size, void* p) { return p; }
+
+  ::grpc::internal::SneakyCallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                    ::grpc::internal::CallOpSendMessage,
+                                    ::grpc::internal::CallOpClientSendClose>
+      init_buf;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_buf;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpRecvMessage<R>,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_buf;
+};
+
+/// Async server-side API for handling unary calls, where the single
+/// response message sent to the client is of type \a W.
+template <class W>
+class ServerAsyncResponseWriter final
+    : public internal::ServerAsyncStreamingInterface {
+ public:
+  explicit ServerAsyncResponseWriter(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Side effect:
+  ///   The initial metadata that will be sent to the client from this op will
+  ///   be taken from the \a ServerContext associated with the call.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_buf_.set_output_tag(tag);
+    meta_buf_.SendInitialMetadata(ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_buf_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_buf_);
+  }
+
+  /// Indicate that the stream is to be finished and request notification
+  /// when the server has sent the appropriate signals to the client to
+  /// end the call. Should not be used concurrently with other operations.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of the call.
+  /// \param[in] msg Message to be sent to the client.
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not already sent (using the
+  ///     \a ServerContext associated with this call).
+  ///
+  /// Note: if \a status has a non-OK code, then \a msg will not be sent,
+  /// and the client will receive only the status with possible trailing
+  /// metadata.
+  void Finish(const W& msg, const Status& status, void* tag) {
+    finish_buf_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_buf_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    // The response is dropped if the status is not OK.
+    if (status.ok()) {
+      finish_buf_.ServerSendStatus(ctx_->trailing_metadata_,
+                                   finish_buf_.SendMessage(msg));
+    } else {
+      finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    }
+    call_.PerformOps(&finish_buf_);
+  }
+
+  /// Indicate that the stream is to be finished with a non-OK status,
+  /// and request notification for when the server has finished sending the
+  /// appropriate signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of the call.
+  ///   - Note: \a status must have a non-OK code.
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not already sent (using the
+  ///     \a ServerContext associated with this call).
+  void FinishWithError(const Status& status, void* tag) {
+    GPR_CODEGEN_ASSERT(!status.ok());
+    finish_buf_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_buf_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_buf_);
+  }
+
+ private:
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_buf_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_buf_;
+};
+
+}  // namespace grpc
+
+namespace std {
+template <class R>
+class default_delete<grpc::ClientAsyncResponseReader<R>> {
+ public:
+  void operator()(void* p) {}
+};
+template <class R>
+class default_delete<grpc::ClientAsyncResponseReaderInterface<R>> {
+ public:
+  void operator()(void* p) {}
+};
+}  // namespace std
+
+#endif  // GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
diff --git a/include/grpcpp/impl/codegen/byte_buffer.h b/include/grpcpp/impl/codegen/byte_buffer.h
new file mode 100644
index 0000000..8d6a169
--- /dev/null
+++ b/include/grpcpp/impl/codegen/byte_buffer.h
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H
+#define GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H
+
+#include <grpc/impl/codegen/byte_buffer.h>
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+
+#include <vector>
+
+namespace grpc {
+
+namespace internal {
+class CallOpSendMessage;
+template <class R>
+class CallOpRecvMessage;
+class CallOpGenericRecvMessage;
+class MethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class R>
+class DeserializeFuncType;
+}  // namespace internal
+/// A sequence of bytes.
+class ByteBuffer final {
+ public:
+  /// Constuct an empty buffer.
+  ByteBuffer() : buffer_(nullptr) {}
+
+  /// Construct buffer from \a slices, of which there are \a nslices.
+  ByteBuffer(const Slice* slices, size_t nslices);
+
+  /// Constuct a byte buffer by referencing elements of existing buffer
+  /// \a buf. Wrapper of core function grpc_byte_buffer_copy
+  ByteBuffer(const ByteBuffer& buf);
+
+  ~ByteBuffer() {
+    if (buffer_) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
+    }
+  }
+
+  ByteBuffer& operator=(const ByteBuffer&);
+
+  /// Dump (read) the buffer contents into \a slices.
+  Status Dump(std::vector<Slice>* slices) const;
+
+  /// Remove all data.
+  void Clear() {
+    if (buffer_) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
+      buffer_ = nullptr;
+    }
+  }
+
+  /// Make a duplicate copy of the internals of this byte
+  /// buffer so that we have our own owned version of it.
+  /// bbuf.Duplicate(); is equivalent to bbuf=bbuf; but is actually readable
+  void Duplicate() {
+    buffer_ = g_core_codegen_interface->grpc_byte_buffer_copy(buffer_);
+  }
+
+  /// Forget underlying byte buffer without destroying
+  /// Use this only for un-owned byte buffers
+  void Release() { buffer_ = nullptr; }
+
+  /// Buffer size in bytes.
+  size_t Length() const;
+
+  /// Swap the state of *this and *other.
+  void Swap(ByteBuffer* other);
+
+  /// Is this ByteBuffer valid?
+  bool Valid() const { return (buffer_ != nullptr); }
+
+ private:
+  friend class SerializationTraits<ByteBuffer, void>;
+  friend class internal::CallOpSendMessage;
+  template <class R>
+  friend class internal::CallOpRecvMessage;
+  friend class internal::CallOpGenericRecvMessage;
+  friend class internal::MethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::ServerStreamingHandler;
+  template <class R>
+  friend class internal::DeserializeFuncType;
+
+  grpc_byte_buffer* buffer_;
+
+  // takes ownership
+  void set_buffer(grpc_byte_buffer* buf) {
+    if (buffer_) {
+      Clear();
+    }
+    buffer_ = buf;
+  }
+
+  grpc_byte_buffer* c_buffer() { return buffer_; }
+  grpc_byte_buffer** c_buffer_ptr() { return &buffer_; }
+
+  class ByteBufferPointer {
+   public:
+    ByteBufferPointer(const ByteBuffer* b)
+        : bbuf_(const_cast<ByteBuffer*>(b)) {}
+    operator ByteBuffer*() { return bbuf_; }
+    operator grpc_byte_buffer*() { return bbuf_->buffer_; }
+    operator grpc_byte_buffer**() { return &bbuf_->buffer_; }
+
+   private:
+    ByteBuffer* bbuf_;
+  };
+  ByteBufferPointer bbuf_ptr() const { return ByteBufferPointer(this); }
+};
+
+template <>
+class SerializationTraits<ByteBuffer, void> {
+ public:
+  static Status Deserialize(ByteBuffer* byte_buffer, ByteBuffer* dest) {
+    dest->set_buffer(byte_buffer->buffer_);
+    return Status::OK;
+  }
+  static Status Serialize(const ByteBuffer& source, ByteBuffer* buffer,
+                          bool* own_buffer) {
+    *buffer = source;
+    *own_buffer = true;
+    return Status::OK;
+  }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H
diff --git a/include/grpcpp/impl/codegen/call.h b/include/grpcpp/impl/codegen/call.h
new file mode 100644
index 0000000..3b0fd60
--- /dev/null
+++ b/include/grpcpp/impl/codegen/call.h
@@ -0,0 +1,709 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CALL_H
+#define GRPCPP_IMPL_CODEGEN_CALL_H
+
+#include <assert.h>
+#include <cstring>
+#include <functional>
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/call_hook.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#include <grpc/impl/codegen/atm.h>
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/grpc_types.h>
+
+namespace grpc {
+
+class ByteBuffer;
+class CompletionQueue;
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+namespace internal {
+class Call;
+class CallHook;
+
+const char kBinaryErrorDetailsKey[] = "grpc-status-details-bin";
+
+// TODO(yangg) if the map is changed before we send, the pointers will be a
+// mess. Make sure it does not happen.
+inline grpc_metadata* FillMetadataArray(
+    const std::multimap<grpc::string, grpc::string>& metadata,
+    size_t* metadata_count, const grpc::string& optional_error_details) {
+  *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1);
+  if (*metadata_count == 0) {
+    return nullptr;
+  }
+  grpc_metadata* metadata_array =
+      (grpc_metadata*)(g_core_codegen_interface->gpr_malloc(
+          (*metadata_count) * sizeof(grpc_metadata)));
+  size_t i = 0;
+  for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
+    metadata_array[i].key = SliceReferencingString(iter->first);
+    metadata_array[i].value = SliceReferencingString(iter->second);
+  }
+  if (!optional_error_details.empty()) {
+    metadata_array[i].key =
+        g_core_codegen_interface->grpc_slice_from_static_buffer(
+            kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1);
+    metadata_array[i].value = SliceReferencingString(optional_error_details);
+  }
+  return metadata_array;
+}
+}  // namespace internal
+
+/// Per-message write options.
+class WriteOptions {
+ public:
+  WriteOptions() : flags_(0), last_message_(false) {}
+  WriteOptions(const WriteOptions& other)
+      : flags_(other.flags_), last_message_(other.last_message_) {}
+
+  /// Clear all flags.
+  inline void Clear() { flags_ = 0; }
+
+  /// Returns raw flags bitset.
+  inline uint32_t flags() const { return flags_; }
+
+  /// Sets flag for the disabling of compression for the next message write.
+  ///
+  /// \sa GRPC_WRITE_NO_COMPRESS
+  inline WriteOptions& set_no_compression() {
+    SetBit(GRPC_WRITE_NO_COMPRESS);
+    return *this;
+  }
+
+  /// Clears flag for the disabling of compression for the next message write.
+  ///
+  /// \sa GRPC_WRITE_NO_COMPRESS
+  inline WriteOptions& clear_no_compression() {
+    ClearBit(GRPC_WRITE_NO_COMPRESS);
+    return *this;
+  }
+
+  /// Get value for the flag indicating whether compression for the next
+  /// message write is forcefully disabled.
+  ///
+  /// \sa GRPC_WRITE_NO_COMPRESS
+  inline bool get_no_compression() const {
+    return GetBit(GRPC_WRITE_NO_COMPRESS);
+  }
+
+  /// Sets flag indicating that the write may be buffered and need not go out on
+  /// the wire immediately.
+  ///
+  /// \sa GRPC_WRITE_BUFFER_HINT
+  inline WriteOptions& set_buffer_hint() {
+    SetBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  /// Clears flag indicating that the write may be buffered and need not go out
+  /// on the wire immediately.
+  ///
+  /// \sa GRPC_WRITE_BUFFER_HINT
+  inline WriteOptions& clear_buffer_hint() {
+    ClearBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  /// Get value for the flag indicating that the write may be buffered and need
+  /// not go out on the wire immediately.
+  ///
+  /// \sa GRPC_WRITE_BUFFER_HINT
+  inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
+
+  /// corked bit: aliases set_buffer_hint currently, with the intent that
+  /// set_buffer_hint will be removed in the future
+  inline WriteOptions& set_corked() {
+    SetBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  inline WriteOptions& clear_corked() {
+    ClearBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
+
+  /// last-message bit: indicates this is the last message in a stream
+  /// client-side:  makes Write the equivalent of performing Write, WritesDone
+  /// in a single step
+  /// server-side:  hold the Write until the service handler returns (sync api)
+  /// or until Finish is called (async api)
+  inline WriteOptions& set_last_message() {
+    last_message_ = true;
+    return *this;
+  }
+
+  /// Clears flag indicating that this is the last message in a stream,
+  /// disabling coalescing.
+  inline WriteOptions& clear_last_message() {
+    last_message_ = false;
+    return *this;
+  }
+
+  /// Guarantee that all bytes have been written to the wire before completing
+  /// this write (usually writes are completed when they pass flow control)
+  inline WriteOptions& set_write_through() {
+    SetBit(GRPC_WRITE_THROUGH);
+    return *this;
+  }
+
+  inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); }
+
+  /// Get value for the flag indicating that this is the last message, and
+  /// should be coalesced with trailing metadata.
+  ///
+  /// \sa GRPC_WRITE_LAST_MESSAGE
+  bool is_last_message() const { return last_message_; }
+
+  WriteOptions& operator=(const WriteOptions& rhs) {
+    flags_ = rhs.flags_;
+    return *this;
+  }
+
+ private:
+  void SetBit(const uint32_t mask) { flags_ |= mask; }
+
+  void ClearBit(const uint32_t mask) { flags_ &= ~mask; }
+
+  bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; }
+
+  uint32_t flags_;
+  bool last_message_;
+};
+
+namespace internal {
+/// Default argument for CallOpSet. I is unused by the class, but can be
+/// used for generating multiple names for the same thing.
+template <int I>
+class CallNoOp {
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {}
+  void FinishOp(bool* status) {}
+};
+
+class CallOpSendInitialMetadata {
+ public:
+  CallOpSendInitialMetadata() : send_(false) {
+    maybe_compression_level_.is_set = false;
+  }
+
+  void SendInitialMetadata(
+      const std::multimap<grpc::string, grpc::string>& metadata,
+      uint32_t flags) {
+    maybe_compression_level_.is_set = false;
+    send_ = true;
+    flags_ = flags;
+    initial_metadata_ =
+        FillMetadataArray(metadata, &initial_metadata_count_, "");
+  }
+
+  void set_compression_level(grpc_compression_level level) {
+    maybe_compression_level_.is_set = true;
+    maybe_compression_level_.level = level;
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_INITIAL_METADATA;
+    op->flags = flags_;
+    op->reserved = NULL;
+    op->data.send_initial_metadata.count = initial_metadata_count_;
+    op->data.send_initial_metadata.metadata = initial_metadata_;
+    op->data.send_initial_metadata.maybe_compression_level.is_set =
+        maybe_compression_level_.is_set;
+    if (maybe_compression_level_.is_set) {
+      op->data.send_initial_metadata.maybe_compression_level.level =
+          maybe_compression_level_.level;
+    }
+  }
+  void FinishOp(bool* status) {
+    if (!send_) return;
+    g_core_codegen_interface->gpr_free(initial_metadata_);
+    send_ = false;
+  }
+
+  bool send_;
+  uint32_t flags_;
+  size_t initial_metadata_count_;
+  grpc_metadata* initial_metadata_;
+  struct {
+    bool is_set;
+    grpc_compression_level level;
+  } maybe_compression_level_;
+};
+
+class CallOpSendMessage {
+ public:
+  CallOpSendMessage() : send_buf_() {}
+
+  /// Send \a message using \a options for the write. The \a options are cleared
+  /// after use.
+  template <class M>
+  Status SendMessage(const M& message,
+                     WriteOptions options) GRPC_MUST_USE_RESULT;
+
+  template <class M>
+  Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_buf_.Valid()) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_MESSAGE;
+    op->flags = write_options_.flags();
+    op->reserved = NULL;
+    op->data.send_message.send_message = send_buf_.c_buffer();
+    // Flags are per-message: clear them after use.
+    write_options_.Clear();
+  }
+  void FinishOp(bool* status) { send_buf_.Clear(); }
+
+ private:
+  ByteBuffer send_buf_;
+  WriteOptions write_options_;
+};
+
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
+  write_options_ = options;
+  bool own_buf;
+  // TODO(vjpai): Remove the void below when possible
+  // The void in the template parameter below should not be needed
+  // (since it should be implicit) but is needed due to an observed
+  // difference in behavior between clang and gcc for certain internal users
+  Status result = SerializationTraits<M, void>::Serialize(
+      message, send_buf_.bbuf_ptr(), &own_buf);
+  if (!own_buf) {
+    send_buf_.Duplicate();
+  }
+  return result;
+}
+
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message) {
+  return SendMessage(message, WriteOptions());
+}
+
+template <class R>
+class CallOpRecvMessage {
+ public:
+  CallOpRecvMessage()
+      : got_message(false),
+        message_(nullptr),
+        allow_not_getting_message_(false) {}
+
+  void RecvMessage(R* message) { message_ = message; }
+
+  // Do not change status if no message is received.
+  void AllowNoMessage() { allow_not_getting_message_ = true; }
+
+  bool got_message;
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (message_ == nullptr) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_MESSAGE;
+    op->flags = 0;
+    op->reserved = NULL;
+    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
+  }
+
+  void FinishOp(bool* status) {
+    if (message_ == nullptr) return;
+    if (recv_buf_.Valid()) {
+      if (*status) {
+        got_message = *status =
+            SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
+                .ok();
+        recv_buf_.Release();
+      } else {
+        got_message = false;
+        recv_buf_.Clear();
+      }
+    } else {
+      got_message = false;
+      if (!allow_not_getting_message_) {
+        *status = false;
+      }
+    }
+    message_ = nullptr;
+  }
+
+ private:
+  R* message_;
+  ByteBuffer recv_buf_;
+  bool allow_not_getting_message_;
+};
+
+class DeserializeFunc {
+ public:
+  virtual Status Deserialize(ByteBuffer* buf) = 0;
+  virtual ~DeserializeFunc() {}
+};
+
+template <class R>
+class DeserializeFuncType final : public DeserializeFunc {
+ public:
+  DeserializeFuncType(R* message) : message_(message) {}
+  Status Deserialize(ByteBuffer* buf) override {
+    return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
+  }
+
+  ~DeserializeFuncType() override {}
+
+ private:
+  R* message_;  // Not a managed pointer because management is external to this
+};
+
+class CallOpGenericRecvMessage {
+ public:
+  CallOpGenericRecvMessage()
+      : got_message(false), allow_not_getting_message_(false) {}
+
+  template <class R>
+  void RecvMessage(R* message) {
+    // Use an explicit base class pointer to avoid resolution error in the
+    // following unique_ptr::reset for some old implementations.
+    DeserializeFunc* func = new DeserializeFuncType<R>(message);
+    deserialize_.reset(func);
+  }
+
+  // Do not change status if no message is received.
+  void AllowNoMessage() { allow_not_getting_message_ = true; }
+
+  bool got_message;
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!deserialize_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_MESSAGE;
+    op->flags = 0;
+    op->reserved = NULL;
+    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
+  }
+
+  void FinishOp(bool* status) {
+    if (!deserialize_) return;
+    if (recv_buf_.Valid()) {
+      if (*status) {
+        got_message = true;
+        *status = deserialize_->Deserialize(&recv_buf_).ok();
+        recv_buf_.Release();
+      } else {
+        got_message = false;
+        recv_buf_.Clear();
+      }
+    } else {
+      got_message = false;
+      if (!allow_not_getting_message_) {
+        *status = false;
+      }
+    }
+    deserialize_.reset();
+  }
+
+ private:
+  std::unique_ptr<DeserializeFunc> deserialize_;
+  ByteBuffer recv_buf_;
+  bool allow_not_getting_message_;
+};
+
+class CallOpClientSendClose {
+ public:
+  CallOpClientSendClose() : send_(false) {}
+
+  void ClientSendClose() { send_ = true; }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+  void FinishOp(bool* status) { send_ = false; }
+
+ private:
+  bool send_;
+};
+
+class CallOpServerSendStatus {
+ public:
+  CallOpServerSendStatus() : send_status_available_(false) {}
+
+  void ServerSendStatus(
+      const std::multimap<grpc::string, grpc::string>& trailing_metadata,
+      const Status& status) {
+    send_error_details_ = status.error_details();
+    trailing_metadata_ = FillMetadataArray(
+        trailing_metadata, &trailing_metadata_count_, send_error_details_);
+    send_status_available_ = true;
+    send_status_code_ = static_cast<grpc_status_code>(status.error_code());
+    send_error_message_ = status.error_message();
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_status_available_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+    op->data.send_status_from_server.trailing_metadata_count =
+        trailing_metadata_count_;
+    op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
+    op->data.send_status_from_server.status = send_status_code_;
+    error_message_slice_ = SliceReferencingString(send_error_message_);
+    op->data.send_status_from_server.status_details =
+        send_error_message_.empty() ? nullptr : &error_message_slice_;
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+
+  void FinishOp(bool* status) {
+    if (!send_status_available_) return;
+    g_core_codegen_interface->gpr_free(trailing_metadata_);
+    send_status_available_ = false;
+  }
+
+ private:
+  bool send_status_available_;
+  grpc_status_code send_status_code_;
+  grpc::string send_error_details_;
+  grpc::string send_error_message_;
+  size_t trailing_metadata_count_;
+  grpc_metadata* trailing_metadata_;
+  grpc_slice error_message_slice_;
+};
+
+class CallOpRecvInitialMetadata {
+ public:
+  CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
+
+  void RecvInitialMetadata(ClientContext* context) {
+    context->initial_metadata_received_ = true;
+    metadata_map_ = &context->recv_initial_metadata_;
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (metadata_map_ == nullptr) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_INITIAL_METADATA;
+    op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+
+  void FinishOp(bool* status) {
+    if (metadata_map_ == nullptr) return;
+    metadata_map_->FillMap();
+    metadata_map_ = nullptr;
+  }
+
+ private:
+  MetadataMap* metadata_map_;
+};
+
+class CallOpClientRecvStatus {
+ public:
+  CallOpClientRecvStatus()
+      : recv_status_(nullptr), debug_error_string_(nullptr) {}
+
+  void ClientRecvStatus(ClientContext* context, Status* status) {
+    client_context_ = context;
+    metadata_map_ = &client_context_->trailing_metadata_;
+    recv_status_ = status;
+    error_message_ = g_core_codegen_interface->grpc_empty_slice();
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (recv_status_ == nullptr) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+    op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr();
+    op->data.recv_status_on_client.status = &status_code_;
+    op->data.recv_status_on_client.status_details = &error_message_;
+    op->data.recv_status_on_client.error_string = &debug_error_string_;
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+
+  void FinishOp(bool* status) {
+    if (recv_status_ == nullptr) return;
+    metadata_map_->FillMap();
+    grpc::string binary_error_details;
+    auto iter = metadata_map_->map()->find(kBinaryErrorDetailsKey);
+    if (iter != metadata_map_->map()->end()) {
+      binary_error_details =
+          grpc::string(iter->second.begin(), iter->second.length());
+    }
+    *recv_status_ = Status(static_cast<StatusCode>(status_code_),
+                           grpc::string(GRPC_SLICE_START_PTR(error_message_),
+                                        GRPC_SLICE_END_PTR(error_message_)),
+                           binary_error_details);
+    client_context_->set_debug_error_string(
+        debug_error_string_ != nullptr ? debug_error_string_ : "");
+    g_core_codegen_interface->grpc_slice_unref(error_message_);
+    if (debug_error_string_ != nullptr) {
+      g_core_codegen_interface->gpr_free((void*)debug_error_string_);
+    }
+    recv_status_ = nullptr;
+  }
+
+ private:
+  ClientContext* client_context_;
+  MetadataMap* metadata_map_;
+  Status* recv_status_;
+  const char* debug_error_string_;
+  grpc_status_code status_code_;
+  grpc_slice error_message_;
+};
+
+/// An abstract collection of call ops, used to generate the
+/// grpc_call_op structure to pass down to the lower layers,
+/// and as it is-a CompletionQueueTag, also massages the final
+/// completion into the correct form for consumption in the C++
+/// API.
+class CallOpSetInterface : public CompletionQueueTag {
+ public:
+  /// Fills in grpc_op, starting from ops[*nops] and moving
+  /// upwards.
+  virtual void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) = 0;
+};
+
+/// Primary implementation of CallOpSetInterface.
+/// Since we cannot use variadic templates, we declare slots up to
+/// the maximum count of ops we'll need in a set. We leverage the
+/// empty base class optimization to slim this class (especially
+/// when there are many unused slots used). To avoid duplicate base classes,
+/// the template parmeter for CallNoOp is varied by argument position.
+template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
+          class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
+          class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
+class CallOpSet : public CallOpSetInterface,
+                  public Op1,
+                  public Op2,
+                  public Op3,
+                  public Op4,
+                  public Op5,
+                  public Op6 {
+ public:
+  CallOpSet() : return_tag_(this), call_(nullptr) {}
+  void FillOps(grpc_call* call, grpc_op* ops, size_t* nops) override {
+    this->Op1::AddOp(ops, nops);
+    this->Op2::AddOp(ops, nops);
+    this->Op3::AddOp(ops, nops);
+    this->Op4::AddOp(ops, nops);
+    this->Op5::AddOp(ops, nops);
+    this->Op6::AddOp(ops, nops);
+    g_core_codegen_interface->grpc_call_ref(call);
+    call_ = call;
+  }
+
+  bool FinalizeResult(void** tag, bool* status) override {
+    this->Op1::FinishOp(status);
+    this->Op2::FinishOp(status);
+    this->Op3::FinishOp(status);
+    this->Op4::FinishOp(status);
+    this->Op5::FinishOp(status);
+    this->Op6::FinishOp(status);
+    *tag = return_tag_;
+
+    g_core_codegen_interface->grpc_call_unref(call_);
+    return true;
+  }
+
+  void set_output_tag(void* return_tag) { return_tag_ = return_tag; }
+
+ private:
+  void* return_tag_;
+  grpc_call* call_;
+};
+
+/// A CallOpSet that does not post completions to the completion queue.
+///
+/// Allows hiding some completions that the C core must generate from
+/// C++ users.
+template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
+          class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
+          class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
+class SneakyCallOpSet : public CallOpSet<Op1, Op2, Op3, Op4, Op5, Op6> {
+ public:
+  bool FinalizeResult(void** tag, bool* status) override {
+    typedef CallOpSet<Op1, Op2, Op3, Op4, Op5, Op6> Base;
+    return Base::FinalizeResult(tag, status) && false;
+  }
+};
+
+/// Straightforward wrapping of the C call object
+class Call final {
+ public:
+  /** call is owned by the caller */
+  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(-1) {}
+
+  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+       int max_receive_message_size)
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(max_receive_message_size) {}
+
+  void PerformOps(CallOpSetInterface* ops) {
+    call_hook_->PerformOpsOnCall(ops, this);
+  }
+
+  grpc_call* call() const { return call_; }
+  CompletionQueue* cq() const { return cq_; }
+
+  int max_receive_message_size() const { return max_receive_message_size_; }
+
+ private:
+  CallHook* call_hook_;
+  CompletionQueue* cq_;
+  grpc_call* call_;
+  int max_receive_message_size_;
+};
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALL_H
diff --git a/include/grpcpp/impl/codegen/call_hook.h b/include/grpcpp/impl/codegen/call_hook.h
new file mode 100644
index 0000000..4f7d370
--- /dev/null
+++ b/include/grpcpp/impl/codegen/call_hook.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CALL_HOOK_H
+#define GRPCPP_IMPL_CODEGEN_CALL_HOOK_H
+
+namespace grpc {
+
+namespace internal {
+class CallOpSetInterface;
+class Call;
+
+/// This is an interface that Channel and Server implement to allow them to hook
+/// performing ops.
+class CallHook {
+ public:
+  virtual ~CallHook() {}
+  virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0;
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALL_HOOK_H
diff --git a/include/grpcpp/impl/codegen/channel_interface.h b/include/grpcpp/impl/codegen/channel_interface.h
new file mode 100644
index 0000000..ec1c6c2
--- /dev/null
+++ b/include/grpcpp/impl/codegen/channel_interface.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
+
+#include <grpc/impl/codegen/connectivity_state.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/time.h>
+
+namespace grpc {
+class ChannelInterface;
+class ClientContext;
+class CompletionQueue;
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+
+namespace internal {
+class Call;
+class CallOpSetInterface;
+class RpcMethod;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+template <class R>
+class ClientAsyncReaderFactory;
+template <class W>
+class ClientAsyncWriterFactory;
+template <class W, class R>
+class ClientAsyncReaderWriterFactory;
+template <class R>
+class ClientAsyncResponseReaderFactory;
+}  // namespace internal
+
+/// Codegen interface for \a grpc::Channel.
+class ChannelInterface {
+ public:
+  virtual ~ChannelInterface() {}
+  /// Get the current channel state. If the channel is in IDLE and
+  /// \a try_to_connect is set to true, try to connect.
+  virtual grpc_connectivity_state GetState(bool try_to_connect) = 0;
+
+  /// Return the \a tag on \a cq when the channel state is changed or \a
+  /// deadline expires. \a GetState needs to called to get the current state.
+  template <typename T>
+  void NotifyOnStateChange(grpc_connectivity_state last_observed, T deadline,
+                           CompletionQueue* cq, void* tag) {
+    TimePoint<T> deadline_tp(deadline);
+    NotifyOnStateChangeImpl(last_observed, deadline_tp.raw_time(), cq, tag);
+  }
+
+  /// Blocking wait for channel state change or \a deadline expiration.
+  /// \a GetState needs to called to get the current state.
+  template <typename T>
+  bool WaitForStateChange(grpc_connectivity_state last_observed, T deadline) {
+    TimePoint<T> deadline_tp(deadline);
+    return WaitForStateChangeImpl(last_observed, deadline_tp.raw_time());
+  }
+
+  /// Wait for this channel to be connected
+  template <typename T>
+  bool WaitForConnected(T deadline) {
+    grpc_connectivity_state state;
+    while ((state = GetState(true)) != GRPC_CHANNEL_READY) {
+      if (!WaitForStateChange(state, deadline)) return false;
+    }
+    return true;
+  }
+
+ private:
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::internal::ClientAsyncReaderFactory;
+  template <class W>
+  friend class ::grpc::internal::ClientAsyncWriterFactory;
+  template <class W, class R>
+  friend class ::grpc::internal::ClientAsyncReaderWriterFactory;
+  template <class R>
+  friend class ::grpc::internal::ClientAsyncResponseReaderFactory;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+  friend class ::grpc::internal::RpcMethod;
+  virtual internal::Call CreateCall(const internal::RpcMethod& method,
+                                    ClientContext* context,
+                                    CompletionQueue* cq) = 0;
+  virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                                internal::Call* call) = 0;
+  virtual void* RegisterMethod(const char* method) = 0;
+  virtual void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+                                       gpr_timespec deadline,
+                                       CompletionQueue* cq, void* tag) = 0;
+  virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                                      gpr_timespec deadline) = 0;
+};
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
diff --git a/include/grpcpp/impl/codegen/client_context.h b/include/grpcpp/impl/codegen/client_context.h
new file mode 100644
index 0000000..9dda4c7
--- /dev/null
+++ b/include/grpcpp/impl/codegen/client_context.h
@@ -0,0 +1,442 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/// A ClientContext allows the person implementing a service client to:
+///
+/// - Add custom metadata key-value pairs that will propagated to the server
+/// side.
+/// - Control call settings such as compression and authentication.
+/// - Initial and trailing metadata coming from the server.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call they are invoked with, that
+/// is to say, they aren't sticky. Some of these settings, such as the
+/// compression options, can be made persistent at channel construction time
+/// (see \a grpc::CreateCustomChannel).
+///
+/// \warning ClientContext instances should \em not be reused across rpcs.
+
+#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/propagation_bits.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/create_auth_context.h>
+#include <grpcpp/impl/codegen/metadata_map.h>
+#include <grpcpp/impl/codegen/security/auth_context.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct census_context;
+struct grpc_call;
+
+namespace grpc {
+
+class Channel;
+class ChannelInterface;
+class CompletionQueue;
+class CallCredentials;
+class ClientContext;
+
+namespace internal {
+class RpcMethod;
+class CallOpClientRecvStatus;
+class CallOpRecvInitialMetadata;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+}  // namespace internal
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+template <class R>
+class ClientAsyncReader;
+template <class W>
+class ClientAsyncWriter;
+template <class W, class R>
+class ClientAsyncReaderWriter;
+template <class R>
+class ClientAsyncResponseReader;
+class ServerContext;
+
+/// Options for \a ClientContext::FromServerContext specifying which traits from
+/// the \a ServerContext to propagate (copy) from it into a new \a
+/// ClientContext.
+///
+/// \see ClientContext::FromServerContext
+class PropagationOptions {
+ public:
+  PropagationOptions() : propagate_(GRPC_PROPAGATE_DEFAULTS) {}
+
+  PropagationOptions& enable_deadline_propagation() {
+    propagate_ |= GRPC_PROPAGATE_DEADLINE;
+    return *this;
+  }
+
+  PropagationOptions& disable_deadline_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_DEADLINE;
+    return *this;
+  }
+
+  PropagationOptions& enable_census_stats_propagation() {
+    propagate_ |= GRPC_PROPAGATE_CENSUS_STATS_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& disable_census_stats_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_CENSUS_STATS_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& enable_census_tracing_propagation() {
+    propagate_ |= GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& disable_census_tracing_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& enable_cancellation_propagation() {
+    propagate_ |= GRPC_PROPAGATE_CANCELLATION;
+    return *this;
+  }
+
+  PropagationOptions& disable_cancellation_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_CANCELLATION;
+    return *this;
+  }
+
+  uint32_t c_bitmask() const { return propagate_; }
+
+ private:
+  uint32_t propagate_;
+};
+
+namespace testing {
+class InteropClientContextInspector;
+}  // namespace testing
+
+/// A ClientContext allows the person implementing a service client to:
+///
+/// - Add custom metadata key-value pairs that will propagated to the server
+///   side.
+/// - Control call settings such as compression and authentication.
+/// - Initial and trailing metadata coming from the server.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call they are invoked with, that
+/// is to say, they aren't sticky. Some of these settings, such as the
+/// compression options, can be made persistent at channel construction time
+/// (see \a grpc::CreateCustomChannel).
+///
+/// \warning ClientContext instances should \em not be reused across rpcs.
+class ClientContext {
+ public:
+  ClientContext();
+  ~ClientContext();
+
+  /// Create a new \a ClientContext as a child of an incoming server call,
+  /// according to \a options (\see PropagationOptions).
+  ///
+  /// \param server_context The source server context to use as the basis for
+  /// constructing the client context.
+  /// \param options The options controlling what to copy from the \a
+  /// server_context.
+  ///
+  /// \return A newly constructed \a ClientContext instance based on \a
+  /// server_context, with traits propagated (copied) according to \a options.
+  static std::unique_ptr<ClientContext> FromServerContext(
+      const ServerContext& server_context,
+      PropagationOptions options = PropagationOptions());
+
+  /// Add the (\a meta_key, \a meta_value) pair to the metadata associated with
+  /// a client call. These are made available at the server side by the \a
+  /// grpc::ServerContext::client_metadata() method.
+  ///
+  /// \warning This method should only be called before invoking the rpc.
+  ///
+  /// \param meta_key The metadata key. If \a meta_value is binary data, it must
+  /// end in "-bin".
+  /// \param meta_value The metadata value. If its value is binary, the key name
+  /// must end in "-bin".
+  void AddMetadata(const grpc::string& meta_key,
+                   const grpc::string& meta_value);
+
+  /// Return a collection of initial metadata key-value pairs. Note that keys
+  /// may happen more than once (ie, a \a std::multimap is returned).
+  ///
+  /// \warning This method should only be called after initial metadata has been
+  /// received. For streaming calls, see \a
+  /// ClientReaderInterface::WaitForInitialMetadata().
+  ///
+  /// \return A multimap of initial metadata key-value pairs from the server.
+  const std::multimap<grpc::string_ref, grpc::string_ref>&
+  GetServerInitialMetadata() const {
+    GPR_CODEGEN_ASSERT(initial_metadata_received_);
+    return *recv_initial_metadata_.map();
+  }
+
+  /// Return a collection of trailing metadata key-value pairs. Note that keys
+  /// may happen more than once (ie, a \a std::multimap is returned).
+  ///
+  /// \warning This method is only callable once the stream has finished.
+  ///
+  /// \return A multimap of metadata trailing key-value pairs from the server.
+  const std::multimap<grpc::string_ref, grpc::string_ref>&
+  GetServerTrailingMetadata() const {
+    // TODO(yangg) check finished
+    return *trailing_metadata_.map();
+  }
+
+  /// Set the deadline for the client call.
+  ///
+  /// \warning This method should only be called before invoking the rpc.
+  ///
+  /// \param deadline the deadline for the client call. Units are determined by
+  /// the type used.
+  template <typename T>
+  void set_deadline(const T& deadline) {
+    TimePoint<T> deadline_tp(deadline);
+    deadline_ = deadline_tp.raw_time();
+  }
+
+  /// EXPERIMENTAL: Indicate that this request is idempotent.
+  /// By default, RPCs are assumed to <i>not</i> be idempotent.
+  ///
+  /// If true, the gRPC library assumes that it's safe to initiate
+  /// this RPC multiple times.
+  void set_idempotent(bool idempotent) { idempotent_ = idempotent; }
+
+  /// EXPERIMENTAL: Set this request to be cacheable.
+  /// If set, grpc is free to use the HTTP GET verb for sending the request,
+  /// with the possibility of receiving a cached response.
+  void set_cacheable(bool cacheable) { cacheable_ = cacheable; }
+
+  /// EXPERIMENTAL: Trigger wait-for-ready or not on this request.
+  /// See https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
+  /// If set, if an RPC is made when a channel's connectivity state is
+  /// TRANSIENT_FAILURE or CONNECTING, the call will not "fail fast",
+  /// and the channel will wait until the channel is READY before making the
+  /// call.
+  void set_wait_for_ready(bool wait_for_ready) {
+    wait_for_ready_ = wait_for_ready;
+    wait_for_ready_explicitly_set_ = true;
+  }
+
+  /// DEPRECATED: Use set_wait_for_ready() instead.
+  void set_fail_fast(bool fail_fast) { set_wait_for_ready(!fail_fast); }
+
+  /// Return the deadline for the client call.
+  std::chrono::system_clock::time_point deadline() const {
+    return Timespec2Timepoint(deadline_);
+  }
+
+  /// Return a \a gpr_timespec representation of the client call's deadline.
+  gpr_timespec raw_deadline() const { return deadline_; }
+
+  /// Set the per call authority header (see
+  /// https://tools.ietf.org/html/rfc7540#section-8.1.2.3).
+  void set_authority(const grpc::string& authority) { authority_ = authority; }
+
+  /// Return the authentication context for this client call.
+  ///
+  /// \see grpc::AuthContext.
+  std::shared_ptr<const AuthContext> auth_context() const {
+    if (auth_context_.get() == nullptr) {
+      auth_context_ = CreateAuthContext(call_);
+    }
+    return auth_context_;
+  }
+
+  /// Set credentials for the client call.
+  ///
+  /// A credentials object encapsulates all the state needed by a client to
+  /// authenticate with a server and make various assertions, e.g., about the
+  /// client’s identity, role, or whether it is authorized to make a particular
+  /// call.
+  ///
+  /// \see  https://grpc.io/docs/guides/auth.html
+  void set_credentials(const std::shared_ptr<CallCredentials>& creds) {
+    creds_ = creds;
+  }
+
+  /// Return the compression algorithm the client call will request be used.
+  /// Note that the gRPC runtime may decide to ignore this request, for example,
+  /// due to resource constraints.
+  grpc_compression_algorithm compression_algorithm() const {
+    return compression_algorithm_;
+  }
+
+  /// Set \a algorithm to be the compression algorithm used for the client call.
+  ///
+  /// \param algorithm The compression algorithm used for the client call.
+  void set_compression_algorithm(grpc_compression_algorithm algorithm);
+
+  /// Flag whether the initial metadata should be \a corked
+  ///
+  /// If \a corked is true, then the initial metadata will be coalesced with the
+  /// write of first message in the stream. As a result, any tag set for the
+  /// initial metadata operation (starting a client-streaming or bidi-streaming
+  /// RPC) will not actually be sent to the completion queue or delivered
+  /// via Next.
+  ///
+  /// \param corked The flag indicating whether the initial metadata is to be
+  /// corked or not.
+  void set_initial_metadata_corked(bool corked) {
+    initial_metadata_corked_ = corked;
+  }
+
+  /// Return the peer uri in a string.
+  ///
+  /// \warning This value is never authenticated or subject to any security
+  /// related code. It must not be used for any authentication related
+  /// functionality. Instead, use auth_context.
+  ///
+  /// \return The call's peer URI.
+  grpc::string peer() const;
+
+  /// Get and set census context.
+  void set_census_context(struct census_context* ccp) { census_context_ = ccp; }
+  struct census_context* census_context() const {
+    return census_context_;
+  }
+
+  /// Send a best-effort out-of-band cancel on the call associated with
+  /// this client context.  The call could be in any stage; e.g., if it is
+  /// already finished, it may still return success.
+  ///
+  /// There is no guarantee the call will be cancelled.
+  ///
+  /// Note that TryCancel() does not change any of the tags that are pending
+  /// on the completion queue. All pending tags will still be delivered
+  /// (though their ok result may reflect the effect of cancellation).
+  void TryCancel();
+
+  /// Global Callbacks
+  ///
+  /// Can be set exactly once per application to install hooks whenever
+  /// a client context is constructed and destructed.
+  class GlobalCallbacks {
+   public:
+    virtual ~GlobalCallbacks() {}
+    virtual void DefaultConstructor(ClientContext* context) = 0;
+    virtual void Destructor(ClientContext* context) = 0;
+  };
+  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
+
+  /// Should be used for framework-level extensions only.
+  /// Applications never need to call this method.
+  grpc_call* c_call() { return call_; }
+
+  /// EXPERIMENTAL debugging API
+  ///
+  /// if status is not ok() for an RPC, this will return a detailed string
+  /// of the gRPC Core error that led to the failure. It should not be relied
+  /// upon for anything other than gaining more debug data in failure cases.
+  grpc::string debug_error_string() const { return debug_error_string_; }
+
+ private:
+  // Disallow copy and assign.
+  ClientContext(const ClientContext&);
+  ClientContext& operator=(const ClientContext&);
+
+  friend class ::grpc::testing::InteropClientContextInspector;
+  friend class ::grpc::internal::CallOpClientRecvStatus;
+  friend class ::grpc::internal::CallOpRecvInitialMetadata;
+  friend class Channel;
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::ClientAsyncReader;
+  template <class W>
+  friend class ::grpc::ClientAsyncWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientAsyncReaderWriter;
+  template <class R>
+  friend class ::grpc::ClientAsyncResponseReader;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+
+  // Used by friend class CallOpClientRecvStatus
+  void set_debug_error_string(const grpc::string& debug_error_string) {
+    debug_error_string_ = debug_error_string;
+  }
+
+  grpc_call* call() const { return call_; }
+  void set_call(grpc_call* call, const std::shared_ptr<Channel>& channel);
+
+  uint32_t initial_metadata_flags() const {
+    return (idempotent_ ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST : 0) |
+           (wait_for_ready_ ? GRPC_INITIAL_METADATA_WAIT_FOR_READY : 0) |
+           (cacheable_ ? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST : 0) |
+           (wait_for_ready_explicitly_set_
+                ? GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET
+                : 0) |
+           (initial_metadata_corked_ ? GRPC_INITIAL_METADATA_CORKED : 0);
+  }
+
+  grpc::string authority() { return authority_; }
+
+  bool initial_metadata_received_;
+  bool wait_for_ready_;
+  bool wait_for_ready_explicitly_set_;
+  bool idempotent_;
+  bool cacheable_;
+  std::shared_ptr<Channel> channel_;
+  std::mutex mu_;
+  grpc_call* call_;
+  bool call_canceled_;
+  gpr_timespec deadline_;
+  grpc::string authority_;
+  std::shared_ptr<CallCredentials> creds_;
+  mutable std::shared_ptr<const AuthContext> auth_context_;
+  struct census_context* census_context_;
+  std::multimap<grpc::string, grpc::string> send_initial_metadata_;
+  internal::MetadataMap recv_initial_metadata_;
+  internal::MetadataMap trailing_metadata_;
+
+  grpc_call* propagate_from_call_;
+  PropagationOptions propagation_options_;
+
+  grpc_compression_algorithm compression_algorithm_;
+  bool initial_metadata_corked_;
+
+  grpc::string debug_error_string_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H
diff --git a/include/grpcpp/impl/codegen/client_unary_call.h b/include/grpcpp/impl/codegen/client_unary_call.h
new file mode 100644
index 0000000..a37a81b
--- /dev/null
+++ b/include/grpcpp/impl/codegen/client_unary_call.h
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
+#define GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class Channel;
+class ClientContext;
+class CompletionQueue;
+
+namespace internal {
+class RpcMethod;
+/// Wrapper that performs a blocking unary call
+template <class InputMessage, class OutputMessage>
+Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
+                         ClientContext* context, const InputMessage& request,
+                         OutputMessage* result) {
+  return BlockingUnaryCallImpl<InputMessage, OutputMessage>(
+             channel, method, context, request, result)
+      .status();
+}
+
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl {
+ public:
+  BlockingUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method,
+                        ClientContext* context, const InputMessage& request,
+                        OutputMessage* result) {
+    CompletionQueue cq(grpc_completion_queue_attributes{
+        GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
+        GRPC_CQ_DEFAULT_POLLING});  // Pluckable completion queue
+    Call call(channel->CreateCall(method, context, &cq));
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpRecvInitialMetadata, CallOpRecvMessage<OutputMessage>,
+              CallOpClientSendClose, CallOpClientRecvStatus>
+        ops;
+    status_ = ops.SendMessage(request);
+    if (!status_.ok()) {
+      return;
+    }
+    ops.SendInitialMetadata(context->send_initial_metadata_,
+                            context->initial_metadata_flags());
+    ops.RecvInitialMetadata(context);
+    ops.RecvMessage(result);
+    ops.AllowNoMessage();
+    ops.ClientSendClose();
+    ops.ClientRecvStatus(context, &status_);
+    call.PerformOps(&ops);
+    if (cq.Pluck(&ops)) {
+      if (!ops.got_message && status_.ok()) {
+        status_ = Status(StatusCode::UNIMPLEMENTED,
+                         "No message returned for unary request");
+      }
+    } else {
+      GPR_CODEGEN_ASSERT(!status_.ok());
+    }
+  }
+  Status status() { return status_; }
+
+ private:
+  Status status_;
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
diff --git a/include/grpcpp/impl/codegen/completion_queue.h b/include/grpcpp/impl/codegen/completion_queue.h
new file mode 100644
index 0000000..80c7c41
--- /dev/null
+++ b/include/grpcpp/impl/codegen/completion_queue.h
@@ -0,0 +1,383 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/// A completion queue implements a concurrent producer-consumer queue, with
+/// two main API-exposed methods: \a Next and \a AsyncNext. These
+/// methods are the essential component of the gRPC C++ asynchronous API.
+/// There is also a \a Shutdown method to indicate that a given completion queue
+/// will no longer have regular events. This must be called before the
+/// completion queue is destroyed.
+/// All completion queue APIs are thread-safe and may be used concurrently with
+/// any other completion queue API invocation; it is acceptable to have
+/// multiple threads calling \a Next or \a AsyncNext on the same or different
+/// completion queues, or to call these methods concurrently with a \a Shutdown
+/// elsewhere.
+/// \remark{All other API calls on completion queue should be completed before
+/// a completion queue destructor is called.}
+#ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
+#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
+
+#include <grpc/impl/codegen/atm.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct grpc_completion_queue;
+
+namespace grpc {
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+template <class R>
+class ServerReader;
+template <class W>
+class ServerWriter;
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody;
+}  // namespace internal
+
+class Channel;
+class ChannelInterface;
+class ClientContext;
+class CompletionQueue;
+class Server;
+class ServerBuilder;
+class ServerContext;
+class ServerInterface;
+
+namespace internal {
+class CompletionQueueTag;
+class RpcMethod;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler;
+class UnknownMethodHandler;
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+}  // namespace internal
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// A thin wrapper around \ref grpc_completion_queue (see \ref
+/// src/core/lib/surface/completion_queue.h).
+/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
+/// performance servers.
+class CompletionQueue : private GrpcLibraryCodegen {
+ public:
+  /// Default constructor. Implicitly creates a \a grpc_completion_queue
+  /// instance.
+  CompletionQueue()
+      : CompletionQueue(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING}) {}
+
+  /// Wrap \a take, taking ownership of the instance.
+  ///
+  /// \param take The completion queue instance to wrap. Ownership is taken.
+  explicit CompletionQueue(grpc_completion_queue* take);
+
+  /// Destructor. Destroys the owned wrapped completion queue / instance.
+  ~CompletionQueue() {
+    g_core_codegen_interface->grpc_completion_queue_destroy(cq_);
+  }
+
+  /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT.
+  enum NextStatus {
+    SHUTDOWN,   ///< The completion queue has been shutdown and fully-drained
+    GOT_EVENT,  ///< Got a new event; \a tag will be filled in with its
+                ///< associated value; \a ok indicating its success.
+    TIMEOUT     ///< deadline was reached.
+  };
+
+  /// Read from the queue, blocking until an event is available or the queue is
+  /// shutting down.
+  ///
+  /// \param tag[out] Updated to point to the read event's tag.
+  /// \param ok[out] true if read a successful event, false otherwise.
+  ///
+  /// Note that each tag sent to the completion queue (through RPC operations
+  /// or alarms) will be delivered out of the completion queue by a call to
+  /// Next (or a related method), regardless of whether the operation succeeded
+  /// or not. Success here means that this operation completed in the normal
+  /// valid manner.
+  ///
+  /// Server-side RPC request: \a ok indicates that the RPC has indeed
+  /// been started. If it is false, the server has been Shutdown
+  /// before this particular call got matched to an incoming RPC.
+  ///
+  /// Client-side StartCall/RPC invocation: \a ok indicates that the RPC is
+  /// going to go to the wire. If it is false, it not going to the wire. This
+  /// would happen if the channel is either permanently broken or
+  /// transiently broken but with the fail-fast option. (Note that async unary
+  /// RPCs don't post a CQ tag at this point, nor do client-streaming
+  /// or bidi-streaming RPCs that have the initial metadata corked option set.)
+  ///
+  /// Client-side Write, Client-side WritesDone, Server-side Write,
+  /// Server-side Finish, Server-side SendInitialMetadata (which is
+  /// typically included in Write or Finish when not done explicitly):
+  /// \a ok means that the data/metadata/status/etc is going to go to the
+  /// wire. If it is false, it not going to the wire because the call
+  /// is already dead (i.e., canceled, deadline expired, other side
+  /// dropped the channel, etc).
+  ///
+  /// Client-side Read, Server-side Read, Client-side
+  /// RecvInitialMetadata (which is typically included in Read if not
+  /// done explicitly): \a ok indicates whether there is a valid message
+  /// that got read. If not, you know that there are certainly no more
+  /// messages that can ever be read from this stream. For the client-side
+  /// operations, this only happens because the call is dead. For the
+  /// server-sider operation, though, this could happen because the client
+  /// has done a WritesDone already.
+  ///
+  /// Client-side Finish: \a ok should always be true
+  ///
+  /// Server-side AsyncNotifyWhenDone: \a ok should always be true
+  ///
+  /// Alarm: \a ok is true if it expired, false if it was canceled
+  ///
+  /// \return true if got an event, false if the queue is fully drained and
+  ///         shut down.
+  bool Next(void** tag, bool* ok) {
+    return (AsyncNextInternal(tag, ok,
+                              g_core_codegen_interface->gpr_inf_future(
+                                  GPR_CLOCK_REALTIME)) != SHUTDOWN);
+  }
+
+  /// Read from the queue, blocking up to \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param tag[out] Upon sucess, updated to point to the event's tag.
+  /// \param ok[out] Upon sucess, true if a successful event, false otherwise
+  ///        See documentation for CompletionQueue::Next for explanation of ok
+  /// \param deadline[in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T>
+  NextStatus AsyncNext(void** tag, bool* ok, const T& deadline) {
+    TimePoint<T> deadline_tp(deadline);
+    return AsyncNextInternal(tag, ok, deadline_tp.raw_time());
+  }
+
+  /// EXPERIMENTAL
+  /// First executes \a F, then reads from the queue, blocking up to
+  /// \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param F[in] Function to execute before calling AsyncNext on this queue.
+  /// \param tag[out] Upon sucess, updated to point to the event's tag.
+  /// \param ok[out] Upon sucess, true if read a regular event, false otherwise.
+  /// \param deadline[in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T, typename F>
+  NextStatus DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) {
+    CompletionQueueTLSCache cache = CompletionQueueTLSCache(this);
+    f();
+    if (cache.Flush(tag, ok)) {
+      return GOT_EVENT;
+    } else {
+      return AsyncNext(tag, ok, deadline);
+    }
+  }
+
+  /// Request the shutdown of the queue.
+  ///
+  /// \warning This method must be called at some point if this completion queue
+  /// is accessed with Next or AsyncNext. \a Next will not return false
+  /// until this method has been called and all pending tags have been drained.
+  /// (Likewise for \a AsyncNext returning \a NextStatus::SHUTDOWN .)
+  /// Only once either one of these methods does that (that is, once the queue
+  /// has been \em drained) can an instance of this class be destroyed.
+  /// Also note that applications must ensure that no work is enqueued on this
+  /// completion queue after this method is called.
+  void Shutdown();
+
+  /// Returns a \em raw pointer to the underlying \a grpc_completion_queue
+  /// instance.
+  ///
+  /// \warning Remember that the returned instance is owned. No transfer of
+  /// owership is performed.
+  grpc_completion_queue* cq() { return cq_; }
+
+ protected:
+  /// Private constructor of CompletionQueue only visible to friend classes
+  CompletionQueue(const grpc_completion_queue_attributes& attributes) {
+    cq_ = g_core_codegen_interface->grpc_completion_queue_create(
+        g_core_codegen_interface->grpc_completion_queue_factory_lookup(
+            &attributes),
+        &attributes, NULL);
+    InitialAvalanching();  // reserve this for the future shutdown
+  }
+
+ private:
+  // Friend synchronous wrappers so that they can access Pluck(), which is
+  // a semi-private API geared towards the synchronous implementation.
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::ServerReader;
+  template <class W>
+  friend class ::grpc::ServerWriter;
+  template <class W, class R>
+  friend class ::grpc::internal::ServerReaderWriterBody;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ClientStreamingHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ServerStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
+  friend class ::grpc::internal::UnknownMethodHandler;
+  friend class ::grpc::Server;
+  friend class ::grpc::ServerContext;
+  friend class ::grpc::ServerInterface;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+
+  /// EXPERIMENTAL
+  /// Creates a Thread Local cache to store the first event
+  /// On this completion queue queued from this thread.  Once
+  /// initialized, it must be flushed on the same thread.
+  class CompletionQueueTLSCache {
+   public:
+    CompletionQueueTLSCache(CompletionQueue* cq);
+    ~CompletionQueueTLSCache();
+    bool Flush(void** tag, bool* ok);
+
+   private:
+    CompletionQueue* cq_;
+    bool flushed_;
+  };
+
+  NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline);
+
+  /// Wraps \a grpc_completion_queue_pluck.
+  /// \warning Must not be mixed with calls to \a Next.
+  bool Pluck(internal::CompletionQueueTag* tag) {
+    auto deadline =
+        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME);
+    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
+        cq_, tag, deadline, nullptr);
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    GPR_CODEGEN_ASSERT(tag->FinalizeResult(&ignored, &ok));
+    GPR_CODEGEN_ASSERT(ignored == tag);
+    // Ignore mutations by FinalizeResult: Pluck returns the C API status
+    return ev.success != 0;
+  }
+
+  /// Performs a single polling pluck on \a tag.
+  /// \warning Must not be mixed with calls to \a Next.
+  ///
+  /// TODO: sreek - This calls tag->FinalizeResult() even if the cq_ is already
+  /// shutdown. This is most likely a bug and if it is a bug, then change this
+  /// implementation to simple call the other TryPluck function with a zero
+  /// timeout. i.e:
+  ///      TryPluck(tag, gpr_time_0(GPR_CLOCK_REALTIME))
+  void TryPluck(internal::CompletionQueueTag* tag) {
+    auto deadline = g_core_codegen_interface->gpr_time_0(GPR_CLOCK_REALTIME);
+    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
+        cq_, tag, deadline, nullptr);
+    if (ev.type == GRPC_QUEUE_TIMEOUT) return;
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    // the tag must be swallowed if using TryPluck
+    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
+  }
+
+  /// Performs a single polling pluck on \a tag. Calls tag->FinalizeResult if
+  /// the pluck() was successful and returned the tag.
+  ///
+  /// This exects tag->FinalizeResult (if called) to return 'false' i.e expects
+  /// that the tag is internal not something that is returned to the user.
+  void TryPluck(internal::CompletionQueueTag* tag, gpr_timespec deadline) {
+    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
+        cq_, tag, deadline, nullptr);
+    if (ev.type == GRPC_QUEUE_TIMEOUT || ev.type == GRPC_QUEUE_SHUTDOWN) {
+      return;
+    }
+
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
+  }
+
+  /// Manage state of avalanching operations : completion queue tags that
+  /// trigger other completion queue operations. The underlying core completion
+  /// queue should not really shutdown until all avalanching operations have
+  /// been finalized. Note that we maintain the requirement that an avalanche
+  /// registration must take place before CQ shutdown (which must be maintained
+  /// elsehwere)
+  void InitialAvalanching() {
+    gpr_atm_rel_store(&avalanches_in_flight_, static_cast<gpr_atm>(1));
+  }
+  void RegisterAvalanching() {
+    gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                 static_cast<gpr_atm>(1));
+  }
+  void CompleteAvalanching();
+
+  grpc_completion_queue* cq_;  // owned
+
+  gpr_atm avalanches_in_flight_;
+};
+
+/// A specific type of completion queue used by the processing of notifications
+/// by servers. Instantiated by \a ServerBuilder.
+class ServerCompletionQueue : public CompletionQueue {
+ public:
+  bool IsFrequentlyPolled() { return polling_type_ != GRPC_CQ_NON_LISTENING; }
+
+ private:
+  grpc_cq_polling_type polling_type_;
+  friend class ServerBuilder;
+  /// \param is_frequently_polled Informs the GRPC library about whether the
+  /// server completion queue would be actively polled (by calling Next() or
+  /// AsyncNext()). By default all server completion queues are assumed to be
+  /// frequently polled.
+  ServerCompletionQueue(grpc_cq_polling_type polling_type)
+      : CompletionQueue(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, polling_type}),
+        polling_type_(polling_type) {}
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
diff --git a/include/grpcpp/impl/codegen/completion_queue_tag.h b/include/grpcpp/impl/codegen/completion_queue_tag.h
new file mode 100644
index 0000000..ffb642c
--- /dev/null
+++ b/include/grpcpp/impl/codegen/completion_queue_tag.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
+#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
+
+namespace grpc {
+
+namespace internal {
+/// An interface allowing implementors to process and filter event tags.
+class CompletionQueueTag {
+ public:
+  virtual ~CompletionQueueTag() {}
+  /// Called prior to returning from Next(), return value is the status of the
+  /// operation (return status is the default thing to do). If this function
+  /// returns false, the tag is dropped and not returned from the completion
+  /// queue
+  virtual bool FinalizeResult(void** tag, bool* status) = 0;
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
diff --git a/include/grpcpp/impl/codegen/config.h b/include/grpcpp/impl/codegen/config.h
new file mode 100644
index 0000000..37f0fd1
--- /dev/null
+++ b/include/grpcpp/impl/codegen/config.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CONFIG_H
+#define GRPCPP_IMPL_CODEGEN_CONFIG_H
+
+#ifndef GRPC_CUSTOM_STRING
+#include <string>
+#define GRPC_CUSTOM_STRING std::string
+#endif
+
+/// The following macros are deprecated and appear only for users
+/// with PB files generated using gRPC 1.0.x plugins. They should
+/// not be used in new code
+#define GRPC_OVERRIDE override  // deprecated
+#define GRPC_FINAL final        // deprecated
+
+namespace grpc {
+
+typedef GRPC_CUSTOM_STRING string;
+
+using std::to_string;
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CONFIG_H
diff --git a/include/grpcpp/impl/codegen/config_protobuf.h b/include/grpcpp/impl/codegen/config_protobuf.h
new file mode 100644
index 0000000..94e593d
--- /dev/null
+++ b/include/grpcpp/impl/codegen/config_protobuf.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H
+#define GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H
+
+#define GRPC_OPEN_SOURCE_PROTO
+
+#ifndef GRPC_CUSTOM_PROTOBUF_INT64
+#include <google/protobuf/stubs/common.h>
+#define GRPC_CUSTOM_PROTOBUF_INT64 ::google::protobuf::int64
+#endif
+
+#ifndef GRPC_CUSTOM_MESSAGE
+#ifdef GRPC_USE_PROTO_LITE
+#include <google/protobuf/message_lite.h>
+#define GRPC_CUSTOM_MESSAGE ::google::protobuf::MessageLite
+#else
+#include <google/protobuf/message.h>
+#define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message
+#endif
+#endif
+
+#ifndef GRPC_CUSTOM_DESCRIPTOR
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#define GRPC_CUSTOM_DESCRIPTOR ::google::protobuf::Descriptor
+#define GRPC_CUSTOM_DESCRIPTORPOOL ::google::protobuf::DescriptorPool
+#define GRPC_CUSTOM_FIELDDESCRIPTOR ::google::protobuf::FieldDescriptor
+#define GRPC_CUSTOM_FILEDESCRIPTOR ::google::protobuf::FileDescriptor
+#define GRPC_CUSTOM_FILEDESCRIPTORPROTO ::google::protobuf::FileDescriptorProto
+#define GRPC_CUSTOM_METHODDESCRIPTOR ::google::protobuf::MethodDescriptor
+#define GRPC_CUSTOM_SERVICEDESCRIPTOR ::google::protobuf::ServiceDescriptor
+#define GRPC_CUSTOM_SOURCELOCATION ::google::protobuf::SourceLocation
+#endif
+
+#ifndef GRPC_CUSTOM_DESCRIPTORDATABASE
+#include <google/protobuf/descriptor_database.h>
+#define GRPC_CUSTOM_DESCRIPTORDATABASE ::google::protobuf::DescriptorDatabase
+#define GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE \
+  ::google::protobuf::SimpleDescriptorDatabase
+#endif
+
+#ifndef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#define GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM \
+  ::google::protobuf::io::ZeroCopyOutputStream
+#define GRPC_CUSTOM_ZEROCOPYINPUTSTREAM \
+  ::google::protobuf::io::ZeroCopyInputStream
+#define GRPC_CUSTOM_CODEDINPUTSTREAM ::google::protobuf::io::CodedInputStream
+#endif
+
+namespace grpc {
+namespace protobuf {
+
+typedef GRPC_CUSTOM_MESSAGE Message;
+typedef GRPC_CUSTOM_PROTOBUF_INT64 int64;
+
+typedef GRPC_CUSTOM_DESCRIPTOR Descriptor;
+typedef GRPC_CUSTOM_DESCRIPTORPOOL DescriptorPool;
+typedef GRPC_CUSTOM_DESCRIPTORDATABASE DescriptorDatabase;
+typedef GRPC_CUSTOM_FIELDDESCRIPTOR FieldDescriptor;
+typedef GRPC_CUSTOM_FILEDESCRIPTOR FileDescriptor;
+typedef GRPC_CUSTOM_FILEDESCRIPTORPROTO FileDescriptorProto;
+typedef GRPC_CUSTOM_METHODDESCRIPTOR MethodDescriptor;
+typedef GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor;
+typedef GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE SimpleDescriptorDatabase;
+typedef GRPC_CUSTOM_SOURCELOCATION SourceLocation;
+
+namespace io {
+typedef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM ZeroCopyOutputStream;
+typedef GRPC_CUSTOM_ZEROCOPYINPUTSTREAM ZeroCopyInputStream;
+typedef GRPC_CUSTOM_CODEDINPUTSTREAM CodedInputStream;
+}  // namespace io
+
+}  // namespace protobuf
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H
diff --git a/include/grpcpp/impl/codegen/core_codegen.h b/include/grpcpp/impl/codegen/core_codegen.h
new file mode 100644
index 0000000..0ca4ad5
--- /dev/null
+++ b/include/grpcpp/impl/codegen/core_codegen.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H
+#define GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H
+
+// This file should be compiled as part of grpcpp.
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+
+namespace grpc {
+
+/// Implementation of the core codegen interface.
+class CoreCodegen final : public CoreCodegenInterface {
+ private:
+  virtual const grpc_completion_queue_factory*
+  grpc_completion_queue_factory_lookup(
+      const grpc_completion_queue_attributes* attributes) override;
+  virtual grpc_completion_queue* grpc_completion_queue_create(
+      const grpc_completion_queue_factory* factory,
+      const grpc_completion_queue_attributes* attributes,
+      void* reserved) override;
+  grpc_completion_queue* grpc_completion_queue_create_for_next(
+      void* reserved) override;
+  grpc_completion_queue* grpc_completion_queue_create_for_pluck(
+      void* reserved) override;
+  void grpc_completion_queue_destroy(grpc_completion_queue* cq) override;
+  grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, void* tag,
+                                         gpr_timespec deadline,
+                                         void* reserved) override;
+
+  void* gpr_malloc(size_t size) override;
+  void gpr_free(void* p) override;
+
+  void grpc_init() override;
+  void grpc_shutdown() override;
+
+  void gpr_mu_init(gpr_mu* mu) override;
+  void gpr_mu_destroy(gpr_mu* mu) override;
+  void gpr_mu_lock(gpr_mu* mu) override;
+  void gpr_mu_unlock(gpr_mu* mu) override;
+  void gpr_cv_init(gpr_cv* cv) override;
+  void gpr_cv_destroy(gpr_cv* cv) override;
+  int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) override;
+  void gpr_cv_signal(gpr_cv* cv) override;
+  void gpr_cv_broadcast(gpr_cv* cv) override;
+
+  grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
+                                               grpc_status_code status,
+                                               const char* description,
+                                               void* reserved) override;
+  void grpc_call_ref(grpc_call* call) override;
+  void grpc_call_unref(grpc_call* call) override;
+  virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) override;
+
+  grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) override;
+  void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) override;
+
+  int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
+                                   grpc_byte_buffer* buffer) override;
+  void grpc_byte_buffer_reader_destroy(
+      grpc_byte_buffer_reader* reader) override;
+  int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                   grpc_slice* slice) override;
+
+  grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
+                                                size_t nslices) override;
+  grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                           void (*destroy)(void*),
+                                           void* user_data) override;
+  grpc_slice grpc_empty_slice() override;
+  grpc_slice grpc_slice_malloc(size_t length) override;
+  void grpc_slice_unref(grpc_slice slice) override;
+  grpc_slice grpc_slice_ref(grpc_slice slice) override;
+  grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) override;
+  grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) override;
+  grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) override;
+  void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice slice) override;
+  void grpc_slice_buffer_pop(grpc_slice_buffer* sb) override;
+  grpc_slice grpc_slice_from_static_buffer(const void* buffer,
+                                           size_t length) override;
+  grpc_slice grpc_slice_from_copied_buffer(const void* buffer,
+                                           size_t length) override;
+  void grpc_metadata_array_init(grpc_metadata_array* array) override;
+  void grpc_metadata_array_destroy(grpc_metadata_array* array) override;
+
+  gpr_timespec gpr_inf_future(gpr_clock_type type) override;
+  gpr_timespec gpr_time_0(gpr_clock_type type) override;
+
+  virtual const Status& ok() override;
+  virtual const Status& cancelled() override;
+
+  void assert_fail(const char* failed_assertion, const char* file,
+                   int line) override;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H
diff --git a/include/grpcpp/impl/codegen/core_codegen_interface.h b/include/grpcpp/impl/codegen/core_codegen_interface.h
new file mode 100644
index 0000000..d72f579
--- /dev/null
+++ b/include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/sync.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+/// Interface between the codegen library and the minimal subset of core
+/// features required by the generated code.
+///
+/// All undocumented methods are simply forwarding the call to their namesakes.
+/// Please refer to their corresponding documentation for details.
+///
+/// \warning This interface should be considered internal and private.
+class CoreCodegenInterface {
+ public:
+  virtual ~CoreCodegenInterface() = default;
+
+  /// Upon a failed assertion, log the error.
+  virtual void assert_fail(const char* failed_assertion, const char* file,
+                           int line) = 0;
+
+  virtual const grpc_completion_queue_factory*
+  grpc_completion_queue_factory_lookup(
+      const grpc_completion_queue_attributes* attributes) = 0;
+  virtual grpc_completion_queue* grpc_completion_queue_create(
+      const grpc_completion_queue_factory* factory,
+      const grpc_completion_queue_attributes* attributes, void* reserved) = 0;
+  virtual grpc_completion_queue* grpc_completion_queue_create_for_next(
+      void* reserved) = 0;
+  virtual grpc_completion_queue* grpc_completion_queue_create_for_pluck(
+      void* reserved) = 0;
+  virtual void grpc_completion_queue_destroy(grpc_completion_queue* cq) = 0;
+  virtual grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq,
+                                                 void* tag,
+                                                 gpr_timespec deadline,
+                                                 void* reserved) = 0;
+
+  virtual void* gpr_malloc(size_t size) = 0;
+  virtual void gpr_free(void* p) = 0;
+
+  // These are only to be used to fix edge cases involving grpc_init and
+  // grpc_shutdown. Calling grpc_init from the codegen interface before
+  // the real grpc_init is called will cause a crash, so if you use this
+  // function, ensure that it is not the first call to grpc_init.
+  virtual void grpc_init() = 0;
+  virtual void grpc_shutdown() = 0;
+
+  virtual void gpr_mu_init(gpr_mu* mu) = 0;
+  virtual void gpr_mu_destroy(gpr_mu* mu) = 0;
+  virtual void gpr_mu_lock(gpr_mu* mu) = 0;
+  virtual void gpr_mu_unlock(gpr_mu* mu) = 0;
+  virtual void gpr_cv_init(gpr_cv* cv) = 0;
+  virtual void gpr_cv_destroy(gpr_cv* cv) = 0;
+  virtual int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu,
+                          gpr_timespec abs_deadline) = 0;
+  virtual void gpr_cv_signal(gpr_cv* cv) = 0;
+  virtual void gpr_cv_broadcast(gpr_cv* cv) = 0;
+
+  virtual grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) = 0;
+  virtual void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) = 0;
+
+  virtual int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
+                                           grpc_byte_buffer* buffer)
+      GRPC_MUST_USE_RESULT = 0;
+  virtual void grpc_byte_buffer_reader_destroy(
+      grpc_byte_buffer_reader* reader) = 0;
+  virtual int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                           grpc_slice* slice) = 0;
+
+  virtual grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
+                                                        size_t nslices) = 0;
+  virtual grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                                   void (*destroy)(void*),
+                                                   void* user_data) = 0;
+  virtual grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
+                                                       grpc_status_code status,
+                                                       const char* description,
+                                                       void* reserved) = 0;
+  virtual void grpc_call_ref(grpc_call* call) = 0;
+  virtual void grpc_call_unref(grpc_call* call) = 0;
+  virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) = 0;
+  virtual grpc_slice grpc_empty_slice() = 0;
+  virtual grpc_slice grpc_slice_malloc(size_t length) = 0;
+  virtual void grpc_slice_unref(grpc_slice slice) = 0;
+  virtual grpc_slice grpc_slice_ref(grpc_slice slice) = 0;
+  virtual grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) = 0;
+  virtual grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) = 0;
+  virtual grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) = 0;
+  virtual void grpc_slice_buffer_add(grpc_slice_buffer* sb,
+                                     grpc_slice slice) = 0;
+  virtual void grpc_slice_buffer_pop(grpc_slice_buffer* sb) = 0;
+  virtual grpc_slice grpc_slice_from_static_buffer(const void* buffer,
+                                                   size_t length) = 0;
+  virtual grpc_slice grpc_slice_from_copied_buffer(const void* buffer,
+                                                   size_t length) = 0;
+
+  virtual void grpc_metadata_array_init(grpc_metadata_array* array) = 0;
+  virtual void grpc_metadata_array_destroy(grpc_metadata_array* array) = 0;
+
+  virtual const Status& ok() = 0;
+  virtual const Status& cancelled() = 0;
+
+  virtual gpr_timespec gpr_inf_future(gpr_clock_type type) = 0;
+  virtual gpr_timespec gpr_time_0(gpr_clock_type type) = 0;
+};
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// Codegen specific version of \a GPR_ASSERT.
+#define GPR_CODEGEN_ASSERT(x)                                              \
+  do {                                                                     \
+    if (!(x)) {                                                            \
+      grpc::g_core_codegen_interface->assert_fail(#x, __FILE__, __LINE__); \
+    }                                                                      \
+  } while (0)
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
diff --git a/include/grpcpp/impl/codegen/create_auth_context.h b/include/grpcpp/impl/codegen/create_auth_context.h
new file mode 100644
index 0000000..cb6095c
--- /dev/null
+++ b/include/grpcpp/impl/codegen/create_auth_context.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
+
+#include <memory>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/security/auth_context.h>
+
+namespace grpc {
+
+std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
diff --git a/include/grpcpp/impl/codegen/grpc_library.h b/include/grpcpp/impl/codegen/grpc_library.h
new file mode 100644
index 0000000..17c904d
--- /dev/null
+++ b/include/grpcpp/impl/codegen/grpc_library.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H
+#define GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H
+
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+
+namespace grpc {
+
+class GrpcLibraryInterface {
+ public:
+  virtual ~GrpcLibraryInterface() = default;
+  virtual void init() = 0;
+  virtual void shutdown() = 0;
+};
+
+/// Initialized by \a grpc::GrpcLibraryInitializer from
+/// <grpcpp/impl/grpc_library.h>
+extern GrpcLibraryInterface* g_glip;
+
+/// Classes that require gRPC to be initialized should inherit from this class.
+class GrpcLibraryCodegen {
+ public:
+  GrpcLibraryCodegen(bool call_grpc_init = true) : grpc_init_called_(false) {
+    if (call_grpc_init) {
+      GPR_CODEGEN_ASSERT(g_glip &&
+                         "gRPC library not initialized. See "
+                         "grpc::internal::GrpcLibraryInitializer.");
+      g_glip->init();
+      grpc_init_called_ = true;
+    }
+  }
+  virtual ~GrpcLibraryCodegen() {
+    if (grpc_init_called_) {
+      GPR_CODEGEN_ASSERT(g_glip &&
+                         "gRPC library not initialized. See "
+                         "grpc::internal::GrpcLibraryInitializer.");
+      g_glip->shutdown();
+    }
+  }
+
+ private:
+  bool grpc_init_called_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H
diff --git a/include/grpcpp/impl/codegen/metadata_map.h b/include/grpcpp/impl/codegen/metadata_map.h
new file mode 100644
index 0000000..0866539
--- /dev/null
+++ b/include/grpcpp/impl/codegen/metadata_map.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
+#define GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
+
+#include <grpcpp/impl/codegen/slice.h>
+
+namespace grpc {
+
+namespace internal {
+class MetadataMap {
+ public:
+  MetadataMap() { memset(&arr_, 0, sizeof(arr_)); }
+
+  ~MetadataMap() {
+    g_core_codegen_interface->grpc_metadata_array_destroy(&arr_);
+  }
+
+  void FillMap() {
+    for (size_t i = 0; i < arr_.count; i++) {
+      // TODO(yangg) handle duplicates?
+      map_.insert(std::pair<grpc::string_ref, grpc::string_ref>(
+          StringRefFromSlice(&arr_.metadata[i].key),
+          StringRefFromSlice(&arr_.metadata[i].value)));
+    }
+  }
+
+  std::multimap<grpc::string_ref, grpc::string_ref>* map() { return &map_; }
+  const std::multimap<grpc::string_ref, grpc::string_ref>* map() const {
+    return &map_;
+  }
+  grpc_metadata_array* arr() { return &arr_; }
+
+ private:
+  grpc_metadata_array arr_;
+  std::multimap<grpc::string_ref, grpc::string_ref> map_;
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
diff --git a/include/grpcpp/impl/codegen/method_handler_impl.h b/include/grpcpp/impl/codegen/method_handler_impl.h
new file mode 100644
index 0000000..27552d7
--- /dev/null
+++ b/include/grpcpp/impl/codegen/method_handler_impl.h
@@ -0,0 +1,302 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
+#define GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/codegen/sync_stream.h>
+
+namespace grpc {
+
+namespace internal {
+
+// Invoke the method handler, fill in the status, and
+// return whether or not we finished safely (without an exception).
+// Note that exception handling is 0-cost in most compiler/library
+// implementations (except when an exception is actually thrown),
+// so this process doesn't require additional overhead in the common case.
+// Additionally, we don't need to return if we caught an exception or not;
+// the handling is the same in either case.
+template <class Callable>
+Status CatchingFunctionHandler(Callable&& handler) {
+#if GRPC_ALLOW_EXCEPTIONS
+  try {
+    return handler();
+  } catch (...) {
+    return Status(StatusCode::UNKNOWN, "Unexpected error in RPC handling");
+  }
+#else   // GRPC_ALLOW_EXCEPTIONS
+  return handler();
+#endif  // GRPC_ALLOW_EXCEPTIONS
+}
+
+/// A wrapper class of an application provided rpc method handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler : public MethodHandler {
+ public:
+  RpcMethodHandler(std::function<Status(ServiceType*, ServerContext*,
+                                        const RequestType*, ResponseType*)>
+                       func,
+                   ServiceType* service)
+      : func_(func), service_(service) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    RequestType req;
+    Status status = SerializationTraits<RequestType>::Deserialize(
+        param.request.bbuf_ptr(), &req);
+    ResponseType rsp;
+    if (status.ok()) {
+      status = CatchingFunctionHandler([this, &param, &req, &rsp] {
+        return func_(service_, param.server_context, &req, &rsp);
+      });
+    }
+
+    GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_);
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        ops;
+    ops.SendInitialMetadata(param.server_context->initial_metadata_,
+                            param.server_context->initial_metadata_flags());
+    if (param.server_context->compression_level_set()) {
+      ops.set_compression_level(param.server_context->compression_level());
+    }
+    if (status.ok()) {
+      status = ops.SendMessage(rsp);
+    }
+    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    param.call->cq()->Pluck(&ops);
+  }
+
+ private:
+  /// Application provided rpc handler function.
+  std::function<Status(ServiceType*, ServerContext*, const RequestType*,
+                       ResponseType*)>
+      func_;
+  // The class the above handler function lives in.
+  ServiceType* service_;
+};
+
+/// A wrapper class of an application provided client streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler : public MethodHandler {
+ public:
+  ClientStreamingHandler(
+      std::function<Status(ServiceType*, ServerContext*,
+                           ServerReader<RequestType>*, ResponseType*)>
+          func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    ServerReader<RequestType> reader(param.call, param.server_context);
+    ResponseType rsp;
+    Status status = CatchingFunctionHandler([this, &param, &reader, &rsp] {
+      return func_(service_, param.server_context, &reader, &rsp);
+    });
+
+    GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_);
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        ops;
+    ops.SendInitialMetadata(param.server_context->initial_metadata_,
+                            param.server_context->initial_metadata_flags());
+    if (param.server_context->compression_level_set()) {
+      ops.set_compression_level(param.server_context->compression_level());
+    }
+    if (status.ok()) {
+      status = ops.SendMessage(rsp);
+    }
+    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    param.call->cq()->Pluck(&ops);
+  }
+
+ private:
+  std::function<Status(ServiceType*, ServerContext*, ServerReader<RequestType>*,
+                       ResponseType*)>
+      func_;
+  ServiceType* service_;
+};
+
+/// A wrapper class of an application provided server streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler : public MethodHandler {
+ public:
+  ServerStreamingHandler(
+      std::function<Status(ServiceType*, ServerContext*, const RequestType*,
+                           ServerWriter<ResponseType>*)>
+          func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    RequestType req;
+    Status status = SerializationTraits<RequestType>::Deserialize(
+        param.request.bbuf_ptr(), &req);
+
+    if (status.ok()) {
+      ServerWriter<ResponseType> writer(param.call, param.server_context);
+      status = CatchingFunctionHandler([this, &param, &req, &writer] {
+        return func_(service_, param.server_context, &req, &writer);
+      });
+    }
+
+    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
+    if (!param.server_context->sent_initial_metadata_) {
+      ops.SendInitialMetadata(param.server_context->initial_metadata_,
+                              param.server_context->initial_metadata_flags());
+      if (param.server_context->compression_level_set()) {
+        ops.set_compression_level(param.server_context->compression_level());
+      }
+    }
+    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    if (param.server_context->has_pending_ops_) {
+      param.call->cq()->Pluck(&param.server_context->pending_ops_);
+    }
+    param.call->cq()->Pluck(&ops);
+  }
+
+ private:
+  std::function<Status(ServiceType*, ServerContext*, const RequestType*,
+                       ServerWriter<ResponseType>*)>
+      func_;
+  ServiceType* service_;
+};
+
+/// A wrapper class of an application provided bidi-streaming handler.
+/// This also applies to server-streamed implementation of a unary method
+/// with the additional requirement that such methods must have done a
+/// write for status to be ok
+/// Since this is used by more than 1 class, the service is not passed in.
+/// Instead, it is expected to be an implicitly-captured argument of func
+/// (through bind or something along those lines)
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler : public MethodHandler {
+ public:
+  TemplatedBidiStreamingHandler(
+      std::function<Status(ServerContext*, Streamer*)> func)
+      : func_(func), write_needed_(WriteNeeded) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    Streamer stream(param.call, param.server_context);
+    Status status = CatchingFunctionHandler([this, &param, &stream] {
+      return func_(param.server_context, &stream);
+    });
+
+    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
+    if (!param.server_context->sent_initial_metadata_) {
+      ops.SendInitialMetadata(param.server_context->initial_metadata_,
+                              param.server_context->initial_metadata_flags());
+      if (param.server_context->compression_level_set()) {
+        ops.set_compression_level(param.server_context->compression_level());
+      }
+      if (write_needed_ && status.ok()) {
+        // If we needed a write but never did one, we need to mark the
+        // status as a fail
+        status = Status(StatusCode::INTERNAL,
+                        "Service did not provide response message");
+      }
+    }
+    ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    if (param.server_context->has_pending_ops_) {
+      param.call->cq()->Pluck(&param.server_context->pending_ops_);
+    }
+    param.call->cq()->Pluck(&ops);
+  }
+
+ private:
+  std::function<Status(ServerContext*, Streamer*)> func_;
+  const bool write_needed_;
+};
+
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler
+    : public TemplatedBidiStreamingHandler<
+          ServerReaderWriter<ResponseType, RequestType>, false> {
+ public:
+  BidiStreamingHandler(
+      std::function<Status(ServiceType*, ServerContext*,
+                           ServerReaderWriter<ResponseType, RequestType>*)>
+          func,
+      ServiceType* service)
+      : TemplatedBidiStreamingHandler<
+            ServerReaderWriter<ResponseType, RequestType>, false>(std::bind(
+            func, service, std::placeholders::_1, std::placeholders::_2)) {}
+};
+
+template <class RequestType, class ResponseType>
+class StreamedUnaryHandler
+    : public TemplatedBidiStreamingHandler<
+          ServerUnaryStreamer<RequestType, ResponseType>, true> {
+ public:
+  explicit StreamedUnaryHandler(
+      std::function<Status(ServerContext*,
+                           ServerUnaryStreamer<RequestType, ResponseType>*)>
+          func)
+      : TemplatedBidiStreamingHandler<
+            ServerUnaryStreamer<RequestType, ResponseType>, true>(func) {}
+};
+
+template <class RequestType, class ResponseType>
+class SplitServerStreamingHandler
+    : public TemplatedBidiStreamingHandler<
+          ServerSplitStreamer<RequestType, ResponseType>, false> {
+ public:
+  explicit SplitServerStreamingHandler(
+      std::function<Status(ServerContext*,
+                           ServerSplitStreamer<RequestType, ResponseType>*)>
+          func)
+      : TemplatedBidiStreamingHandler<
+            ServerSplitStreamer<RequestType, ResponseType>, false>(func) {}
+};
+
+/// Handle unknown method by returning UNIMPLEMENTED error.
+class UnknownMethodHandler : public MethodHandler {
+ public:
+  template <class T>
+  static void FillOps(ServerContext* context, T* ops) {
+    Status status(StatusCode::UNIMPLEMENTED, "");
+    if (!context->sent_initial_metadata_) {
+      ops->SendInitialMetadata(context->initial_metadata_,
+                               context->initial_metadata_flags());
+      if (context->compression_level_set()) {
+        ops->set_compression_level(context->compression_level());
+      }
+      context->sent_initial_metadata_ = true;
+    }
+    ops->ServerSendStatus(context->trailing_metadata_, status);
+  }
+
+  void RunHandler(const HandlerParameter& param) final {
+    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
+    FillOps(param.server_context, &ops);
+    param.call->PerformOps(&ops);
+    param.call->cq()->Pluck(&ops);
+  }
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
diff --git a/include/grpcpp/impl/codegen/proto_utils.h b/include/grpcpp/impl/codegen/proto_utils.h
new file mode 100644
index 0000000..81438ee
--- /dev/null
+++ b/include/grpcpp/impl/codegen/proto_utils.h
@@ -0,0 +1,268 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H
+#define GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H
+
+#include <type_traits>
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/config_protobuf.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+namespace internal {
+
+class GrpcBufferWriterPeer;
+
+const int kGrpcBufferWriterMaxBufferLength = 1024 * 1024;
+
+class GrpcBufferWriter : public ::grpc::protobuf::io::ZeroCopyOutputStream {
+ public:
+  GrpcBufferWriter(grpc_byte_buffer** bp, int block_size, int total_size)
+      : block_size_(block_size),
+        total_size_(total_size),
+        byte_count_(0),
+        have_backup_(false) {
+    *bp = g_core_codegen_interface->grpc_raw_byte_buffer_create(NULL, 0);
+    slice_buffer_ = &(*bp)->data.raw.slice_buffer;
+  }
+
+  ~GrpcBufferWriter() override {
+    if (have_backup_) {
+      g_core_codegen_interface->grpc_slice_unref(backup_slice_);
+    }
+  }
+
+  bool Next(void** data, int* size) override {
+    // Protobuf should not ask for more memory than total_size_.
+    GPR_CODEGEN_ASSERT(byte_count_ < total_size_);
+    size_t remain = total_size_ - byte_count_;
+    if (have_backup_) {
+      slice_ = backup_slice_;
+      have_backup_ = false;
+      if (GRPC_SLICE_LENGTH(slice_) > remain) {
+        GRPC_SLICE_SET_LENGTH(slice_, remain);
+      }
+    } else {
+      // When less than a whole block is needed, only allocate that much.
+      // But make sure the allocated slice is not inlined.
+      size_t allocate_length =
+          remain > static_cast<size_t>(block_size_) ? block_size_ : remain;
+      slice_ = g_core_codegen_interface->grpc_slice_malloc(
+          allocate_length > GRPC_SLICE_INLINED_SIZE
+              ? allocate_length
+              : GRPC_SLICE_INLINED_SIZE + 1);
+    }
+    *data = GRPC_SLICE_START_PTR(slice_);
+    // On win x64, int is only 32bit
+    GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
+    byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_);
+    g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_);
+    return true;
+  }
+
+  void BackUp(int count) override {
+    g_core_codegen_interface->grpc_slice_buffer_pop(slice_buffer_);
+    if ((size_t)count == GRPC_SLICE_LENGTH(slice_)) {
+      backup_slice_ = slice_;
+    } else {
+      backup_slice_ = g_core_codegen_interface->grpc_slice_split_tail(
+          &slice_, GRPC_SLICE_LENGTH(slice_) - count);
+      g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_);
+    }
+    // It's dangerous to keep an inlined grpc_slice as the backup slice, since
+    // on a following Next() call, a reference will be returned to this slice
+    // via GRPC_SLICE_START_PTR, which will not be an adddress held by
+    // slice_buffer_.
+    have_backup_ = backup_slice_.refcount != NULL;
+    byte_count_ -= count;
+  }
+
+  grpc::protobuf::int64 ByteCount() const override { return byte_count_; }
+
+ protected:
+  friend class GrpcBufferWriterPeer;
+  const int block_size_;
+  const int total_size_;
+  int64_t byte_count_;
+  grpc_slice_buffer* slice_buffer_;
+  bool have_backup_;
+  grpc_slice backup_slice_;
+  grpc_slice slice_;
+};
+
+class GrpcBufferReader : public ::grpc::protobuf::io::ZeroCopyInputStream {
+ public:
+  explicit GrpcBufferReader(grpc_byte_buffer* buffer)
+      : byte_count_(0), backup_count_(0), status_() {
+    if (!g_core_codegen_interface->grpc_byte_buffer_reader_init(&reader_,
+                                                                buffer)) {
+      status_ = Status(StatusCode::INTERNAL,
+                       "Couldn't initialize byte buffer reader");
+    }
+  }
+  ~GrpcBufferReader() override {
+    g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader_);
+  }
+
+  bool Next(const void** data, int* size) override {
+    if (!status_.ok()) {
+      return false;
+    }
+    if (backup_count_ > 0) {
+      *data = GRPC_SLICE_START_PTR(slice_) + GRPC_SLICE_LENGTH(slice_) -
+              backup_count_;
+      GPR_CODEGEN_ASSERT(backup_count_ <= INT_MAX);
+      *size = (int)backup_count_;
+      backup_count_ = 0;
+      return true;
+    }
+    if (!g_core_codegen_interface->grpc_byte_buffer_reader_next(&reader_,
+                                                                &slice_)) {
+      return false;
+    }
+    g_core_codegen_interface->grpc_slice_unref(slice_);
+    *data = GRPC_SLICE_START_PTR(slice_);
+    // On win x64, int is only 32bit
+    GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
+    byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_);
+    return true;
+  }
+
+  Status status() const { return status_; }
+
+  void BackUp(int count) override { backup_count_ = count; }
+
+  bool Skip(int count) override {
+    const void* data;
+    int size;
+    while (Next(&data, &size)) {
+      if (size >= count) {
+        BackUp(size - count);
+        return true;
+      }
+      // size < count;
+      count -= size;
+    }
+    // error or we have too large count;
+    return false;
+  }
+
+  grpc::protobuf::int64 ByteCount() const override {
+    return byte_count_ - backup_count_;
+  }
+
+ protected:
+  int64_t byte_count_;
+  int64_t backup_count_;
+  grpc_byte_buffer_reader reader_;
+  grpc_slice slice_;
+  Status status_;
+};
+
+// BufferWriter must be a subclass of io::ZeroCopyOutputStream.
+template <class BufferWriter, class T>
+Status GenericSerialize(const grpc::protobuf::Message& msg,
+                        grpc_byte_buffer** bp, bool* own_buffer) {
+  static_assert(
+      std::is_base_of<protobuf::io::ZeroCopyOutputStream, BufferWriter>::value,
+      "BufferWriter must be a subclass of io::ZeroCopyOutputStream");
+  *own_buffer = true;
+  int byte_size = msg.ByteSize();
+  if ((size_t)byte_size <= GRPC_SLICE_INLINED_SIZE) {
+    grpc_slice slice = g_core_codegen_interface->grpc_slice_malloc(byte_size);
+    GPR_CODEGEN_ASSERT(
+        GRPC_SLICE_END_PTR(slice) ==
+        msg.SerializeWithCachedSizesToArray(GRPC_SLICE_START_PTR(slice)));
+    *bp = g_core_codegen_interface->grpc_raw_byte_buffer_create(&slice, 1);
+    g_core_codegen_interface->grpc_slice_unref(slice);
+
+    return g_core_codegen_interface->ok();
+  }
+  BufferWriter writer(bp, kGrpcBufferWriterMaxBufferLength, byte_size);
+  return msg.SerializeToZeroCopyStream(&writer)
+             ? g_core_codegen_interface->ok()
+             : Status(StatusCode::INTERNAL, "Failed to serialize message");
+}
+
+// BufferReader must be a subclass of io::ZeroCopyInputStream.
+template <class BufferReader, class T>
+Status GenericDeserialize(grpc_byte_buffer* buffer,
+                          grpc::protobuf::Message* msg) {
+  static_assert(
+      std::is_base_of<protobuf::io::ZeroCopyInputStream, BufferReader>::value,
+      "BufferReader must be a subclass of io::ZeroCopyInputStream");
+  if (buffer == nullptr) {
+    return Status(StatusCode::INTERNAL, "No payload");
+  }
+  Status result = g_core_codegen_interface->ok();
+  {
+    BufferReader reader(buffer);
+    if (!reader.status().ok()) {
+      return reader.status();
+    }
+    ::grpc::protobuf::io::CodedInputStream decoder(&reader);
+    decoder.SetTotalBytesLimit(INT_MAX, INT_MAX);
+    if (!msg->ParseFromCodedStream(&decoder)) {
+      result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
+    }
+    if (!decoder.ConsumedEntireMessage()) {
+      result = Status(StatusCode::INTERNAL, "Did not read entire message");
+    }
+  }
+  g_core_codegen_interface->grpc_byte_buffer_destroy(buffer);
+  return result;
+}
+
+}  // namespace internal
+
+// this is needed so the following class does not conflict with protobuf
+// serializers that utilize internal-only tools.
+#ifdef GRPC_OPEN_SOURCE_PROTO
+// This class provides a protobuf serializer. It translates between protobuf
+// objects and grpc_byte_buffers. More information about SerializationTraits can
+// be found in include/grpcpp/impl/codegen/serialization_traits.h.
+template <class T>
+class SerializationTraits<T, typename std::enable_if<std::is_base_of<
+                                 grpc::protobuf::Message, T>::value>::type> {
+ public:
+  static Status Serialize(const grpc::protobuf::Message& msg,
+                          grpc_byte_buffer** bp, bool* own_buffer) {
+    return internal::GenericSerialize<internal::GrpcBufferWriter, T>(
+        msg, bp, own_buffer);
+  }
+
+  static Status Deserialize(grpc_byte_buffer* buffer,
+                            grpc::protobuf::Message* msg) {
+    return internal::GenericDeserialize<internal::GrpcBufferReader, T>(buffer,
+                                                                       msg);
+  }
+};
+#endif
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H
diff --git a/include/grpcpp/impl/codegen/rpc_method.h b/include/grpcpp/impl/codegen/rpc_method.h
new file mode 100644
index 0000000..9dcde95
--- /dev/null
+++ b/include/grpcpp/impl/codegen/rpc_method.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_RPC_METHOD_H
+#define GRPCPP_IMPL_CODEGEN_RPC_METHOD_H
+
+#include <memory>
+
+#include <grpcpp/impl/codegen/channel_interface.h>
+
+namespace grpc {
+namespace internal {
+/// Descriptor of an RPC method
+class RpcMethod {
+ public:
+  enum RpcType {
+    NORMAL_RPC = 0,
+    CLIENT_STREAMING,  // request streaming
+    SERVER_STREAMING,  // response streaming
+    BIDI_STREAMING
+  };
+
+  RpcMethod(const char* name, RpcType type)
+      : name_(name), method_type_(type), channel_tag_(NULL) {}
+
+  RpcMethod(const char* name, RpcType type,
+            const std::shared_ptr<ChannelInterface>& channel)
+      : name_(name),
+        method_type_(type),
+        channel_tag_(channel->RegisterMethod(name)) {}
+
+  const char* name() const { return name_; }
+  RpcType method_type() const { return method_type_; }
+  void SetMethodType(RpcType type) { method_type_ = type; }
+  void* channel_tag() const { return channel_tag_; }
+
+ private:
+  const char* const name_;
+  RpcType method_type_;
+  void* const channel_tag_;
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_RPC_METHOD_H
diff --git a/include/grpcpp/impl/codegen/rpc_service_method.h b/include/grpcpp/impl/codegen/rpc_service_method.h
new file mode 100644
index 0000000..dd85405
--- /dev/null
+++ b/include/grpcpp/impl/codegen/rpc_service_method.h
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
+#define GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
+
+#include <climits>
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+class ServerContext;
+
+namespace internal {
+/// Base class for running an RPC handler.
+class MethodHandler {
+ public:
+  virtual ~MethodHandler() {}
+  struct HandlerParameter {
+    HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req)
+        : call(c), server_context(context) {
+      request.set_buffer(req);
+    }
+    ~HandlerParameter() { request.Release(); }
+    Call* call;
+    ServerContext* server_context;
+    // Handler required to destroy these contents
+    ByteBuffer request;
+  };
+  virtual void RunHandler(const HandlerParameter& param) = 0;
+};
+
+/// Server side rpc method class
+class RpcServiceMethod : public RpcMethod {
+ public:
+  /// Takes ownership of the handler
+  RpcServiceMethod(const char* name, RpcMethod::RpcType type,
+                   MethodHandler* handler)
+      : RpcMethod(name, type), server_tag_(nullptr), handler_(handler) {}
+
+  void set_server_tag(void* tag) { server_tag_ = tag; }
+  void* server_tag() const { return server_tag_; }
+  /// if MethodHandler is nullptr, then this is an async method
+  MethodHandler* handler() const { return handler_.get(); }
+  void ResetHandler() { handler_.reset(); }
+  void SetHandler(MethodHandler* handler) { handler_.reset(handler); }
+
+ private:
+  void* server_tag_;
+  std::unique_ptr<MethodHandler> handler_;
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
diff --git a/include/grpcpp/impl/codegen/security/auth_context.h b/include/grpcpp/impl/codegen/security/auth_context.h
new file mode 100644
index 0000000..0e30f7c
--- /dev/null
+++ b/include/grpcpp/impl/codegen/security/auth_context.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
+
+#include <iterator>
+#include <vector>
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+struct grpc_auth_context;
+struct grpc_auth_property;
+struct grpc_auth_property_iterator;
+
+namespace grpc {
+class SecureAuthContext;
+
+typedef std::pair<grpc::string_ref, grpc::string_ref> AuthProperty;
+
+class AuthPropertyIterator
+    : public std::iterator<std::input_iterator_tag, const AuthProperty> {
+ public:
+  ~AuthPropertyIterator();
+  AuthPropertyIterator& operator++();
+  AuthPropertyIterator operator++(int);
+  bool operator==(const AuthPropertyIterator& rhs) const;
+  bool operator!=(const AuthPropertyIterator& rhs) const;
+  const AuthProperty operator*();
+
+ protected:
+  AuthPropertyIterator();
+  AuthPropertyIterator(const grpc_auth_property* property,
+                       const grpc_auth_property_iterator* iter);
+
+ private:
+  friend class SecureAuthContext;
+  const grpc_auth_property* property_;
+  // The following items form a grpc_auth_property_iterator.
+  const grpc_auth_context* ctx_;
+  size_t index_;
+  const char* name_;
+};
+
+/// Class encapsulating the Authentication Information.
+///
+/// It includes the secure identity of the peer, the type of secure transport
+/// used as well as any other properties required by the authorization layer.
+class AuthContext {
+ public:
+  virtual ~AuthContext() {}
+
+  /// Returns true if the peer is authenticated.
+  virtual bool IsPeerAuthenticated() const = 0;
+
+  /// A peer identity.
+  ///
+  /// It is, in general, comprised of one or more properties (in which case they
+  /// have the same name).
+  virtual std::vector<grpc::string_ref> GetPeerIdentity() const = 0;
+  virtual grpc::string GetPeerIdentityPropertyName() const = 0;
+
+  /// Returns all the property values with the given name.
+  virtual std::vector<grpc::string_ref> FindPropertyValues(
+      const grpc::string& name) const = 0;
+
+  /// Iteration over all the properties.
+  virtual AuthPropertyIterator begin() const = 0;
+  virtual AuthPropertyIterator end() const = 0;
+
+  /// Mutation functions: should only be used by an AuthMetadataProcessor.
+  virtual void AddProperty(const grpc::string& key,
+                           const grpc::string_ref& value) = 0;
+  virtual bool SetPeerIdentityPropertyName(const grpc::string& name) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
diff --git a/include/grpcpp/impl/codegen/serialization_traits.h b/include/grpcpp/impl/codegen/serialization_traits.h
new file mode 100644
index 0000000..8f79223
--- /dev/null
+++ b/include/grpcpp/impl/codegen/serialization_traits.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
+#define GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
+
+namespace grpc {
+
+/// Defines how to serialize and deserialize some type.
+///
+/// Used for hooking different message serialization API's into GRPC.
+/// Each SerializationTraits<Message> implementation must provide the
+/// following functions:
+/// 1.  static Status Serialize(const Message& msg,
+///                             ByteBuffer* buffer,
+///                             bool* own_buffer);
+///     OR
+///     static Status Serialize(const Message& msg,
+///                             grpc_byte_buffer** buffer,
+///                             bool* own_buffer);
+///     The former is preferred; the latter is deprecated
+///
+/// 2.  static Status Deserialize(ByteBuffer* buffer,
+///                               Message* msg);
+///     OR
+///     static Status Deserialize(grpc_byte_buffer* buffer,
+///                               Message* msg);
+///     The former is preferred; the latter is deprecated
+///
+/// Serialize is required to convert message to a ByteBuffer, and
+/// return that byte buffer through *buffer. *own_buffer should
+/// be set to true if the caller owns said byte buffer, or false if
+/// ownership is retained elsewhere.
+///
+/// Deserialize is required to convert buffer into the message stored at
+/// msg. max_receive_message_size is passed in as a bound on the maximum
+/// number of message bytes Deserialize should accept.
+///
+/// Both functions return a Status, allowing them to explain what went
+/// wrong if required.
+template <class Message,
+          class UnusedButHereForPartialTemplateSpecialization = void>
+class SerializationTraits;
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
diff --git a/include/grpcpp/impl/codegen/server_context.h b/include/grpcpp/impl/codegen/server_context.h
new file mode 100644
index 0000000..bb357d6
--- /dev/null
+++ b/include/grpcpp/impl/codegen/server_context.h
@@ -0,0 +1,309 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/impl/codegen/compression_types.h>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/create_auth_context.h>
+#include <grpcpp/impl/codegen/metadata_map.h>
+#include <grpcpp/impl/codegen/security/auth_context.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct grpc_metadata;
+struct grpc_call;
+struct census_context;
+
+namespace grpc {
+class ClientContext;
+template <class W, class R>
+class ServerAsyncReader;
+template <class W>
+class ServerAsyncWriter;
+template <class W>
+class ServerAsyncResponseWriter;
+template <class W, class R>
+class ServerAsyncReaderWriter;
+template <class R>
+class ServerReader;
+template <class W>
+class ServerWriter;
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler;
+class UnknownMethodHandler;
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler;
+class Call;
+}  // namespace internal
+
+class CompletionQueue;
+class Server;
+class ServerInterface;
+
+namespace testing {
+class InteropServerContextInspector;
+class ServerContextTestSpouse;
+}  // namespace testing
+
+/// A ServerContext allows the person implementing a service handler to:
+///
+/// - Add custom initial and trailing metadata key-value pairs that will
+///   propagated to the client side.
+/// - Control call settings such as compression and authentication.
+/// - Access metadata coming from the client.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call handler they are supplied to,
+/// that is to say, they aren't sticky across multiple calls. Some of these
+/// settings, such as the compression options, can be made persistant at server
+/// construction time by specifying the approriate \a ChannelArguments
+/// to a \a grpc::ServerBuilder, via \a ServerBuilder::AddChannelArgument.
+///
+/// \warning ServerContext instances should \em not be reused across rpcs.
+class ServerContext {
+ public:
+  ServerContext();  // for async calls
+  ~ServerContext();
+
+  /// Return the deadline for the server call.
+  std::chrono::system_clock::time_point deadline() const {
+    return Timespec2Timepoint(deadline_);
+  }
+
+  /// Return a \a gpr_timespec representation of the server call's deadline.
+  gpr_timespec raw_deadline() const { return deadline_; }
+
+  /// Add the (\a meta_key, \a meta_value) pair to the initial metadata
+  /// associated with a server call. These are made available at the client side
+  /// by the \a grpc::ClientContext::GetServerInitialMetadata() method.
+  ///
+  /// \warning This method should only be called before sending initial metadata
+  /// to the client (which can happen explicitly, or implicitly when sending a
+  /// a response message or status to the client).
+  ///
+  /// \param meta_key The metadata key. If \a meta_value is binary data, it must
+  /// end in "-bin".
+  /// \param meta_value The metadata value. If its value is binary, the key name
+  /// must end in "-bin".
+  void AddInitialMetadata(const grpc::string& key, const grpc::string& value);
+
+  /// Add the (\a meta_key, \a meta_value) pair to the initial metadata
+  /// associated with a server call. These are made available at the client
+  /// side by the \a grpc::ClientContext::GetServerTrailingMetadata() method.
+  ///
+  /// \warning This method should only be called before sending trailing
+  /// metadata to the client (which happens when the call is finished and a
+  /// status is sent to the client).
+  ///
+  /// \param meta_key The metadata key. If \a meta_value is binary data,
+  /// it must end in "-bin".
+  /// \param meta_value The metadata value. If its value is binary, the key name
+  /// must end in "-bin".
+  void AddTrailingMetadata(const grpc::string& key, const grpc::string& value);
+
+  /// IsCancelled is always safe to call when using sync API.
+  /// When using async API, it is only safe to call IsCancelled after
+  /// the AsyncNotifyWhenDone tag has been delivered.
+  bool IsCancelled() const;
+
+  /// Cancel the Call from the server. This is a best-effort API and
+  /// depending on when it is called, the RPC may still appear successful to
+  /// the client.
+  /// For example, if TryCancel() is called on a separate thread, it might race
+  /// with the server handler which might return success to the client before
+  /// TryCancel() was even started by the thread.
+  ///
+  /// It is the caller's responsibility to prevent such races and ensure that if
+  /// TryCancel() is called, the serverhandler must return Status::CANCELLED.
+  /// The only exception is that if the serverhandler is already returning an
+  /// error status code, it is ok to not return Status::CANCELLED even if
+  /// TryCancel() was called.
+  ///
+  /// Note that TryCancel() does not change any of the tags that are pending
+  /// on the completion queue. All pending tags will still be delivered
+  /// (though their ok result may reflect the effect of cancellation).
+  void TryCancel() const;
+
+  /// Return a collection of initial metadata key-value pairs sent from the
+  /// client. Note that keys may happen more than
+  /// once (ie, a \a std::multimap is returned).
+  ///
+  /// It is safe to use this method after initial metadata has been received,
+  /// Calls always begin with the client sending initial metadata, so this is
+  /// safe to access as soon as the call has begun on the server side.
+  ///
+  /// \return A multimap of initial metadata key-value pairs from the server.
+  const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata()
+      const {
+    return *client_metadata_.map();
+  }
+
+  /// Return the compression algorithm to be used by the server call.
+  grpc_compression_level compression_level() const {
+    return compression_level_;
+  }
+
+  /// Set \a algorithm to be the compression algorithm used for the server call.
+  ///
+  /// \param algorithm The compression algorithm used for the server call.
+  void set_compression_level(grpc_compression_level level) {
+    compression_level_set_ = true;
+    compression_level_ = level;
+  }
+
+  /// Return a bool indicating whether the compression level for this call
+  /// has been set (either implicitly or through a previous call to
+  /// \a set_compression_level.
+  bool compression_level_set() const { return compression_level_set_; }
+
+  /// Return the compression algorithm the server call will request be used.
+  /// Note that the gRPC runtime may decide to ignore this request, for example,
+  /// due to resource constraints, or if the server is aware the client doesn't
+  /// support the requested algorithm.
+  grpc_compression_algorithm compression_algorithm() const {
+    return compression_algorithm_;
+  }
+  /// Set \a algorithm to be the compression algorithm used for the server call.
+  ///
+  /// \param algorithm The compression algorithm used for the server call.
+  void set_compression_algorithm(grpc_compression_algorithm algorithm);
+
+  /// Set the load reporting costs in \a cost_data for the call.
+  void SetLoadReportingCosts(const std::vector<grpc::string>& cost_data);
+
+  /// Return the authentication context for this server call.
+  ///
+  /// \see grpc::AuthContext.
+  std::shared_ptr<const AuthContext> auth_context() const {
+    if (auth_context_.get() == nullptr) {
+      auth_context_ = CreateAuthContext(call_);
+    }
+    return auth_context_;
+  }
+
+  /// Return the peer uri in a string.
+  /// WARNING: this value is never authenticated or subject to any security
+  /// related code. It must not be used for any authentication related
+  /// functionality. Instead, use auth_context.
+  grpc::string peer() const;
+
+  /// Get the census context associated with this server call.
+  const struct census_context* census_context() const;
+
+  /// Async only. Has to be called before the rpc starts.
+  /// Returns the tag in completion queue when the rpc finishes.
+  /// IsCancelled() can then be called to check whether the rpc was cancelled.
+  void AsyncNotifyWhenDone(void* tag) {
+    has_notify_when_done_tag_ = true;
+    async_notify_when_done_tag_ = tag;
+  }
+
+  /// Should be used for framework-level extensions only.
+  /// Applications never need to call this method.
+  grpc_call* c_call() { return call_; }
+
+ private:
+  friend class ::grpc::testing::InteropServerContextInspector;
+  friend class ::grpc::testing::ServerContextTestSpouse;
+  friend class ::grpc::ServerInterface;
+  friend class ::grpc::Server;
+  template <class W, class R>
+  friend class ::grpc::ServerAsyncReader;
+  template <class W>
+  friend class ::grpc::ServerAsyncWriter;
+  template <class W>
+  friend class ::grpc::ServerAsyncResponseWriter;
+  template <class W, class R>
+  friend class ::grpc::ServerAsyncReaderWriter;
+  template <class R>
+  friend class ::grpc::ServerReader;
+  template <class W>
+  friend class ::grpc::ServerWriter;
+  template <class W, class R>
+  friend class ::grpc::internal::ServerReaderWriterBody;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ClientStreamingHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ServerStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
+  friend class ::grpc::internal::UnknownMethodHandler;
+  friend class ::grpc::ClientContext;
+
+  /// Prevent copying.
+  ServerContext(const ServerContext&);
+  ServerContext& operator=(const ServerContext&);
+
+  class CompletionOp;
+
+  void BeginCompletionOp(internal::Call* call);
+  /// Return the tag queued by BeginCompletionOp()
+  internal::CompletionQueueTag* GetCompletionOpTag();
+
+  ServerContext(gpr_timespec deadline, grpc_metadata_array* arr);
+
+  void set_call(grpc_call* call) { call_ = call; }
+
+  uint32_t initial_metadata_flags() const { return 0; }
+
+  CompletionOp* completion_op_;
+  bool has_notify_when_done_tag_;
+  void* async_notify_when_done_tag_;
+
+  gpr_timespec deadline_;
+  grpc_call* call_;
+  CompletionQueue* cq_;
+  bool sent_initial_metadata_;
+  mutable std::shared_ptr<const AuthContext> auth_context_;
+  internal::MetadataMap client_metadata_;
+  std::multimap<grpc::string, grpc::string> initial_metadata_;
+  std::multimap<grpc::string, grpc::string> trailing_metadata_;
+
+  bool compression_level_set_;
+  grpc_compression_level compression_level_;
+  grpc_compression_algorithm compression_algorithm_;
+
+  internal::CallOpSet<internal::CallOpSendInitialMetadata,
+                      internal::CallOpSendMessage>
+      pending_ops_;
+  bool has_pending_ops_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H
diff --git a/include/grpcpp/impl/codegen/server_interface.h b/include/grpcpp/impl/codegen/server_interface.h
new file mode 100644
index 0000000..4700763
--- /dev/null
+++ b/include/grpcpp/impl/codegen/server_interface.h
@@ -0,0 +1,274 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/call_hook.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+
+namespace grpc {
+
+class AsyncGenericService;
+class Channel;
+class GenericServerContext;
+class ServerCompletionQueue;
+class ServerContext;
+class ServerCredentials;
+class Service;
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// Models a gRPC server.
+///
+/// Servers are configured and started via \a grpc::ServerBuilder.
+namespace internal {
+class ServerAsyncStreamingInterface;
+}  // namespace internal
+
+class ServerInterface : public internal::CallHook {
+ public:
+  virtual ~ServerInterface() {}
+
+  /// Shutdown the server, blocking until all rpc processing finishes.
+  /// Forcefully terminate pending calls after \a deadline expires.
+  ///
+  /// All completion queue associated with the server (for example, for async
+  /// serving) must be shutdown *after* this method has returned:
+  /// See \a ServerBuilder::AddCompletionQueue for details.
+  ///
+  /// \param deadline How long to wait until pending rpcs are forcefully
+  /// terminated.
+  template <class T>
+  void Shutdown(const T& deadline) {
+    ShutdownInternal(TimePoint<T>(deadline).raw_time());
+  }
+
+  /// Shutdown the server, waiting for all rpc processing to finish.
+  ///
+  /// All completion queue associated with the server (for example, for async
+  /// serving) must be shutdown *after* this method has returned:
+  /// See \a ServerBuilder::AddCompletionQueue for details.
+  void Shutdown() {
+    ShutdownInternal(
+        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_MONOTONIC));
+  }
+
+  /// Block waiting for all work to complete.
+  ///
+  /// \warning The server must be either shutting down or some other thread must
+  /// call \a Shutdown for this function to ever return.
+  virtual void Wait() = 0;
+
+ protected:
+  friend class ::grpc::Service;
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the Server instance.
+  virtual bool RegisterService(const grpc::string* host, Service* service) = 0;
+
+  /// Register a generic service. This call does not take ownership of the
+  /// service. The service must exist for the lifetime of the Server instance.
+  virtual void RegisterAsyncGenericService(AsyncGenericService* service) = 0;
+
+  /// Tries to bind \a server to the given \a addr.
+  ///
+  /// It can be invoked multiple times.
+  ///
+  /// \param addr The address to try to bind to the server (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.).
+  /// \params creds The credentials associated with the server.
+  ///
+  /// \return bound port number on sucess, 0 on failure.
+  ///
+  /// \warning It's an error to call this method on an already started server.
+  virtual int AddListeningPort(const grpc::string& addr,
+                               ServerCredentials* creds) = 0;
+
+  /// Start the server.
+  ///
+  /// \param cqs Completion queues for handling asynchronous services. The
+  /// caller is required to keep all completion queues live until the server is
+  /// destroyed.
+  /// \param num_cqs How many completion queues does \a cqs hold.
+  virtual void Start(ServerCompletionQueue** cqs, size_t num_cqs) = 0;
+
+  virtual void ShutdownInternal(gpr_timespec deadline) = 0;
+
+  virtual int max_receive_message_size() const = 0;
+
+  virtual grpc_server* server() = 0;
+
+  virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                                internal::Call* call) = 0;
+
+  class BaseAsyncRequest : public internal::CompletionQueueTag {
+   public:
+    BaseAsyncRequest(ServerInterface* server, ServerContext* context,
+                     internal::ServerAsyncStreamingInterface* stream,
+                     CompletionQueue* call_cq, void* tag,
+                     bool delete_on_finalize);
+    virtual ~BaseAsyncRequest();
+
+    bool FinalizeResult(void** tag, bool* status) override;
+
+   protected:
+    ServerInterface* const server_;
+    ServerContext* const context_;
+    internal::ServerAsyncStreamingInterface* const stream_;
+    CompletionQueue* const call_cq_;
+    void* const tag_;
+    const bool delete_on_finalize_;
+    grpc_call* call_;
+  };
+
+  class RegisteredAsyncRequest : public BaseAsyncRequest {
+   public:
+    RegisteredAsyncRequest(ServerInterface* server, ServerContext* context,
+                           internal::ServerAsyncStreamingInterface* stream,
+                           CompletionQueue* call_cq, void* tag);
+
+    // uses BaseAsyncRequest::FinalizeResult
+
+   protected:
+    void IssueRequest(void* registered_method, grpc_byte_buffer** payload,
+                      ServerCompletionQueue* notification_cq);
+  };
+
+  class NoPayloadAsyncRequest final : public RegisteredAsyncRequest {
+   public:
+    NoPayloadAsyncRequest(void* registered_method, ServerInterface* server,
+                          ServerContext* context,
+                          internal::ServerAsyncStreamingInterface* stream,
+                          CompletionQueue* call_cq,
+                          ServerCompletionQueue* notification_cq, void* tag)
+        : RegisteredAsyncRequest(server, context, stream, call_cq, tag) {
+      IssueRequest(registered_method, nullptr, notification_cq);
+    }
+
+    // uses RegisteredAsyncRequest::FinalizeResult
+  };
+
+  template <class Message>
+  class PayloadAsyncRequest final : public RegisteredAsyncRequest {
+   public:
+    PayloadAsyncRequest(void* registered_method, ServerInterface* server,
+                        ServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag,
+                        Message* request)
+        : RegisteredAsyncRequest(server, context, stream, call_cq, tag),
+          registered_method_(registered_method),
+          server_(server),
+          context_(context),
+          stream_(stream),
+          call_cq_(call_cq),
+          notification_cq_(notification_cq),
+          tag_(tag),
+          request_(request) {
+      IssueRequest(registered_method, &payload_, notification_cq);
+    }
+
+    bool FinalizeResult(void** tag, bool* status) override {
+      if (*status) {
+        if (payload_ == nullptr ||
+            !SerializationTraits<Message>::Deserialize(payload_, request_)
+                 .ok()) {
+          // If deserialization fails, we cancel the call and instantiate
+          // a new instance of ourselves to request another call.  We then
+          // return false, which prevents the call from being returned to
+          // the application.
+          g_core_codegen_interface->grpc_call_cancel_with_status(
+              call_, GRPC_STATUS_INTERNAL, "Unable to parse request", nullptr);
+          g_core_codegen_interface->grpc_call_unref(call_);
+          new PayloadAsyncRequest(registered_method_, server_, context_,
+                                  stream_, call_cq_, notification_cq_, tag_,
+                                  request_);
+          delete this;
+          return false;
+        }
+      }
+      return RegisteredAsyncRequest::FinalizeResult(tag, status);
+    }
+
+   private:
+    void* const registered_method_;
+    ServerInterface* const server_;
+    ServerContext* const context_;
+    internal::ServerAsyncStreamingInterface* const stream_;
+    CompletionQueue* const call_cq_;
+    ServerCompletionQueue* const notification_cq_;
+    void* const tag_;
+    Message* const request_;
+    grpc_byte_buffer* payload_;
+  };
+
+  class GenericAsyncRequest : public BaseAsyncRequest {
+   public:
+    GenericAsyncRequest(ServerInterface* server, GenericServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag,
+                        bool delete_on_finalize);
+
+    bool FinalizeResult(void** tag, bool* status) override;
+
+   private:
+    grpc_call_details call_details_;
+  };
+
+  template <class Message>
+  void RequestAsyncCall(internal::RpcServiceMethod* method,
+                        ServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag,
+                        Message* message) {
+    GPR_CODEGEN_ASSERT(method);
+    new PayloadAsyncRequest<Message>(method->server_tag(), this, context,
+                                     stream, call_cq, notification_cq, tag,
+                                     message);
+  }
+
+  void RequestAsyncCall(internal::RpcServiceMethod* method,
+                        ServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag) {
+    GPR_CODEGEN_ASSERT(method);
+    new NoPayloadAsyncRequest(method->server_tag(), this, context, stream,
+                              call_cq, notification_cq, tag);
+  }
+
+  void RequestAsyncGenericCall(GenericServerContext* context,
+                               internal::ServerAsyncStreamingInterface* stream,
+                               CompletionQueue* call_cq,
+                               ServerCompletionQueue* notification_cq,
+                               void* tag) {
+    new GenericAsyncRequest(this, context, stream, call_cq, notification_cq,
+                            tag, true);
+  }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H
diff --git a/include/grpcpp/impl/codegen/service_type.h b/include/grpcpp/impl/codegen/service_type.h
new file mode 100644
index 0000000..a576f66
--- /dev/null
+++ b/include/grpcpp/impl/codegen/service_type.h
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H
+#define GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+class Server;
+class ServerInterface;
+class ServerCompletionQueue;
+class ServerContext;
+
+namespace internal {
+class Call;
+class ServerAsyncStreamingInterface {
+ public:
+  virtual ~ServerAsyncStreamingInterface() {}
+
+  /// Request notification of the sending of initial metadata to the client.
+  /// Completion will be notified by \a tag on the associated completion
+  /// queue. This call is optional, but if it is used, it cannot be used
+  /// concurrently with or after the \a Finish method.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  virtual void SendInitialMetadata(void* tag) = 0;
+
+ private:
+  friend class ::grpc::ServerInterface;
+  virtual void BindCall(Call* call) = 0;
+};
+}  // namespace internal
+
+/// Desriptor of an RPC service and its various RPC methods
+class Service {
+ public:
+  Service() : server_(nullptr) {}
+  virtual ~Service() {}
+
+  bool has_async_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (*it && (*it)->handler() == nullptr) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool has_synchronous_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (*it && (*it)->handler() != nullptr) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool has_generic_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (it->get() == nullptr) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+ protected:
+  template <class Message>
+  void RequestAsyncUnary(int index, ServerContext* context, Message* request,
+                         internal::ServerAsyncStreamingInterface* stream,
+                         CompletionQueue* call_cq,
+                         ServerCompletionQueue* notification_cq, void* tag) {
+    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
+                              notification_cq, tag, request);
+  }
+  void RequestAsyncClientStreaming(
+      int index, ServerContext* context,
+      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+      ServerCompletionQueue* notification_cq, void* tag) {
+    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
+                              notification_cq, tag);
+  }
+  template <class Message>
+  void RequestAsyncServerStreaming(
+      int index, ServerContext* context, Message* request,
+      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+      ServerCompletionQueue* notification_cq, void* tag) {
+    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
+                              notification_cq, tag, request);
+  }
+  void RequestAsyncBidiStreaming(
+      int index, ServerContext* context,
+      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+      ServerCompletionQueue* notification_cq, void* tag) {
+    server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq,
+                              notification_cq, tag);
+  }
+
+  void AddMethod(internal::RpcServiceMethod* method) {
+    methods_.emplace_back(method);
+  }
+
+  void MarkMethodAsync(int index) {
+    GPR_CODEGEN_ASSERT(
+        methods_[index].get() != nullptr &&
+        "Cannot mark the method as 'async' because it has already been "
+        "marked as 'generic'.");
+    methods_[index]->ResetHandler();
+  }
+
+  void MarkMethodGeneric(int index) {
+    GPR_CODEGEN_ASSERT(
+        methods_[index]->handler() != nullptr &&
+        "Cannot mark the method as 'generic' because it has already been "
+        "marked as 'async'.");
+    methods_[index].reset();
+  }
+
+  void MarkMethodStreamed(int index, internal::MethodHandler* streamed_method) {
+    GPR_CODEGEN_ASSERT(methods_[index] && methods_[index]->handler() &&
+                       "Cannot mark an async or generic method Streamed");
+    methods_[index]->SetHandler(streamed_method);
+
+    // From the server's point of view, streamed unary is a special
+    // case of BIDI_STREAMING that has 1 read and 1 write, in that order,
+    // and split server-side streaming is BIDI_STREAMING with 1 read and
+    // any number of writes, in that order.
+    methods_[index]->SetMethodType(internal::RpcMethod::BIDI_STREAMING);
+  }
+
+ private:
+  friend class Server;
+  friend class ServerInterface;
+  ServerInterface* server_;
+  std::vector<std::unique_ptr<internal::RpcServiceMethod>> methods_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H
diff --git a/include/grpcpp/impl/codegen/slice.h b/include/grpcpp/impl/codegen/slice.h
new file mode 100644
index 0000000..fcccd4b
--- /dev/null
+++ b/include/grpcpp/impl/codegen/slice.h
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SLICE_H
+#define GRPCPP_IMPL_CODEGEN_SLICE_H
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#include <grpc/impl/codegen/slice.h>
+
+namespace grpc {
+
+/// A wrapper around \a grpc_slice.
+///
+/// A slice represents a contiguous reference counted array of bytes.
+/// It is cheap to take references to a slice, and it is cheap to create a
+/// slice pointing to a subset of another slice.
+class Slice final {
+ public:
+  /// Construct an empty slice.
+  Slice();
+  /// Destructor - drops one reference.
+  ~Slice();
+
+  enum AddRef { ADD_REF };
+  /// Construct a slice from \a slice, adding a reference.
+  Slice(grpc_slice slice, AddRef);
+
+  enum StealRef { STEAL_REF };
+  /// Construct a slice from \a slice, stealing a reference.
+  Slice(grpc_slice slice, StealRef);
+
+  /// Allocate a slice of specified size
+  Slice(size_t len);
+
+  /// Construct a slice from a copied buffer
+  Slice(const void* buf, size_t len);
+
+  /// Construct a slice from a copied string
+  Slice(const grpc::string& str);
+
+  enum StaticSlice { STATIC_SLICE };
+
+  /// Construct a slice from a static buffer
+  Slice(const void* buf, size_t len, StaticSlice);
+
+  /// Copy constructor, adds a reference.
+  Slice(const Slice& other);
+
+  /// Assignment, reference count is unchanged.
+  Slice& operator=(Slice other) {
+    std::swap(slice_, other.slice_);
+    return *this;
+  }
+
+  /// Create a slice pointing at some data. Calls malloc to allocate a refcount
+  /// for the object, and arranges that destroy will be called with the
+  /// user data pointer passed in at destruction. Can be the same as buf or
+  /// different (e.g., if data is part of a larger structure that must be
+  /// destroyed when the data is no longer needed)
+  Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data);
+
+  /// Specialization of above for common case where buf == user_data
+  Slice(void* buf, size_t len, void (*destroy)(void*))
+      : Slice(buf, len, destroy, buf) {}
+
+  /// Similar to the above but has a destroy that also takes slice length
+  Slice(void* buf, size_t len, void (*destroy)(void*, size_t));
+
+  /// Byte size.
+  size_t size() const { return GRPC_SLICE_LENGTH(slice_); }
+
+  /// Raw pointer to the beginning (first element) of the slice.
+  const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); }
+
+  /// Raw pointer to the end (one byte \em past the last element) of the slice.
+  const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
+
+  /// Raw C slice. Caller needs to call grpc_slice_unref when done.
+  grpc_slice c_slice() const;
+
+ private:
+  friend class ByteBuffer;
+
+  grpc_slice slice_;
+};
+
+inline grpc::string_ref StringRefFromSlice(const grpc_slice* slice) {
+  return grpc::string_ref(
+      reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(*slice)),
+      GRPC_SLICE_LENGTH(*slice));
+}
+
+inline grpc::string StringFromCopiedSlice(grpc_slice slice) {
+  return grpc::string(reinterpret_cast<char*>(GRPC_SLICE_START_PTR(slice)),
+                      GRPC_SLICE_LENGTH(slice));
+}
+
+inline grpc_slice SliceReferencingString(const grpc::string& str) {
+  return g_core_codegen_interface->grpc_slice_from_static_buffer(str.data(),
+                                                                 str.length());
+}
+
+inline grpc_slice SliceFromCopiedString(const grpc::string& str) {
+  return g_core_codegen_interface->grpc_slice_from_copied_buffer(str.data(),
+                                                                 str.length());
+}
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SLICE_H
diff --git a/include/grpcpp/impl/codegen/status.h b/include/grpcpp/impl/codegen/status.h
new file mode 100644
index 0000000..9f409eb
--- /dev/null
+++ b/include/grpcpp/impl/codegen/status.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_STATUS_H
+#define GRPCPP_IMPL_CODEGEN_STATUS_H
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/status_code_enum.h>
+
+namespace grpc {
+
+/// Did it work? If it didn't, why?
+///
+/// See \a grpc::StatusCode for details on the available code and their meaning.
+class Status {
+ public:
+  /// Construct an OK instance.
+  Status() : code_(StatusCode::OK) {}
+
+  /// Construct an instance with associated \a code and \a error_message.
+  /// It is an error to construct an OK status with non-empty \a error_message.
+  Status(StatusCode code, const grpc::string& error_message)
+      : code_(code), error_message_(error_message) {}
+
+  /// Construct an instance with \a code,  \a error_message and
+  /// \a error_details. It is an error to construct an OK status with non-empty
+  /// \a error_message and/or \a error_details.
+  Status(StatusCode code, const grpc::string& error_message,
+         const grpc::string& error_details)
+      : code_(code),
+        error_message_(error_message),
+        binary_error_details_(error_details) {}
+
+  // Pre-defined special status objects.
+  /// An OK pre-defined instance.
+  static const Status& OK;
+  /// A CANCELLED pre-defined instance.
+  static const Status& CANCELLED;
+
+  /// Return the instance's error code.
+  StatusCode error_code() const { return code_; }
+  /// Return the instance's error message.
+  grpc::string error_message() const { return error_message_; }
+  /// Return the (binary) error details.
+  // Usually it contains a serialized google.rpc.Status proto.
+  grpc::string error_details() const { return binary_error_details_; }
+
+  /// Is the status OK?
+  bool ok() const { return code_ == StatusCode::OK; }
+
+  // Ignores any errors. This method does nothing except potentially suppress
+  // complaints from any tools that are checking that errors are not dropped on
+  // the floor.
+  void IgnoreError() const {}
+
+ private:
+  StatusCode code_;
+  grpc::string error_message_;
+  grpc::string binary_error_details_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STATUS_H
diff --git a/include/grpcpp/impl/codegen/status_code_enum.h b/include/grpcpp/impl/codegen/status_code_enum.h
new file mode 100644
index 0000000..09943f1
--- /dev/null
+++ b/include/grpcpp/impl/codegen/status_code_enum.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H
+#define GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H
+
+namespace grpc {
+
+enum StatusCode {
+  /// Not an error; returned on success.
+  OK = 0,
+
+  /// The operation was cancelled (typically by the caller).
+  CANCELLED = 1,
+
+  /// Unknown error. An example of where this error may be returned is if a
+  /// Status value received from another address space belongs to an error-space
+  /// that is not known in this address space. Also errors raised by APIs that
+  /// do not return enough error information may be converted to this error.
+  UNKNOWN = 2,
+
+  /// Client specified an invalid argument. Note that this differs from
+  /// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
+  /// problematic regardless of the state of the system (e.g., a malformed file
+  /// name).
+  INVALID_ARGUMENT = 3,
+
+  /// Deadline expired before operation could complete. For operations that
+  /// change the state of the system, this error may be returned even if the
+  /// operation has completed successfully. For example, a successful response
+  /// from a server could have been delayed long enough for the deadline to
+  /// expire.
+  DEADLINE_EXCEEDED = 4,
+
+  /// Some requested entity (e.g., file or directory) was not found.
+  NOT_FOUND = 5,
+
+  /// Some entity that we attempted to create (e.g., file or directory) already
+  /// exists.
+  ALREADY_EXISTS = 6,
+
+  /// The caller does not have permission to execute the specified operation.
+  /// PERMISSION_DENIED must not be used for rejections caused by exhausting
+  /// some resource (use RESOURCE_EXHAUSTED instead for those errors).
+  /// PERMISSION_DENIED must not be used if the caller can not be identified
+  /// (use UNAUTHENTICATED instead for those errors).
+  PERMISSION_DENIED = 7,
+
+  /// The request does not have valid authentication credentials for the
+  /// operation.
+  UNAUTHENTICATED = 16,
+
+  /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
+  /// entire file system is out of space.
+  RESOURCE_EXHAUSTED = 8,
+
+  /// Operation was rejected because the system is not in a state required for
+  /// the operation's execution. For example, directory to be deleted may be
+  /// non-empty, an rmdir operation is applied to a non-directory, etc.
+  ///
+  /// A litmus test that may help a service implementor in deciding
+  /// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+  ///  (a) Use UNAVAILABLE if the client can retry just the failing call.
+  ///  (b) Use ABORTED if the client should retry at a higher-level
+  ///      (e.g., restarting a read-modify-write sequence).
+  ///  (c) Use FAILED_PRECONDITION if the client should not retry until
+  ///      the system state has been explicitly fixed. E.g., if an "rmdir"
+  ///      fails because the directory is non-empty, FAILED_PRECONDITION
+  ///      should be returned since the client should not retry unless
+  ///      they have first fixed up the directory by deleting files from it.
+  ///  (d) Use FAILED_PRECONDITION if the client performs conditional
+  ///      REST Get/Update/Delete on a resource and the resource on the
+  ///      server does not match the condition. E.g., conflicting
+  ///      read-modify-write on the same resource.
+  FAILED_PRECONDITION = 9,
+
+  /// The operation was aborted, typically due to a concurrency issue like
+  /// sequencer check failures, transaction aborts, etc.
+  ///
+  /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
+  /// and UNAVAILABLE.
+  ABORTED = 10,
+
+  /// Operation was attempted past the valid range. E.g., seeking or reading
+  /// past end of file.
+  ///
+  /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed
+  /// if the system state changes. For example, a 32-bit file system will
+  /// generate INVALID_ARGUMENT if asked to read at an offset that is not in the
+  /// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from
+  /// an offset past the current file size.
+  ///
+  /// There is a fair bit of overlap between FAILED_PRECONDITION and
+  /// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
+  /// when it applies so that callers who are iterating through a space can
+  /// easily look for an OUT_OF_RANGE error to detect when they are done.
+  OUT_OF_RANGE = 11,
+
+  /// Operation is not implemented or not supported/enabled in this service.
+  UNIMPLEMENTED = 12,
+
+  /// Internal errors. Means some invariants expected by underlying System has
+  /// been broken. If you see one of these errors, Something is very broken.
+  INTERNAL = 13,
+
+  /// The service is currently unavailable. This is a most likely a transient
+  /// condition and may be corrected by retrying with a backoff.
+  ///
+  /// \warning Although data MIGHT not have been transmitted when this
+  /// status occurs, there is NOT A GUARANTEE that the server has not seen
+  /// anything. So in general it is unsafe to retry on this status code
+  /// if the call is non-idempotent.
+  ///
+  /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
+  /// and UNAVAILABLE.
+  UNAVAILABLE = 14,
+
+  /// Unrecoverable data loss or corruption.
+  DATA_LOSS = 15,
+
+  /// Force users to include a default branch:
+  DO_NOT_USE = -1
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H
diff --git a/include/grpcpp/impl/codegen/string_ref.h b/include/grpcpp/impl/codegen/string_ref.h
new file mode 100644
index 0000000..5d55fc4
--- /dev/null
+++ b/include/grpcpp/impl/codegen/string_ref.h
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_STRING_REF_H
+#define GRPCPP_IMPL_CODEGEN_STRING_REF_H
+
+#include <string.h>
+
+#include <algorithm>
+#include <iosfwd>
+#include <iostream>
+#include <iterator>
+
+#include <grpcpp/impl/codegen/config.h>
+
+namespace grpc {
+
+/// This class is a non owning reference to a string.
+///
+/// It should be a strict subset of the upcoming std::string_ref.
+///
+/// \see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html
+///
+/// The constexpr is dropped or replaced with const for legacy compiler
+/// compatibility.
+class string_ref {
+ public:
+  /// types
+  typedef const char* const_iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+  /// constants
+  const static size_t npos;
+
+  /// construct/copy.
+  string_ref() : data_(nullptr), length_(0) {}
+  string_ref(const string_ref& other)
+      : data_(other.data_), length_(other.length_) {}
+  string_ref& operator=(const string_ref& rhs) {
+    data_ = rhs.data_;
+    length_ = rhs.length_;
+    return *this;
+  }
+
+  string_ref(const char* s) : data_(s), length_(strlen(s)) {}
+  string_ref(const char* s, size_t l) : data_(s), length_(l) {}
+  string_ref(const grpc::string& s) : data_(s.data()), length_(s.length()) {}
+
+  /// iterators
+  const_iterator begin() const { return data_; }
+  const_iterator end() const { return data_ + length_; }
+  const_iterator cbegin() const { return data_; }
+  const_iterator cend() const { return data_ + length_; }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+  const_reverse_iterator crbegin() const {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator crend() const {
+    return const_reverse_iterator(begin());
+  }
+
+  /// capacity
+  size_t size() const { return length_; }
+  size_t length() const { return length_; }
+  size_t max_size() const { return length_; }
+  bool empty() const { return length_ == 0; }
+
+  /// element access
+  const char* data() const { return data_; }
+
+  /// string operations
+  int compare(string_ref x) const {
+    size_t min_size = length_ < x.length_ ? length_ : x.length_;
+    int r = memcmp(data_, x.data_, min_size);
+    if (r < 0) return -1;
+    if (r > 0) return 1;
+    if (length_ < x.length_) return -1;
+    if (length_ > x.length_) return 1;
+    return 0;
+  }
+
+  bool starts_with(string_ref x) const {
+    return length_ >= x.length_ && (memcmp(data_, x.data_, x.length_) == 0);
+  }
+
+  bool ends_with(string_ref x) const {
+    return length_ >= x.length_ &&
+           (memcmp(data_ + (length_ - x.length_), x.data_, x.length_) == 0);
+  }
+
+  size_t find(string_ref s) const {
+    auto it = std::search(cbegin(), cend(), s.cbegin(), s.cend());
+    return it == cend() ? npos : std::distance(cbegin(), it);
+  }
+
+  size_t find(char c) const {
+    auto it = std::find(cbegin(), cend(), c);
+    return it == cend() ? npos : std::distance(cbegin(), it);
+  }
+
+  string_ref substr(size_t pos, size_t n = npos) const {
+    if (pos > length_) pos = length_;
+    if (n > (length_ - pos)) n = length_ - pos;
+    return string_ref(data_ + pos, n);
+  }
+
+ private:
+  const char* data_;
+  size_t length_;
+};
+
+/// Comparison operators
+inline bool operator==(string_ref x, string_ref y) { return x.compare(y) == 0; }
+inline bool operator!=(string_ref x, string_ref y) { return x.compare(y) != 0; }
+inline bool operator<(string_ref x, string_ref y) { return x.compare(y) < 0; }
+inline bool operator<=(string_ref x, string_ref y) { return x.compare(y) <= 0; }
+inline bool operator>(string_ref x, string_ref y) { return x.compare(y) > 0; }
+inline bool operator>=(string_ref x, string_ref y) { return x.compare(y) >= 0; }
+
+inline std::ostream& operator<<(std::ostream& out, const string_ref& string) {
+  return out << grpc::string(string.begin(), string.end());
+}
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STRING_REF_H
diff --git a/include/grpcpp/impl/codegen/stub_options.h b/include/grpcpp/impl/codegen/stub_options.h
new file mode 100644
index 0000000..a56695a
--- /dev/null
+++ b/include/grpcpp/impl/codegen/stub_options.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H
+#define GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H
+
+namespace grpc {
+
+/// Useful interface for generated stubs
+class StubOptions {};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H
diff --git a/include/grpcpp/impl/codegen/sync_stream.h b/include/grpcpp/impl/codegen/sync_stream.h
new file mode 100644
index 0000000..7152eaf
--- /dev/null
+++ b/include/grpcpp/impl/codegen/sync_stream.h
@@ -0,0 +1,934 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H
+#define GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/completion_queue.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+namespace internal {
+/// Common interface for all synchronous client side streaming.
+class ClientStreamingInterface {
+ public:
+  virtual ~ClientStreamingInterface() {}
+
+  /// Block waiting until the stream finishes and a final status of the call is
+  /// available.
+  ///
+  /// It is appropriate to call this method when both:
+  ///   * the calling code (client-side) has no more message to send
+  ///     (this can be declared implicitly by calling this method, or
+  ///     explicitly through an earlier call to <i>WritesDone</i> method of the
+  ///     class in use, e.g. \a ClientWriterInterface::WritesDone or
+  ///     \a ClientReaderWriterInterface::WritesDone).
+  ///   * there are no more messages to be received from the server (which can
+  ///     be known implicitly, or explicitly from an earlier call to \a
+  ///     ReaderInterface::Read that returned "false").
+  ///
+  /// This function will return either:
+  /// - when all incoming messages have been read and the server has
+  ///   returned status.
+  /// - when the server has returned a non-OK status.
+  /// - OR when the call failed for some reason and the library generated a
+  ///   status.
+  ///
+  /// Return values:
+  ///   - \a Status contains the status code, message and details for the call
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible trailing metadata sent from the server.
+  virtual Status Finish() = 0;
+};
+
+/// Common interface for all synchronous server side streaming.
+class ServerStreamingInterface {
+ public:
+  virtual ~ServerStreamingInterface() {}
+
+  /// Block to send initial metadata to client.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a Finish method.
+  ///
+  /// The initial metadata that will be sent to the client will be
+  /// taken from the \a ServerContext associated with the call.
+  virtual void SendInitialMetadata() = 0;
+};
+
+/// An interface that yields a sequence of messages of type \a R.
+template <class R>
+class ReaderInterface {
+ public:
+  virtual ~ReaderInterface() {}
+
+  /// Get an upper bound on the next message size available for reading on this
+  /// stream.
+  virtual bool NextMessageSize(uint32_t* sz) = 0;
+
+  /// Block to read a message and parse to \a msg. Returns \a true on success.
+  /// This is thread-safe with respect to \a Write or \WritesDone methods on
+  /// the same stream. It should not be called concurrently with another \a
+  /// Read on the same stream as the order of delivery will not be defined.
+  ///
+  /// \param[out] msg The read message.
+  ///
+  /// \return \a false when there will be no more incoming messages, either
+  /// because the other side has called \a WritesDone() or the stream has failed
+  /// (or been cancelled).
+  virtual bool Read(R* msg) = 0;
+};
+
+/// An interface that can be fed a sequence of messages of type \a W.
+template <class W>
+class WriterInterface {
+ public:
+  virtual ~WriterInterface() {}
+
+  /// Block to write \a msg to the stream with WriteOptions \a options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  /// \param options The WriteOptions affecting the write operation.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  virtual bool Write(const W& msg, WriteOptions options) = 0;
+
+  /// Block to write \a msg to the stream with default write options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  inline bool Write(const W& msg) { return Write(msg, WriteOptions()); }
+
+  /// Write \a msg and coalesce it with the writing of trailing metadata, using
+  /// WriteOptions \a options.
+  ///
+  /// For client, WriteLast is equivalent of performing Write and WritesDone in
+  /// a single step. \a msg and trailing metadata are coalesced and sent on wire
+  /// by calling this function. For server, WriteLast buffers the \a msg.
+  /// The writing of \a msg is held until the service handler returns,
+  /// where \a msg and trailing metadata are coalesced and sent on wire.
+  /// Note that WriteLast can only buffer \a msg up to the flow control window
+  /// size. If \a msg size is larger than the window size, it will be sent on
+  /// wire without buffering.
+  ///
+  /// \param[in] msg The message to be written to the stream.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  void WriteLast(const W& msg, WriteOptions options) {
+    Write(msg, options.set_last_message());
+  }
+};
+
+}  // namespace internal
+
+/// Client-side interface for streaming reads of message of type \a R.
+template <class R>
+class ClientReaderInterface : public internal::ClientStreamingInterface,
+                              public internal::ReaderInterface<R> {
+ public:
+  /// Block to wait for initial metadata from server. The received metadata
+  /// can only be accessed after this call returns. Should only be called before
+  /// the first read. Calling this method is optional, and if it is not called
+  /// the metadata will be available in ClientContext after the first read.
+  virtual void WaitForInitialMetadata() = 0;
+};
+
+namespace internal {
+template <class R>
+class ClientReaderFactory {
+ public:
+  template <class W>
+  static ClientReader<R>* Create(ChannelInterface* channel,
+                                 const ::grpc::internal::RpcMethod& method,
+                                 ClientContext* context, const W& request) {
+    return new ClientReader<R>(channel, method, context, request);
+  }
+};
+}  // namespace internal
+
+/// Synchronous (blocking) client-side API for doing server-streaming RPCs,
+/// where the stream of messages coming from the server has messages
+/// of type \a R.
+template <class R>
+class ClientReader final : public ClientReaderInterface<R> {
+ public:
+  /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
+  /// semantics.
+  ///
+  //  Side effect:
+  ///   Once complete, the initial metadata read from
+  ///   the server will be accessable through the \a ClientContext used to
+  ///   construct this object.
+  void WaitForInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+        ops;
+    ops.RecvInitialMetadata(context_);
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);  /// status ignored
+  }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    *sz = call_.max_receive_message_size();
+    return true;
+  }
+
+  /// See the \a ReaderInterface.Read method for semantics.
+  /// Side effect:
+  ///   This also receives initial metadata from the server, if not
+  ///   already received (if initial metadata is received, it can be then
+  ///   accessed through the \a ClientContext associated with this call).
+  bool Read(R* msg) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                                ::grpc::internal::CallOpRecvMessage<R>>
+        ops;
+    if (!context_->initial_metadata_received_) {
+      ops.RecvInitialMetadata(context_);
+    }
+    ops.RecvMessage(msg);
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops) && ops.got_message;
+  }
+
+  /// See the \a ClientStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   The \a ClientContext associated with this call is updated with
+  ///   possible metadata received from the server.
+  Status Finish() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientRecvStatus> ops;
+    Status status;
+    ops.ClientRecvStatus(context_, &status);
+    call_.PerformOps(&ops);
+    GPR_CODEGEN_ASSERT(cq_.Pluck(&ops));
+    return status;
+  }
+
+ private:
+  friend class internal::ClientReaderFactory<R>;
+  ClientContext* context_;
+  CompletionQueue cq_;
+  ::grpc::internal::Call call_;
+
+  /// Block to create a stream and write the initial metadata and \a request
+  /// out. Note that \a context will be used to fill in custom initial
+  /// metadata used to send to the server when starting the call.
+  template <class W>
+  ClientReader(::grpc::ChannelInterface* channel,
+               const ::grpc::internal::RpcMethod& method,
+               ClientContext* context, const W& request)
+      : context_(context),
+        cq_(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
+            GRPC_CQ_DEFAULT_POLLING}),  // Pluckable cq
+        call_(channel->CreateCall(method, context, &cq_)) {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                ::grpc::internal::CallOpSendMessage,
+                                ::grpc::internal::CallOpClientSendClose>
+        ops;
+    ops.SendInitialMetadata(context->send_initial_metadata_,
+                            context->initial_metadata_flags());
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(ops.SendMessage(request).ok());
+    ops.ClientSendClose();
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);
+  }
+};
+
+/// Client-side interface for streaming writes of message type \a W.
+template <class W>
+class ClientWriterInterface : public internal::ClientStreamingInterface,
+                              public internal::WriterInterface<W> {
+ public:
+  /// Half close writing from the client. (signal that the stream of messages
+  /// coming from the client is complete).
+  /// Blocks until currently-pending writes are completed.
+  /// Thread safe with respect to \a ReaderInterface::Read operations only
+  ///
+  /// \return Whether the writes were successful.
+  virtual bool WritesDone() = 0;
+};
+
+namespace internal {
+template <class W>
+class ClientWriterFactory {
+ public:
+  template <class R>
+  static ClientWriter<W>* Create(::grpc::ChannelInterface* channel,
+                                 const ::grpc::internal::RpcMethod& method,
+                                 ClientContext* context, R* response) {
+    return new ClientWriter<W>(channel, method, context, response);
+  }
+};
+}  // namespace internal
+
+/// Synchronous (blocking) client-side API for doing client-streaming RPCs,
+/// where the outgoing message stream coming from the client has messages of
+/// type \a W.
+template <class W>
+class ClientWriter : public ClientWriterInterface<W> {
+ public:
+  /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
+  /// semantics.
+  ///
+  //  Side effect:
+  ///   Once complete, the initial metadata read from the server will be
+  ///   accessable through the \a ClientContext used to construct this object.
+  void WaitForInitialMetadata() {
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+        ops;
+    ops.RecvInitialMetadata(context_);
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);  // status ignored
+  }
+
+  /// See the WriterInterface.Write(const W& msg, WriteOptions options) method
+  /// for semantics.
+  ///
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the
+  ///   \a ClientContext associated with this call).
+  using ::grpc::internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                ::grpc::internal::CallOpSendMessage,
+                                ::grpc::internal::CallOpClientSendClose>
+        ops;
+
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      ops.ClientSendClose();
+    }
+    if (context_->initial_metadata_corked_) {
+      ops.SendInitialMetadata(context_->send_initial_metadata_,
+                              context_->initial_metadata_flags());
+      context_->set_initial_metadata_corked(false);
+    }
+    if (!ops.SendMessage(msg, options).ok()) {
+      return false;
+    }
+
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  bool WritesDone() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops;
+    ops.ClientSendClose();
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  /// See the ClientStreamingInterface.Finish method for semantics.
+  /// Side effects:
+  ///   - Also receives initial metadata if not already received.
+  ///   - Attempts to fill in the \a response parameter passed
+  ///     to the constructor of this instance with the response
+  ///     message from the server.
+  Status Finish() override {
+    Status status;
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, &status);
+    call_.PerformOps(&finish_ops_);
+    GPR_CODEGEN_ASSERT(cq_.Pluck(&finish_ops_));
+    return status;
+  }
+
+ private:
+  friend class internal::ClientWriterFactory<W>;
+
+  /// Block to create a stream (i.e. send request headers and other initial
+  /// metadata to the server). Note that \a context will be used to fill
+  /// in custom initial metadata. \a response will be filled in with the
+  /// single expected response message from the server upon a successful
+  /// call to the \a Finish method of this instance.
+  template <class R>
+  ClientWriter(ChannelInterface* channel,
+               const ::grpc::internal::RpcMethod& method,
+               ClientContext* context, R* response)
+      : context_(context),
+        cq_(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
+            GRPC_CQ_DEFAULT_POLLING}),  // Pluckable cq
+        call_(channel->CreateCall(method, context, &cq_)) {
+    finish_ops_.RecvMessage(response);
+    finish_ops_.AllowNoMessage();
+
+    if (!context_->initial_metadata_corked_) {
+      ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+          ops;
+      ops.SendInitialMetadata(context->send_initial_metadata_,
+                              context->initial_metadata_flags());
+      call_.PerformOps(&ops);
+      cq_.Pluck(&ops);
+    }
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpGenericRecvMessage,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+  CompletionQueue cq_;
+  ::grpc::internal::Call call_;
+};
+
+/// Client-side interface for bi-directional streaming with
+/// client-to-server stream messages of type \a W and
+/// server-to-client stream messages of type \a R.
+template <class W, class R>
+class ClientReaderWriterInterface : public internal::ClientStreamingInterface,
+                                    public internal::WriterInterface<W>,
+                                    public internal::ReaderInterface<R> {
+ public:
+  /// Block to wait for initial metadata from server. The received metadata
+  /// can only be accessed after this call returns. Should only be called before
+  /// the first read. Calling this method is optional, and if it is not called
+  /// the metadata will be available in ClientContext after the first read.
+  virtual void WaitForInitialMetadata() = 0;
+
+  /// Half close writing from the client. (signal that the stream of messages
+  /// coming from the clinet is complete).
+  /// Blocks until currently-pending writes are completed.
+  /// Thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \return Whether the writes were successful.
+  virtual bool WritesDone() = 0;
+};
+
+namespace internal {
+template <class W, class R>
+class ClientReaderWriterFactory {
+ public:
+  static ClientReaderWriter<W, R>* Create(
+      ::grpc::ChannelInterface* channel,
+      const ::grpc::internal::RpcMethod& method, ClientContext* context) {
+    return new ClientReaderWriter<W, R>(channel, method, context);
+  }
+};
+}  // namespace internal
+
+/// Synchronous (blocking) client-side API for bi-directional streaming RPCs,
+/// where the outgoing message stream coming from the client has messages of
+/// type \a W, and the incoming messages stream coming from the server has
+/// messages of type \a R.
+template <class W, class R>
+class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
+ public:
+  /// Block waiting to read initial metadata from the server.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a Finish method.
+  ///
+  /// Once complete, the initial metadata read from the server will be
+  /// accessable through the \a ClientContext used to construct this object.
+  void WaitForInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+        ops;
+    ops.RecvInitialMetadata(context_);
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);  // status ignored
+  }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    *sz = call_.max_receive_message_size();
+    return true;
+  }
+
+  /// See the \a ReaderInterface.Read method for semantics.
+  /// Side effect:
+  ///   Also receives initial metadata if not already received (updates the \a
+  ///   ClientContext associated with this call in that case).
+  bool Read(R* msg) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                                ::grpc::internal::CallOpRecvMessage<R>>
+        ops;
+    if (!context_->initial_metadata_received_) {
+      ops.RecvInitialMetadata(context_);
+    }
+    ops.RecvMessage(msg);
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops) && ops.got_message;
+  }
+
+  /// See the \a WriterInterface.Write method for semantics.
+  ///
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the
+  ///   \a ClientContext associated with this call to fill in values).
+  using ::grpc::internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                ::grpc::internal::CallOpSendMessage,
+                                ::grpc::internal::CallOpClientSendClose>
+        ops;
+
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      ops.ClientSendClose();
+    }
+    if (context_->initial_metadata_corked_) {
+      ops.SendInitialMetadata(context_->send_initial_metadata_,
+                              context_->initial_metadata_flags());
+      context_->set_initial_metadata_corked(false);
+    }
+    if (!ops.SendMessage(msg, options).ok()) {
+      return false;
+    }
+
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  bool WritesDone() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops;
+    ops.ClientSendClose();
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  /// See the ClientStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible trailing metadata sent from the server.
+  Status Finish() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                                ::grpc::internal::CallOpClientRecvStatus>
+        ops;
+    if (!context_->initial_metadata_received_) {
+      ops.RecvInitialMetadata(context_);
+    }
+    Status status;
+    ops.ClientRecvStatus(context_, &status);
+    call_.PerformOps(&ops);
+    GPR_CODEGEN_ASSERT(cq_.Pluck(&ops));
+    return status;
+  }
+
+ private:
+  friend class internal::ClientReaderWriterFactory<W, R>;
+
+  ClientContext* context_;
+  CompletionQueue cq_;
+  ::grpc::internal::Call call_;
+
+  /// Block to create a stream and write the initial metadata and \a request
+  /// out. Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  ClientReaderWriter(::grpc::ChannelInterface* channel,
+                     const ::grpc::internal::RpcMethod& method,
+                     ClientContext* context)
+      : context_(context),
+        cq_(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK,
+            GRPC_CQ_DEFAULT_POLLING}),  // Pluckable cq
+        call_(channel->CreateCall(method, context, &cq_)) {
+    if (!context_->initial_metadata_corked_) {
+      ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+          ops;
+      ops.SendInitialMetadata(context->send_initial_metadata_,
+                              context->initial_metadata_flags());
+      call_.PerformOps(&ops);
+      cq_.Pluck(&ops);
+    }
+  }
+};
+
+/// Server-side interface for streaming reads of message of type \a R.
+template <class R>
+class ServerReaderInterface : public internal::ServerStreamingInterface,
+                              public internal::ReaderInterface<R> {};
+
+/// Synchronous (blocking) server-side API for doing client-streaming RPCs,
+/// where the incoming message stream coming from the client has messages of
+/// type \a R.
+template <class R>
+class ServerReader final : public ServerReaderInterface<R> {
+ public:
+  /// See the \a ServerStreamingInterface.SendInitialMetadata method
+  /// for semantics. Note that initial metadata will be affected by the
+  /// \a ServerContext associated with this call.
+  void SendInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
+    ops.SendInitialMetadata(ctx_->initial_metadata_,
+                            ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_->PerformOps(&ops);
+    call_->cq()->Pluck(&ops);
+  }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    *sz = call_->max_receive_message_size();
+    return true;
+  }
+
+  bool Read(R* msg) override {
+    internal::CallOpSet<internal::CallOpRecvMessage<R>> ops;
+    ops.RecvMessage(msg);
+    call_->PerformOps(&ops);
+    return call_->cq()->Pluck(&ops) && ops.got_message;
+  }
+
+ private:
+  internal::Call* const call_;
+  ServerContext* const ctx_;
+
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::ClientStreamingHandler;
+
+  ServerReader(internal::Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
+};
+
+/// Server-side interface for streaming writes of message of type \a W.
+template <class W>
+class ServerWriterInterface : public internal::ServerStreamingInterface,
+                              public internal::WriterInterface<W> {};
+
+/// Synchronous (blocking) server-side API for doing for doing a
+/// server-streaming RPCs, where the outgoing message stream coming from the
+/// server has messages of type \a W.
+template <class W>
+class ServerWriter final : public ServerWriterInterface<W> {
+ public:
+  /// See the \a ServerStreamingInterface.SendInitialMetadata method
+  /// for semantics.
+  /// Note that initial metadata will be affected by the
+  /// \a ServerContext associated with this call.
+  void SendInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
+    ops.SendInitialMetadata(ctx_->initial_metadata_,
+                            ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_->PerformOps(&ops);
+    call_->cq()->Pluck(&ops);
+  }
+
+  /// See the \a WriterInterface.Write method for semantics.
+  ///
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the
+  ///   \a ClientContext associated with this call to fill in values).
+  using internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+
+    if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) {
+      return false;
+    }
+    if (!ctx_->sent_initial_metadata_) {
+      ctx_->pending_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                             ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    call_->PerformOps(&ctx_->pending_ops_);
+    // if this is the last message we defer the pluck until AFTER we start
+    // the trailing md op. This prevents hangs. See
+    // https://github.com/grpc/grpc/issues/11546
+    if (options.is_last_message()) {
+      ctx_->has_pending_ops_ = true;
+      return true;
+    }
+    ctx_->has_pending_ops_ = false;
+    return call_->cq()->Pluck(&ctx_->pending_ops_);
+  }
+
+ private:
+  internal::Call* const call_;
+  ServerContext* const ctx_;
+
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::ServerStreamingHandler;
+
+  ServerWriter(internal::Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
+};
+
+/// Server-side interface for bi-directional streaming.
+template <class W, class R>
+class ServerReaderWriterInterface : public internal::ServerStreamingInterface,
+                                    public internal::WriterInterface<W>,
+                                    public internal::ReaderInterface<R> {};
+
+/// Actual implementation of bi-directional streaming
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody final {
+ public:
+  ServerReaderWriterBody(Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
+
+  void SendInitialMetadata() {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    CallOpSet<CallOpSendInitialMetadata> ops;
+    ops.SendInitialMetadata(ctx_->initial_metadata_,
+                            ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_->PerformOps(&ops);
+    call_->cq()->Pluck(&ops);
+  }
+
+  bool NextMessageSize(uint32_t* sz) {
+    *sz = call_->max_receive_message_size();
+    return true;
+  }
+
+  bool Read(R* msg) {
+    CallOpSet<CallOpRecvMessage<R>> ops;
+    ops.RecvMessage(msg);
+    call_->PerformOps(&ops);
+    return call_->cq()->Pluck(&ops) && ops.got_message;
+  }
+
+  bool Write(const W& msg, WriteOptions options) {
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+    if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) {
+      return false;
+    }
+    if (!ctx_->sent_initial_metadata_) {
+      ctx_->pending_ops_.SendInitialMetadata(ctx_->initial_metadata_,
+                                             ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    call_->PerformOps(&ctx_->pending_ops_);
+    // if this is the last message we defer the pluck until AFTER we start
+    // the trailing md op. This prevents hangs. See
+    // https://github.com/grpc/grpc/issues/11546
+    if (options.is_last_message()) {
+      ctx_->has_pending_ops_ = true;
+      return true;
+    }
+    ctx_->has_pending_ops_ = false;
+    return call_->cq()->Pluck(&ctx_->pending_ops_);
+  }
+
+ private:
+  Call* const call_;
+  ServerContext* const ctx_;
+};
+
+}  // namespace internal
+
+/// Synchronous (blocking) server-side API for a bidirectional
+/// streaming call, where the incoming message stream coming from the client has
+/// messages of type \a R, and the outgoing message streaming coming from
+/// the server has messages of type \a W.
+template <class W, class R>
+class ServerReaderWriter final : public ServerReaderWriterInterface<W, R> {
+ public:
+  /// See the \a ServerStreamingInterface.SendInitialMetadata method
+  /// for semantics. Note that initial metadata will be affected by the
+  /// \a ServerContext associated with this call.
+  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    return body_.NextMessageSize(sz);
+  }
+
+  bool Read(R* msg) override { return body_.Read(msg); }
+
+  /// See the \a WriterInterface.Write(const W& msg, WriteOptions options)
+  /// method for semantics.
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the \a
+  ///   ServerContext associated with this call).
+  using internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    return body_.Write(msg, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<W, R> body_;
+
+  friend class internal::TemplatedBidiStreamingHandler<ServerReaderWriter<W, R>,
+                                                       false>;
+  ServerReaderWriter(internal::Call* call, ServerContext* ctx)
+      : body_(call, ctx) {}
+};
+
+/// A class to represent a flow-controlled unary call. This is something
+/// of a hybrid between conventional unary and streaming. This is invoked
+/// through a unary call on the client side, but the server responds to it
+/// as though it were a single-ping-pong streaming call. The server can use
+/// the \a NextMessageSize method to determine an upper-bound on the size of
+/// the message. A key difference relative to streaming: ServerUnaryStreamer
+/// must have exactly 1 Read and exactly 1 Write, in that order, to function
+/// correctly. Otherwise, the RPC is in error.
+template <class RequestType, class ResponseType>
+class ServerUnaryStreamer final
+    : public ServerReaderWriterInterface<ResponseType, RequestType> {
+ public:
+  /// Block to send initial metadata to client.
+  /// Implicit input parameter:
+  ///    - the \a ServerContext associated with this call will be used for
+  ///      sending initial metadata.
+  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
+
+  /// Get an upper bound on the request message size from the client.
+  bool NextMessageSize(uint32_t* sz) override {
+    return body_.NextMessageSize(sz);
+  }
+
+  /// Read a message of type \a R into \a msg. Completion will be notified by \a
+  /// tag on the associated completion queue.
+  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
+  /// should not be called concurrently with other streaming APIs
+  /// on the same stream. It is not meaningful to call it concurrently
+  /// with another \a ReaderInterface::Read on the same stream since reads on
+  /// the same stream are delivered in order.
+  ///
+  /// \param[out] msg Where to eventually store the read message.
+  /// \param[in] tag The tag identifying the operation.
+  bool Read(RequestType* request) override {
+    if (read_done_) {
+      return false;
+    }
+    read_done_ = true;
+    return body_.Read(request);
+  }
+
+  /// Block to write \a msg to the stream with WriteOptions \a options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  /// \param options The WriteOptions affecting the write operation.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  using internal::WriterInterface<ResponseType>::Write;
+  bool Write(const ResponseType& response, WriteOptions options) override {
+    if (write_done_ || !read_done_) {
+      return false;
+    }
+    write_done_ = true;
+    return body_.Write(response, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
+  bool read_done_;
+  bool write_done_;
+
+  friend class internal::TemplatedBidiStreamingHandler<
+      ServerUnaryStreamer<RequestType, ResponseType>, true>;
+  ServerUnaryStreamer(internal::Call* call, ServerContext* ctx)
+      : body_(call, ctx), read_done_(false), write_done_(false) {}
+};
+
+/// A class to represent a flow-controlled server-side streaming call.
+/// This is something of a hybrid between server-side and bidi streaming.
+/// This is invoked through a server-side streaming call on the client side,
+/// but the server responds to it as though it were a bidi streaming call that
+/// must first have exactly 1 Read and then any number of Writes.
+template <class RequestType, class ResponseType>
+class ServerSplitStreamer final
+    : public ServerReaderWriterInterface<ResponseType, RequestType> {
+ public:
+  /// Block to send initial metadata to client.
+  /// Implicit input parameter:
+  ///    - the \a ServerContext associated with this call will be used for
+  ///      sending initial metadata.
+  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
+
+  /// Get an upper bound on the request message size from the client.
+  bool NextMessageSize(uint32_t* sz) override {
+    return body_.NextMessageSize(sz);
+  }
+
+  /// Read a message of type \a R into \a msg. Completion will be notified by \a
+  /// tag on the associated completion queue.
+  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
+  /// should not be called concurrently with other streaming APIs
+  /// on the same stream. It is not meaningful to call it concurrently
+  /// with another \a ReaderInterface::Read on the same stream since reads on
+  /// the same stream are delivered in order.
+  ///
+  /// \param[out] msg Where to eventually store the read message.
+  /// \param[in] tag The tag identifying the operation.
+  bool Read(RequestType* request) override {
+    if (read_done_) {
+      return false;
+    }
+    read_done_ = true;
+    return body_.Read(request);
+  }
+
+  /// Block to write \a msg to the stream with WriteOptions \a options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  /// \param options The WriteOptions affecting the write operation.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  using internal::WriterInterface<ResponseType>::Write;
+  bool Write(const ResponseType& response, WriteOptions options) override {
+    return read_done_ && body_.Write(response, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
+  bool read_done_;
+
+  friend class internal::TemplatedBidiStreamingHandler<
+      ServerSplitStreamer<RequestType, ResponseType>, false>;
+  ServerSplitStreamer(internal::Call* call, ServerContext* ctx)
+      : body_(call, ctx), read_done_(false) {}
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H
diff --git a/include/grpcpp/impl/codegen/time.h b/include/grpcpp/impl/codegen/time.h
new file mode 100644
index 0000000..c32f254
--- /dev/null
+++ b/include/grpcpp/impl/codegen/time.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_CODEGEN_TIME_H
+#define GRPCPP_IMPL_CODEGEN_TIME_H
+
+#include <chrono>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/config.h>
+
+namespace grpc {
+
+/** If you are trying to use CompletionQueue::AsyncNext with a time class that
+    isn't either gpr_timespec or std::chrono::system_clock::time_point, you
+    will most likely be looking at this comment as your compiler will have
+    fired an error below. In order to fix this issue, you have two potential
+    solutions:
+
+      1. Use gpr_timespec or std::chrono::system_clock::time_point instead
+      2. Specialize the TimePoint class with whichever time class that you
+         want to use here. See below for two examples of how to do this.
+ */
+template <typename T>
+class TimePoint {
+ public:
+  TimePoint(const T& time) { you_need_a_specialization_of_TimePoint(); }
+  gpr_timespec raw_time() {
+    gpr_timespec t;
+    return t;
+  }
+
+ private:
+  void you_need_a_specialization_of_TimePoint();
+};
+
+template <>
+class TimePoint<gpr_timespec> {
+ public:
+  TimePoint(const gpr_timespec& time) : time_(time) {}
+  gpr_timespec raw_time() { return time_; }
+
+ private:
+  gpr_timespec time_;
+};
+
+}  // namespace grpc
+
+namespace grpc {
+
+// from and to should be absolute time.
+void Timepoint2Timespec(const std::chrono::system_clock::time_point& from,
+                        gpr_timespec* to);
+void TimepointHR2Timespec(
+    const std::chrono::high_resolution_clock::time_point& from,
+    gpr_timespec* to);
+
+std::chrono::system_clock::time_point Timespec2Timepoint(gpr_timespec t);
+
+template <>
+class TimePoint<std::chrono::system_clock::time_point> {
+ public:
+  TimePoint(const std::chrono::system_clock::time_point& time) {
+    Timepoint2Timespec(time, &time_);
+  }
+  gpr_timespec raw_time() const { return time_; }
+
+ private:
+  gpr_timespec time_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_TIME_H
diff --git a/include/grpcpp/impl/grpc_library.h b/include/grpcpp/impl/grpc_library.h
new file mode 100644
index 0000000..d1f3ff1
--- /dev/null
+++ b/include/grpcpp/impl/grpc_library.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_GRPC_LIBRARY_H
+#define GRPCPP_IMPL_GRPC_LIBRARY_H
+
+#include <iostream>
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+namespace grpc {
+
+namespace internal {
+class GrpcLibrary final : public GrpcLibraryInterface {
+ public:
+  void init() override { grpc_init(); }
+  void shutdown() override { grpc_shutdown(); }
+};
+
+static GrpcLibrary g_gli;
+static CoreCodegen g_core_codegen;
+
+/// Instantiating this class ensures the proper initialization of gRPC.
+class GrpcLibraryInitializer final {
+ public:
+  GrpcLibraryInitializer() {
+    if (grpc::g_glip == nullptr) {
+      grpc::g_glip = &g_gli;
+    }
+    if (grpc::g_core_codegen_interface == nullptr) {
+      grpc::g_core_codegen_interface = &g_core_codegen;
+    }
+  }
+
+  /// A no-op method to force the linker to reference this class, which will
+  /// take care of initializing and shutting down the gRPC runtime.
+  int summon() { return 0; }
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_GRPC_LIBRARY_H
diff --git a/include/grpcpp/impl/method_handler_impl.h b/include/grpcpp/impl/method_handler_impl.h
new file mode 100644
index 0000000..7f3be64
--- /dev/null
+++ b/include/grpcpp/impl/method_handler_impl.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_METHOD_HANDLER_IMPL_H
+#define GRPCPP_IMPL_METHOD_HANDLER_IMPL_H
+
+#include <grpcpp/impl/codegen/method_handler_impl.h>
+
+#endif  // GRPCPP_IMPL_METHOD_HANDLER_IMPL_H
diff --git a/include/grpcpp/impl/rpc_method.h b/include/grpcpp/impl/rpc_method.h
new file mode 100644
index 0000000..5da7041
--- /dev/null
+++ b/include/grpcpp/impl/rpc_method.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_RPC_METHOD_H
+#define GRPCPP_IMPL_RPC_METHOD_H
+
+#include <grpcpp/impl/codegen/rpc_method.h>
+
+#endif  // GRPCPP_IMPL_RPC_METHOD_H
diff --git a/include/grpcpp/impl/rpc_service_method.h b/include/grpcpp/impl/rpc_service_method.h
new file mode 100644
index 0000000..ef70a3a
--- /dev/null
+++ b/include/grpcpp/impl/rpc_service_method.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_RPC_SERVICE_METHOD_H
+#define GRPCPP_IMPL_RPC_SERVICE_METHOD_H
+
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+
+#endif  // GRPCPP_IMPL_RPC_SERVICE_METHOD_H
diff --git a/include/grpcpp/impl/serialization_traits.h b/include/grpcpp/impl/serialization_traits.h
new file mode 100644
index 0000000..95194fb
--- /dev/null
+++ b/include/grpcpp/impl/serialization_traits.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SERIALIZATION_TRAITS_H
+#define GRPCPP_IMPL_SERIALIZATION_TRAITS_H
+
+#include <grpcpp/impl/codegen/serialization_traits.h>
+
+#endif  // GRPCPP_IMPL_SERIALIZATION_TRAITS_H
diff --git a/include/grpcpp/impl/server_builder_option.h b/include/grpcpp/impl/server_builder_option.h
new file mode 100644
index 0000000..c7b7801
--- /dev/null
+++ b/include/grpcpp/impl/server_builder_option.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SERVER_BUILDER_OPTION_H
+#define GRPCPP_IMPL_SERVER_BUILDER_OPTION_H
+
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/channel_arguments.h>
+
+namespace grpc {
+
+/// Interface to pass an option to a \a ServerBuilder.
+class ServerBuilderOption {
+ public:
+  virtual ~ServerBuilderOption() {}
+  /// Alter the \a ChannelArguments used to create the gRPC server.
+  virtual void UpdateArguments(ChannelArguments* args) = 0;
+  /// Alter the ServerBuilderPlugin map that will be added into ServerBuilder.
+  virtual void UpdatePlugins(
+      std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_SERVER_BUILDER_OPTION_H
diff --git a/include/grpcpp/impl/server_builder_plugin.h b/include/grpcpp/impl/server_builder_plugin.h
new file mode 100644
index 0000000..d73511e
--- /dev/null
+++ b/include/grpcpp/impl/server_builder_plugin.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H
+#define GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H
+
+#include <memory>
+
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+class ServerBuilder;
+class ServerInitializer;
+class ChannelArguments;
+
+/// This interface is meant for internal usage only. Implementations of this
+/// interface should add themselves to a \a ServerBuilder instance through the
+/// \a InternalAddPluginFactory method.
+class ServerBuilderPlugin {
+ public:
+  virtual ~ServerBuilderPlugin() {}
+  virtual grpc::string name() = 0;
+
+  /// UpdateServerBuilder will be called at the beginning of
+  /// \a ServerBuilder::BuildAndStart().
+  virtual void UpdateServerBuilder(ServerBuilder* builder) {}
+
+  /// InitServer will be called in ServerBuilder::BuildAndStart(), after the
+  /// Server instance is created.
+  virtual void InitServer(ServerInitializer* si) = 0;
+
+  /// Finish will be called at the end of ServerBuilder::BuildAndStart().
+  virtual void Finish(ServerInitializer* si) = 0;
+
+  /// ChangeArguments is an interface that can be used in
+  /// ServerBuilderOption::UpdatePlugins
+  virtual void ChangeArguments(const grpc::string& name, void* value) = 0;
+
+  /// UpdateChannelArguments will be called in ServerBuilder::BuildAndStart(),
+  /// before the Server instance is created.
+  virtual void UpdateChannelArguments(ChannelArguments* args) {}
+
+  virtual bool has_sync_methods() const { return false; }
+  virtual bool has_async_methods() const { return false; }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H
diff --git a/include/grpcpp/impl/server_initializer.h b/include/grpcpp/impl/server_initializer.h
new file mode 100644
index 0000000..f949fab
--- /dev/null
+++ b/include/grpcpp/impl/server_initializer.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SERVER_INITIALIZER_H
+#define GRPCPP_IMPL_SERVER_INITIALIZER_H
+
+#include <memory>
+#include <vector>
+
+#include <grpcpp/server.h>
+
+namespace grpc {
+
+class Server;
+class Service;
+
+class ServerInitializer {
+ public:
+  ServerInitializer(Server* server) : server_(server) {}
+
+  bool RegisterService(std::shared_ptr<Service> service) {
+    if (!server_->RegisterService(nullptr, service.get())) {
+      return false;
+    }
+    default_services_.push_back(service);
+    return true;
+  }
+
+  const std::vector<grpc::string>* GetServiceList() {
+    return &server_->services_;
+  }
+
+ private:
+  Server* server_;
+  std::vector<std::shared_ptr<Service> > default_services_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_SERVER_INITIALIZER_H
diff --git a/include/grpcpp/impl/service_type.h b/include/grpcpp/impl/service_type.h
new file mode 100644
index 0000000..250bc8c
--- /dev/null
+++ b/include/grpcpp/impl/service_type.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SERVICE_TYPE_H
+#define GRPCPP_IMPL_SERVICE_TYPE_H
+
+#include <grpcpp/impl/codegen/service_type.h>
+
+#endif  // GRPCPP_IMPL_SERVICE_TYPE_H
diff --git a/include/grpcpp/impl/sync_cxx11.h b/include/grpcpp/impl/sync_cxx11.h
new file mode 100644
index 0000000..76dcfe3
--- /dev/null
+++ b/include/grpcpp/impl/sync_cxx11.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SYNC_CXX11_H
+#define GRPCPP_IMPL_SYNC_CXX11_H
+
+#include <grpcpp/impl/codegen/sync_cxx11.h>
+
+#endif  // GRPCPP_IMPL_SYNC_CXX11_H
diff --git a/include/grpcpp/impl/sync_no_cxx11.h b/include/grpcpp/impl/sync_no_cxx11.h
new file mode 100644
index 0000000..cc2d4f1
--- /dev/null
+++ b/include/grpcpp/impl/sync_no_cxx11.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_IMPL_SYNC_NO_CXX11_H
+#define GRPCPP_IMPL_SYNC_NO_CXX11_H
+
+#include <grpcpp/impl/codegen/sync_no_cxx11.h>
+
+#endif  // GRPCPP_IMPL_SYNC_NO_CXX11_H
diff --git a/include/grpcpp/resource_quota.h b/include/grpcpp/resource_quota.h
new file mode 100644
index 0000000..554437a
--- /dev/null
+++ b/include/grpcpp/resource_quota.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_RESOURCE_QUOTA_H
+#define GRPCPP_RESOURCE_QUOTA_H
+
+struct grpc_resource_quota;
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+namespace grpc {
+
+/// ResourceQuota represents a bound on memory usage by the gRPC library.
+/// A ResourceQuota can be attached to a server (via \a ServerBuilder),
+/// or a client channel (via \a ChannelArguments).
+/// gRPC will attempt to keep memory used by all attached entities
+/// below the ResourceQuota bound.
+class ResourceQuota final : private GrpcLibraryCodegen {
+ public:
+  /// \param name - a unique name for this ResourceQuota.
+  explicit ResourceQuota(const grpc::string& name);
+  ResourceQuota();
+  ~ResourceQuota();
+
+  /// Resize this \a ResourceQuota to a new size. If \a new_size is smaller
+  /// than the current size of the pool, memory usage will be monotonically
+  /// decreased until it falls under \a new_size.
+  /// No time bound is given for this to occur however.
+  ResourceQuota& Resize(size_t new_size);
+
+  grpc_resource_quota* c_resource_quota() const { return impl_; }
+
+ private:
+  ResourceQuota(const ResourceQuota& rhs);
+  ResourceQuota& operator=(const ResourceQuota& rhs);
+
+  grpc_resource_quota* const impl_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_RESOURCE_QUOTA_H
diff --git a/include/grpcpp/security/auth_context.h b/include/grpcpp/security/auth_context.h
new file mode 100644
index 0000000..7a6f2cb
--- /dev/null
+++ b/include/grpcpp/security/auth_context.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SECURITY_AUTH_CONTEXT_H
+#define GRPCPP_SECURITY_AUTH_CONTEXT_H
+
+#include <grpcpp/impl/codegen/security/auth_context.h>
+
+#endif  // GRPCPP_SECURITY_AUTH_CONTEXT_H
diff --git a/include/grpcpp/security/auth_metadata_processor.h b/include/grpcpp/security/auth_metadata_processor.h
new file mode 100644
index 0000000..30e24c9
--- /dev/null
+++ b/include/grpcpp/security/auth_metadata_processor.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
+#define GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
+
+#include <map>
+
+#include <grpcpp/security/auth_context.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/string_ref.h>
+
+namespace grpc {
+
+/// Interface allowing custom server-side authorization based on credentials
+/// encoded in metadata.  Objects of this type can be passed to
+/// \a ServerCredentials::SetAuthMetadataProcessor().
+class AuthMetadataProcessor {
+ public:
+  typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
+  typedef std::multimap<grpc::string, grpc::string> OutputMetadata;
+
+  virtual ~AuthMetadataProcessor() {}
+
+  /// If this method returns true, the \a Process function will be scheduled in
+  /// a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  /// context is read/write: it contains the properties of the channel peer and
+  /// it is the job of the Process method to augment it with properties derived
+  /// from the passed-in auth_metadata.
+  /// consumed_auth_metadata needs to be filled with metadata that has been
+  /// consumed by the processor and will be removed from the call.
+  /// response_metadata is the metadata that will be sent as part of the
+  /// response.
+  /// If the return value is not Status::OK, the rpc call will be aborted with
+  /// the error code and error message sent back to the client.
+  virtual Status Process(const InputMetadata& auth_metadata,
+                         AuthContext* context,
+                         OutputMetadata* consumed_auth_metadata,
+                         OutputMetadata* response_metadata) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
diff --git a/include/grpcpp/security/credentials.h b/include/grpcpp/security/credentials.h
new file mode 100644
index 0000000..837a0e4
--- /dev/null
+++ b/include/grpcpp/security/credentials.h
@@ -0,0 +1,224 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SECURITY_CREDENTIALS_H
+#define GRPCPP_SECURITY_CREDENTIALS_H
+
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/security/auth_context.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/string_ref.h>
+
+struct grpc_call;
+
+namespace grpc {
+class ChannelArguments;
+class Channel;
+class SecureChannelCredentials;
+class CallCredentials;
+class SecureCallCredentials;
+
+/// A channel credentials object encapsulates all the state needed by a client
+/// to authenticate with a server for a given channel.
+/// It can make various assertions, e.g., about the client’s identity, role
+/// for all the calls on that channel.
+///
+/// \see https://grpc.io/docs/guides/auth.html
+class ChannelCredentials : private GrpcLibraryCodegen {
+ public:
+  ChannelCredentials();
+  ~ChannelCredentials();
+
+ protected:
+  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+      const std::shared_ptr<ChannelCredentials>& channel_creds,
+      const std::shared_ptr<CallCredentials>& call_creds);
+
+  virtual SecureChannelCredentials* AsSecureCredentials() = 0;
+
+ private:
+  friend std::shared_ptr<Channel> CreateCustomChannel(
+      const grpc::string& target,
+      const std::shared_ptr<ChannelCredentials>& creds,
+      const ChannelArguments& args);
+
+  virtual std::shared_ptr<Channel> CreateChannel(
+      const grpc::string& target, const ChannelArguments& args) = 0;
+};
+
+/// A call credentials object encapsulates the state needed by a client to
+/// authenticate with a server for a given call on a channel.
+///
+/// \see https://grpc.io/docs/guides/auth.html
+class CallCredentials : private GrpcLibraryCodegen {
+ public:
+  CallCredentials();
+  ~CallCredentials();
+
+  /// Apply this instance's credentials to \a call.
+  virtual bool ApplyToCall(grpc_call* call) = 0;
+
+ protected:
+  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+      const std::shared_ptr<ChannelCredentials>& channel_creds,
+      const std::shared_ptr<CallCredentials>& call_creds);
+
+  friend std::shared_ptr<CallCredentials> CompositeCallCredentials(
+      const std::shared_ptr<CallCredentials>& creds1,
+      const std::shared_ptr<CallCredentials>& creds2);
+
+  virtual SecureCallCredentials* AsSecureCredentials() = 0;
+};
+
+/// Options used to build SslCredentials.
+struct SslCredentialsOptions {
+  /// The buffer containing the PEM encoding of the server root certificates. If
+  /// this parameter is empty, the default roots will be used.  The default
+  /// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
+  /// environment variable pointing to a file on the file system containing the
+  /// roots.
+  grpc::string pem_root_certs;
+
+  /// The buffer containing the PEM encoding of the client's private key. This
+  /// parameter can be empty if the client does not have a private key.
+  grpc::string pem_private_key;
+
+  /// The buffer containing the PEM encoding of the client's certificate chain.
+  /// This parameter can be empty if the client does not have a certificate
+  /// chain.
+  grpc::string pem_cert_chain;
+};
+
+// Factories for building different types of Credentials The functions may
+// return empty shared_ptr when credentials cannot be created. If a
+// Credentials pointer is returned, it can still be invalid when used to create
+// a channel. A lame channel will be created then and all rpcs will fail on it.
+
+/// Builds credentials with reasonable defaults.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials();
+
+/// Builds SSL Credentials given SSL specific options
+std::shared_ptr<ChannelCredentials> SslCredentials(
+    const SslCredentialsOptions& options);
+
+/// Builds credentials for use when running in GCE
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials();
+
+/// Constant for maximum auth token lifetime.
+constexpr long kMaxAuthTokenLifetimeSecs = 3600;
+
+/// Builds Service Account JWT Access credentials.
+/// json_key is the JSON key string containing the client's private key.
+/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token
+/// (JWT) created with this credentials. It should not exceed
+/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value.
+std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
+    const grpc::string& json_key,
+    long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs);
+
+/// Builds refresh token credentials.
+/// json_refresh_token is the JSON string containing the refresh token along
+/// with a client_id and client_secret.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
+    const grpc::string& json_refresh_token);
+
+/// Builds access token credentials.
+/// access_token is an oauth2 access token that was fetched using an out of band
+/// mechanism.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> AccessTokenCredentials(
+    const grpc::string& access_token);
+
+/// Builds IAM credentials.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleIAMCredentials(
+    const grpc::string& authorization_token,
+    const grpc::string& authority_selector);
+
+/// Combines a channel credentials and a call credentials into a composite
+/// channel credentials.
+std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+    const std::shared_ptr<ChannelCredentials>& channel_creds,
+    const std::shared_ptr<CallCredentials>& call_creds);
+
+/// Combines two call credentials objects into a composite call credentials.
+std::shared_ptr<CallCredentials> CompositeCallCredentials(
+    const std::shared_ptr<CallCredentials>& creds1,
+    const std::shared_ptr<CallCredentials>& creds2);
+
+/// Credentials for an unencrypted, unauthenticated channel
+std::shared_ptr<ChannelCredentials> InsecureChannelCredentials();
+
+/// Credentials for a channel using Cronet.
+std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine);
+
+/// User defined metadata credentials.
+class MetadataCredentialsPlugin {
+ public:
+  virtual ~MetadataCredentialsPlugin() {}
+
+  /// If this method returns true, the Process function will be scheduled in
+  /// a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  /// Type of credentials this plugin is implementing.
+  virtual const char* GetType() const { return ""; }
+
+  /// Gets the auth metatada produced by this plugin.
+  /// The fully qualified method name is:
+  /// service_url + "/" + method_name.
+  /// The channel_auth_context contains (among other things), the identity of
+  /// the server.
+  virtual Status GetMetadata(
+      grpc::string_ref service_url, grpc::string_ref method_name,
+      const AuthContext& channel_auth_context,
+      std::multimap<grpc::string, grpc::string>* metadata) = 0;
+};
+
+std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_CREDENTIALS_H
diff --git a/include/grpcpp/security/server_credentials.h b/include/grpcpp/security/server_credentials.h
new file mode 100644
index 0000000..892863e
--- /dev/null
+++ b/include/grpcpp/security/server_credentials.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SECURITY_SERVER_CREDENTIALS_H
+#define GRPCPP_SECURITY_SERVER_CREDENTIALS_H
+
+#include <memory>
+#include <vector>
+
+#include <grpc/grpc_security_constants.h>
+#include <grpcpp/security/auth_metadata_processor.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_server;
+
+namespace grpc {
+class Server;
+
+/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
+class ServerCredentials {
+ public:
+  virtual ~ServerCredentials();
+
+  /// This method is not thread-safe and has to be called before the server is
+  /// started. The last call to this function wins.
+  virtual void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) = 0;
+
+ private:
+  friend class ::grpc::Server;
+
+  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.)
+  ///
+  /// \return bound port number on sucess, 0 on failure.
+  // TODO(dgq): the "port" part seems to be a misnomer.
+  virtual int AddPortToServer(const grpc::string& addr,
+                              grpc_server* server) = 0;
+};
+
+/// Options to create ServerCredentials with SSL
+struct SslServerCredentialsOptions {
+  /// \warning Deprecated
+  SslServerCredentialsOptions()
+      : force_client_auth(false),
+        client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {}
+  SslServerCredentialsOptions(
+      grpc_ssl_client_certificate_request_type request_type)
+      : force_client_auth(false), client_certificate_request(request_type) {}
+
+  struct PemKeyCertPair {
+    grpc::string private_key;
+    grpc::string cert_chain;
+  };
+  grpc::string pem_root_certs;
+  std::vector<PemKeyCertPair> pem_key_cert_pairs;
+  /// \warning Deprecated
+  bool force_client_auth;
+
+  /// If both \a force_client_auth and \a client_certificate_request
+  /// fields are set, \a force_client_auth takes effect, i.e.
+  /// \a REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+  /// will be enforced.
+  grpc_ssl_client_certificate_request_type client_certificate_request;
+};
+
+/// Builds SSL ServerCredentials given SSL specific options
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const SslServerCredentialsOptions& options);
+
+/// Builds insecure server credentials.
+std::shared_ptr<ServerCredentials> InsecureServerCredentials();
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_SERVER_CREDENTIALS_H
diff --git a/include/grpcpp/server.h b/include/grpcpp/server.h
new file mode 100644
index 0000000..f99a6c2
--- /dev/null
+++ b/include/grpcpp/server.h
@@ -0,0 +1,227 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SERVER_H
+#define GRPCPP_SERVER_H
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/rpc_service_method.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/status.h>
+
+struct grpc_server;
+
+namespace grpc {
+
+class AsyncGenericService;
+class HealthCheckServiceInterface;
+class ServerContext;
+class ServerInitializer;
+
+/// Represents a gRPC server.
+///
+/// Use a \a grpc::ServerBuilder to create, configure, and start
+/// \a Server instances.
+class Server final : public ServerInterface, private GrpcLibraryCodegen {
+ public:
+  ~Server();
+
+  /// Block until the server shuts down.
+  ///
+  /// \warning The server must be either shutting down or some other thread must
+  /// call \a Shutdown for this function to ever return.
+  void Wait() override;
+
+  /// Global callbacks are a set of hooks that are called when server
+  /// events occur.  \a SetGlobalCallbacks method is used to register
+  /// the hooks with gRPC.  Note that
+  /// the \a GlobalCallbacks instance will be shared among all
+  /// \a Server instances in an application and can be set exactly
+  /// once per application.
+  class GlobalCallbacks {
+   public:
+    virtual ~GlobalCallbacks() {}
+    /// Called before server is created.
+    virtual void UpdateArguments(ChannelArguments* args) {}
+    /// Called before application callback for each synchronous server request
+    virtual void PreSynchronousRequest(ServerContext* context) = 0;
+    /// Called after application callback for each synchronous server request
+    virtual void PostSynchronousRequest(ServerContext* context) = 0;
+    /// Called before server is started.
+    virtual void PreServerStart(Server* server) {}
+    /// Called after a server port is added.
+    virtual void AddPort(Server* server, const grpc::string& addr,
+                         ServerCredentials* creds, int port) {}
+  };
+  /// Set the global callback object. Can only be called once per application.
+  /// Does not take ownership of callbacks, and expects the pointed to object
+  /// to be alive until all server objects in the process have been destroyed.
+  /// The same \a GlobalCallbacks object will be used throughout the
+  /// application and is shared among all \a Server objects.
+  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
+
+  // Returns a \em raw pointer to the underlying \a grpc_server instance.
+  grpc_server* c_server();
+
+  /// Returns the health check service.
+  HealthCheckServiceInterface* GetHealthCheckService() const {
+    return health_check_service_.get();
+  }
+
+  /// Establish a channel for in-process communication
+  std::shared_ptr<Channel> InProcessChannel(const ChannelArguments& args);
+
+ private:
+  friend class AsyncGenericService;
+  friend class ServerBuilder;
+  friend class ServerInitializer;
+
+  class SyncRequest;
+  class AsyncRequest;
+  class ShutdownRequest;
+
+  /// SyncRequestThreadManager is an implementation of ThreadManager. This class
+  /// is responsible for polling for incoming RPCs and calling the RPC handlers.
+  /// This is only used in case of a Sync server (i.e a server exposing a sync
+  /// interface)
+  class SyncRequestThreadManager;
+
+  class UnimplementedAsyncRequestContext;
+  class UnimplementedAsyncRequest;
+  class UnimplementedAsyncResponse;
+
+  /// Server constructors. To be used by \a ServerBuilder only.
+  ///
+  /// \param max_message_size Maximum message length that the channel can
+  /// receive.
+  ///
+  /// \param args The channel args
+  ///
+  /// \param sync_server_cqs The completion queues to use if the server is a
+  /// synchronous server (or a hybrid server). The server polls for new RPCs on
+  /// these queues
+  ///
+  /// \param min_pollers The minimum number of polling threads per server
+  /// completion queue (in param sync_server_cqs) to use for listening to
+  /// incoming requests (used only in case of sync server)
+  ///
+  /// \param max_pollers The maximum number of polling threads per server
+  /// completion queue (in param sync_server_cqs) to use for listening to
+  /// incoming requests (used only in case of sync server)
+  ///
+  /// \param sync_cq_timeout_msec The timeout to use when calling AsyncNext() on
+  /// server completion queues passed via sync_server_cqs param.
+  Server(int max_message_size, ChannelArguments* args,
+         std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+             sync_server_cqs,
+         int min_pollers, int max_pollers, int sync_cq_timeout_msec);
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the Server instance.
+  bool RegisterService(const grpc::string* host, Service* service) override;
+
+  /// Register a generic service. This call does not take ownership of the
+  /// service. The service must exist for the lifetime of the Server instance.
+  void RegisterAsyncGenericService(AsyncGenericService* service) override;
+
+  /// Try binding the server to the given \a addr endpoint
+  /// (port, and optionally including IP address to bind to).
+  ///
+  /// It can be invoked multiple times. Should be used before
+  /// starting the server.
+  ///
+  /// \param addr The address to try to bind to the server (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.).
+  /// \param creds The credentials associated with the server.
+  ///
+  /// \return bound port number on success, 0 on failure.
+  ///
+  /// \warning It is an error to call this method on an already started server.
+  int AddListeningPort(const grpc::string& addr,
+                       ServerCredentials* creds) override;
+
+  /// Start the server.
+  ///
+  /// \param cqs Completion queues for handling asynchronous services. The
+  /// caller is required to keep all completion queues live until the server is
+  /// destroyed.
+  /// \param num_cqs How many completion queues does \a cqs hold.
+  void Start(ServerCompletionQueue** cqs, size_t num_cqs) override;
+
+  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                        internal::Call* call) override;
+
+  void ShutdownInternal(gpr_timespec deadline) override;
+
+  int max_receive_message_size() const override {
+    return max_receive_message_size_;
+  };
+
+  grpc_server* server() override { return server_; };
+
+  ServerInitializer* initializer();
+
+  const int max_receive_message_size_;
+
+  /// The following completion queues are ONLY used in case of Sync API
+  /// i.e. if the server has any services with sync methods. The server uses
+  /// these completion queues to poll for new RPCs
+  std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+      sync_server_cqs_;
+
+  /// List of \a ThreadManager instances (one for each cq in
+  /// the \a sync_server_cqs)
+  std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
+
+  // Sever status
+  std::mutex mu_;
+  bool started_;
+  bool shutdown_;
+  bool shutdown_notified_;  // Was notify called on the shutdown_cv_
+
+  std::condition_variable shutdown_cv_;
+
+  std::shared_ptr<GlobalCallbacks> global_callbacks_;
+
+  std::vector<grpc::string> services_;
+  bool has_generic_service_;
+
+  // Pointer to the wrapped grpc_server.
+  grpc_server* server_;
+
+  std::unique_ptr<ServerInitializer> server_initializer_;
+
+  std::unique_ptr<HealthCheckServiceInterface> health_check_service_;
+  bool health_check_service_disabled_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SERVER_H
diff --git a/include/grpcpp/server_builder.h b/include/grpcpp/server_builder.h
new file mode 100644
index 0000000..c35a6cf
--- /dev/null
+++ b/include/grpcpp/server_builder.h
@@ -0,0 +1,278 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * 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 GRPCPP_SERVER_BUILDER_H
+#define GRPCPP_SERVER_BUILDER_H
+
+#include <climits>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/workaround_list.h>
+#include <grpcpp/impl/channel_argument_option.h>
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_resource_quota;
+
+namespace grpc {
+
+class AsyncGenericService;
+class ResourceQuota;
+class CompletionQueue;
+class Server;
+class ServerCompletionQueue;
+class ServerCredentials;
+class Service;
+
+namespace testing {
+class ServerBuilderPluginTest;
+}  // namespace testing
+
+/// A builder class for the creation and startup of \a grpc::Server instances.
+class ServerBuilder {
+ public:
+  ServerBuilder();
+  ~ServerBuilder();
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Primary API's
+
+  /// Return a running server which is ready for processing calls.
+  /// Before calling, one typically needs to ensure that:
+  ///  1. a service is registered - so that the server knows what to serve
+  ///     (via RegisterService, or RegisterAsyncGenericService)
+  ///  2. a listening port has been added - so the server knows where to receive
+  ///     traffic (via AddListeningPort)
+  ///  3. [for async api only] completion queues have been added via
+  ///     AddCompletionQueue
+  std::unique_ptr<Server> BuildAndStart();
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the \a Server instance returned
+  /// by \a BuildAndStart().
+  /// Matches requests with any :authority
+  ServerBuilder& RegisterService(Service* service);
+
+  /// Enlists an endpoint \a addr (port with an optional IP address) to
+  /// bind the \a grpc::Server object to be created to.
+  ///
+  /// It can be invoked multiple times.
+  ///
+  /// \param addr_uri The address to try to bind to the server in URI form. If
+  /// the scheme name is omitted, "dns:///" is assumed. To bind to any address,
+  /// please use IPv6 any, i.e., [::]:<port>, which also accepts IPv4
+  /// connections.  Valid values include dns:///localhost:1234, /
+  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.).
+  /// \param creds The credentials associated with the server.
+  /// \param selected_port[out] If not `nullptr`, gets populated with the port
+  /// number bound to the \a grpc::Server for the corresponding endpoint after
+  /// it is successfully bound, 0 otherwise.
+  ///
+  ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
+                                  std::shared_ptr<ServerCredentials> creds,
+                                  int* selected_port = nullptr);
+
+  /// Add a completion queue for handling asynchronous services.
+  ///
+  /// Best performance is typically obtained by using one thread per polling
+  /// completion queue.
+  ///
+  /// Caller is required to shutdown the server prior to shutting down the
+  /// returned completion queue. Caller is also required to drain the
+  /// completion queue after shutting it down. A typical usage scenario:
+  ///
+  /// // While building the server:
+  /// ServerBuilder builder;
+  /// ...
+  /// cq_ = builder.AddCompletionQueue();
+  /// server_ = builder.BuildAndStart();
+  ///
+  /// // While shutting down the server;
+  /// server_->Shutdown();
+  /// cq_->Shutdown();  // Always *after* the associated server's Shutdown()!
+  /// // Drain the cq_ that was created
+  /// void* ignored_tag;
+  /// bool ignored_ok;
+  /// while (cq_->Next(&ignored_tag, &ignored_ok)) { }
+  ///
+  /// \param is_frequently_polled This is an optional parameter to inform gRPC
+  /// library about whether this completion queue would be frequently polled
+  /// (i.e. by calling \a Next() or \a AsyncNext()). The default value is
+  /// 'true' and is the recommended setting. Setting this to 'false' (i.e.
+  /// not polling the completion queue frequently) will have a significantly
+  /// negative performance impact and hence should not be used in production
+  /// use cases.
+  std::unique_ptr<ServerCompletionQueue> AddCompletionQueue(
+      bool is_frequently_polled = true);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Less commonly used RegisterService variants
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the \a Server instance returned
+  /// by \a BuildAndStart().
+  /// Only matches requests with :authority \a host
+  ServerBuilder& RegisterService(const grpc::string& host, Service* service);
+
+  /// Register a generic service.
+  /// Matches requests with any :authority
+  /// This is mostly useful for writing generic gRPC Proxies where the exact
+  /// serialization format is unknown
+  ServerBuilder& RegisterAsyncGenericService(AsyncGenericService* service);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Fine control knobs
+
+  /// Set max receive message size in bytes.
+  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
+    return *this;
+  }
+
+  /// Set max send message size in bytes.
+  ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) {
+    max_send_message_size_ = max_send_message_size;
+    return *this;
+  }
+
+  /// \deprecated For backward compatibility.
+  ServerBuilder& SetMaxMessageSize(int max_message_size) {
+    return SetMaxReceiveMessageSize(max_message_size);
+  }
+
+  /// Set the support status for compression algorithms. All algorithms are
+  /// enabled by default.
+  ///
+  /// Incoming calls compressed with an unsupported algorithm will fail with
+  /// \a GRPC_STATUS_UNIMPLEMENTED.
+  ServerBuilder& SetCompressionAlgorithmSupportStatus(
+      grpc_compression_algorithm algorithm, bool enabled);
+
+  /// The default compression level to use for all channel calls in the
+  /// absence of a call-specific level.
+  ServerBuilder& SetDefaultCompressionLevel(grpc_compression_level level);
+
+  /// The default compression algorithm to use for all channel calls in the
+  /// absence of a call-specific level. Note that it overrides any compression
+  /// level set by \a SetDefaultCompressionLevel.
+  ServerBuilder& SetDefaultCompressionAlgorithm(
+      grpc_compression_algorithm algorithm);
+
+  /// Set the attached buffer pool for this server
+  ServerBuilder& SetResourceQuota(const ResourceQuota& resource_quota);
+
+  ServerBuilder& SetOption(std::unique_ptr<ServerBuilderOption> option);
+
+  /// Options for synchronous servers.
+  enum SyncServerOption {
+    NUM_CQS,         ///< Number of completion queues.
+    MIN_POLLERS,     ///< Minimum number of polling threads.
+    MAX_POLLERS,     ///< Maximum number of polling threads.
+    CQ_TIMEOUT_MSEC  ///< Completion queue timeout in milliseconds.
+  };
+
+  /// Only useful if this is a Synchronous server.
+  ServerBuilder& SetSyncServerOption(SyncServerOption option, int value);
+
+  /// Add a channel argument (an escape hatch to tuning core library parameters
+  /// directly)
+  template <class T>
+  ServerBuilder& AddChannelArgument(const grpc::string& arg, const T& value) {
+    return SetOption(MakeChannelArgumentOption(arg, value));
+  }
+
+  /// For internal use only: Register a ServerBuilderPlugin factory function.
+  static void InternalAddPluginFactory(
+      std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)());
+
+  /// Enable a server workaround. Do not use unless you know what the workaround
+  /// does. For explanation and detailed descriptions of workarounds, see
+  /// doc/workarounds.md.
+  ServerBuilder& EnableWorkaround(grpc_workaround_list id);
+
+ private:
+  friend class ::grpc::testing::ServerBuilderPluginTest;
+
+  struct Port {
+    grpc::string addr;
+    std::shared_ptr<ServerCredentials> creds;
+    int* selected_port;
+  };
+
+  struct SyncServerSettings {
+    SyncServerSettings()
+        : num_cqs(1), min_pollers(1), max_pollers(2), cq_timeout_msec(10000) {}
+
+    /// Number of server completion queues to create to listen to incoming RPCs.
+    int num_cqs;
+
+    /// Minimum number of threads per completion queue that should be listening
+    /// to incoming RPCs.
+    int min_pollers;
+
+    /// Maximum number of threads per completion queue that can be listening to
+    /// incoming RPCs.
+    int max_pollers;
+
+    /// The timeout for server completion queue's AsyncNext call.
+    int cq_timeout_msec;
+  };
+
+  typedef std::unique_ptr<grpc::string> HostString;
+  struct NamedService {
+    explicit NamedService(Service* s) : service(s) {}
+    NamedService(const grpc::string& h, Service* s)
+        : host(new grpc::string(h)), service(s) {}
+    HostString host;
+    Service* service;
+  };
+
+  int max_receive_message_size_;
+  int max_send_message_size_;
+  std::vector<std::unique_ptr<ServerBuilderOption>> options_;
+  std::vector<std::unique_ptr<NamedService>> services_;
+  std::vector<Port> ports_;
+
+  SyncServerSettings sync_server_settings_;
+
+  /// List of completion queues added via \a AddCompletionQueue method.
+  std::vector<ServerCompletionQueue*> cqs_;
+
+  std::shared_ptr<ServerCredentials> creds_;
+  std::vector<std::unique_ptr<ServerBuilderPlugin>> plugins_;
+  grpc_resource_quota* resource_quota_;
+  AsyncGenericService* generic_service_;
+  struct {
+    bool is_set;
+    grpc_compression_level level;
+  } maybe_default_compression_level_;
+  struct {
+    bool is_set;
+    grpc_compression_algorithm algorithm;
+  } maybe_default_compression_algorithm_;
+  uint32_t enabled_compression_algorithms_bitset_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SERVER_BUILDER_H
diff --git a/include/grpcpp/server_context.h b/include/grpcpp/server_context.h
new file mode 100644
index 0000000..45f2614
--- /dev/null
+++ b/include/grpcpp/server_context.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SERVER_CONTEXT_H
+#define GRPCPP_SERVER_CONTEXT_H
+
+#include <grpcpp/impl/codegen/server_context.h>
+
+#endif  // GRPCPP_SERVER_CONTEXT_H
diff --git a/include/grpcpp/server_posix.h b/include/grpcpp/server_posix.h
new file mode 100644
index 0000000..ef3ee01
--- /dev/null
+++ b/include/grpcpp/server_posix.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_SERVER_POSIX_H
+#define GRPCPP_SERVER_POSIX_H
+
+#include <memory>
+
+#include <grpc/support/port_platform.h>
+#include <grpcpp/server.h>
+
+namespace grpc {
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+/// Add a new client to a \a Server communicating over the given
+/// file descriptor.
+///
+/// \param server The server to add the client to.
+/// \param fd The file descriptor representing a socket.
+void AddInsecureChannelFromFd(Server* server, int fd);
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SERVER_POSIX_H
diff --git a/include/grpcpp/support/async_stream.h b/include/grpcpp/support/async_stream.h
new file mode 100644
index 0000000..ff9e455
--- /dev/null
+++ b/include/grpcpp/support/async_stream.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_ASYNC_STREAM_H
+#define GRPCPP_SUPPORT_ASYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/async_stream.h>
+
+#endif  // GRPCPP_SUPPORT_ASYNC_STREAM_H
diff --git a/include/grpcpp/support/async_unary_call.h b/include/grpcpp/support/async_unary_call.h
new file mode 100644
index 0000000..2e5181c
--- /dev/null
+++ b/include/grpcpp/support/async_unary_call.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
+#define GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/async_unary_call.h>
+
+#endif  // GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
diff --git a/include/grpcpp/support/byte_buffer.h b/include/grpcpp/support/byte_buffer.h
new file mode 100644
index 0000000..53aeff1
--- /dev/null
+++ b/include/grpcpp/support/byte_buffer.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_BYTE_BUFFER_H
+#define GRPCPP_SUPPORT_BYTE_BUFFER_H
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/serialization_traits.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/slice.h>
+#include <grpcpp/support/status.h>
+
+#endif  // GRPCPP_SUPPORT_BYTE_BUFFER_H
diff --git a/include/grpcpp/support/channel_arguments.h b/include/grpcpp/support/channel_arguments.h
new file mode 100644
index 0000000..1eead4e
--- /dev/null
+++ b/include/grpcpp/support/channel_arguments.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
+#define GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
+
+#include <list>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+namespace testing {
+class ChannelArgumentsTest;
+}  // namespace testing
+
+class ResourceQuota;
+
+/// Options for channel creation. The user can use generic setters to pass
+/// key value pairs down to C channel creation code. For gRPC related options,
+/// concrete setters are provided.
+class ChannelArguments {
+ public:
+  ChannelArguments();
+  ~ChannelArguments();
+
+  ChannelArguments(const ChannelArguments& other);
+  ChannelArguments& operator=(ChannelArguments other) {
+    Swap(other);
+    return *this;
+  }
+
+  void Swap(ChannelArguments& other);
+
+  /// Dump arguments in this instance to \a channel_args. Does not take
+  /// ownership of \a channel_args.
+  ///
+  /// Note that the underlying arguments are shared. Changes made to either \a
+  /// channel_args or this instance would be reflected on both.
+  void SetChannelArgs(grpc_channel_args* channel_args) const;
+
+  // gRPC specific channel argument setters
+  /// Set target name override for SSL host name checking. This option is for
+  /// testing only and should never be used in production.
+  void SetSslTargetNameOverride(const grpc::string& name);
+  // TODO(yangg) add flow control options
+  /// Set the compression algorithm for the channel.
+  void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
+
+  /// Set the grpclb fallback timeout (in ms) for the channel. If this amount
+  /// of time has passed but we have not gotten any non-empty \a serverlist from
+  /// the balancer, we will fall back to use the backend address(es) returned by
+  /// the resolver.
+  void SetGrpclbFallbackTimeout(int fallback_timeout);
+
+  /// Set the socket mutator for the channel.
+  void SetSocketMutator(grpc_socket_mutator* mutator);
+
+  /// Set the string to prepend to the user agent.
+  void SetUserAgentPrefix(const grpc::string& user_agent_prefix);
+
+  /// Set the buffer pool to be attached to the constructed channel.
+  void SetResourceQuota(const ResourceQuota& resource_quota);
+
+  /// Set the max receive and send message sizes.
+  void SetMaxReceiveMessageSize(int size);
+  void SetMaxSendMessageSize(int size);
+
+  /// Set LB policy name.
+  /// Note that if the name resolver returns only balancer addresses, the
+  /// grpclb LB policy will be used, regardless of what is specified here.
+  void SetLoadBalancingPolicyName(const grpc::string& lb_policy_name);
+
+  /// Set service config in JSON form.
+  /// Primarily meant for use in unit tests.
+  void SetServiceConfigJSON(const grpc::string& service_config_json);
+
+  // Generic channel argument setters. Only for advanced use cases.
+  /// Set an integer argument \a value under \a key.
+  void SetInt(const grpc::string& key, int value);
+
+  // Generic channel argument setter. Only for advanced use cases.
+  /// Set a pointer argument \a value under \a key. Owership is not transferred.
+  void SetPointer(const grpc::string& key, void* value);
+
+  void SetPointerWithVtable(const grpc::string& key, void* value,
+                            const grpc_arg_pointer_vtable* vtable);
+
+  /// Set a textual argument \a value under \a key.
+  void SetString(const grpc::string& key, const grpc::string& value);
+
+  /// Return (by value) a C \a grpc_channel_args structure which points to
+  /// arguments owned by this \a ChannelArguments instance
+  grpc_channel_args c_channel_args() const {
+    grpc_channel_args out;
+    out.num_args = args_.size();
+    out.args = args_.empty() ? NULL : const_cast<grpc_arg*>(&args_[0]);
+    return out;
+  }
+
+ private:
+  friend class SecureChannelCredentials;
+  friend class testing::ChannelArgumentsTest;
+
+  /// Default pointer argument operations.
+  struct PointerVtableMembers {
+    static void* Copy(void* in) { return in; }
+    static void Destroy(void* in) {}
+    static int Compare(void* a, void* b) {
+      if (a < b) return -1;
+      if (a > b) return 1;
+      return 0;
+    }
+  };
+
+  // Returns empty string when it is not set.
+  grpc::string GetSslTargetNameOverride() const;
+
+  std::vector<grpc_arg> args_;
+  std::list<grpc::string> strings_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
diff --git a/include/grpcpp/support/config.h b/include/grpcpp/support/config.h
new file mode 100644
index 0000000..16bdab6
--- /dev/null
+++ b/include/grpcpp/support/config.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_CONFIG_H
+#define GRPCPP_SUPPORT_CONFIG_H
+
+#include <grpcpp/impl/codegen/config.h>
+
+#endif  // GRPCPP_SUPPORT_CONFIG_H
diff --git a/include/grpcpp/support/error_details.h b/include/grpcpp/support/error_details.h
new file mode 100644
index 0000000..84931d9
--- /dev/null
+++ b/include/grpcpp/support/error_details.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_ERROR_DETAILS_H
+#define GRPCPP_SUPPORT_ERROR_DETAILS_H
+
+#include <grpcpp/support/status.h>
+
+namespace google {
+namespace rpc {
+class Status;
+}  // namespace rpc
+}  // namespace google
+
+namespace grpc {
+
+/// Map a \a grpc::Status to a \a google::rpc::Status.
+/// The given \a to object will be cleared.
+/// On success, returns status with OK.
+/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
+/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
+Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to);
+
+/// Map \a google::rpc::Status to a \a grpc::Status.
+/// Returns OK on success.
+/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
+Status SetErrorDetails(const ::google::rpc::Status& from, Status* to);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_H
diff --git a/include/grpcpp/support/slice.h b/include/grpcpp/support/slice.h
new file mode 100644
index 0000000..eaeb29a
--- /dev/null
+++ b/include/grpcpp/support/slice.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_SLICE_H
+#define GRPCPP_SUPPORT_SLICE_H
+
+#include <grpc/slice.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/support/config.h>
+
+#endif  // GRPCPP_SUPPORT_SLICE_H
diff --git a/include/grpcpp/support/status.h b/include/grpcpp/support/status.h
new file mode 100644
index 0000000..91b629f
--- /dev/null
+++ b/include/grpcpp/support/status.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_STATUS_H
+#define GRPCPP_SUPPORT_STATUS_H
+
+#include <grpcpp/impl/codegen/status.h>
+
+#endif  // GRPCPP_SUPPORT_STATUS_H
diff --git a/include/grpcpp/support/status_code_enum.h b/include/grpcpp/support/status_code_enum.h
new file mode 100644
index 0000000..bfb47f3
--- /dev/null
+++ b/include/grpcpp/support/status_code_enum.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
+#define GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
+
+#include <grpcpp/impl/codegen/status_code_enum.h>
+
+#endif  // GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
diff --git a/include/grpcpp/support/string_ref.h b/include/grpcpp/support/string_ref.h
new file mode 100644
index 0000000..0e0d3d4
--- /dev/null
+++ b/include/grpcpp/support/string_ref.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_STRING_REF_H
+#define GRPCPP_SUPPORT_STRING_REF_H
+
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#endif  // GRPCPP_SUPPORT_STRING_REF_H
diff --git a/include/grpcpp/support/stub_options.h b/include/grpcpp/support/stub_options.h
new file mode 100644
index 0000000..e9700ea
--- /dev/null
+++ b/include/grpcpp/support/stub_options.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_STUB_OPTIONS_H
+#define GRPCPP_SUPPORT_STUB_OPTIONS_H
+
+#include <grpcpp/impl/codegen/stub_options.h>
+
+#endif  // GRPCPP_SUPPORT_STUB_OPTIONS_H
diff --git a/include/grpcpp/support/sync_stream.h b/include/grpcpp/support/sync_stream.h
new file mode 100644
index 0000000..ea60b6d
--- /dev/null
+++ b/include/grpcpp/support/sync_stream.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_SYNC_STREAM_H
+#define GRPCPP_SUPPORT_SYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/sync_stream.h>
+
+#endif  // GRPCPP_SUPPORT_SYNC_STREAM_H
diff --git a/include/grpcpp/support/time.h b/include/grpcpp/support/time.h
new file mode 100644
index 0000000..c7408ff
--- /dev/null
+++ b/include/grpcpp/support/time.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPCPP_SUPPORT_TIME_H
+#define GRPCPP_SUPPORT_TIME_H
+
+#include <grpcpp/impl/codegen/time.h>
+
+#endif  // GRPCPP_SUPPORT_TIME_H
diff --git a/include/grpcpp/test/mock_stream.h b/include/grpcpp/test/mock_stream.h
new file mode 100644
index 0000000..93963f7
--- /dev/null
+++ b/include/grpcpp/test/mock_stream.h
@@ -0,0 +1,148 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPCPP_TEST_MOCK_STREAM_H
+#define GRPCPP_TEST_MOCK_STREAM_H
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/support/sync_stream.h>
+
+namespace grpc {
+namespace testing {
+
+template <class R>
+class MockClientReader : public ClientReaderInterface<R> {
+ public:
+  MockClientReader() = default;
+
+  /// ClientStreamingInterface
+  MOCK_METHOD0_T(Finish, Status());
+
+  /// ReaderInterface
+  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
+  MOCK_METHOD1_T(Read, bool(R*));
+
+  /// ClientReaderInterface
+  MOCK_METHOD0_T(WaitForInitialMetadata, void());
+};
+
+template <class W>
+class MockClientWriter : public ClientWriterInterface<W> {
+ public:
+  MockClientWriter() = default;
+
+  /// ClientStreamingInterface
+  MOCK_METHOD0_T(Finish, Status());
+
+  /// WriterInterface
+  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
+
+  /// ClientWriterInterface
+  MOCK_METHOD0_T(WritesDone, bool());
+};
+
+template <class W, class R>
+class MockClientReaderWriter : public ClientReaderWriterInterface<W, R> {
+ public:
+  MockClientReaderWriter() = default;
+
+  /// ClientStreamingInterface
+  MOCK_METHOD0_T(Finish, Status());
+
+  /// ReaderInterface
+  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
+  MOCK_METHOD1_T(Read, bool(R*));
+
+  /// WriterInterface
+  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
+
+  /// ClientReaderWriterInterface
+  MOCK_METHOD0_T(WaitForInitialMetadata, void());
+  MOCK_METHOD0_T(WritesDone, bool());
+};
+
+/// TODO: We do not support mocking an async RPC for now.
+
+template <class R>
+class MockClientAsyncResponseReader
+    : public ClientAsyncResponseReaderInterface<R> {
+ public:
+  MockClientAsyncResponseReader() = default;
+
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD3_T(Finish, void(R*, Status*, void*));
+};
+
+template <class R>
+class MockClientAsyncReader : public ClientAsyncReaderInterface<R> {
+ public:
+  MockClientAsyncReader() = default;
+
+  /// ClientAsyncStreamingInterface
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD2_T(Finish, void(Status*, void*));
+
+  /// AsyncReaderInterface
+  MOCK_METHOD2_T(Read, void(R*, void*));
+};
+
+template <class W>
+class MockClientAsyncWriter : public ClientAsyncWriterInterface<W> {
+ public:
+  MockClientAsyncWriter() = default;
+
+  /// ClientAsyncStreamingInterface
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD2_T(Finish, void(Status*, void*));
+
+  /// AsyncWriterInterface
+  MOCK_METHOD2_T(Write, void(const W&, void*));
+
+  /// ClientAsyncWriterInterface
+  MOCK_METHOD1_T(WritesDone, void(void*));
+};
+
+template <class W, class R>
+class MockClientAsyncReaderWriter
+    : public ClientAsyncReaderWriterInterface<W, R> {
+ public:
+  MockClientAsyncReaderWriter() = default;
+
+  /// ClientAsyncStreamingInterface
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD2_T(Finish, void(Status*, void*));
+
+  /// AsyncWriterInterface
+  MOCK_METHOD2_T(Write, void(const W&, void*));
+
+  /// AsyncReaderInterface
+  MOCK_METHOD2_T(Read, void(R*, void*));
+
+  /// ClientAsyncReaderWriterInterface
+  MOCK_METHOD1_T(WritesDone, void(void*));
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPCPP_TEST_MOCK_STREAM_H
diff --git a/include/grpcpp/test/server_context_test_spouse.h b/include/grpcpp/test/server_context_test_spouse.h
new file mode 100644
index 0000000..f5ca552
--- /dev/null
+++ b/include/grpcpp/test/server_context_test_spouse.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
+#define GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
+
+#include <map>
+
+#include <grpcpp/server_context.h>
+
+namespace grpc {
+namespace testing {
+
+/// A test-only class to access private members and methods of ServerContext.
+class ServerContextTestSpouse {
+ public:
+  explicit ServerContextTestSpouse(ServerContext* ctx) : ctx_(ctx) {}
+
+  /// Inject client metadata to the ServerContext for the test. The test spouse
+  /// must be alive when \a ServerContext::client_metadata is called.
+  void AddClientMetadata(const grpc::string& key, const grpc::string& value) {
+    client_metadata_storage_.insert(
+        std::pair<grpc::string, grpc::string>(key, value));
+    ctx_->client_metadata_.map()->clear();
+    for (auto iter = client_metadata_storage_.begin();
+         iter != client_metadata_storage_.end(); ++iter) {
+      ctx_->client_metadata_.map()->insert(
+          std::pair<grpc::string_ref, grpc::string_ref>(
+              iter->first.c_str(),
+              grpc::string_ref(iter->second.data(), iter->second.size())));
+    }
+  }
+
+  std::multimap<grpc::string, grpc::string> GetInitialMetadata() const {
+    return ctx_->initial_metadata_;
+  }
+
+  std::multimap<grpc::string, grpc::string> GetTrailingMetadata() const {
+    return ctx_->trailing_metadata_;
+  }
+
+ private:
+  ServerContext* ctx_;  // not owned
+  std::multimap<grpc::string, grpc::string> client_metadata_storage_;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
diff --git a/package.xml b/package.xml
index 5575855..c4a6f6f 100644
--- a/package.xml
+++ b/package.xml
@@ -13,8 +13,8 @@
  <date>2018-01-19</date>
  <time>16:06:07</time>
  <version>
-  <release>1.10.0dev</release>
-  <api>1.10.0dev</api>
+  <release>1.11.0dev</release>
+  <api>1.11.0dev</api>
  </version>
  <stability>
   <release>beta</release>
@@ -90,7 +90,6 @@
     <file baseinstalldir="/" name="src/core/lib/gpr/spinlock.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/string.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/string_windows.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gpr/thd.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/time_precise.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tls.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tls_gcc.h" role="src" />
@@ -104,6 +103,7 @@
     <file baseinstalldir="/" name="src/core/lib/gprpp/atomic_with_std.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/manual_constructor.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gprpp/memory.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/thd.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/timers.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/alloc.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/arena.cc" role="src" />
@@ -131,9 +131,6 @@
     <file baseinstalldir="/" name="src/core/lib/gpr/sync.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/sync_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/sync_windows.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gpr/thd.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gpr/thd_posix.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gpr/thd_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/time.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/time_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/time_precise.cc" role="src" />
@@ -143,6 +140,8 @@
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/wrap_memcpy.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/thd_posix.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/gprpp/thd_windows.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/basic_timers.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/stap_timers.cc" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/byte_buffer.h" role="src" />
@@ -205,6 +204,7 @@
     <file baseinstalldir="/" name="src/core/ext/filters/http/message_compress/message_compress_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/http/server/http_server_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/context/security_context.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/alts_credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/composite/composite_credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/fake/fake_credentials.h" role="src" />
@@ -216,22 +216,47 @@
     <file baseinstalldir="/" name="src/core/lib/security/credentials/oauth2/oauth2_credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/plugin/plugin_credentials.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/ssl/ssl_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/security_connector/alts_security_connector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/security_connector/security_connector.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/auth_filters.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/transport/lb_targets_info.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/secure_endpoint.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/transport/security_connector.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/security_handshaker.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/target_authority_table.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/tsi_error.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/util/json_util.h" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.h" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/fake_transport_security.h" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/ssl_transport_security.h" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/ssl_types.h" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/transport_security_grpc.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/crypt/gsec.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_counter.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_crypter.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_frame_protector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/frame_handler.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_client.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_event.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/check_gcp_environment.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_utils.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/transport_security_common_api.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/altscontext.pb.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/handshaker.pb.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/transport_security_common.pb.h" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb.h" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb_common.h" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb_decode.h" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb_encode.h" role="src" />
     <file baseinstalldir="/" name="src/core/tsi/transport_security.h" role="src" />
     <file baseinstalldir="/" name="src/core/tsi/transport_security_adapter.h" role="src" />
     <file baseinstalldir="/" name="src/core/tsi/transport_security_interface.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/backup_poller.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/client_channel.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/client_channel_factory.h" role="src" />
@@ -241,6 +266,7 @@
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_factory.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_registry.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/method_params.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/parse_address.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper_registry.h" role="src" />
@@ -248,11 +274,17 @@
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_factory.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_registry.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_throttle.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/status_util.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel_index.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/uri_parser.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/fake_transport_security.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/ssl_transport_security.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/ssl_types.h" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/transport_security_grpc.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/inproc/inproc_transport.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/avl/avl.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/backoff/backoff.h" role="src" />
@@ -352,6 +384,7 @@
     <file baseinstalldir="/" name="src/core/lib/slice/slice_hash_table.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/slice/slice_weak_hash_table.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/api_trace.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/call.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/call_test_only.h" role="src" />
@@ -376,20 +409,16 @@
     <file baseinstalldir="/" name="src/core/lib/transport/service_config.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/status_conversion.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/status_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport_impl.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb.h" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb_common.h" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb_decode.h" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb_encode.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/subchannel_list.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" role="src" />
@@ -501,7 +530,6 @@
     <file baseinstalldir="/" name="src/core/lib/slice/percent_encoding.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_buffer.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/slice/slice_hash_table.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_intern.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/api_trace.cc" role="src" />
@@ -532,6 +560,7 @@
     <file baseinstalldir="/" name="src/core/lib/transport/service_config.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/status_conversion.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/status_metadata.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport_op_string.cc" role="src" />
@@ -566,6 +595,7 @@
     <file baseinstalldir="/" name="src/core/ext/filters/http/server/http_server_filter.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/httpcli_security_connector.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/context/security_context.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/alts_credentials.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/composite/composite_credentials.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/credentials.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/credentials_metadata.cc" role="src" />
@@ -579,23 +609,55 @@
     <file baseinstalldir="/" name="src/core/lib/security/credentials/oauth2/oauth2_credentials.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/plugin/plugin_credentials.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/credentials/ssl/ssl_credentials.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/security_connector/alts_security_connector.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/security_connector/security_connector.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/client_auth_filter.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/transport/lb_targets_info.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/secure_endpoint.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/transport/security_connector.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/security_handshaker.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/server_auth_filter.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/target_authority_table.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/transport/tsi_error.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/util/json_util.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/init_secure.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/fake_transport_security.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/ssl_transport_security.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/tsi/transport_security_grpc.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/crypt/aes_gcm.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/crypt/gsec.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_counter.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_crypter.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_frame_protector.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/frame_protector/frame_handler.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_client.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_event.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/check_gcp_environment.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/alts_tsi_utils.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/transport_security_common_api.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/altscontext.pb.c" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/handshaker.pb.c" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts/handshaker/transport_security_common.pb.c" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb_common.c" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb_decode.c" role="src" />
+    <file baseinstalldir="/" name="third_party/nanopb/pb_encode.c" role="src" />
     <file baseinstalldir="/" name="src/core/tsi/transport_security.cc" role="src" />
     <file baseinstalldir="/" name="src/core/tsi/transport_security_adapter.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/backup_poller.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/channel_connectivity.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/client_channel.cc" role="src" />
@@ -607,21 +669,26 @@
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_factory.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy_registry.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/method_params.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/parse_address.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/proxy_mapper_registry.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_registry.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_throttle.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/status_util.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/subchannel_index.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/uri_parser.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/fake_transport_security.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/ssl_transport_security.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/tsi/transport_security_grpc.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/inproc/inproc_plugin.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/inproc/inproc_transport.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc" role="src" />
@@ -630,9 +697,6 @@
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb_common.c" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb_decode.c" role="src" />
-    <file baseinstalldir="/" name="third_party/nanopb/pb_encode.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc" role="src" />
@@ -660,20 +724,80 @@
     <file baseinstalldir="/" name="third_party/boringssl/crypto/curve25519/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/err/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/evp/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/aes/aes.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/aes/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/aes/key_wrap.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/aes/mode_wrappers.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/add.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/asm/x86_64-gcc.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/bn.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/bytes.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/cmp.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/ctx.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/div.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/exponentiation.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/gcd.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/generic.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/jacobi.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/montgomery.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/montgomery_inv.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/mul.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/prime.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/random.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/rsaz_exp.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/rsaz_exp.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/shift.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/bn/sqrt.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/cipher/aead.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/cipher/cipher.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/cipher/e_aes.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/cipher/e_des.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/cipher/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/delocate.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/des/des.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/des/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/digest/digest.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/digest/digests.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/digest/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/digest/md32_common.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/ec.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/ec_key.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/ec_montgomery.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/oct.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/p224-64.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/p256-64.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64-table.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/simple.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/util-64.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ec/wnaf.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/ecdsa/ecdsa.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/hmac/hmac.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/md4/md4.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/md5/md5.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/cbc.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/cfb.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/ctr.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/gcm.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/ofb.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/modes/polyval.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rand/ctrdrbg.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rand/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rand/rand.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rand/urandom.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rsa/blinding.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rsa/internal.h" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rsa/padding.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rsa/rsa.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/rsa/rsa_impl.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/sha/sha1-altivec.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/sha/sha1.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/sha/sha256.c" role="src" />
+    <file baseinstalldir="/" name="third_party/boringssl/crypto/fipsmodule/sha/sha512.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/internal.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/obj/obj_dat.h" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/pkcs7/internal.h" role="src" />
diff --git a/setup.py b/setup.py
index 4f67f82..7c07c56 100644
--- a/setup.py
+++ b/setup.py
@@ -118,6 +118,7 @@
     EXTRA_ENV_COMPILE_ARGS += ' -std=c++11 -std=gnu99 -fvisibility=hidden -fno-wrapv -fno-exceptions'
   elif "darwin" in sys.platform:
     EXTRA_ENV_COMPILE_ARGS += ' -fvisibility=hidden -fno-wrapv -fno-exceptions'
+EXTRA_ENV_COMPILE_ARGS += ' -DPB_FIELD_16BIT'	
 
 if EXTRA_ENV_LINK_ARGS is None:
   EXTRA_ENV_LINK_ARGS = ''
@@ -160,7 +161,7 @@
 
 DEFINE_MACROS = (
     ('OPENSSL_NO_ASM', 1), ('_WIN32_WINNT', 0x600),
-    ('GPR_BACKWARDS_COMPATIBILITY_MODE', 1),)
+    ('GPR_BACKWARDS_COMPATIBILITY_MODE', 1))
 if "win32" in sys.platform:
   # TODO(zyc): Re-enble c-ares on x64 and x86 windows after fixing the
   # ares_library_init compilation issue
diff --git a/src/boringssl/gen_build_yaml.py b/src/boringssl/gen_build_yaml.py
index 221cbe7..593b66d 100755
--- a/src/boringssl/gen_build_yaml.py
+++ b/src/boringssl/gen_build_yaml.py
@@ -67,7 +67,9 @@
             ),
             'headers': sorted(
               map_dir(f)
-              for f in files['ssl_headers'] + files['ssl_internal_headers'] + files['crypto_headers'] + files['crypto_internal_headers']
+              # We want to include files['fips_fragments'], but not build them as objects.
+              # See https://boringssl-review.googlesource.com/c/boringssl/+/16946
+              for f in files['ssl_headers'] + files['ssl_internal_headers'] + files['crypto_headers'] + files['crypto_internal_headers'] + files['fips_fragments']
             ),
             'boringssl': True,
             'defaults': 'boringssl',
diff --git a/src/compiler/config.h b/src/compiler/config.h
index 36d28df..cfdc303 100644
--- a/src/compiler/config.h
+++ b/src/compiler/config.h
@@ -19,7 +19,7 @@
 #ifndef SRC_COMPILER_CONFIG_H
 #define SRC_COMPILER_CONFIG_H
 
-#include <grpc++/impl/codegen/config_protobuf.h>
+#include <grpcpp/impl/codegen/config_protobuf.h>
 
 #ifndef GRPC_CUSTOM_CODEGENERATOR
 #include <google/protobuf/compiler/code_generator.h>
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc
index 3a15a60..30d66b4 100644
--- a/src/compiler/cpp_generator.cc
+++ b/src/compiler/cpp_generator.cc
@@ -128,15 +128,15 @@
                     "");
     }
     static const char* headers_strs[] = {
-        "grpc++/impl/codegen/async_stream.h",
-        "grpc++/impl/codegen/async_unary_call.h",
-        "grpc++/impl/codegen/method_handler_impl.h",
-        "grpc++/impl/codegen/proto_utils.h",
-        "grpc++/impl/codegen/rpc_method.h",
-        "grpc++/impl/codegen/service_type.h",
-        "grpc++/impl/codegen/status.h",
-        "grpc++/impl/codegen/stub_options.h",
-        "grpc++/impl/codegen/sync_stream.h"};
+        "grpcpp/impl/codegen/async_stream.h",
+        "grpcpp/impl/codegen/async_unary_call.h",
+        "grpcpp/impl/codegen/method_handler_impl.h",
+        "grpcpp/impl/codegen/proto_utils.h",
+        "grpcpp/impl/codegen/rpc_method.h",
+        "grpcpp/impl/codegen/service_type.h",
+        "grpcpp/impl/codegen/status.h",
+        "grpcpp/impl/codegen/stub_options.h",
+        "grpcpp/impl/codegen/sync_stream.h"};
     std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
     PrintIncludes(printer.get(), headers, params.use_system_headers,
                   params.grpc_search_path);
@@ -1148,14 +1148,14 @@
     std::map<grpc::string, grpc::string> vars;
 
     static const char* headers_strs[] = {
-        "grpc++/impl/codegen/async_stream.h",
-        "grpc++/impl/codegen/async_unary_call.h",
-        "grpc++/impl/codegen/channel_interface.h",
-        "grpc++/impl/codegen/client_unary_call.h",
-        "grpc++/impl/codegen/method_handler_impl.h",
-        "grpc++/impl/codegen/rpc_service_method.h",
-        "grpc++/impl/codegen/service_type.h",
-        "grpc++/impl/codegen/sync_stream.h"};
+        "grpcpp/impl/codegen/async_stream.h",
+        "grpcpp/impl/codegen/async_unary_call.h",
+        "grpcpp/impl/codegen/channel_interface.h",
+        "grpcpp/impl/codegen/client_unary_call.h",
+        "grpcpp/impl/codegen/method_handler_impl.h",
+        "grpcpp/impl/codegen/rpc_service_method.h",
+        "grpcpp/impl/codegen/service_type.h",
+        "grpcpp/impl/codegen/sync_stream.h"};
     std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
     PrintIncludes(printer.get(), headers, params.use_system_headers,
                   params.grpc_search_path);
@@ -1571,8 +1571,8 @@
     std::map<grpc::string, grpc::string> vars;
 
     static const char* headers_strs[] = {
-        "grpc++/impl/codegen/async_stream.h",
-        "grpc++/impl/codegen/sync_stream.h",
+        "grpcpp/impl/codegen/async_stream.h",
+        "grpcpp/impl/codegen/sync_stream.h",
     };
     std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
     PrintIncludes(printer.get(), headers, params.use_system_headers,
diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index 7c97056..6e27305 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -451,8 +451,10 @@
       out->Print(
           "public virtual $response$ $methodname$($request$ request, "
           "grpc::Metadata "
-          "headers = null, DateTime? deadline = null, CancellationToken "
-          "cancellationToken = default(CancellationToken))\n",
+          "headers = null, global::System.DateTime? deadline = null, "
+          "global::System.Threading.CancellationToken "
+          "cancellationToken = "
+          "default(global::System.Threading.CancellationToken))\n",
           "methodname", method->name(), "request",
           GetClassName(method->input_type()), "response",
           GetClassName(method->output_type()));
@@ -492,8 +494,10 @@
     out->Print(
         "public virtual $returntype$ "
         "$methodname$($request_maybe$grpc::Metadata "
-        "headers = null, DateTime? deadline = null, CancellationToken "
-        "cancellationToken = default(CancellationToken))\n",
+        "headers = null, global::System.DateTime? deadline = null, "
+        "global::System.Threading.CancellationToken "
+        "cancellationToken = "
+        "default(global::System.Threading.CancellationToken))\n",
         "methodname", method_name, "request_maybe",
         GetMethodRequestParamMaybe(method), "returntype",
         GetMethodReturnTypeClient(method));
@@ -675,9 +679,6 @@
     out.Print("#pragma warning disable 1591\n");
     out.Print("#region Designer generated code\n");
     out.Print("\n");
-    out.Print("using System;\n");
-    out.Print("using System.Threading;\n");
-    out.Print("using System.Threading.Tasks;\n");
     out.Print("using grpc = global::Grpc.Core;\n");
     out.Print("\n");
 
diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc
index ab7d869..ffdeb8f 100644
--- a/src/compiler/objective_c_generator.cc
+++ b/src/compiler/objective_c_generator.cc
@@ -212,37 +212,49 @@
   return output;
 }
 
-::grpc::string GetHeader(const ServiceDescriptor* service) {
+::grpc::string GetProtocol(const ServiceDescriptor* service) {
   ::grpc::string output;
-  {
-    // Scope the output stream so it closes and finalizes output to the string.
-    grpc::protobuf::io::StringOutputStream output_stream(&output);
-    Printer printer(&output_stream, '$');
 
-    map< ::grpc::string, ::grpc::string> vars = {
-        {"service_class", ServiceClassName(service)}};
+  // Scope the output stream so it closes and finalizes output to the string.
+  grpc::protobuf::io::StringOutputStream output_stream(&output);
+  Printer printer(&output_stream, '$');
 
-    printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
+  map< ::grpc::string, ::grpc::string> vars = {
+      {"service_class", ServiceClassName(service)}};
 
-    for (int i = 0; i < service->method_count(); i++) {
-      PrintMethodDeclarations(&printer, service->method(i));
-    }
-    printer.Print("@end\n\n");
-
-    printer.Print(
-        "/**\n"
-        " * Basic service implementation, over gRPC, that only does\n"
-        " * marshalling and parsing.\n"
-        " */\n");
-    printer.Print(vars,
-                  "@interface $service_class$ :"
-                  " GRPCProtoService<$service_class$>\n");
-    printer.Print(
-        "- (instancetype)initWithHost:(NSString *)host"
-        " NS_DESIGNATED_INITIALIZER;\n");
-    printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
-    printer.Print("@end\n");
+  printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
+  for (int i = 0; i < service->method_count(); i++) {
+    PrintMethodDeclarations(&printer, service->method(i));
   }
+  printer.Print("@end\n\n");
+
+  return output;
+}
+
+::grpc::string GetInterface(const ServiceDescriptor* service) {
+  ::grpc::string output;
+
+  // Scope the output stream so it closes and finalizes output to the string.
+  grpc::protobuf::io::StringOutputStream output_stream(&output);
+  Printer printer(&output_stream, '$');
+
+  map< ::grpc::string, ::grpc::string> vars = {
+      {"service_class", ServiceClassName(service)}};
+
+  printer.Print(vars,
+                "/**\n"
+                " * Basic service implementation, over gRPC, that only does\n"
+                " * marshalling and parsing.\n"
+                " */\n");
+  printer.Print(vars,
+                "@interface $service_class$ :"
+                " GRPCProtoService<$service_class$>\n");
+  printer.Print(
+      "- (instancetype)initWithHost:(NSString *)host"
+      " NS_DESIGNATED_INITIALIZER;\n");
+  printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
+  printer.Print("@end\n");
+
   return output;
 }
 
@@ -258,26 +270,32 @@
         {"service_class", ServiceClassName(service)},
         {"package", service->file()->package()}};
 
-    printer.Print(vars, "@implementation $service_class$\n\n");
+    printer.Print(vars,
+                  "@implementation $service_class$\n\n"
+                  "// Designated initializer\n"
+                  "- (instancetype)initWithHost:(NSString *)host {\n"
+                  "  self = [super initWithHost:host\n"
+                  "                 packageName:@\"$package$\"\n"
+                  "                 serviceName:@\"$service_name$\"];\n"
+                  "  return self;\n"
+                  "}\n\n");
 
-    printer.Print("// Designated initializer\n");
-    printer.Print("- (instancetype)initWithHost:(NSString *)host {\n");
-    printer.Print(
-        vars,
-        "  return (self = [super initWithHost:host"
-        " packageName:@\"$package$\" serviceName:@\"$service_name$\"]);\n");
-    printer.Print("}\n\n");
     printer.Print(
         "// Override superclass initializer to disallow different"
-        " package and service names.\n");
-    printer.Print("- (instancetype)initWithHost:(NSString *)host\n");
-    printer.Print("                 packageName:(NSString *)packageName\n");
-    printer.Print("                 serviceName:(NSString *)serviceName {\n");
-    printer.Print("  return [self initWithHost:host];\n");
-    printer.Print("}\n\n");
-    printer.Print("+ (instancetype)serviceWithHost:(NSString *)host {\n");
-    printer.Print("  return [[self alloc] initWithHost:host];\n");
-    printer.Print("}\n\n\n");
+        " package and service names.\n"
+        "- (instancetype)initWithHost:(NSString *)host\n"
+        "                 packageName:(NSString *)packageName\n"
+        "                 serviceName:(NSString *)serviceName {\n"
+        "  return [self initWithHost:host];\n"
+        "}\n\n");
+
+    printer.Print(
+        "#pragma mark - Class Methods\n\n"
+        "+ (instancetype)serviceWithHost:(NSString *)host {\n"
+        "  return [[self alloc] initWithHost:host];\n"
+        "}\n\n");
+
+    printer.Print("#pragma mark - Method Implementations\n\n");
 
     for (int i = 0; i < service->method_count(); i++) {
       PrintMethodImplementations(&printer, service->method(i));
diff --git a/src/compiler/objective_c_generator.h b/src/compiler/objective_c_generator.h
index d3aed76..eb1c7ff 100644
--- a/src/compiler/objective_c_generator.h
+++ b/src/compiler/objective_c_generator.h
@@ -31,9 +31,13 @@
 // Returns forward declaration of classes in the generated header file.
 string GetAllMessageClasses(const FileDescriptor* file);
 
-// Returns the content to be included in the "global_scope" insertion point of
-// the generated header file.
-string GetHeader(const ServiceDescriptor* service);
+// Returns the content to be included defining the @protocol segment at the
+// insertion point of the generated implementation file.
+string GetProtocol(const ServiceDescriptor* service);
+
+// Returns the content to be included defining the @interface segment at the
+// insertion point of the generated implementation file.
+string GetInterface(const ServiceDescriptor* service);
 
 // Returns the content to be included in the "global_scope" insertion point of
 // the generated implementation file.
diff --git a/src/compiler/objective_c_generator_helpers.h b/src/compiler/objective_c_generator_helpers.h
index 4004e6a..a284da9 100644
--- a/src/compiler/objective_c_generator_helpers.h
+++ b/src/compiler/objective_c_generator_helpers.h
@@ -40,5 +40,45 @@
   string prefix = file->options().objc_class_prefix();
   return prefix + service->name();
 }
+
+inline ::grpc::string LocalImport(const ::grpc::string& import) {
+  return ::grpc::string("#import \"" + import + "\"\n");
+}
+
+inline ::grpc::string SystemImport(const ::grpc::string& import) {
+  return ::grpc::string("#import <" + import + ">\n");
+}
+
+inline ::grpc::string PreprocConditional(::grpc::string symbol, bool invert) {
+  return invert ? "!defined(" + symbol + ") || !" + symbol
+                : "defined(" + symbol + ") && " + symbol;
+}
+
+inline ::grpc::string PreprocIf(const ::grpc::string& symbol,
+                                const ::grpc::string& if_true) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" +
+                        if_true + "#endif\n");
+}
+
+inline ::grpc::string PreprocIfNot(const ::grpc::string& symbol,
+                                   const ::grpc::string& if_true) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" +
+                        if_true + "#endif\n");
+}
+
+inline ::grpc::string PreprocIfElse(const ::grpc::string& symbol,
+                                    const ::grpc::string& if_true,
+                                    const ::grpc::string& if_false) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" +
+                        if_true + "#else\n" + if_false + "#endif\n");
+}
+
+inline ::grpc::string PreprocIfNotElse(const ::grpc::string& symbol,
+                                       const ::grpc::string& if_true,
+                                       const ::grpc::string& if_false) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" +
+                        if_true + "#else\n" + if_false + "#endif\n");
+}
+
 }  // namespace grpc_objective_c_generator
 #endif  // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H
diff --git a/src/compiler/objective_c_plugin.cc b/src/compiler/objective_c_plugin.cc
index d5d488e..76703d7 100644
--- a/src/compiler/objective_c_plugin.cc
+++ b/src/compiler/objective_c_plugin.cc
@@ -29,12 +29,42 @@
 using ::google::protobuf::compiler::objectivec::
     IsProtobufLibraryBundledProtoFile;
 using ::google::protobuf::compiler::objectivec::ProtobufLibraryFrameworkName;
+using ::grpc_objective_c_generator::LocalImport;
+using ::grpc_objective_c_generator::PreprocIfElse;
+using ::grpc_objective_c_generator::PreprocIfNot;
+using ::grpc_objective_c_generator::SystemImport;
+
+namespace {
+
+inline ::grpc::string ImportProtoHeaders(
+    const grpc::protobuf::FileDescriptor* dep, const char* indent) {
+  ::grpc::string header = grpc_objective_c_generator::MessageHeaderName(dep);
+
+  if (!IsProtobufLibraryBundledProtoFile(dep)) {
+    return indent + LocalImport(header);
+  }
+
+  ::grpc::string base_name = header;
+  grpc_generator::StripPrefix(&base_name, "google/protobuf/");
+  // create the import code snippet
+  ::grpc::string framework_header =
+      ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name;
+
+  static const ::grpc::string kFrameworkImportsCondition =
+      "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS";
+  return PreprocIfElse(kFrameworkImportsCondition,
+                       indent + SystemImport(framework_header),
+                       indent + LocalImport(header));
+}
+
+}  // namespace
 
 class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
  public:
   ObjectiveCGrpcGenerator() {}
   virtual ~ObjectiveCGrpcGenerator() {}
 
+ public:
   virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
                         const ::grpc::string& parameter,
                         grpc::protobuf::compiler::GeneratorContext* context,
@@ -44,97 +74,68 @@
       return true;
     }
 
+    static const ::grpc::string kNonNullBegin = "NS_ASSUME_NONNULL_BEGIN\n";
+    static const ::grpc::string kNonNullEnd = "NS_ASSUME_NONNULL_END\n";
+    static const ::grpc::string kProtocolOnly = "GPB_GRPC_PROTOCOL_ONLY";
+    static const ::grpc::string kForwardDeclare =
+        "GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO";
+
     ::grpc::string file_name =
         google::protobuf::compiler::objectivec::FilePath(file);
-    ::grpc::string prefix = file->options().objc_class_prefix();
 
     {
       // Generate .pbrpc.h
 
-      ::grpc::string imports =
-          ::grpc::string("#if !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO\n") +
-          "#import \"" + file_name +
-          ".pbobjc.h\"\n"
-          "#endif\n\n"
-          "#import <ProtoRPC/ProtoService.h>\n"
-          "#import <ProtoRPC/ProtoRPC.h>\n"
-          "#import <RxLibrary/GRXWriteable.h>\n"
-          "#import <RxLibrary/GRXWriter.h>\n";
+      ::grpc::string imports = LocalImport(file_name + ".pbobjc.h");
 
-      ::grpc::string proto_imports;
-      proto_imports += "#if GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO\n" +
-                       grpc_objective_c_generator::GetAllMessageClasses(file) +
-                       "#else\n";
+      ::grpc::string system_imports = SystemImport("ProtoRPC/ProtoService.h") +
+                                      SystemImport("ProtoRPC/ProtoRPC.h") +
+                                      SystemImport("RxLibrary/GRXWriteable.h") +
+                                      SystemImport("RxLibrary/GRXWriter.h");
+
+      ::grpc::string forward_declarations = "@class GRPCProtoCall;\n\n";
+
+      ::grpc::string class_declarations =
+          grpc_objective_c_generator::GetAllMessageClasses(file);
+
+      ::grpc::string class_imports;
       for (int i = 0; i < file->dependency_count(); i++) {
-        ::grpc::string header =
-            grpc_objective_c_generator::MessageHeaderName(file->dependency(i));
-        const grpc::protobuf::FileDescriptor* dependency = file->dependency(i);
-        if (IsProtobufLibraryBundledProtoFile(dependency)) {
-          ::grpc::string base_name = header;
-          grpc_generator::StripPrefix(&base_name, "google/protobuf/");
-          // create the import code snippet
-          proto_imports +=
-              "  #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS\n"
-              "    #import <" +
-              ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name +
-              ">\n"
-              "  #else\n"
-              "    #import \"" +
-              header +
-              "\"\n"
-              "  #endif\n";
-        } else {
-          proto_imports += ::grpc::string("  #import \"") + header + "\"\n";
-        }
+        class_imports += ImportProtoHeaders(file->dependency(i), "  ");
       }
-      proto_imports += "#endif\n";
 
-      ::grpc::string declarations;
+      ::grpc::string protocols;
       for (int i = 0; i < file->service_count(); i++) {
         const grpc::protobuf::ServiceDescriptor* service = file->service(i);
-        declarations += grpc_objective_c_generator::GetHeader(service);
+        protocols += grpc_objective_c_generator::GetProtocol(service);
       }
 
-      static const ::grpc::string kNonNullBegin =
-          "\nNS_ASSUME_NONNULL_BEGIN\n\n";
-      static const ::grpc::string kNonNullEnd = "\nNS_ASSUME_NONNULL_END\n";
+      ::grpc::string interfaces;
+      for (int i = 0; i < file->service_count(); i++) {
+        const grpc::protobuf::ServiceDescriptor* service = file->service(i);
+        interfaces += grpc_objective_c_generator::GetInterface(service);
+      }
 
       Write(context, file_name + ".pbrpc.h",
-            imports + '\n' + proto_imports + '\n' + kNonNullBegin +
-                declarations + kNonNullEnd);
+            PreprocIfNot(kForwardDeclare, imports) + "\n" +
+                PreprocIfNot(kProtocolOnly, system_imports) + "\n" +
+                PreprocIfElse(kForwardDeclare, class_declarations,
+                              class_imports) +
+                "\n" + forward_declarations + "\n" + kNonNullBegin + "\n" +
+                protocols + "\n" + PreprocIfNot(kProtocolOnly, interfaces) +
+                "\n" + kNonNullEnd + "\n");
     }
 
     {
       // Generate .pbrpc.m
 
-      ::grpc::string imports = ::grpc::string("#import \"") + file_name +
-                               ".pbrpc.h\"\n"
-                               "#import \"" +
-                               file_name +
-                               ".pbobjc.h\"\n\n"
-                               "#import <ProtoRPC/ProtoRPC.h>\n"
-                               "#import <RxLibrary/GRXWriter+Immediate.h>\n";
+      ::grpc::string imports = LocalImport(file_name + ".pbrpc.h") +
+                               LocalImport(file_name + ".pbobjc.h") +
+                               SystemImport("ProtoRPC/ProtoRPC.h") +
+                               SystemImport("RxLibrary/GRXWriter+Immediate.h");
+
+      ::grpc::string class_imports;
       for (int i = 0; i < file->dependency_count(); i++) {
-        ::grpc::string header =
-            grpc_objective_c_generator::MessageHeaderName(file->dependency(i));
-        const grpc::protobuf::FileDescriptor* dependency = file->dependency(i);
-        if (IsProtobufLibraryBundledProtoFile(dependency)) {
-          ::grpc::string base_name = header;
-          grpc_generator::StripPrefix(&base_name, "google/protobuf/");
-          // create the import code snippet
-          imports +=
-              "#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS\n"
-              "  #import <" +
-              ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name +
-              ">\n"
-              "#else\n"
-              "  #import \"" +
-              header +
-              "\"\n"
-              "#endif\n";
-        } else {
-          imports += ::grpc::string("#import \"") + header + "\"\n";
-        }
+        class_imports += ImportProtoHeaders(file->dependency(i), "");
       }
 
       ::grpc::string definitions;
@@ -143,7 +144,9 @@
         definitions += grpc_objective_c_generator::GetSource(service);
       }
 
-      Write(context, file_name + ".pbrpc.m", imports + '\n' + definitions);
+      Write(context, file_name + ".pbrpc.m",
+            PreprocIfNot(kProtocolOnly,
+                         imports + "\n" + class_imports + "\n" + definitions));
     }
 
     return true;
diff --git a/src/compiler/php_generator.cc b/src/compiler/php_generator.cc
index d9705e8..ca084cc 100644
--- a/src/compiler/php_generator.cc
+++ b/src/compiler/php_generator.cc
@@ -18,10 +18,12 @@
 
 #include <map>
 
+#include <google/protobuf/compiler/php/php_generator.h>
 #include "src/compiler/config.h"
 #include "src/compiler/generator_helpers.h"
 #include "src/compiler/php_generator_helpers.h"
 
+using google::protobuf::compiler::php::GeneratedClassName;
 using grpc::protobuf::Descriptor;
 using grpc::protobuf::FileDescriptor;
 using grpc::protobuf::MethodDescriptor;
@@ -55,8 +57,10 @@
                                    const FileDescriptor* file) {
   std::vector<grpc::string> tokens = grpc_generator::tokenize(name, ".");
   std::ostringstream oss;
-  oss << PackageName(file) << "\\"
-      << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]);
+  if (PackageName(file) != "") {
+    oss << PackageName(file) << "\\";
+  }
+  oss << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]);
   return oss.str();
 }
 
@@ -67,9 +71,9 @@
   vars["service_name"] = method->service()->full_name();
   vars["name"] = method->name();
   vars["input_type_id"] =
-      MessageIdentifierName(input_type->full_name(), input_type->file());
-  vars["output_type_id"] =
-      MessageIdentifierName(output_type->full_name(), output_type->file());
+      MessageIdentifierName(GeneratedClassName(input_type), input_type->file());
+  vars["output_type_id"] = MessageIdentifierName(
+      GeneratedClassName(output_type), output_type->file());
 
   out->Print("/**\n");
   out->Print(GetPHPComments(method, " *").c_str());
diff --git a/src/core/ext/census/grpc_context.cc b/src/core/ext/census/grpc_context.cc
index 069e8f1..599a798 100644
--- a/src/core/ext/census/grpc_context.cc
+++ b/src/core/ext/census/grpc_context.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/census.h>
 #include <grpc/grpc.h>
 #include "src/core/lib/surface/api_trace.h"
diff --git a/src/core/ext/filters/client_channel/backup_poller.cc b/src/core/ext/filters/client_channel/backup_poller.cc
index ee90b49..3e2faa5 100644
--- a/src/core/ext/filters/client_channel/backup_poller.cc
+++ b/src/core/ext/filters/client_channel/backup_poller.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2017 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/backup_poller.h"
 
 #include <grpc/grpc.h>
@@ -125,13 +127,7 @@
                   &p->run_poller_closure);
 }
 
-void grpc_client_channel_start_backup_polling(
-    grpc_pollset_set* interested_parties) {
-  gpr_once_init(&g_once, init_globals);
-  if (g_poll_interval_ms == 0) {
-    return;
-  }
-  gpr_mu_lock(&g_poller_mu);
+static void g_poller_init_locked() {
   if (g_poller == nullptr) {
     g_poller = static_cast<backup_poller*>(gpr_zalloc(sizeof(backup_poller)));
     g_poller->pollset =
@@ -147,7 +143,16 @@
                     grpc_core::ExecCtx::Get()->Now() + g_poll_interval_ms,
                     &g_poller->run_poller_closure);
   }
+}
 
+void grpc_client_channel_start_backup_polling(
+    grpc_pollset_set* interested_parties) {
+  gpr_once_init(&g_once, init_globals);
+  if (g_poll_interval_ms == 0) {
+    return;
+  }
+  gpr_mu_lock(&g_poller_mu);
+  g_poller_init_locked();
   gpr_ref(&g_poller->refs);
   /* Get a reference to g_poller->pollset before releasing g_poller_mu to make
    * TSAN happy. Otherwise, reading from g_poller (i.e g_poller->pollset) after
diff --git a/src/core/ext/filters/client_channel/backup_poller.h b/src/core/ext/filters/client_channel/backup_poller.h
index 551e033..7285b9b 100644
--- a/src/core/ext/filters/client_channel/backup_poller.h
+++ b/src/core/ext/filters/client_channel/backup_poller.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015 gRPC authors.
+ * Copyright 2017 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc
index 31a5c31..37860e8 100644
--- a/src/core/ext/filters/client_channel/channel_connectivity.cc
+++ b/src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/channel.h"
 
 #include <inttypes.h>
diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc
index 7b04bdc..90b93fb 100644
--- a/src/core/ext/filters/client_channel/client_channel.cc
+++ b/src/core/ext/filters/client_channel/client_channel.cc
@@ -21,6 +21,7 @@
 #include "src/core/ext/filters/client_channel/client_channel.h"
 
 #include <inttypes.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
@@ -33,153 +34,74 @@
 #include "src/core/ext/filters/client_channel/backup_poller.h"
 #include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/method_params.h"
 #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/ext/filters/client_channel/retry_throttle.h"
+#include "src/core/ext/filters/client_channel/status_util.h"
 #include "src/core/ext/filters/client_channel/subchannel.h"
 #include "src/core/ext/filters/deadline/deadline_filter.h"
+#include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/metadata_batch.h"
 #include "src/core/lib/transport/service_config.h"
 #include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_metadata.h"
+
+using grpc_core::internal::ClientChannelMethodParams;
 
 /* Client channel implementation */
 
+// By default, we buffer 256 KiB per RPC for retries.
+// TODO(roth): Do we have any data to suggest a better value?
+#define DEFAULT_PER_RPC_RETRY_BUFFER_SIZE (256 << 10)
+
+// This value was picked arbitrarily.  It can be changed if there is
+// any even moderately compelling reason to do so.
+#define RETRY_BACKOFF_JITTER 0.2
+
 grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel");
 
 /*************************************************************************
- * METHOD-CONFIG TABLE
- */
-
-typedef enum {
-  /* zero so it can be default initialized */
-  WAIT_FOR_READY_UNSET = 0,
-  WAIT_FOR_READY_FALSE,
-  WAIT_FOR_READY_TRUE
-} wait_for_ready_value;
-
-typedef struct {
-  gpr_refcount refs;
-  grpc_millis timeout;
-  wait_for_ready_value wait_for_ready;
-} method_parameters;
-
-static method_parameters* method_parameters_ref(
-    method_parameters* method_params) {
-  gpr_ref(&method_params->refs);
-  return method_params;
-}
-
-static void method_parameters_unref(method_parameters* method_params) {
-  if (gpr_unref(&method_params->refs)) {
-    gpr_free(method_params);
-  }
-}
-
-// Wrappers to pass to grpc_service_config_create_method_config_table().
-static void* method_parameters_ref_wrapper(void* value) {
-  return method_parameters_ref(static_cast<method_parameters*>(value));
-}
-static void method_parameters_unref_wrapper(void* value) {
-  method_parameters_unref(static_cast<method_parameters*>(value));
-}
-
-static bool parse_wait_for_ready(grpc_json* field,
-                                 wait_for_ready_value* wait_for_ready) {
-  if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
-    return false;
-  }
-  *wait_for_ready = field->type == GRPC_JSON_TRUE ? WAIT_FOR_READY_TRUE
-                                                  : WAIT_FOR_READY_FALSE;
-  return true;
-}
-
-static bool parse_timeout(grpc_json* field, grpc_millis* timeout) {
-  if (field->type != GRPC_JSON_STRING) return false;
-  size_t len = strlen(field->value);
-  if (field->value[len - 1] != 's') return false;
-  char* buf = gpr_strdup(field->value);
-  buf[len - 1] = '\0';  // Remove trailing 's'.
-  char* decimal_point = strchr(buf, '.');
-  int nanos = 0;
-  if (decimal_point != nullptr) {
-    *decimal_point = '\0';
-    nanos = gpr_parse_nonnegative_int(decimal_point + 1);
-    if (nanos == -1) {
-      gpr_free(buf);
-      return false;
-    }
-    int num_digits = static_cast<int>(strlen(decimal_point + 1));
-    if (num_digits > 9) {  // We don't accept greater precision than nanos.
-      gpr_free(buf);
-      return false;
-    }
-    for (int i = 0; i < (9 - num_digits); ++i) {
-      nanos *= 10;
-    }
-  }
-  int seconds = decimal_point == buf ? 0 : gpr_parse_nonnegative_int(buf);
-  gpr_free(buf);
-  if (seconds == -1) return false;
-  *timeout = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
-  return true;
-}
-
-static void* method_parameters_create_from_json(const grpc_json* json) {
-  wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET;
-  grpc_millis timeout = 0;
-  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
-    if (field->key == nullptr) continue;
-    if (strcmp(field->key, "waitForReady") == 0) {
-      if (wait_for_ready != WAIT_FOR_READY_UNSET) return nullptr;  // Duplicate.
-      if (!parse_wait_for_ready(field, &wait_for_ready)) return nullptr;
-    } else if (strcmp(field->key, "timeout") == 0) {
-      if (timeout > 0) return nullptr;  // Duplicate.
-      if (!parse_timeout(field, &timeout)) return nullptr;
-    }
-  }
-  method_parameters* value =
-      static_cast<method_parameters*>(gpr_malloc(sizeof(method_parameters)));
-  gpr_ref_init(&value->refs, 1);
-  value->timeout = timeout;
-  value->wait_for_ready = wait_for_ready;
-  return value;
-}
-
-struct external_connectivity_watcher;
-
-/*************************************************************************
  * CHANNEL-WIDE FUNCTIONS
  */
 
+struct external_connectivity_watcher;
+
+typedef grpc_core::SliceHashTable<
+    grpc_core::RefCountedPtr<ClientChannelMethodParams>>
+    MethodParamsTable;
+
 typedef struct client_channel_channel_data {
-  /** resolver for this channel */
   grpc_core::OrphanablePtr<grpc_core::Resolver> resolver;
-  /** have we started resolving this channel */
   bool started_resolving;
-  /** is deadline checking enabled? */
   bool deadline_checking_enabled;
-  /** client channel factory */
   grpc_client_channel_factory* client_channel_factory;
+  bool enable_retries;
+  size_t per_rpc_retry_buffer_size;
 
   /** combiner protecting all variables below in this data structure */
   grpc_combiner* combiner;
   /** currently active load balancer */
-  grpc_lb_policy* lb_policy;
+  grpc_core::OrphanablePtr<grpc_core::LoadBalancingPolicy> lb_policy;
   /** retry throttle data */
   grpc_server_retry_throttle_data* retry_throttle_data;
   /** maps method names to method_parameters structs */
-  grpc_slice_hash_table* method_params_table;
+  grpc_core::RefCountedPtr<MethodParamsTable> method_params_table;
   /** incoming resolver result - set by resolver.next() */
   grpc_channel_args* resolver_result;
   /** a list of closures that are all waiting for resolver result to come in */
@@ -200,7 +122,7 @@
   gpr_mu external_connectivity_watcher_list_mu;
   struct external_connectivity_watcher* external_connectivity_watcher_list_head;
 
-  /* the following properties are guarded by a mutex since API's require them
+  /* the following properties are guarded by a mutex since APIs require them
      to be instantaneously available */
   gpr_mu info_mu;
   char* info_lb_policy_name;
@@ -212,7 +134,7 @@
   channel_data* chand;
   /** used as an identifier, don't dereference it because the LB policy may be
    * non-existing when the callback is run */
-  grpc_lb_policy* lb_policy;
+  grpc_core::LoadBalancingPolicy* lb_policy;
   grpc_closure closure;
 } reresolution_request_args;
 
@@ -223,11 +145,11 @@
   channel_data* chand;
   grpc_closure on_changed;
   grpc_connectivity_state state;
-  grpc_lb_policy* lb_policy;
+  grpc_core::LoadBalancingPolicy* lb_policy;
 } lb_policy_connectivity_watcher;
 
 static void watch_lb_policy_locked(channel_data* chand,
-                                   grpc_lb_policy* lb_policy,
+                                   grpc_core::LoadBalancingPolicy* lb_policy,
                                    grpc_connectivity_state current_state);
 
 static void set_channel_connectivity_state_locked(channel_data* chand,
@@ -241,15 +163,13 @@
   if (chand->lb_policy != nullptr) {
     if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
       /* cancel picks with wait_for_ready=false */
-      grpc_lb_policy_cancel_picks_locked(
-          chand->lb_policy,
+      chand->lb_policy->CancelMatchingPicksLocked(
           /* mask= */ GRPC_INITIAL_METADATA_WAIT_FOR_READY,
           /* check= */ 0, GRPC_ERROR_REF(error));
     } else if (state == GRPC_CHANNEL_SHUTDOWN) {
       /* cancel all picks */
-      grpc_lb_policy_cancel_picks_locked(chand->lb_policy,
-                                         /* mask= */ 0, /* check= */ 0,
-                                         GRPC_ERROR_REF(error));
+      chand->lb_policy->CancelMatchingPicksLocked(/* mask= */ 0, /* check= */ 0,
+                                                  GRPC_ERROR_REF(error));
     }
   }
   if (grpc_client_channel_trace.enabled()) {
@@ -263,7 +183,7 @@
   lb_policy_connectivity_watcher* w =
       static_cast<lb_policy_connectivity_watcher*>(arg);
   /* check if the notification is for the latest policy */
-  if (w->lb_policy == w->chand->lb_policy) {
+  if (w->lb_policy == w->chand->lb_policy.get()) {
     if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p: lb_policy=%p state changed to %s", w->chand,
               w->lb_policy, grpc_connectivity_state_name(w->state));
@@ -279,7 +199,7 @@
 }
 
 static void watch_lb_policy_locked(channel_data* chand,
-                                   grpc_lb_policy* lb_policy,
+                                   grpc_core::LoadBalancingPolicy* lb_policy,
                                    grpc_connectivity_state current_state) {
   lb_policy_connectivity_watcher* w =
       static_cast<lb_policy_connectivity_watcher*>(gpr_malloc(sizeof(*w)));
@@ -289,8 +209,7 @@
                     grpc_combiner_scheduler(chand->combiner));
   w->state = current_state;
   w->lb_policy = lb_policy;
-  grpc_lb_policy_notify_on_state_change_locked(lb_policy, &w->state,
-                                               &w->on_changed);
+  lb_policy->NotifyOnStateChangeLocked(&w->state, &w->on_changed);
 }
 
 static void start_resolving_locked(channel_data* chand) {
@@ -309,9 +228,8 @@
   grpc_server_retry_throttle_data* retry_throttle_data;
 } service_config_parsing_state;
 
-static void parse_retry_throttle_params(const grpc_json* field, void* arg) {
-  service_config_parsing_state* parsing_state =
-      static_cast<service_config_parsing_state*>(arg);
+static void parse_retry_throttle_params(
+    const grpc_json* field, service_config_parsing_state* parsing_state) {
   if (strcmp(field->key, "retryThrottling") == 0) {
     if (parsing_state->retry_throttle_data != nullptr) return;  // Duplicate.
     if (field->type != GRPC_JSON_OBJECT) return;
@@ -371,7 +289,7 @@
   channel_data* chand = args->chand;
   // If this invocation is for a stale LB policy, treat it as an LB shutdown
   // signal.
-  if (args->lb_policy != chand->lb_policy || error != GRPC_ERROR_NONE ||
+  if (args->lb_policy != chand->lb_policy.get() || error != GRPC_ERROR_NONE ||
       chand->resolver == nullptr) {
     GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "re-resolution");
     gpr_free(args);
@@ -382,7 +300,7 @@
   }
   chand->resolver->RequestReresolutionLocked();
   // Give back the closure to the LB policy.
-  grpc_lb_policy_set_reresolve_closure_locked(chand->lb_policy, &args->closure);
+  chand->lb_policy->SetReresolutionClosureLocked(&args->closure);
 }
 
 static void on_resolver_result_changed_locked(void* arg, grpc_error* error) {
@@ -391,24 +309,21 @@
     gpr_log(GPR_DEBUG, "chand=%p: got resolver result: error=%s", chand,
             grpc_error_string(error));
   }
-  // Extract the following fields from the resolver result, if non-NULL.
+  // Extract the following fields from the resolver result, if non-nullptr.
   bool lb_policy_updated = false;
+  bool lb_policy_created = false;
   char* lb_policy_name_dup = nullptr;
   bool lb_policy_name_changed = false;
-  grpc_lb_policy* new_lb_policy = nullptr;
+  grpc_core::OrphanablePtr<grpc_core::LoadBalancingPolicy> new_lb_policy;
   char* service_config_json = nullptr;
   grpc_server_retry_throttle_data* retry_throttle_data = nullptr;
-  grpc_slice_hash_table* method_params_table = nullptr;
+  grpc_core::RefCountedPtr<MethodParamsTable> method_params_table;
   if (chand->resolver_result != nullptr) {
     if (chand->resolver != nullptr) {
       // Find LB policy name.
-      const char* lb_policy_name = nullptr;
       const grpc_arg* channel_arg = grpc_channel_args_find(
           chand->resolver_result, GRPC_ARG_LB_POLICY_NAME);
-      if (channel_arg != nullptr) {
-        GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
-        lb_policy_name = channel_arg->value.string;
-      }
+      const char* lb_policy_name = grpc_channel_arg_get_string(channel_arg);
       // Special case: If at least one balancer address is present, we use
       // the grpclb policy, regardless of what the resolver actually specified.
       channel_arg =
@@ -437,10 +352,6 @@
       // Use pick_first if nothing was specified and we didn't select grpclb
       // above.
       if (lb_policy_name == nullptr) lb_policy_name = "pick_first";
-      grpc_lb_policy_args lb_policy_args;
-      lb_policy_args.args = chand->resolver_result;
-      lb_policy_args.client_channel_factory = chand->client_channel_factory;
-      lb_policy_args.combiner = chand->combiner;
       // Check to see if we're already using the right LB policy.
       // Note: It's safe to use chand->info_lb_policy_name here without
       // taking a lock on chand->info_mu, because this function is the
@@ -452,58 +363,65 @@
       if (chand->lb_policy != nullptr && !lb_policy_name_changed) {
         // Continue using the same LB policy.  Update with new addresses.
         lb_policy_updated = true;
-        grpc_lb_policy_update_locked(chand->lb_policy, &lb_policy_args);
+        chand->lb_policy->UpdateLocked(*chand->resolver_result);
       } else {
         // Instantiate new LB policy.
-        new_lb_policy = grpc_lb_policy_create(lb_policy_name, &lb_policy_args);
+        grpc_core::LoadBalancingPolicy::Args lb_policy_args;
+        lb_policy_args.combiner = chand->combiner;
+        lb_policy_args.client_channel_factory = chand->client_channel_factory;
+        lb_policy_args.args = chand->resolver_result;
+        new_lb_policy =
+            grpc_core::LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+                lb_policy_name, lb_policy_args);
         if (new_lb_policy == nullptr) {
           gpr_log(GPR_ERROR, "could not create LB policy \"%s\"",
                   lb_policy_name);
         } else {
+          lb_policy_created = true;
           reresolution_request_args* args =
               static_cast<reresolution_request_args*>(
                   gpr_zalloc(sizeof(*args)));
           args->chand = chand;
-          args->lb_policy = new_lb_policy;
+          args->lb_policy = new_lb_policy.get();
           GRPC_CLOSURE_INIT(&args->closure, request_reresolution_locked, args,
                             grpc_combiner_scheduler(chand->combiner));
           GRPC_CHANNEL_STACK_REF(chand->owning_stack, "re-resolution");
-          grpc_lb_policy_set_reresolve_closure_locked(new_lb_policy,
-                                                      &args->closure);
-        }
-      }
-      // Find service config.
-      channel_arg = grpc_channel_args_find(chand->resolver_result,
-                                           GRPC_ARG_SERVICE_CONFIG);
-      if (channel_arg != nullptr && channel_arg->type == GRPC_ARG_STRING) {
-        service_config_json = gpr_strdup(channel_arg->value.string);
-        grpc_service_config* service_config =
-            grpc_service_config_create(service_config_json);
-        if (service_config != nullptr) {
-          channel_arg = grpc_channel_args_find(chand->resolver_result,
-                                               GRPC_ARG_SERVER_URI);
-          GPR_ASSERT(channel_arg != nullptr);
-          GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
-          grpc_uri* uri = grpc_uri_parse(channel_arg->value.string, true);
-          GPR_ASSERT(uri->path[0] != '\0');
-          service_config_parsing_state parsing_state;
-          memset(&parsing_state, 0, sizeof(parsing_state));
-          parsing_state.server_name =
-              uri->path[0] == '/' ? uri->path + 1 : uri->path;
-          grpc_service_config_parse_global_params(
-              service_config, parse_retry_throttle_params, &parsing_state);
-          grpc_uri_destroy(uri);
-          retry_throttle_data = parsing_state.retry_throttle_data;
-          method_params_table = grpc_service_config_create_method_config_table(
-              service_config, method_parameters_create_from_json,
-              method_parameters_ref_wrapper, method_parameters_unref_wrapper);
-          grpc_service_config_destroy(service_config);
+          new_lb_policy->SetReresolutionClosureLocked(&args->closure);
         }
       }
       // Before we clean up, save a copy of lb_policy_name, since it might
       // be pointing to data inside chand->resolver_result.
       // The copy will be saved in chand->lb_policy_name below.
       lb_policy_name_dup = gpr_strdup(lb_policy_name);
+      // Find service config.
+      channel_arg = grpc_channel_args_find(chand->resolver_result,
+                                           GRPC_ARG_SERVICE_CONFIG);
+      service_config_json =
+          gpr_strdup(grpc_channel_arg_get_string(channel_arg));
+      if (service_config_json != nullptr) {
+        grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config =
+            grpc_core::ServiceConfig::Create(service_config_json);
+        if (service_config != nullptr) {
+          if (chand->enable_retries) {
+            channel_arg = grpc_channel_args_find(chand->resolver_result,
+                                                 GRPC_ARG_SERVER_URI);
+            const char* server_uri = grpc_channel_arg_get_string(channel_arg);
+            GPR_ASSERT(server_uri != nullptr);
+            grpc_uri* uri = grpc_uri_parse(server_uri, true);
+            GPR_ASSERT(uri->path[0] != '\0');
+            service_config_parsing_state parsing_state;
+            memset(&parsing_state, 0, sizeof(parsing_state));
+            parsing_state.server_name =
+                uri->path[0] == '/' ? uri->path + 1 : uri->path;
+            service_config->ParseGlobalParams(parse_retry_throttle_params,
+                                              &parsing_state);
+            grpc_uri_destroy(uri);
+            retry_throttle_data = parsing_state.retry_throttle_data;
+          }
+          method_params_table = service_config->CreateMethodConfigTable(
+              ClientChannelMethodParams::CreateFromJson);
+        }
+      }
     }
     grpc_channel_args_destroy(chand->resolver_result);
     chand->resolver_result = nullptr;
@@ -516,7 +434,7 @@
             lb_policy_name_changed ? " (changed)" : "", service_config_json);
   }
   // Now swap out fields in chand.  Note that the new values may still
-  // be NULL if (e.g.) the resolver failed to return results or the
+  // be nullptr if (e.g.) the resolver failed to return results or the
   // results did not contain the necessary data.
   //
   // First, swap out the data used by cc_get_channel_info().
@@ -536,29 +454,26 @@
   }
   chand->retry_throttle_data = retry_throttle_data;
   // Swap out the method params table.
-  if (chand->method_params_table != nullptr) {
-    grpc_slice_hash_table_unref(chand->method_params_table);
-  }
-  chand->method_params_table = method_params_table;
+  chand->method_params_table = std::move(method_params_table);
   // If we have a new LB policy or are shutting down (in which case
-  // new_lb_policy will be NULL), swap out the LB policy, unreffing the old one
-  // and removing its fds from chand->interested_parties. Note that we do NOT do
-  // this if either (a) we updated the existing LB policy above or (b) we failed
-  // to create the new LB policy (in which case we want to continue using the
-  // most recent one we had).
+  // new_lb_policy will be nullptr), swap out the LB policy, unreffing the
+  // old one and removing its fds from chand->interested_parties.
+  // Note that we do NOT do this if either (a) we updated the existing
+  // LB policy above or (b) we failed to create the new LB policy (in
+  // which case we want to continue using the most recent one we had).
   if (new_lb_policy != nullptr || error != GRPC_ERROR_NONE ||
       chand->resolver == nullptr) {
     if (chand->lb_policy != nullptr) {
       if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: unreffing lb_policy=%p", chand,
-                chand->lb_policy);
+                chand->lb_policy.get());
       }
-      grpc_pollset_set_del_pollset_set(chand->lb_policy->interested_parties,
+      grpc_pollset_set_del_pollset_set(chand->lb_policy->interested_parties(),
                                        chand->interested_parties);
-      grpc_lb_policy_shutdown_locked(chand->lb_policy, new_lb_policy);
-      GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel");
+      chand->lb_policy->HandOffPendingPicksLocked(new_lb_policy.get());
+      chand->lb_policy.reset();
     }
-    chand->lb_policy = new_lb_policy;
+    chand->lb_policy = std::move(new_lb_policy);
   }
   // Now that we've swapped out the relevant fields of chand, check for
   // error or shutdown.
@@ -577,30 +492,29 @@
         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
             "Got resolver result after disconnection", &error, 1),
         "resolver_gone");
-    GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "resolver");
     grpc_closure_list_fail_all(&chand->waiting_for_resolver_result_closures,
                                GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
                                    "Channel disconnected", &error, 1));
     GRPC_CLOSURE_LIST_SCHED(&chand->waiting_for_resolver_result_closures);
+    GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "resolver");
   } else {  // Not shutting down.
     grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE;
     grpc_error* state_error =
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy");
-    if (new_lb_policy != nullptr) {
+    if (lb_policy_created) {
       if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: initializing new LB policy", chand);
       }
       GRPC_ERROR_UNREF(state_error);
-      state =
-          grpc_lb_policy_check_connectivity_locked(new_lb_policy, &state_error);
-      grpc_pollset_set_add_pollset_set(new_lb_policy->interested_parties,
+      state = chand->lb_policy->CheckConnectivityLocked(&state_error);
+      grpc_pollset_set_add_pollset_set(chand->lb_policy->interested_parties(),
                                        chand->interested_parties);
       GRPC_CLOSURE_LIST_SCHED(&chand->waiting_for_resolver_result_closures);
       if (chand->exit_idle_when_lb_policy_arrives) {
-        grpc_lb_policy_exit_idle_locked(new_lb_policy);
+        chand->lb_policy->ExitIdleLocked();
         chand->exit_idle_when_lb_policy_arrives = false;
       }
-      watch_lb_policy_locked(chand, new_lb_policy, state);
+      watch_lb_policy_locked(chand, chand->lb_policy.get(), state);
     }
     if (!lb_policy_updated) {
       set_channel_connectivity_state_locked(
@@ -635,8 +549,8 @@
           op->send_ping.on_ack,
           GRPC_ERROR_CREATE_FROM_STATIC_STRING("Ping with no load balancing"));
     } else {
-      grpc_lb_policy_ping_one_locked(
-          chand->lb_policy, op->send_ping.on_initiate, op->send_ping.on_ack);
+      chand->lb_policy->PingOneLocked(op->send_ping.on_initiate,
+                                      op->send_ping.on_ack);
       op->bind_pollset = nullptr;
     }
     op->send_ping.on_initiate = nullptr;
@@ -655,11 +569,9 @@
         GRPC_CLOSURE_LIST_SCHED(&chand->waiting_for_resolver_result_closures);
       }
       if (chand->lb_policy != nullptr) {
-        grpc_pollset_set_del_pollset_set(chand->lb_policy->interested_parties,
+        grpc_pollset_set_del_pollset_set(chand->lb_policy->interested_parties(),
                                          chand->interested_parties);
-        grpc_lb_policy_shutdown_locked(chand->lb_policy, nullptr);
-        GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel");
-        chand->lb_policy = nullptr;
+        chand->lb_policy.reset();
       }
     }
     GRPC_ERROR_UNREF(op->disconnect_with_error);
@@ -727,9 +639,17 @@
   grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
                                "client_channel");
   grpc_client_channel_start_backup_polling(chand->interested_parties);
+  // Record max per-RPC retry buffer size.
+  const grpc_arg* arg = grpc_channel_args_find(
+      args->channel_args, GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE);
+  chand->per_rpc_retry_buffer_size = (size_t)grpc_channel_arg_get_integer(
+      arg, {DEFAULT_PER_RPC_RETRY_BUFFER_SIZE, 0, INT_MAX});
+  // Record enable_retries.
+  arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_ENABLE_RETRIES);
+  chand->enable_retries = grpc_channel_arg_get_bool(arg, true);
   // Record client channel factory.
-  const grpc_arg* arg = grpc_channel_args_find(args->channel_args,
-                                               GRPC_ARG_CLIENT_CHANNEL_FACTORY);
+  arg = grpc_channel_args_find(args->channel_args,
+                               GRPC_ARG_CLIENT_CHANNEL_FACTORY);
   if (arg == nullptr) {
     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
         "Missing client channel factory in args for client channel filter");
@@ -789,10 +709,9 @@
     grpc_client_channel_factory_unref(chand->client_channel_factory);
   }
   if (chand->lb_policy != nullptr) {
-    grpc_pollset_set_del_pollset_set(chand->lb_policy->interested_parties,
+    grpc_pollset_set_del_pollset_set(chand->lb_policy->interested_parties(),
                                      chand->interested_parties);
-    grpc_lb_policy_shutdown_locked(chand->lb_policy, nullptr);
-    GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel");
+    chand->lb_policy.reset();
   }
   gpr_free(chand->info_lb_policy_name);
   gpr_free(chand->info_service_config_json);
@@ -800,7 +719,7 @@
     grpc_server_retry_throttle_data_unref(chand->retry_throttle_data);
   }
   if (chand->method_params_table != nullptr) {
-    grpc_slice_hash_table_unref(chand->method_params_table);
+    chand->method_params_table.reset();
   }
   grpc_client_channel_stop_backup_polling(chand->interested_parties);
   grpc_connectivity_state_destroy(&chand->state_tracker);
@@ -815,15 +734,122 @@
  */
 
 // Max number of batches that can be pending on a call at any given
-// time.  This includes:
+// time.  This includes one batch for each of the following ops:
 //   recv_initial_metadata
 //   send_initial_metadata
 //   recv_message
 //   send_message
 //   recv_trailing_metadata
 //   send_trailing_metadata
-// We also add room for a single cancel_stream batch.
-#define MAX_WAITING_BATCHES 7
+#define MAX_PENDING_BATCHES 6
+
+// Retry support:
+//
+// In order to support retries, we act as a proxy for stream op batches.
+// When we get a batch from the surface, we add it to our list of pending
+// batches, and we then use those batches to construct separate "child"
+// batches to be started on the subchannel call.  When the child batches
+// return, we then decide which pending batches have been completed and
+// schedule their callbacks accordingly.  If a subchannel call fails and
+// we want to retry it, we do a new pick and start again, constructing
+// new "child" batches for the new subchannel call.
+//
+// Note that retries are committed when receiving data from the server
+// (except for Trailers-Only responses).  However, there may be many
+// send ops started before receiving any data, so we may have already
+// completed some number of send ops (and returned the completions up to
+// the surface) by the time we realize that we need to retry.  To deal
+// with this, we cache data for send ops, so that we can replay them on a
+// different subchannel call even after we have completed the original
+// batches.
+//
+// There are two sets of data to maintain:
+// - In call_data (in the parent channel), we maintain a list of pending
+//   ops and cached data for send ops.
+// - In the subchannel call, we maintain state to indicate what ops have
+//   already been sent down to that call.
+//
+// When constructing the "child" batches, we compare those two sets of
+// data to see which batches need to be sent to the subchannel call.
+
+// TODO(roth): In subsequent PRs:
+// - add support for transparent retries (including initial metadata)
+// - figure out how to record stats in census for retries
+//   (census filter is on top of this one)
+// - add census stats for retries
+
+// State used for starting a retryable batch on a subchannel call.
+// This provides its own grpc_transport_stream_op_batch and other data
+// structures needed to populate the ops in the batch.
+// We allocate one struct on the arena for each attempt at starting a
+// batch on a given subchannel call.
+typedef struct {
+  gpr_refcount refs;
+  grpc_call_element* elem;
+  grpc_subchannel_call* subchannel_call;  // Holds a ref.
+  // The batch to use in the subchannel call.
+  // Its payload field points to subchannel_call_retry_state.batch_payload.
+  grpc_transport_stream_op_batch batch;
+  // For send_initial_metadata.
+  // Note that we need to make a copy of the initial metadata for each
+  // subchannel call instead of just referring to the copy in call_data,
+  // because filters in the subchannel stack will probably add entries,
+  // so we need to start in a pristine state for each attempt of the call.
+  grpc_linked_mdelem* send_initial_metadata_storage;
+  grpc_metadata_batch send_initial_metadata;
+  // For send_message.
+  grpc_caching_byte_stream send_message;
+  // For send_trailing_metadata.
+  grpc_linked_mdelem* send_trailing_metadata_storage;
+  grpc_metadata_batch send_trailing_metadata;
+  // For intercepting recv_initial_metadata.
+  grpc_metadata_batch recv_initial_metadata;
+  grpc_closure recv_initial_metadata_ready;
+  bool trailing_metadata_available;
+  // For intercepting recv_message.
+  grpc_closure recv_message_ready;
+  grpc_byte_stream* recv_message;
+  // For intercepting recv_trailing_metadata.
+  grpc_metadata_batch recv_trailing_metadata;
+  grpc_transport_stream_stats collect_stats;
+  // For intercepting on_complete.
+  grpc_closure on_complete;
+} subchannel_batch_data;
+
+// Retry state associated with a subchannel call.
+// Stored in the parent_data of the subchannel call object.
+typedef struct {
+  // subchannel_batch_data.batch.payload points to this.
+  grpc_transport_stream_op_batch_payload batch_payload;
+  // These fields indicate which ops have been started and completed on
+  // this subchannel call.
+  size_t started_send_message_count;
+  size_t completed_send_message_count;
+  size_t started_recv_message_count;
+  size_t completed_recv_message_count;
+  bool started_send_initial_metadata : 1;
+  bool completed_send_initial_metadata : 1;
+  bool started_send_trailing_metadata : 1;
+  bool completed_send_trailing_metadata : 1;
+  bool started_recv_initial_metadata : 1;
+  bool completed_recv_initial_metadata : 1;
+  bool started_recv_trailing_metadata : 1;
+  bool completed_recv_trailing_metadata : 1;
+  // State for callback processing.
+  bool retry_dispatched : 1;
+  bool recv_initial_metadata_ready_deferred : 1;
+  bool recv_message_ready_deferred : 1;
+  grpc_error* recv_initial_metadata_error;
+  grpc_error* recv_message_error;
+} subchannel_call_retry_state;
+
+// Pending batches stored in call data.
+typedef struct {
+  // The pending batch.  If nullptr, this slot is empty.
+  grpc_transport_stream_op_batch* batch;
+  // Indicates whether payload for send ops has been cached in call data.
+  bool send_ops_cached;
+} pending_batch;
 
 /** Call data.  Holds a pointer to grpc_subchannel_call and the
     associated machinery to create such a pointer.
@@ -847,159 +873,1592 @@
   grpc_call_combiner* call_combiner;
 
   grpc_server_retry_throttle_data* retry_throttle_data;
-  method_parameters* method_params;
+  grpc_core::RefCountedPtr<ClientChannelMethodParams> method_params;
 
   grpc_subchannel_call* subchannel_call;
-  grpc_error* error;
 
-  grpc_lb_policy_pick_state pick;
-  grpc_closure lb_pick_closure;
-  grpc_closure lb_pick_cancel_closure;
+  // Set when we get a cancel_stream op.
+  grpc_error* cancel_error;
+
+  grpc_core::LoadBalancingPolicy::PickState pick;
+  grpc_closure pick_closure;
+  grpc_closure pick_cancel_closure;
 
   grpc_polling_entity* pollent;
 
-  grpc_transport_stream_op_batch* waiting_for_pick_batches[MAX_WAITING_BATCHES];
-  size_t waiting_for_pick_batches_count;
-  grpc_closure handle_pending_batch_in_call_combiner[MAX_WAITING_BATCHES];
+  // Batches are added to this list when received from above.
+  // They are removed when we are done handling the batch (i.e., when
+  // either we have invoked all of the batch's callbacks or we have
+  // passed the batch down to the subchannel call and are not
+  // intercepting any of its callbacks).
+  pending_batch pending_batches[MAX_PENDING_BATCHES];
+  bool pending_send_initial_metadata : 1;
+  bool pending_send_message : 1;
+  bool pending_send_trailing_metadata : 1;
 
-  grpc_transport_stream_op_batch* initial_metadata_batch;
+  // Retry state.
+  bool enable_retries : 1;
+  bool retry_committed : 1;
+  bool last_attempt_got_server_pushback : 1;
+  int num_attempts_completed;
+  size_t bytes_buffered_for_retry;
+  grpc_core::ManualConstructor<grpc_core::BackOff> retry_backoff;
+  grpc_timer retry_timer;
 
-  grpc_closure on_complete;
-  grpc_closure* original_on_complete;
+  // Cached data for retrying send ops.
+  // send_initial_metadata
+  bool seen_send_initial_metadata;
+  grpc_linked_mdelem* send_initial_metadata_storage;
+  grpc_metadata_batch send_initial_metadata;
+  uint32_t send_initial_metadata_flags;
+  gpr_atm* peer_string;
+  // send_message
+  // When we get a send_message op, we replace the original byte stream
+  // with a grpc_caching_byte_stream that caches the slices to a
+  // local buffer for use in retries.
+  // Note: We inline the cache for the first 3 send_message ops and use
+  // dynamic allocation after that.  This number was essentially picked
+  // at random; it could be changed in the future to tune performance.
+  grpc_core::InlinedVector<grpc_byte_stream_cache*, 3> send_messages;
+  // send_trailing_metadata
+  bool seen_send_trailing_metadata;
+  grpc_linked_mdelem* send_trailing_metadata_storage;
+  grpc_metadata_batch send_trailing_metadata;
 } call_data;
 
-grpc_subchannel_call* grpc_client_channel_get_subchannel_call(
-    grpc_call_element* elem) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  return calld->subchannel_call;
-}
+// Forward declarations.
+static void retry_commit(grpc_call_element* elem,
+                         subchannel_call_retry_state* retry_state);
+static void start_internal_recv_trailing_metadata(grpc_call_element* elem);
+static void on_complete(void* arg, grpc_error* error);
+static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored);
+static void pick_after_resolver_result_start_locked(grpc_call_element* elem);
+static void start_pick_locked(void* arg, grpc_error* ignored);
 
-// This is called via the call combiner, so access to calld is synchronized.
-static void waiting_for_pick_batches_add(
-    call_data* calld, grpc_transport_stream_op_batch* batch) {
+//
+// send op data caching
+//
+
+// Caches data for send ops so that it can be retried later, if not
+// already cached.
+static void maybe_cache_send_ops_for_batch(call_data* calld,
+                                           pending_batch* pending) {
+  if (pending->send_ops_cached) return;
+  pending->send_ops_cached = true;
+  grpc_transport_stream_op_batch* batch = pending->batch;
+  // Save a copy of metadata for send_initial_metadata ops.
   if (batch->send_initial_metadata) {
-    GPR_ASSERT(calld->initial_metadata_batch == nullptr);
-    calld->initial_metadata_batch = batch;
-  } else {
-    GPR_ASSERT(calld->waiting_for_pick_batches_count < MAX_WAITING_BATCHES);
-    calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count++] =
-        batch;
+    calld->seen_send_initial_metadata = true;
+    GPR_ASSERT(calld->send_initial_metadata_storage == nullptr);
+    grpc_metadata_batch* send_initial_metadata =
+        batch->payload->send_initial_metadata.send_initial_metadata;
+    calld->send_initial_metadata_storage = (grpc_linked_mdelem*)gpr_arena_alloc(
+        calld->arena,
+        sizeof(grpc_linked_mdelem) * send_initial_metadata->list.count);
+    grpc_metadata_batch_copy(send_initial_metadata,
+                             &calld->send_initial_metadata,
+                             calld->send_initial_metadata_storage);
+    calld->send_initial_metadata_flags =
+        batch->payload->send_initial_metadata.send_initial_metadata_flags;
+    calld->peer_string = batch->payload->send_initial_metadata.peer_string;
+  }
+  // Set up cache for send_message ops.
+  if (batch->send_message) {
+    grpc_byte_stream_cache* cache = (grpc_byte_stream_cache*)gpr_arena_alloc(
+        calld->arena, sizeof(grpc_byte_stream_cache));
+    grpc_byte_stream_cache_init(cache,
+                                batch->payload->send_message.send_message);
+    calld->send_messages.push_back(cache);
+  }
+  // Save metadata batch for send_trailing_metadata ops.
+  if (batch->send_trailing_metadata) {
+    calld->seen_send_trailing_metadata = true;
+    GPR_ASSERT(calld->send_trailing_metadata_storage == nullptr);
+    grpc_metadata_batch* send_trailing_metadata =
+        batch->payload->send_trailing_metadata.send_trailing_metadata;
+    calld->send_trailing_metadata_storage =
+        (grpc_linked_mdelem*)gpr_arena_alloc(
+            calld->arena,
+            sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count);
+    grpc_metadata_batch_copy(send_trailing_metadata,
+                             &calld->send_trailing_metadata,
+                             calld->send_trailing_metadata_storage);
   }
 }
 
-// This is called via the call combiner, so access to calld is synchronized.
-static void fail_pending_batch_in_call_combiner(void* arg, grpc_error* error) {
-  call_data* calld = static_cast<call_data*>(arg);
-  if (calld->waiting_for_pick_batches_count > 0) {
-    --calld->waiting_for_pick_batches_count;
-    grpc_transport_stream_op_batch_finish_with_failure(
-        calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count],
-        GRPC_ERROR_REF(error), calld->call_combiner);
-  }
-}
-
-// This is called via the call combiner, so access to calld is synchronized.
-static void waiting_for_pick_batches_fail(grpc_call_element* elem,
-                                          grpc_error* error) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_trace.enabled()) {
-    gpr_log(GPR_DEBUG,
-            "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s",
-            elem->channel_data, calld, calld->waiting_for_pick_batches_count,
-            grpc_error_string(error));
-  }
-  for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) {
-    GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i],
-                      fail_pending_batch_in_call_combiner, calld,
-                      grpc_schedule_on_exec_ctx);
-    GRPC_CALL_COMBINER_START(
-        calld->call_combiner, &calld->handle_pending_batch_in_call_combiner[i],
-        GRPC_ERROR_REF(error), "waiting_for_pick_batches_fail");
-  }
-  if (calld->initial_metadata_batch != nullptr) {
-    grpc_transport_stream_op_batch_finish_with_failure(
-        calld->initial_metadata_batch, GRPC_ERROR_REF(error),
-        calld->call_combiner);
-  } else {
-    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
-                            "waiting_for_pick_batches_fail");
-  }
-  GRPC_ERROR_UNREF(error);
-}
-
-// This is called via the call combiner, so access to calld is synchronized.
-static void run_pending_batch_in_call_combiner(void* arg, grpc_error* ignored) {
-  call_data* calld = static_cast<call_data*>(arg);
-  if (calld->waiting_for_pick_batches_count > 0) {
-    --calld->waiting_for_pick_batches_count;
-    grpc_subchannel_call_process_op(
-        calld->subchannel_call,
-        calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count]);
-  }
-}
-
-// This is called via the call combiner, so access to calld is synchronized.
-static void waiting_for_pick_batches_resume(grpc_call_element* elem) {
+// Frees cached send ops that have already been completed after
+// committing the call.
+static void free_cached_send_op_data_after_commit(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state) {
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (grpc_client_channel_trace.enabled()) {
-    gpr_log(GPR_DEBUG,
-            "chand=%p calld=%p: sending %" PRIuPTR
-            " pending batches to subchannel_call=%p",
-            chand, calld, calld->waiting_for_pick_batches_count,
-            calld->subchannel_call);
+  if (retry_state->completed_send_initial_metadata) {
+    grpc_metadata_batch_destroy(&calld->send_initial_metadata);
   }
-  for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) {
-    GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i],
-                      run_pending_batch_in_call_combiner, calld,
-                      grpc_schedule_on_exec_ctx);
-    GRPC_CALL_COMBINER_START(
-        calld->call_combiner, &calld->handle_pending_batch_in_call_combiner[i],
-        GRPC_ERROR_NONE, "waiting_for_pick_batches_resume");
+  for (size_t i = 0; i < retry_state->completed_send_message_count; ++i) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR
+              "]",
+              chand, calld, i);
+    }
+    grpc_byte_stream_cache_destroy(calld->send_messages[i]);
   }
-  GPR_ASSERT(calld->initial_metadata_batch != nullptr);
-  grpc_subchannel_call_process_op(calld->subchannel_call,
-                                  calld->initial_metadata_batch);
+  if (retry_state->completed_send_trailing_metadata) {
+    grpc_metadata_batch_destroy(&calld->send_trailing_metadata);
+  }
 }
 
-// Applies service config to the call.  Must be invoked once we know
-// that the resolver has returned results to the channel.
-static void apply_service_config_to_call_locked(grpc_call_element* elem) {
+// Frees cached send ops that were completed by the completed batch in
+// batch_data.  Used when batches are completed after the call is committed.
+static void free_cached_send_op_data_for_completed_batch(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state) {
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (batch_data->batch.send_initial_metadata) {
+    grpc_metadata_batch_destroy(&calld->send_initial_metadata);
+  }
+  if (batch_data->batch.send_message) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR
+              "]",
+              chand, calld, retry_state->completed_send_message_count - 1);
+    }
+    grpc_byte_stream_cache_destroy(
+        calld->send_messages[retry_state->completed_send_message_count - 1]);
+  }
+  if (batch_data->batch.send_trailing_metadata) {
+    grpc_metadata_batch_destroy(&calld->send_trailing_metadata);
+  }
+}
+
+//
+// pending_batches management
+//
+
+// Returns the index into calld->pending_batches to be used for batch.
+static size_t get_batch_index(grpc_transport_stream_op_batch* batch) {
+  // Note: It is important the send_initial_metadata be the first entry
+  // here, since the code in pick_subchannel_locked() assumes it will be.
+  if (batch->send_initial_metadata) return 0;
+  if (batch->send_message) return 1;
+  if (batch->send_trailing_metadata) return 2;
+  if (batch->recv_initial_metadata) return 3;
+  if (batch->recv_message) return 4;
+  if (batch->recv_trailing_metadata) return 5;
+  GPR_UNREACHABLE_CODE(return (size_t)-1);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void pending_batches_add(grpc_call_element* elem,
+                                grpc_transport_stream_op_batch* batch) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  const size_t idx = get_batch_index(batch);
   if (grpc_client_channel_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call",
-            chand, calld);
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: adding pending batch at index %" PRIuPTR, chand,
+            calld, idx);
   }
-  if (chand->retry_throttle_data != nullptr) {
-    calld->retry_throttle_data =
-        grpc_server_retry_throttle_data_ref(chand->retry_throttle_data);
-  }
-  if (chand->method_params_table != nullptr) {
-    calld->method_params = static_cast<method_parameters*>(
-        grpc_method_config_table_get(chand->method_params_table, calld->path));
-    if (calld->method_params != nullptr) {
-      method_parameters_ref(calld->method_params);
-      // If the deadline from the service config is shorter than the one
-      // from the client API, reset the deadline timer.
-      if (chand->deadline_checking_enabled &&
-          calld->method_params->timeout != 0) {
-        const grpc_millis per_method_deadline =
-            grpc_timespec_to_millis_round_up(calld->call_start_time) +
-            calld->method_params->timeout;
-        if (per_method_deadline < calld->deadline) {
-          calld->deadline = per_method_deadline;
-          grpc_deadline_state_reset(elem, calld->deadline);
+  pending_batch* pending = &calld->pending_batches[idx];
+  GPR_ASSERT(pending->batch == nullptr);
+  pending->batch = batch;
+  pending->send_ops_cached = false;
+  if (calld->enable_retries) {
+    // Update state in calld about pending batches.
+    // Also check if the batch takes us over the retry buffer limit.
+    // Note: We don't check the size of trailing metadata here, because
+    // gRPC clients do not send trailing metadata.
+    if (batch->send_initial_metadata) {
+      calld->pending_send_initial_metadata = true;
+      calld->bytes_buffered_for_retry += grpc_metadata_batch_size(
+          batch->payload->send_initial_metadata.send_initial_metadata);
+    }
+    if (batch->send_message) {
+      calld->pending_send_message = true;
+      calld->bytes_buffered_for_retry +=
+          batch->payload->send_message.send_message->length;
+    }
+    if (batch->send_trailing_metadata) {
+      calld->pending_send_trailing_metadata = true;
+    }
+    if (calld->bytes_buffered_for_retry > chand->per_rpc_retry_buffer_size) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: exceeded retry buffer size, committing",
+                chand, calld);
+      }
+      subchannel_call_retry_state* retry_state =
+          calld->subchannel_call == nullptr
+              ? nullptr
+              : static_cast<subchannel_call_retry_state*>(
+                    grpc_connected_subchannel_call_get_parent_data(
+                        calld->subchannel_call));
+      retry_commit(elem, retry_state);
+      // If we are not going to retry and have not yet started, pretend
+      // retries are disabled so that we don't bother with retry overhead.
+      if (calld->num_attempts_completed == 0) {
+        if (grpc_client_channel_trace.enabled()) {
+          gpr_log(GPR_DEBUG,
+                  "chand=%p calld=%p: disabling retries before first attempt",
+                  chand, calld);
         }
+        calld->enable_retries = false;
       }
     }
   }
 }
 
-static void create_subchannel_call_locked(grpc_call_element* elem,
-                                          grpc_error* error) {
+static void pending_batch_clear(call_data* calld, pending_batch* pending) {
+  if (calld->enable_retries) {
+    if (pending->batch->send_initial_metadata) {
+      calld->pending_send_initial_metadata = false;
+    }
+    if (pending->batch->send_message) {
+      calld->pending_send_message = false;
+    }
+    if (pending->batch->send_trailing_metadata) {
+      calld->pending_send_trailing_metadata = false;
+    }
+  }
+  pending->batch = nullptr;
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void fail_pending_batch_in_call_combiner(void* arg, grpc_error* error) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  call_data* calld = static_cast<call_data*>(batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  grpc_transport_stream_op_batch_finish_with_failure(
+      batch, GRPC_ERROR_REF(error), calld->call_combiner);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+// If yield_call_combiner is true, assumes responsibility for yielding
+// the call combiner.
+static void pending_batches_fail(grpc_call_element* elem, grpc_error* error,
+                                 bool yield_call_combiner) {
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    size_t num_batches = 0;
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+      if (calld->pending_batches[i].batch != nullptr) ++num_batches;
+    }
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s",
+            elem->channel_data, calld, num_batches, grpc_error_string(error));
+  }
+  grpc_transport_stream_op_batch*
+      batches[GPR_ARRAY_SIZE(calld->pending_batches)];
+  size_t num_batches = 0;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch != nullptr) {
+      batches[num_batches++] = batch;
+      pending_batch_clear(calld, pending);
+    }
+  }
+  for (size_t i = yield_call_combiner ? 1 : 0; i < num_batches; ++i) {
+    grpc_transport_stream_op_batch* batch = batches[i];
+    batch->handler_private.extra_arg = calld;
+    GRPC_CLOSURE_INIT(&batch->handler_private.closure,
+                      fail_pending_batch_in_call_combiner, batch,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &batch->handler_private.closure,
+                             GRPC_ERROR_REF(error), "pending_batches_fail");
+  }
+  if (yield_call_combiner) {
+    if (num_batches > 0) {
+      // Note: This will release the call combiner.
+      grpc_transport_stream_op_batch_finish_with_failure(
+          batches[0], GRPC_ERROR_REF(error), calld->call_combiner);
+    } else {
+      GRPC_CALL_COMBINER_STOP(calld->call_combiner, "pending_batches_fail");
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void resume_pending_batch_in_call_combiner(void* arg,
+                                                  grpc_error* ignored) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_subchannel_call* subchannel_call =
+      static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(subchannel_call, batch);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void pending_batches_resume(grpc_call_element* elem) {
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->enable_retries) {
+    start_retriable_subchannel_batches(elem, GRPC_ERROR_NONE);
+    return;
+  }
+  // Retries not enabled; send down batches as-is.
+  if (grpc_client_channel_trace.enabled()) {
+    size_t num_batches = 0;
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+      if (calld->pending_batches[i].batch != nullptr) ++num_batches;
+    }
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: starting %" PRIuPTR
+            " pending batches on subchannel_call=%p",
+            chand, calld, num_batches, calld->subchannel_call);
+  }
+  grpc_transport_stream_op_batch*
+      batches[GPR_ARRAY_SIZE(calld->pending_batches)];
+  size_t num_batches = 0;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch != nullptr) {
+      batches[num_batches++] = batch;
+      pending_batch_clear(calld, pending);
+    }
+  }
+  for (size_t i = 1; i < num_batches; ++i) {
+    grpc_transport_stream_op_batch* batch = batches[i];
+    batch->handler_private.extra_arg = calld->subchannel_call;
+    GRPC_CLOSURE_INIT(&batch->handler_private.closure,
+                      resume_pending_batch_in_call_combiner, batch,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &batch->handler_private.closure, GRPC_ERROR_NONE,
+                             "pending_batches_resume");
+  }
+  GPR_ASSERT(num_batches > 0);
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(calld->subchannel_call, batches[0]);
+}
+
+static void maybe_clear_pending_batch(grpc_call_element* elem,
+                                      pending_batch* pending) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_transport_stream_op_batch* batch = pending->batch;
+  // We clear the pending batch if all of its callbacks have been
+  // scheduled and reset to nullptr.
+  if (batch->on_complete == nullptr &&
+      (!batch->recv_initial_metadata ||
+       batch->payload->recv_initial_metadata.recv_initial_metadata_ready ==
+           nullptr) &&
+      (!batch->recv_message ||
+       batch->payload->recv_message.recv_message_ready == nullptr)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: clearing pending batch", chand,
+              calld);
+    }
+    pending_batch_clear(calld, pending);
+  }
+}
+
+// Returns true if all ops in the pending batch have been completed.
+static bool pending_batch_is_completed(
+    pending_batch* pending, call_data* calld,
+    subchannel_call_retry_state* retry_state) {
+  if (pending->batch == nullptr || pending->batch->on_complete == nullptr) {
+    return false;
+  }
+  if (pending->batch->send_initial_metadata &&
+      !retry_state->completed_send_initial_metadata) {
+    return false;
+  }
+  if (pending->batch->send_message &&
+      retry_state->completed_send_message_count < calld->send_messages.size()) {
+    return false;
+  }
+  if (pending->batch->send_trailing_metadata &&
+      !retry_state->completed_send_trailing_metadata) {
+    return false;
+  }
+  if (pending->batch->recv_initial_metadata &&
+      !retry_state->completed_recv_initial_metadata) {
+    return false;
+  }
+  if (pending->batch->recv_message &&
+      retry_state->completed_recv_message_count <
+          retry_state->started_recv_message_count) {
+    return false;
+  }
+  if (pending->batch->recv_trailing_metadata &&
+      !retry_state->completed_recv_trailing_metadata) {
+    return false;
+  }
+  return true;
+}
+
+// Returns true if any op in the batch was not yet started.
+static bool pending_batch_is_unstarted(
+    pending_batch* pending, call_data* calld,
+    subchannel_call_retry_state* retry_state) {
+  if (pending->batch == nullptr || pending->batch->on_complete == nullptr) {
+    return false;
+  }
+  if (pending->batch->send_initial_metadata &&
+      !retry_state->started_send_initial_metadata) {
+    return true;
+  }
+  if (pending->batch->send_message &&
+      retry_state->started_send_message_count < calld->send_messages.size()) {
+    return true;
+  }
+  if (pending->batch->send_trailing_metadata &&
+      !retry_state->started_send_trailing_metadata) {
+    return true;
+  }
+  if (pending->batch->recv_initial_metadata &&
+      !retry_state->started_recv_initial_metadata) {
+    return true;
+  }
+  if (pending->batch->recv_message &&
+      retry_state->completed_recv_message_count ==
+          retry_state->started_recv_message_count) {
+    return true;
+  }
+  if (pending->batch->recv_trailing_metadata &&
+      !retry_state->started_recv_trailing_metadata) {
+    return true;
+  }
+  return false;
+}
+
+//
+// retry code
+//
+
+// Commits the call so that no further retry attempts will be performed.
+static void retry_commit(grpc_call_element* elem,
+                         subchannel_call_retry_state* retry_state) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->retry_committed) return;
+  calld->retry_committed = true;
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "chand=%p calld=%p: committing retries", chand, calld);
+  }
+  if (retry_state != nullptr) {
+    free_cached_send_op_data_after_commit(elem, retry_state);
+  }
+}
+
+// Starts a retry after appropriate back-off.
+static void do_retry(grpc_call_element* elem,
+                     subchannel_call_retry_state* retry_state,
+                     grpc_millis server_pushback_ms) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  GPR_ASSERT(calld->method_params != nullptr);
+  const ClientChannelMethodParams::RetryPolicy* retry_policy =
+      calld->method_params->retry_policy();
+  GPR_ASSERT(retry_policy != nullptr);
+  // Reset subchannel call and connected subchannel.
+  if (calld->subchannel_call != nullptr) {
+    GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call,
+                               "client_channel_call_retry");
+    calld->subchannel_call = nullptr;
+  }
+  if (calld->pick.connected_subchannel != nullptr) {
+    calld->pick.connected_subchannel.reset();
+  }
+  // Compute backoff delay.
+  grpc_millis next_attempt_time;
+  if (server_pushback_ms >= 0) {
+    next_attempt_time = grpc_core::ExecCtx::Get()->Now() + server_pushback_ms;
+    calld->last_attempt_got_server_pushback = true;
+  } else {
+    if (calld->num_attempts_completed == 1 ||
+        calld->last_attempt_got_server_pushback) {
+      calld->retry_backoff.Init(
+          grpc_core::BackOff::Options()
+              .set_initial_backoff(retry_policy->initial_backoff)
+              .set_multiplier(retry_policy->backoff_multiplier)
+              .set_jitter(RETRY_BACKOFF_JITTER)
+              .set_max_backoff(retry_policy->max_backoff));
+      calld->last_attempt_got_server_pushback = false;
+    }
+    next_attempt_time = calld->retry_backoff->NextAttemptTime();
+  }
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: retrying failed call in %" PRIuPTR " ms", chand,
+            calld, next_attempt_time - grpc_core::ExecCtx::Get()->Now());
+  }
+  // Schedule retry after computed delay.
+  GRPC_CLOSURE_INIT(&calld->pick_closure, start_pick_locked, elem,
+                    grpc_combiner_scheduler(chand->combiner));
+  grpc_timer_init(&calld->retry_timer, next_attempt_time, &calld->pick_closure);
+  // Update bookkeeping.
+  if (retry_state != nullptr) retry_state->retry_dispatched = true;
+}
+
+// Returns true if the call is being retried.
+static bool maybe_retry(grpc_call_element* elem,
+                        subchannel_batch_data* batch_data,
+                        grpc_status_code status,
+                        grpc_mdelem* server_pushback_md) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Get retry policy.
+  if (calld->method_params == nullptr) return false;
+  const ClientChannelMethodParams::RetryPolicy* retry_policy =
+      calld->method_params->retry_policy();
+  if (retry_policy == nullptr) return false;
+  // If we've already dispatched a retry from this call, return true.
+  // This catches the case where the batch has multiple callbacks
+  // (i.e., it includes either recv_message or recv_initial_metadata).
+  subchannel_call_retry_state* retry_state = nullptr;
+  if (batch_data != nullptr) {
+    retry_state = static_cast<subchannel_call_retry_state*>(
+        grpc_connected_subchannel_call_get_parent_data(
+            batch_data->subchannel_call));
+    if (retry_state->retry_dispatched) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG, "chand=%p calld=%p: retry already dispatched", chand,
+                calld);
+      }
+      return true;
+    }
+  }
+  // Check status.
+  if (status == GRPC_STATUS_OK) {
+    grpc_server_retry_throttle_data_record_success(calld->retry_throttle_data);
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: call succeeded", chand, calld);
+    }
+    return false;
+  }
+  // Status is not OK.  Check whether the status is retryable.
+  if (!retry_policy->retryable_status_codes.Contains(status)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: status %s not configured as retryable", chand,
+              calld, grpc_status_code_to_string(status));
+    }
+    return false;
+  }
+  // Record the failure and check whether retries are throttled.
+  // Note that it's important for this check to come after the status
+  // code check above, since we should only record failures whose statuses
+  // match the configured retryable status codes, so that we don't count
+  // things like failures due to malformed requests (INVALID_ARGUMENT).
+  // Conversely, it's important for this to come before the remaining
+  // checks, so that we don't fail to record failures due to other factors.
+  if (!grpc_server_retry_throttle_data_record_failure(
+          calld->retry_throttle_data)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: retries throttled", chand, calld);
+    }
+    return false;
+  }
+  // Check whether the call is committed.
+  if (calld->retry_committed) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: retries already committed", chand,
+              calld);
+    }
+    return false;
+  }
+  // Check whether we have retries remaining.
+  ++calld->num_attempts_completed;
+  if (calld->num_attempts_completed >= retry_policy->max_attempts) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: exceeded %d retry attempts", chand,
+              calld, retry_policy->max_attempts);
+    }
+    return false;
+  }
+  // If the call was cancelled from the surface, don't retry.
+  if (calld->cancel_error != GRPC_ERROR_NONE) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: call cancelled from surface, not retrying",
+              chand, calld);
+    }
+    return false;
+  }
+  // Check server push-back.
+  grpc_millis server_pushback_ms = -1;
+  if (server_pushback_md != nullptr) {
+    // If the value is "-1" or any other unparseable string, we do not retry.
+    uint32_t ms;
+    if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: not retrying due to server push-back",
+                chand, calld);
+      }
+      return false;
+    } else {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: server push-back: retry in %u ms", chand,
+                calld, ms);
+      }
+      server_pushback_ms = (grpc_millis)ms;
+    }
+  }
+  do_retry(elem, retry_state, server_pushback_ms);
+  return true;
+}
+
+//
+// subchannel_batch_data
+//
+
+static subchannel_batch_data* batch_data_create(grpc_call_element* elem,
+                                                int refcount) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              calld->subchannel_call));
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(
+      gpr_arena_alloc(calld->arena, sizeof(*batch_data)));
+  batch_data->elem = elem;
+  batch_data->subchannel_call =
+      GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call, "batch_data_create");
+  batch_data->batch.payload = &retry_state->batch_payload;
+  gpr_ref_init(&batch_data->refs, refcount);
+  GRPC_CLOSURE_INIT(&batch_data->on_complete, on_complete, batch_data,
+                    grpc_schedule_on_exec_ctx);
+  batch_data->batch.on_complete = &batch_data->on_complete;
+  GRPC_CALL_STACK_REF(calld->owning_call, "batch_data");
+  return batch_data;
+}
+
+static void batch_data_unref(subchannel_batch_data* batch_data) {
+  if (gpr_unref(&batch_data->refs)) {
+    if (batch_data->send_initial_metadata_storage != nullptr) {
+      grpc_metadata_batch_destroy(&batch_data->send_initial_metadata);
+    }
+    if (batch_data->send_trailing_metadata_storage != nullptr) {
+      grpc_metadata_batch_destroy(&batch_data->send_trailing_metadata);
+    }
+    if (batch_data->batch.recv_initial_metadata) {
+      grpc_metadata_batch_destroy(&batch_data->recv_initial_metadata);
+    }
+    if (batch_data->batch.recv_trailing_metadata) {
+      grpc_metadata_batch_destroy(&batch_data->recv_trailing_metadata);
+    }
+    GRPC_SUBCHANNEL_CALL_UNREF(batch_data->subchannel_call, "batch_data_unref");
+    call_data* calld = static_cast<call_data*>(batch_data->elem->call_data);
+    GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data");
+  }
+}
+
+//
+// recv_initial_metadata callback handling
+//
+
+// Invokes recv_initial_metadata_ready for a subchannel batch.
+static void invoke_recv_initial_metadata_callback(void* arg,
+                                                  grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  channel_data* chand =
+      static_cast<channel_data*>(batch_data->elem->channel_data);
+  call_data* calld = static_cast<call_data*>(batch_data->elem->call_data);
+  // Find pending batch.
+  pending_batch* pending = nullptr;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    grpc_transport_stream_op_batch* batch = calld->pending_batches[i].batch;
+    if (batch != nullptr && batch->recv_initial_metadata &&
+        batch->payload->recv_initial_metadata.recv_initial_metadata_ready !=
+            nullptr) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: invoking recv_initial_metadata_ready for "
+                "pending batch at index %" PRIuPTR,
+                chand, calld, i);
+      }
+      pending = &calld->pending_batches[i];
+      break;
+    }
+  }
+  GPR_ASSERT(pending != nullptr);
+  // Return metadata.
+  grpc_metadata_batch_move(
+      &batch_data->recv_initial_metadata,
+      pending->batch->payload->recv_initial_metadata.recv_initial_metadata);
+  // Update bookkeeping.
+  // Note: Need to do this before invoking the callback, since invoking
+  // the callback will result in yielding the call combiner.
+  grpc_closure* recv_initial_metadata_ready =
+      pending->batch->payload->recv_initial_metadata
+          .recv_initial_metadata_ready;
+  pending->batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+      nullptr;
+  maybe_clear_pending_batch(batch_data->elem, pending);
+  batch_data_unref(batch_data);
+  // Invoke callback.
+  GRPC_CLOSURE_RUN(recv_initial_metadata_ready, GRPC_ERROR_REF(error));
+}
+
+// Intercepts recv_initial_metadata_ready callback for retries.
+// Commits the call and returns the initial metadata up the stack.
+static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: got recv_initial_metadata_ready, error=%s",
+            chand, calld, grpc_error_string(error));
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  // If we got an error or a Trailers-Only response and have not yet gotten
+  // the recv_trailing_metadata on_complete callback, then defer
+  // propagating this callback back to the surface.  We can evaluate whether
+  // to retry when recv_trailing_metadata comes back.
+  if ((batch_data->trailing_metadata_available || error != GRPC_ERROR_NONE) &&
+      !retry_state->completed_recv_trailing_metadata) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: deferring recv_initial_metadata_ready "
+              "(Trailers-Only)",
+              chand, calld);
+    }
+    retry_state->recv_initial_metadata_ready_deferred = true;
+    retry_state->recv_initial_metadata_error = GRPC_ERROR_REF(error);
+    if (!retry_state->started_recv_trailing_metadata) {
+      // recv_trailing_metadata not yet started by application; start it
+      // ourselves to get status.
+      start_internal_recv_trailing_metadata(elem);
+    } else {
+      GRPC_CALL_COMBINER_STOP(
+          calld->call_combiner,
+          "recv_initial_metadata_ready trailers-only or error");
+    }
+    return;
+  }
+  // Received valid initial metadata, so commit the call.
+  retry_commit(elem, retry_state);
+  // Manually invoking a callback function; it does not take ownership of error.
+  invoke_recv_initial_metadata_callback(batch_data, error);
+  GRPC_ERROR_UNREF(error);
+}
+
+//
+// recv_message callback handling
+//
+
+// Invokes recv_message_ready for a subchannel batch.
+static void invoke_recv_message_callback(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  channel_data* chand =
+      static_cast<channel_data*>(batch_data->elem->channel_data);
+  call_data* calld = static_cast<call_data*>(batch_data->elem->call_data);
+  // Find pending op.
+  pending_batch* pending = nullptr;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    grpc_transport_stream_op_batch* batch = calld->pending_batches[i].batch;
+    if (batch != nullptr && batch->recv_message &&
+        batch->payload->recv_message.recv_message_ready != nullptr) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: invoking recv_message_ready for "
+                "pending batch at index %" PRIuPTR,
+                chand, calld, i);
+      }
+      pending = &calld->pending_batches[i];
+      break;
+    }
+  }
+  GPR_ASSERT(pending != nullptr);
+  // Return payload.
+  *pending->batch->payload->recv_message.recv_message =
+      batch_data->recv_message;
+  // Update bookkeeping.
+  // Note: Need to do this before invoking the callback, since invoking
+  // the callback will result in yielding the call combiner.
+  grpc_closure* recv_message_ready =
+      pending->batch->payload->recv_message.recv_message_ready;
+  pending->batch->payload->recv_message.recv_message_ready = nullptr;
+  maybe_clear_pending_batch(batch_data->elem, pending);
+  batch_data_unref(batch_data);
+  // Invoke callback.
+  GRPC_CLOSURE_RUN(recv_message_ready, GRPC_ERROR_REF(error));
+}
+
+// Intercepts recv_message_ready callback for retries.
+// Commits the call and returns the message up the stack.
+static void recv_message_ready(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "chand=%p calld=%p: got recv_message_ready, error=%s",
+            chand, calld, grpc_error_string(error));
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  // If we got an error or the payload was nullptr and we have not yet gotten
+  // the recv_trailing_metadata on_complete callback, then defer
+  // propagating this callback back to the surface.  We can evaluate whether
+  // to retry when recv_trailing_metadata comes back.
+  if ((batch_data->recv_message == nullptr || error != GRPC_ERROR_NONE) &&
+      !retry_state->completed_recv_trailing_metadata) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: deferring recv_message_ready (nullptr "
+              "message and recv_trailing_metadata pending)",
+              chand, calld);
+    }
+    retry_state->recv_message_ready_deferred = true;
+    retry_state->recv_message_error = GRPC_ERROR_REF(error);
+    if (!retry_state->started_recv_trailing_metadata) {
+      // recv_trailing_metadata not yet started by application; start it
+      // ourselves to get status.
+      start_internal_recv_trailing_metadata(elem);
+    } else {
+      GRPC_CALL_COMBINER_STOP(calld->call_combiner, "recv_message_ready null");
+    }
+    return;
+  }
+  // Received a valid message, so commit the call.
+  retry_commit(elem, retry_state);
+  // Manually invoking a callback function; it does not take ownership of error.
+  invoke_recv_message_callback(batch_data, error);
+  GRPC_ERROR_UNREF(error);
+}
+
+//
+// on_complete callback handling
+//
+
+// Updates retry_state to reflect the ops completed in batch_data.
+static void update_retry_state_for_completed_batch(
+    subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state) {
+  if (batch_data->batch.send_initial_metadata) {
+    retry_state->completed_send_initial_metadata = true;
+  }
+  if (batch_data->batch.send_message) {
+    ++retry_state->completed_send_message_count;
+  }
+  if (batch_data->batch.send_trailing_metadata) {
+    retry_state->completed_send_trailing_metadata = true;
+  }
+  if (batch_data->batch.recv_initial_metadata) {
+    retry_state->completed_recv_initial_metadata = true;
+  }
+  if (batch_data->batch.recv_message) {
+    ++retry_state->completed_recv_message_count;
+  }
+  if (batch_data->batch.recv_trailing_metadata) {
+    retry_state->completed_recv_trailing_metadata = true;
+  }
+}
+
+// Represents a closure that needs to run as a result of a completed batch.
+typedef struct {
+  grpc_closure* closure;
+  grpc_error* error;
+  const char* reason;
+} closure_to_execute;
+
+// Adds any necessary closures for deferred recv_initial_metadata and
+// recv_message callbacks to closures, updating *num_closures as needed.
+static void add_closures_for_deferred_recv_callbacks(
+    subchannel_batch_data* batch_data, subchannel_call_retry_state* retry_state,
+    closure_to_execute* closures, size_t* num_closures) {
+  if (batch_data->batch.recv_trailing_metadata &&
+      retry_state->recv_initial_metadata_ready_deferred) {
+    closure_to_execute* closure = &closures[(*num_closures)++];
+    closure->closure =
+        GRPC_CLOSURE_INIT(&batch_data->recv_initial_metadata_ready,
+                          invoke_recv_initial_metadata_callback, batch_data,
+                          grpc_schedule_on_exec_ctx);
+    closure->error = retry_state->recv_initial_metadata_error;
+    closure->reason = "resuming recv_initial_metadata_ready";
+  }
+  if (batch_data->batch.recv_trailing_metadata &&
+      retry_state->recv_message_ready_deferred) {
+    closure_to_execute* closure = &closures[(*num_closures)++];
+    closure->closure = GRPC_CLOSURE_INIT(&batch_data->recv_message_ready,
+                                         invoke_recv_message_callback,
+                                         batch_data, grpc_schedule_on_exec_ctx);
+    closure->error = retry_state->recv_message_error;
+    closure->reason = "resuming recv_message_ready";
+  }
+}
+
+// If there are any cached ops to replay or pending ops to start on the
+// subchannel call, adds a closure to closures to invoke
+// start_retriable_subchannel_batches(), updating *num_closures as needed.
+static void add_closures_for_replay_or_pending_send_ops(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state, closure_to_execute* closures,
+    size_t* num_closures) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  bool have_pending_send_message_ops =
+      retry_state->started_send_message_count < calld->send_messages.size();
+  bool have_pending_send_trailing_metadata_op =
+      calld->seen_send_trailing_metadata &&
+      !retry_state->started_send_trailing_metadata;
+  if (!have_pending_send_message_ops &&
+      !have_pending_send_trailing_metadata_op) {
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+      pending_batch* pending = &calld->pending_batches[i];
+      grpc_transport_stream_op_batch* batch = pending->batch;
+      if (batch == nullptr || pending->send_ops_cached) continue;
+      if (batch->send_message) have_pending_send_message_ops = true;
+      if (batch->send_trailing_metadata) {
+        have_pending_send_trailing_metadata_op = true;
+      }
+    }
+  }
+  if (have_pending_send_message_ops || have_pending_send_trailing_metadata_op) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: starting next batch for pending send op(s)",
+              chand, calld);
+    }
+    closure_to_execute* closure = &closures[(*num_closures)++];
+    closure->closure = GRPC_CLOSURE_INIT(
+        &batch_data->batch.handler_private.closure,
+        start_retriable_subchannel_batches, elem, grpc_schedule_on_exec_ctx);
+    closure->error = GRPC_ERROR_NONE;
+    closure->reason = "starting next batch for send_* op(s)";
+  }
+}
+
+// For any pending batch completed in batch_data, adds the necessary
+// completion closures to closures, updating *num_closures as needed.
+static void add_closures_for_completed_pending_batches(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state, grpc_error* error,
+    closure_to_execute* closures, size_t* num_closures) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    if (pending_batch_is_completed(pending, calld, retry_state)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: pending batch completed at index %" PRIuPTR,
+                chand, calld, i);
+      }
+      // Copy the trailing metadata to return it to the surface.
+      if (batch_data->batch.recv_trailing_metadata) {
+        grpc_metadata_batch_move(&batch_data->recv_trailing_metadata,
+                                 pending->batch->payload->recv_trailing_metadata
+                                     .recv_trailing_metadata);
+      }
+      closure_to_execute* closure = &closures[(*num_closures)++];
+      closure->closure = pending->batch->on_complete;
+      closure->error = GRPC_ERROR_REF(error);
+      closure->reason = "on_complete for pending batch";
+      pending->batch->on_complete = nullptr;
+      maybe_clear_pending_batch(elem, pending);
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// For any pending batch containing an op that has not yet been started,
+// adds the pending batch's completion closures to closures, updating
+// *num_closures as needed.
+static void add_closures_to_fail_unstarted_pending_batches(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
+    grpc_error* error, closure_to_execute* closures, size_t* num_closures) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    if (pending_batch_is_unstarted(pending, calld, retry_state)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: failing unstarted pending batch at index "
+                "%" PRIuPTR,
+                chand, calld, i);
+      }
+      if (pending->batch->recv_initial_metadata) {
+        closure_to_execute* closure = &closures[(*num_closures)++];
+        closure->closure = pending->batch->payload->recv_initial_metadata
+                               .recv_initial_metadata_ready;
+        closure->error = GRPC_ERROR_REF(error);
+        closure->reason =
+            "failing recv_initial_metadata_ready for pending batch";
+        pending->batch->payload->recv_initial_metadata
+            .recv_initial_metadata_ready = nullptr;
+      }
+      if (pending->batch->recv_message) {
+        *pending->batch->payload->recv_message.recv_message = nullptr;
+        closure_to_execute* closure = &closures[(*num_closures)++];
+        closure->closure =
+            pending->batch->payload->recv_message.recv_message_ready;
+        closure->error = GRPC_ERROR_REF(error);
+        closure->reason = "failing recv_message_ready for pending batch";
+        pending->batch->payload->recv_message.recv_message_ready = nullptr;
+      }
+      closure_to_execute* closure = &closures[(*num_closures)++];
+      closure->closure = pending->batch->on_complete;
+      closure->error = GRPC_ERROR_REF(error);
+      closure->reason = "failing on_complete for pending batch";
+      pending->batch->on_complete = nullptr;
+      maybe_clear_pending_batch(elem, pending);
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Callback used to intercept on_complete from subchannel calls.
+// Called only when retries are enabled.
+static void on_complete(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    char* batch_str = grpc_transport_stream_op_batch_string(&batch_data->batch);
+    gpr_log(GPR_DEBUG, "chand=%p calld=%p: got on_complete, error=%s, batch=%s",
+            chand, calld, grpc_error_string(error), batch_str);
+    gpr_free(batch_str);
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  // If we have previously completed recv_trailing_metadata, then the
+  // call is finished.
+  bool call_finished = retry_state->completed_recv_trailing_metadata;
+  // Update bookkeeping in retry_state.
+  update_retry_state_for_completed_batch(batch_data, retry_state);
+  if (call_finished) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: call already finished", chand,
+              calld);
+    }
+  } else {
+    // Check if this batch finished the call, and if so, get its status.
+    // The call is finished if either (a) this callback was invoked with
+    // an error or (b) we receive status.
+    grpc_status_code status = GRPC_STATUS_OK;
+    grpc_mdelem* server_pushback_md = nullptr;
+    if (error != GRPC_ERROR_NONE) {  // Case (a).
+      call_finished = true;
+      grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr,
+                            nullptr);
+    } else if (batch_data->batch.recv_trailing_metadata) {  // Case (b).
+      call_finished = true;
+      grpc_metadata_batch* md_batch =
+          batch_data->batch.payload->recv_trailing_metadata
+              .recv_trailing_metadata;
+      GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr);
+      status = grpc_get_status_code_from_metadata(
+          md_batch->idx.named.grpc_status->md);
+      if (md_batch->idx.named.grpc_retry_pushback_ms != nullptr) {
+        server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md;
+      }
+    } else if (retry_state->completed_recv_trailing_metadata) {
+      call_finished = true;
+    }
+    if (call_finished && grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: call finished, status=%s", chand,
+              calld, grpc_status_code_to_string(status));
+    }
+    // If the call is finished, check if we should retry.
+    if (call_finished &&
+        maybe_retry(elem, batch_data, status, server_pushback_md)) {
+      // Unref batch_data for deferred recv_initial_metadata_ready or
+      // recv_message_ready callbacks, if any.
+      if (batch_data->batch.recv_trailing_metadata &&
+          retry_state->recv_initial_metadata_ready_deferred) {
+        batch_data_unref(batch_data);
+        GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error);
+      }
+      if (batch_data->batch.recv_trailing_metadata &&
+          retry_state->recv_message_ready_deferred) {
+        batch_data_unref(batch_data);
+        GRPC_ERROR_UNREF(retry_state->recv_message_error);
+      }
+      batch_data_unref(batch_data);
+      return;
+    }
+  }
+  // If the call is finished or retries are committed, free cached data for
+  // send ops that we've just completed.
+  if (call_finished || calld->retry_committed) {
+    free_cached_send_op_data_for_completed_batch(elem, batch_data, retry_state);
+  }
+  // Call not being retried.
+  // Construct list of closures to execute.
+  // Max number of closures is number of pending batches plus one for
+  // each of:
+  // - recv_initial_metadata_ready (either deferred or unstarted)
+  // - recv_message_ready (either deferred or unstarted)
+  // - starting a new batch for pending send ops
+  closure_to_execute closures[GPR_ARRAY_SIZE(calld->pending_batches) + 3];
+  size_t num_closures = 0;
+  // If there are deferred recv_initial_metadata_ready or recv_message_ready
+  // callbacks, add them to closures.
+  add_closures_for_deferred_recv_callbacks(batch_data, retry_state, closures,
+                                           &num_closures);
+  // Find pending batches whose ops are now complete and add their
+  // on_complete callbacks to closures.
+  add_closures_for_completed_pending_batches(elem, batch_data, retry_state,
+                                             GRPC_ERROR_REF(error), closures,
+                                             &num_closures);
+  // Add closures to handle any pending batches that have not yet been started.
+  // If the call is finished, we fail these batches; otherwise, we add a
+  // callback to start_retriable_subchannel_batches() to start them on
+  // the subchannel call.
+  if (call_finished) {
+    add_closures_to_fail_unstarted_pending_batches(
+        elem, retry_state, GRPC_ERROR_REF(error), closures, &num_closures);
+  } else {
+    add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state,
+                                                closures, &num_closures);
+  }
+  // Don't need batch_data anymore.
+  batch_data_unref(batch_data);
+  // Schedule all of the closures identified above.
+  // Note that the call combiner will be yielded for each closure that
+  // we schedule.  We're already running in the call combiner, so one of
+  // the closures can be scheduled directly, but the others will
+  // have to re-enter the call combiner.
+  if (num_closures > 0) {
+    GRPC_CLOSURE_SCHED(closures[0].closure, closures[0].error);
+    for (size_t i = 1; i < num_closures; ++i) {
+      GRPC_CALL_COMBINER_START(calld->call_combiner, closures[i].closure,
+                               closures[i].error, closures[i].reason);
+    }
+  } else {
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "no closures to run for on_complete");
+  }
+}
+
+//
+// subchannel batch construction
+//
+
+// Helper function used to start a subchannel batch in the call combiner.
+static void start_batch_in_call_combiner(void* arg, grpc_error* ignored) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_subchannel_call* subchannel_call =
+      static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(subchannel_call, batch);
+}
+
+// Adds retriable send_initial_metadata op to batch_data.
+static void add_retriable_send_initial_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  // Maps the number of retries to the corresponding metadata value slice.
+  static const grpc_slice* retry_count_strings[] = {
+      &GRPC_MDSTR_1, &GRPC_MDSTR_2, &GRPC_MDSTR_3, &GRPC_MDSTR_4};
+  // We need to make a copy of the metadata batch for each attempt, since
+  // the filters in the subchannel stack may modify this batch, and we don't
+  // want those modifications to be passed forward to subsequent attempts.
+  //
+  // If we've already completed one or more attempts, add the
+  // grpc-retry-attempts header.
+  batch_data->send_initial_metadata_storage =
+      static_cast<grpc_linked_mdelem*>(gpr_arena_alloc(
+          calld->arena, sizeof(grpc_linked_mdelem) *
+                            (calld->send_initial_metadata.list.count +
+                             (calld->num_attempts_completed > 0))));
+  grpc_metadata_batch_copy(&calld->send_initial_metadata,
+                           &batch_data->send_initial_metadata,
+                           batch_data->send_initial_metadata_storage);
+  if (batch_data->send_initial_metadata.idx.named.grpc_previous_rpc_attempts !=
+      nullptr) {
+    grpc_metadata_batch_remove(
+        &batch_data->send_initial_metadata,
+        batch_data->send_initial_metadata.idx.named.grpc_previous_rpc_attempts);
+  }
+  if (calld->num_attempts_completed > 0) {
+    grpc_mdelem retry_md = grpc_mdelem_from_slices(
+        GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS,
+        *retry_count_strings[calld->num_attempts_completed - 1]);
+    grpc_error* error = grpc_metadata_batch_add_tail(
+        &batch_data->send_initial_metadata,
+        &batch_data->send_initial_metadata_storage[calld->send_initial_metadata
+                                                       .list.count],
+        retry_md);
+    if (error != GRPC_ERROR_NONE) {
+      gpr_log(GPR_ERROR, "error adding retry metadata: %s",
+              grpc_error_string(error));
+      GPR_ASSERT(false);
+    }
+  }
+  retry_state->started_send_initial_metadata = true;
+  batch_data->batch.send_initial_metadata = true;
+  batch_data->batch.payload->send_initial_metadata.send_initial_metadata =
+      &batch_data->send_initial_metadata;
+  batch_data->batch.payload->send_initial_metadata.send_initial_metadata_flags =
+      calld->send_initial_metadata_flags;
+  batch_data->batch.payload->send_initial_metadata.peer_string =
+      calld->peer_string;
+}
+
+// Adds retriable send_message op to batch_data.
+static void add_retriable_send_message_op(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: starting calld->send_messages[%" PRIuPTR "]",
+            chand, calld, retry_state->started_send_message_count);
+  }
+  grpc_byte_stream_cache* cache =
+      calld->send_messages[retry_state->started_send_message_count];
+  ++retry_state->started_send_message_count;
+  grpc_caching_byte_stream_init(&batch_data->send_message, cache);
+  batch_data->batch.send_message = true;
+  batch_data->batch.payload->send_message.send_message =
+      &batch_data->send_message.base;
+}
+
+// Adds retriable send_trailing_metadata op to batch_data.
+static void add_retriable_send_trailing_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  // We need to make a copy of the metadata batch for each attempt, since
+  // the filters in the subchannel stack may modify this batch, and we don't
+  // want those modifications to be passed forward to subsequent attempts.
+  batch_data->send_trailing_metadata_storage =
+      static_cast<grpc_linked_mdelem*>(gpr_arena_alloc(
+          calld->arena, sizeof(grpc_linked_mdelem) *
+                            calld->send_trailing_metadata.list.count));
+  grpc_metadata_batch_copy(&calld->send_trailing_metadata,
+                           &batch_data->send_trailing_metadata,
+                           batch_data->send_trailing_metadata_storage);
+  retry_state->started_send_trailing_metadata = true;
+  batch_data->batch.send_trailing_metadata = true;
+  batch_data->batch.payload->send_trailing_metadata.send_trailing_metadata =
+      &batch_data->send_trailing_metadata;
+}
+
+// Adds retriable recv_initial_metadata op to batch_data.
+static void add_retriable_recv_initial_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  retry_state->started_recv_initial_metadata = true;
+  batch_data->batch.recv_initial_metadata = true;
+  grpc_metadata_batch_init(&batch_data->recv_initial_metadata);
+  batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata =
+      &batch_data->recv_initial_metadata;
+  batch_data->batch.payload->recv_initial_metadata.trailing_metadata_available =
+      &batch_data->trailing_metadata_available;
+  GRPC_CLOSURE_INIT(&batch_data->recv_initial_metadata_ready,
+                    recv_initial_metadata_ready, batch_data,
+                    grpc_schedule_on_exec_ctx);
+  batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata_ready =
+      &batch_data->recv_initial_metadata_ready;
+}
+
+// Adds retriable recv_message op to batch_data.
+static void add_retriable_recv_message_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  ++retry_state->started_recv_message_count;
+  batch_data->batch.recv_message = true;
+  batch_data->batch.payload->recv_message.recv_message =
+      &batch_data->recv_message;
+  GRPC_CLOSURE_INIT(&batch_data->recv_message_ready, recv_message_ready,
+                    batch_data, grpc_schedule_on_exec_ctx);
+  batch_data->batch.payload->recv_message.recv_message_ready =
+      &batch_data->recv_message_ready;
+}
+
+// Adds retriable recv_trailing_metadata op to batch_data.
+static void add_retriable_recv_trailing_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  retry_state->started_recv_trailing_metadata = true;
+  batch_data->batch.recv_trailing_metadata = true;
+  grpc_metadata_batch_init(&batch_data->recv_trailing_metadata);
+  batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata =
+      &batch_data->recv_trailing_metadata;
+  batch_data->batch.collect_stats = true;
+  batch_data->batch.payload->collect_stats.collect_stats =
+      &batch_data->collect_stats;
+}
+
+// Helper function used to start a recv_trailing_metadata batch.  This
+// is used in the case where a recv_initial_metadata or recv_message
+// op fails in a way that we know the call is over but when the application
+// has not yet started its own recv_trailing_metadata op.
+static void start_internal_recv_trailing_metadata(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: call failed but recv_trailing_metadata not "
+            "started; starting it internally",
+            chand, calld);
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              calld->subchannel_call));
+  subchannel_batch_data* batch_data = batch_data_create(elem, 1);
+  add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data);
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(calld->subchannel_call, &batch_data->batch);
+}
+
+// If there are any cached send ops that need to be replayed on the
+// current subchannel call, creates and returns a new subchannel batch
+// to replay those ops.  Otherwise, returns nullptr.
+static subchannel_batch_data* maybe_create_subchannel_batch_for_replay(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  subchannel_batch_data* replay_batch_data = nullptr;
+  // send_initial_metadata.
+  if (calld->seen_send_initial_metadata &&
+      !retry_state->started_send_initial_metadata &&
+      !calld->pending_send_initial_metadata) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: replaying previously completed "
+              "send_initial_metadata op",
+              chand, calld);
+    }
+    replay_batch_data = batch_data_create(elem, 1);
+    add_retriable_send_initial_metadata_op(calld, retry_state,
+                                           replay_batch_data);
+  }
+  // send_message.
+  // Note that we can only have one send_message op in flight at a time.
+  if (retry_state->started_send_message_count < calld->send_messages.size() &&
+      retry_state->started_send_message_count ==
+          retry_state->completed_send_message_count &&
+      !calld->pending_send_message) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: replaying previously completed "
+              "send_message op",
+              chand, calld);
+    }
+    if (replay_batch_data == nullptr) {
+      replay_batch_data = batch_data_create(elem, 1);
+    }
+    add_retriable_send_message_op(elem, retry_state, replay_batch_data);
+  }
+  // send_trailing_metadata.
+  // Note that we only add this op if we have no more send_message ops
+  // to start, since we can't send down any more send_message ops after
+  // send_trailing_metadata.
+  if (calld->seen_send_trailing_metadata &&
+      retry_state->started_send_message_count == calld->send_messages.size() &&
+      !retry_state->started_send_trailing_metadata &&
+      !calld->pending_send_trailing_metadata) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "chand=%p calld=%p: replaying previously completed "
+              "send_trailing_metadata op",
+              chand, calld);
+    }
+    if (replay_batch_data == nullptr) {
+      replay_batch_data = batch_data_create(elem, 1);
+    }
+    add_retriable_send_trailing_metadata_op(calld, retry_state,
+                                            replay_batch_data);
+  }
+  return replay_batch_data;
+}
+
+// Adds subchannel batches for pending batches to batches, updating
+// *num_batches as needed.
+static void add_subchannel_batches_for_pending_batches(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
+    grpc_transport_stream_op_batch** batches, size_t* num_batches) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch == nullptr) continue;
+    // Skip any batch that either (a) has already been started on this
+    // subchannel call or (b) we can't start yet because we're still
+    // replaying send ops that need to be completed first.
+    // TODO(roth): Note that if any one op in the batch can't be sent
+    // yet due to ops that we're replaying, we don't start any of the ops
+    // in the batch.  This is probably okay, but it could conceivably
+    // lead to increased latency in some cases -- e.g., we could delay
+    // starting a recv op due to it being in the same batch with a send
+    // op.  If/when we revamp the callback protocol in
+    // transport_stream_op_batch, we may be able to fix this.
+    if (batch->send_initial_metadata &&
+        retry_state->started_send_initial_metadata) {
+      continue;
+    }
+    if (batch->send_message && retry_state->completed_send_message_count <
+                                   retry_state->started_send_message_count) {
+      continue;
+    }
+    // Note that we only start send_trailing_metadata if we have no more
+    // send_message ops to start, since we can't send down any more
+    // send_message ops after send_trailing_metadata.
+    if (batch->send_trailing_metadata &&
+        (retry_state->started_send_message_count + batch->send_message <
+             calld->send_messages.size() ||
+         retry_state->started_send_trailing_metadata)) {
+      continue;
+    }
+    if (batch->recv_initial_metadata &&
+        retry_state->started_recv_initial_metadata) {
+      continue;
+    }
+    if (batch->recv_message && retry_state->completed_recv_message_count <
+                                   retry_state->started_recv_message_count) {
+      continue;
+    }
+    if (batch->recv_trailing_metadata &&
+        retry_state->started_recv_trailing_metadata) {
+      continue;
+    }
+    // If we're not retrying, just send the batch as-is.
+    if (calld->method_params == nullptr ||
+        calld->method_params->retry_policy() == nullptr ||
+        calld->retry_committed) {
+      batches[(*num_batches)++] = batch;
+      pending_batch_clear(calld, pending);
+      continue;
+    }
+    // Create batch with the right number of callbacks.
+    const int num_callbacks =
+        1 + batch->recv_initial_metadata + batch->recv_message;
+    subchannel_batch_data* batch_data = batch_data_create(elem, num_callbacks);
+    // Cache send ops if needed.
+    maybe_cache_send_ops_for_batch(calld, pending);
+    // send_initial_metadata.
+    if (batch->send_initial_metadata) {
+      add_retriable_send_initial_metadata_op(calld, retry_state, batch_data);
+    }
+    // send_message.
+    if (batch->send_message) {
+      add_retriable_send_message_op(elem, retry_state, batch_data);
+    }
+    // send_trailing_metadata.
+    if (batch->send_trailing_metadata) {
+      add_retriable_send_trailing_metadata_op(calld, retry_state, batch_data);
+    }
+    // recv_initial_metadata.
+    if (batch->recv_initial_metadata) {
+      // recv_flags is only used on the server side.
+      GPR_ASSERT(batch->payload->recv_initial_metadata.recv_flags == nullptr);
+      add_retriable_recv_initial_metadata_op(calld, retry_state, batch_data);
+    }
+    // recv_message.
+    if (batch->recv_message) {
+      add_retriable_recv_message_op(calld, retry_state, batch_data);
+    }
+    // recv_trailing_metadata.
+    if (batch->recv_trailing_metadata) {
+      GPR_ASSERT(batch->collect_stats);
+      add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data);
+    }
+    batches[(*num_batches)++] = &batch_data->batch;
+  }
+}
+
+// Constructs and starts whatever subchannel batches are needed on the
+// subchannel call.
+static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "chand=%p calld=%p: constructing retriable batches",
+            chand, calld);
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              calld->subchannel_call));
+  // We can start up to 6 batches.
+  grpc_transport_stream_op_batch*
+      batches[GPR_ARRAY_SIZE(calld->pending_batches)];
+  size_t num_batches = 0;
+  // Replay previously-returned send_* ops if needed.
+  subchannel_batch_data* replay_batch_data =
+      maybe_create_subchannel_batch_for_replay(elem, retry_state);
+  if (replay_batch_data != nullptr) {
+    batches[num_batches++] = &replay_batch_data->batch;
+  }
+  // Now add pending batches.
+  add_subchannel_batches_for_pending_batches(elem, retry_state, batches,
+                                             &num_batches);
+  // Start batches on subchannel call.
+  // Note that the call combiner will be yielded for each batch that we
+  // send down.  We're already running in the call combiner, so one of
+  // the batches can be started directly, but the others will have to
+  // re-enter the call combiner.
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "chand=%p calld=%p: starting %" PRIuPTR
+            " retriable batches on subchannel_call=%p",
+            chand, calld, num_batches, calld->subchannel_call);
+  }
+  if (num_batches == 0) {
+    // This should be fairly rare, but it can happen when (e.g.) an
+    // attempt completes before it has finished replaying all
+    // previously sent messages.
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "no retriable subchannel batches to start");
+  } else {
+    for (size_t i = 1; i < num_batches; ++i) {
+      if (grpc_client_channel_trace.enabled()) {
+        char* batch_str = grpc_transport_stream_op_batch_string(batches[i]);
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: starting batch in call combiner: %s", chand,
+                calld, batch_str);
+        gpr_free(batch_str);
+      }
+      batches[i]->handler_private.extra_arg = calld->subchannel_call;
+      GRPC_CLOSURE_INIT(&batches[i]->handler_private.closure,
+                        start_batch_in_call_combiner, batches[i],
+                        grpc_schedule_on_exec_ctx);
+      GRPC_CALL_COMBINER_START(calld->call_combiner,
+                               &batches[i]->handler_private.closure,
+                               GRPC_ERROR_NONE, "start_subchannel_batch");
+    }
+    if (grpc_client_channel_trace.enabled()) {
+      char* batch_str = grpc_transport_stream_op_batch_string(batches[0]);
+      gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting batch: %s", chand, calld,
+              batch_str);
+      gpr_free(batch_str);
+    }
+    // Note: This will release the call combiner.
+    grpc_subchannel_call_process_op(calld->subchannel_call, batches[0]);
+  }
+}
+
+//
+// LB pick
+//
+
+static void create_subchannel_call(grpc_call_element* elem, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  const size_t parent_data_size =
+      calld->enable_retries ? sizeof(subchannel_call_retry_state) : 0;
   const grpc_core::ConnectedSubchannel::CallArgs call_args = {
       calld->pollent,                       // pollent
       calld->path,                          // path
@@ -1007,7 +2466,8 @@
       calld->deadline,                      // deadline
       calld->arena,                         // arena
       calld->pick.subchannel_call_context,  // context
-      calld->call_combiner                  // call_combiner
+      calld->call_combiner,                 // call_combiner
+      parent_data_size                      // parent_data_size
   };
   grpc_error* new_error = calld->pick.connected_subchannel->CreateCall(
       call_args, &calld->subchannel_call);
@@ -1017,36 +2477,61 @@
   }
   if (new_error != GRPC_ERROR_NONE) {
     new_error = grpc_error_add_child(new_error, error);
-    waiting_for_pick_batches_fail(elem, new_error);
+    pending_batches_fail(elem, new_error, true /* yield_call_combiner */);
   } else {
-    waiting_for_pick_batches_resume(elem);
+    if (parent_data_size > 0) {
+      subchannel_call_retry_state* retry_state =
+          static_cast<subchannel_call_retry_state*>(
+              grpc_connected_subchannel_call_get_parent_data(
+                  calld->subchannel_call));
+      retry_state->batch_payload.context = calld->pick.subchannel_call_context;
+    }
+    pending_batches_resume(elem);
   }
   GRPC_ERROR_UNREF(error);
 }
 
 // Invoked when a pick is completed, on both success or failure.
-static void pick_done_locked(grpc_call_element* elem, grpc_error* error) {
-  call_data* calld = static_cast<call_data*>(elem->call_data);
+static void pick_done(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
   if (calld->pick.connected_subchannel == nullptr) {
     // Failed to create subchannel.
-    GRPC_ERROR_UNREF(calld->error);
-    calld->error = error == GRPC_ERROR_NONE
-                       ? GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                             "Call dropped by load balancing policy")
-                       : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                             "Failed to create subchannel", &error, 1);
-    if (grpc_client_channel_trace.enabled()) {
-      gpr_log(GPR_DEBUG,
-              "chand=%p calld=%p: failed to create subchannel: error=%s", chand,
-              calld, grpc_error_string(calld->error));
+    // If there was no error, this is an LB policy drop, in which case
+    // we return an error; otherwise, we may retry.
+    grpc_status_code status = GRPC_STATUS_OK;
+    grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr,
+                          nullptr);
+    if (error == GRPC_ERROR_NONE || !calld->enable_retries ||
+        !maybe_retry(elem, nullptr /* batch_data */, status,
+                     nullptr /* server_pushback_md */)) {
+      grpc_error* new_error =
+          error == GRPC_ERROR_NONE
+              ? GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                    "Call dropped by load balancing policy")
+              : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                    "Failed to create subchannel", &error, 1);
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_DEBUG,
+                "chand=%p calld=%p: failed to create subchannel: error=%s",
+                chand, calld, grpc_error_string(new_error));
+      }
+      pending_batches_fail(elem, new_error, true /* yield_call_combiner */);
     }
-    waiting_for_pick_batches_fail(elem, GRPC_ERROR_REF(calld->error));
   } else {
     /* Create call on subchannel. */
-    create_subchannel_call_locked(elem, GRPC_ERROR_REF(error));
+    create_subchannel_call(elem, GRPC_ERROR_REF(error));
   }
-  GRPC_ERROR_UNREF(error);
+}
+
+// Invoked when a pick is completed to leave the client_channel combiner
+// and continue processing in the call combiner.
+static void pick_done_locked(grpc_call_element* elem, grpc_error* error) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  GRPC_CLOSURE_INIT(&calld->pick_closure, pick_done, elem,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_SCHED(&calld->pick_closure, error);
 }
 
 // A wrapper around pick_done_locked() that is used in cases where
@@ -1073,15 +2558,14 @@
   if (error != GRPC_ERROR_NONE && chand->lb_policy != nullptr) {
     if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p",
-              chand, calld, chand->lb_policy);
+              chand, calld, chand->lb_policy.get());
     }
-    grpc_lb_policy_cancel_pick_locked(chand->lb_policy, &calld->pick,
-                                      GRPC_ERROR_REF(error));
+    chand->lb_policy->CancelPickLocked(&calld->pick, GRPC_ERROR_REF(error));
   }
   GRPC_CALL_STACK_UNREF(calld->owning_call, "pick_callback_cancel");
 }
 
-// Callback invoked by grpc_lb_policy_pick_locked() for async picks.
+// Callback invoked by LoadBalancingPolicy::PickLocked() for async picks.
 // Unrefs the LB policy and invokes async_pick_done_locked().
 static void pick_callback_done_locked(void* arg, grpc_error* error) {
   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
@@ -1095,48 +2579,98 @@
   GRPC_CALL_STACK_UNREF(calld->owning_call, "pick_callback");
 }
 
-// Takes a ref to chand->lb_policy and calls grpc_lb_policy_pick_locked().
-// If the pick was completed synchronously, unrefs the LB policy and
-// returns true.
+// Applies service config to the call.  Must be invoked once we know
+// that the resolver has returned results to the channel.
+static void apply_service_config_to_call_locked(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call",
+            chand, calld);
+  }
+  if (chand->retry_throttle_data != nullptr) {
+    calld->retry_throttle_data =
+        grpc_server_retry_throttle_data_ref(chand->retry_throttle_data);
+  }
+  if (chand->method_params_table != nullptr) {
+    calld->method_params = grpc_core::ServiceConfig::MethodConfigTableLookup(
+        *chand->method_params_table, calld->path);
+    if (calld->method_params != nullptr) {
+      // If the deadline from the service config is shorter than the one
+      // from the client API, reset the deadline timer.
+      if (chand->deadline_checking_enabled &&
+          calld->method_params->timeout() != 0) {
+        const grpc_millis per_method_deadline =
+            grpc_timespec_to_millis_round_up(calld->call_start_time) +
+            calld->method_params->timeout();
+        if (per_method_deadline < calld->deadline) {
+          calld->deadline = per_method_deadline;
+          grpc_deadline_state_reset(elem, calld->deadline);
+        }
+      }
+    }
+  }
+  // If no retry policy, disable retries.
+  // TODO(roth): Remove this when adding support for transparent retries.
+  if (calld->method_params == nullptr ||
+      calld->method_params->retry_policy() == nullptr) {
+    calld->enable_retries = false;
+  }
+}
+
+// Starts a pick on chand->lb_policy.
+// Returns true if pick is completed synchronously.
 static bool pick_callback_start_locked(grpc_call_element* elem) {
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
   call_data* calld = static_cast<call_data*>(elem->call_data);
   if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p",
-            chand, calld, chand->lb_policy);
+            chand, calld, chand->lb_policy.get());
   }
-  apply_service_config_to_call_locked(elem);
+  // Only get service config data on the first attempt.
+  if (calld->num_attempts_completed == 0) {
+    apply_service_config_to_call_locked(elem);
+  }
   // If the application explicitly set wait_for_ready, use that.
   // Otherwise, if the service config specified a value for this
   // method, use that.
-  uint32_t initial_metadata_flags =
-      calld->initial_metadata_batch->payload->send_initial_metadata
-          .send_initial_metadata_flags;
+  //
+  // The send_initial_metadata batch will be the first one in the list,
+  // as set by get_batch_index() above.
+  calld->pick.initial_metadata =
+      calld->seen_send_initial_metadata
+          ? &calld->send_initial_metadata
+          : calld->pending_batches[0]
+                .batch->payload->send_initial_metadata.send_initial_metadata;
+  uint32_t send_initial_metadata_flags =
+      calld->seen_send_initial_metadata
+          ? calld->send_initial_metadata_flags
+          : calld->pending_batches[0]
+                .batch->payload->send_initial_metadata
+                .send_initial_metadata_flags;
   const bool wait_for_ready_set_from_api =
-      initial_metadata_flags &
+      send_initial_metadata_flags &
       GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET;
   const bool wait_for_ready_set_from_service_config =
       calld->method_params != nullptr &&
-      calld->method_params->wait_for_ready != WAIT_FOR_READY_UNSET;
+      calld->method_params->wait_for_ready() !=
+          ClientChannelMethodParams::WAIT_FOR_READY_UNSET;
   if (!wait_for_ready_set_from_api && wait_for_ready_set_from_service_config) {
-    if (calld->method_params->wait_for_ready == WAIT_FOR_READY_TRUE) {
-      initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY;
+    if (calld->method_params->wait_for_ready() ==
+        ClientChannelMethodParams::WAIT_FOR_READY_TRUE) {
+      send_initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY;
     } else {
-      initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY;
+      send_initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY;
     }
   }
-  calld->pick.initial_metadata =
-      calld->initial_metadata_batch->payload->send_initial_metadata
-          .send_initial_metadata;
-  calld->pick.initial_metadata_flags = initial_metadata_flags;
-  GRPC_CLOSURE_INIT(&calld->lb_pick_closure, pick_callback_done_locked, elem,
+  calld->pick.initial_metadata_flags = send_initial_metadata_flags;
+  GRPC_CLOSURE_INIT(&calld->pick_closure, pick_callback_done_locked, elem,
                     grpc_combiner_scheduler(chand->combiner));
-  calld->pick.on_complete = &calld->lb_pick_closure;
+  calld->pick.on_complete = &calld->pick_closure;
   GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback");
-  const bool pick_done =
-      grpc_lb_policy_pick_locked(chand->lb_policy, &calld->pick);
+  const bool pick_done = chand->lb_policy->PickLocked(&calld->pick);
   if (pick_done) {
-    /* synchronous grpc_lb_policy_pick call. Unref the LB policy. */
+    // Pick completed synchronously.
     if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously",
               chand, calld);
@@ -1146,7 +2680,7 @@
     GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback_cancel");
     grpc_call_combiner_set_notify_on_cancel(
         calld->call_combiner,
-        GRPC_CLOSURE_INIT(&calld->lb_pick_cancel_closure,
+        GRPC_CLOSURE_INIT(&calld->pick_cancel_closure,
                           pick_callback_cancel_locked, elem,
                           grpc_combiner_scheduler(chand->combiner)));
   }
@@ -1195,8 +2729,6 @@
                                    "Pick cancelled", &error, 1));
 }
 
-static void pick_after_resolver_result_start_locked(grpc_call_element* elem);
-
 static void pick_after_resolver_result_done_locked(void* arg,
                                                    grpc_error* error) {
   pick_after_resolver_result_args* args =
@@ -1233,7 +2765,7 @@
       async_pick_done_locked(elem, GRPC_ERROR_NONE);
     }
   }
-  // TODO(roth): It should be impossible for chand->lb_policy to be NULL
+  // TODO(roth): It should be impossible for chand->lb_policy to be nullptr
   // here, so the rest of this code should never actually be executed.
   // However, we have reports of a crash on iOS that triggers this case,
   // so we are temporarily adding this to restore branches that were
@@ -1286,6 +2818,7 @@
   call_data* calld = static_cast<call_data*>(elem->call_data);
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
   GPR_ASSERT(calld->pick.connected_subchannel == nullptr);
+  GPR_ASSERT(calld->subchannel_call == nullptr);
   if (chand->lb_policy != nullptr) {
     // We already have an LB policy, so ask it for a pick.
     if (pick_callback_start_locked(elem)) {
@@ -1314,24 +2847,9 @@
                                          chand->interested_parties);
 }
 
-static void on_complete(void* arg, grpc_error* error) {
-  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
-  call_data* calld = static_cast<call_data*>(elem->call_data);
-  if (calld->retry_throttle_data != nullptr) {
-    if (error == GRPC_ERROR_NONE) {
-      grpc_server_retry_throttle_data_record_success(
-          calld->retry_throttle_data);
-    } else {
-      // TODO(roth): In a subsequent PR, check the return value here and
-      // decide whether or not to retry.  Note that we should only
-      // record failures whose statuses match the configured retryable
-      // or non-fatal status codes.
-      grpc_server_retry_throttle_data_record_failure(
-          calld->retry_throttle_data);
-    }
-  }
-  GRPC_CLOSURE_RUN(calld->original_on_complete, GRPC_ERROR_REF(error));
-}
+//
+// filter call vtable functions
+//
 
 static void cc_start_transport_stream_op_batch(
     grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
@@ -1342,46 +2860,47 @@
     grpc_deadline_state_client_start_transport_stream_op_batch(elem, batch);
   }
   // If we've previously been cancelled, immediately fail any new batches.
-  if (calld->error != GRPC_ERROR_NONE) {
+  if (calld->cancel_error != GRPC_ERROR_NONE) {
     if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing batch with error: %s",
-              chand, calld, grpc_error_string(calld->error));
+              chand, calld, grpc_error_string(calld->cancel_error));
     }
+    // Note: This will release the call combiner.
     grpc_transport_stream_op_batch_finish_with_failure(
-        batch, GRPC_ERROR_REF(calld->error), calld->call_combiner);
+        batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
     return;
   }
+  // Handle cancellation.
   if (batch->cancel_stream) {
     // Stash a copy of cancel_error in our call data, so that we can use
     // it for subsequent operations.  This ensures that if the call is
     // cancelled before any batches are passed down (e.g., if the deadline
     // is in the past when the call starts), we can return the right
     // error to the caller when the first batch does get passed down.
-    GRPC_ERROR_UNREF(calld->error);
-    calld->error = GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
+    GRPC_ERROR_UNREF(calld->cancel_error);
+    calld->cancel_error =
+        GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
     if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: recording cancel_error=%s", chand,
-              calld, grpc_error_string(calld->error));
+              calld, grpc_error_string(calld->cancel_error));
     }
-    // If we have a subchannel call, send the cancellation batch down.
-    // Otherwise, fail all pending batches.
-    if (calld->subchannel_call != nullptr) {
-      grpc_subchannel_call_process_op(calld->subchannel_call, batch);
+    // If we do not have a subchannel call (i.e., a pick has not yet
+    // been started), fail all pending batches.  Otherwise, send the
+    // cancellation down to the subchannel call.
+    if (calld->subchannel_call == nullptr) {
+      pending_batches_fail(elem, GRPC_ERROR_REF(calld->cancel_error),
+                           false /* yield_call_combiner */);
+      // Note: This will release the call combiner.
+      grpc_transport_stream_op_batch_finish_with_failure(
+          batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
     } else {
-      waiting_for_pick_batches_add(calld, batch);
-      waiting_for_pick_batches_fail(elem, GRPC_ERROR_REF(calld->error));
+      // Note: This will release the call combiner.
+      grpc_subchannel_call_process_op(calld->subchannel_call, batch);
     }
     return;
   }
-  // Intercept on_complete for recv_trailing_metadata so that we can
-  // check retry throttle status.
-  if (batch->recv_trailing_metadata) {
-    GPR_ASSERT(batch->on_complete != nullptr);
-    calld->original_on_complete = batch->on_complete;
-    GRPC_CLOSURE_INIT(&calld->on_complete, on_complete, elem,
-                      grpc_schedule_on_exec_ctx);
-    batch->on_complete = &calld->on_complete;
-  }
+  // Add the batch to the pending list.
+  pending_batches_add(elem, batch);
   // Check if we've already gotten a subchannel call.
   // Note that once we have completed the pick, we do not need to enter
   // the channel combiner, which is more efficient (especially for
@@ -1389,15 +2908,13 @@
   if (calld->subchannel_call != nullptr) {
     if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
-              "chand=%p calld=%p: sending batch to subchannel_call=%p", chand,
+              "chand=%p calld=%p: starting batch on subchannel_call=%p", chand,
               calld, calld->subchannel_call);
     }
-    grpc_subchannel_call_process_op(calld->subchannel_call, batch);
+    pending_batches_resume(elem);
     return;
   }
   // We do not yet have a subchannel call.
-  // Add the batch to the waiting-for-pick list.
-  waiting_for_pick_batches_add(calld, batch);
   // For batches containing a send_initial_metadata op, enter the channel
   // combiner to start a pick.
   if (batch->send_initial_metadata) {
@@ -1437,6 +2954,7 @@
     grpc_deadline_state_init(elem, args->call_stack, args->call_combiner,
                              calld->deadline);
   }
+  calld->enable_retries = chand->enable_retries;
   return GRPC_ERROR_NONE;
 }
 
@@ -1450,10 +2968,8 @@
     grpc_deadline_state_destroy(elem);
   }
   grpc_slice_unref_internal(calld->path);
-  if (calld->method_params != nullptr) {
-    method_parameters_unref(calld->method_params);
-  }
-  GRPC_ERROR_UNREF(calld->error);
+  calld->method_params.reset();
+  GRPC_ERROR_UNREF(calld->cancel_error);
   if (calld->subchannel_call != nullptr) {
     grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call,
                                              then_schedule_closure);
@@ -1461,7 +2977,9 @@
     GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call,
                                "client_channel_destroy_call");
   }
-  GPR_ASSERT(calld->waiting_for_pick_batches_count == 0);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    GPR_ASSERT(calld->pending_batches[i].batch == nullptr);
+  }
   if (calld->pick.connected_subchannel != nullptr) {
     calld->pick.connected_subchannel.reset();
   }
@@ -1501,7 +3019,7 @@
 static void try_to_connect_locked(void* arg, grpc_error* error_ignored) {
   channel_data* chand = static_cast<channel_data*>(arg);
   if (chand->lb_policy != nullptr) {
-    grpc_lb_policy_exit_idle_locked(chand->lb_policy);
+    chand->lb_policy->ExitIdleLocked();
   } else {
     chand->exit_idle_when_lb_policy_arrives = true;
     if (!chand->started_resolving && chand->resolver != nullptr) {
@@ -1661,3 +3179,9 @@
                         grpc_combiner_scheduler(chand->combiner)),
       GRPC_ERROR_NONE);
 }
+
+grpc_subchannel_call* grpc_client_channel_get_subchannel_call(
+    grpc_call_element* elem) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  return calld->subchannel_call;
+}
diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h
index 9670405..a21e562 100644
--- a/src/core/ext/filters/client_channel/client_channel.h
+++ b/src/core/ext/filters/client_channel/client_channel.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/client_channel_factory.h"
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/channel/channel_stack.h"
diff --git a/src/core/ext/filters/client_channel/client_channel_factory.cc b/src/core/ext/filters/client_channel/client_channel_factory.cc
index 3baf5b3..172e9f0 100644
--- a/src/core/ext/filters/client_channel/client_channel_factory.cc
+++ b/src/core/ext/filters/client_channel/client_channel_factory.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/client_channel_factory.h"
 #include "src/core/lib/channel/channel_args.h"
 
diff --git a/src/core/ext/filters/client_channel/client_channel_factory.h b/src/core/ext/filters/client_channel/client_channel_factory.h
index 766ebb9..601ec46 100644
--- a/src/core/ext/filters/client_channel/client_channel_factory.h
+++ b/src/core/ext/filters/client_channel/client_channel_factory.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/ext/filters/client_channel/subchannel.h"
diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.cc b/src/core/ext/filters/client_channel/client_channel_plugin.cc
index 9172fa7..3c3a975 100644
--- a/src/core/ext/filters/client_channel/client_channel_plugin.cc
+++ b/src/core/ext/filters/client_channel/client_channel_plugin.cc
@@ -63,7 +63,7 @@
 }
 
 void grpc_client_channel_init(void) {
-  grpc_lb_policy_registry_init();
+  grpc_core::LoadBalancingPolicyRegistry::Builder::InitRegistry();
   grpc_core::ResolverRegistry::Builder::InitRegistry();
   grpc_retry_throttle_map_init();
   grpc_proxy_mapper_registry_init();
@@ -83,5 +83,5 @@
   grpc_proxy_mapper_registry_shutdown();
   grpc_retry_throttle_map_shutdown();
   grpc_core::ResolverRegistry::Builder::ShutdownRegistry();
-  grpc_lb_policy_registry_shutdown();
+  grpc_core::LoadBalancingPolicyRegistry::Builder::ShutdownRegistry();
 }
diff --git a/src/core/ext/filters/client_channel/connector.cc b/src/core/ext/filters/client_channel/connector.cc
index c8bf2f3..5e04b3b 100644
--- a/src/core/ext/filters/client_channel/connector.cc
+++ b/src/core/ext/filters/client_channel/connector.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/connector.h"
 
 grpc_connector* grpc_connector_ref(grpc_connector* connector) {
diff --git a/src/core/ext/filters/client_channel/connector.h b/src/core/ext/filters/client_channel/connector.h
index d657658..5565949 100644
--- a/src/core/ext/filters/client_channel/connector.h
+++ b/src/core/ext/filters/client_channel/connector.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/transport/transport.h"
diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/src/core/ext/filters/client_channel/http_connect_handshaker.cc
index c1959bd..fb29fa7 100644
--- a/src/core/ext/filters/client_channel/http_connect_handshaker.cc
+++ b/src/core/ext/filters/client_channel/http_connect_handshaker.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
 
 #include <string.h>
@@ -254,7 +256,8 @@
   // If not found, invoke on_handshake_done without doing anything.
   const grpc_arg* arg =
       grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_SERVER);
-  if (arg == nullptr || arg->type != GRPC_ARG_STRING) {
+  char* server_name = grpc_channel_arg_get_string(arg);
+  if (server_name == nullptr) {
     // Set shutdown to true so that subsequent calls to
     // http_connect_handshaker_shutdown() do nothing.
     gpr_mu_lock(&handshaker->mu);
@@ -263,16 +266,15 @@
     GRPC_CLOSURE_SCHED(on_handshake_done, GRPC_ERROR_NONE);
     return;
   }
-  char* server_name = arg->value.string;
   // Get headers from channel args.
   arg = grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_HEADERS);
+  char* arg_header_string = grpc_channel_arg_get_string(arg);
   grpc_http_header* headers = nullptr;
   size_t num_headers = 0;
   char** header_strings = nullptr;
   size_t num_header_strings = 0;
-  if (arg != nullptr) {
-    GPR_ASSERT(arg->type == GRPC_ARG_STRING);
-    gpr_string_split(arg->value.string, "\n", &header_strings,
+  if (arg_header_string != nullptr) {
+    gpr_string_split(arg_header_string, "\n", &header_strings,
                      &num_header_strings);
     headers = static_cast<grpc_http_header*>(
         gpr_malloc(sizeof(grpc_http_header) * num_header_strings));
diff --git a/src/core/ext/filters/client_channel/http_proxy.cc b/src/core/ext/filters/client_channel/http_proxy.cc
index d423764..29a6c0e 100644
--- a/src/core/ext/filters/client_channel/http_proxy.cc
+++ b/src/core/ext/filters/client_channel/http_proxy.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/http_proxy.h"
 
 #include <stdbool.h>
diff --git a/src/core/ext/filters/client_channel/lb_policy.cc b/src/core/ext/filters/client_channel/lb_policy.cc
index cc4fe7e..fa63dd7 100644
--- a/src/core/ext/filters/client_channel/lb_policy.cc
+++ b/src/core/ext/filters/client_channel/lb_policy.cc
@@ -16,126 +16,44 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy.h"
 #include "src/core/lib/iomgr/combiner.h"
 
 grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount(
     false, "lb_policy_refcount");
 
-void grpc_lb_policy_init(grpc_lb_policy* policy,
-                         const grpc_lb_policy_vtable* vtable,
-                         grpc_combiner* combiner) {
-  policy->vtable = vtable;
-  gpr_ref_init(&policy->refs, 1);
-  policy->interested_parties = grpc_pollset_set_create();
-  policy->combiner = GRPC_COMBINER_REF(combiner, "lb_policy");
+namespace grpc_core {
+
+LoadBalancingPolicy::LoadBalancingPolicy(const Args& args)
+    : InternallyRefCountedWithTracing(&grpc_trace_lb_policy_refcount),
+      combiner_(GRPC_COMBINER_REF(args.combiner, "lb_policy")),
+      client_channel_factory_(args.client_channel_factory),
+      interested_parties_(grpc_pollset_set_create()),
+      request_reresolution_(nullptr) {}
+
+LoadBalancingPolicy::~LoadBalancingPolicy() {
+  grpc_pollset_set_destroy(interested_parties_);
+  GRPC_COMBINER_UNREF(combiner_, "lb_policy");
 }
 
-#ifndef NDEBUG
-void grpc_lb_policy_ref(grpc_lb_policy* lb_policy, const char* file, int line,
-                        const char* reason) {
-  if (grpc_trace_lb_policy_refcount.enabled()) {
-    gpr_atm old_refs = gpr_atm_no_barrier_load(&lb_policy->refs.count);
-    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-            "LB_POLICY:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", lb_policy,
-            old_refs, old_refs + 1, reason);
-  }
-#else
-void grpc_lb_policy_ref(grpc_lb_policy* lb_policy) {
-#endif
-  gpr_ref(&lb_policy->refs);
-}
-
-#ifndef NDEBUG
-void grpc_lb_policy_unref(grpc_lb_policy* lb_policy, const char* file, int line,
-                          const char* reason) {
-  if (grpc_trace_lb_policy_refcount.enabled()) {
-    gpr_atm old_refs = gpr_atm_no_barrier_load(&lb_policy->refs.count);
-    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-            "LB_POLICY:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", lb_policy,
-            old_refs, old_refs - 1, reason);
-  }
-#else
-void grpc_lb_policy_unref(grpc_lb_policy* lb_policy) {
-#endif
-  if (gpr_unref(&lb_policy->refs)) {
-    grpc_pollset_set_destroy(lb_policy->interested_parties);
-    grpc_combiner* combiner = lb_policy->combiner;
-    lb_policy->vtable->destroy(lb_policy);
-    GRPC_COMBINER_UNREF(combiner, "lb_policy");
-  }
-}
-
-void grpc_lb_policy_shutdown_locked(grpc_lb_policy* policy,
-                                    grpc_lb_policy* new_policy) {
-  policy->vtable->shutdown_locked(policy, new_policy);
-}
-
-int grpc_lb_policy_pick_locked(grpc_lb_policy* policy,
-                               grpc_lb_policy_pick_state* pick) {
-  return policy->vtable->pick_locked(policy, pick);
-}
-
-void grpc_lb_policy_cancel_pick_locked(grpc_lb_policy* policy,
-                                       grpc_lb_policy_pick_state* pick,
-                                       grpc_error* error) {
-  policy->vtable->cancel_pick_locked(policy, pick, error);
-}
-
-void grpc_lb_policy_cancel_picks_locked(grpc_lb_policy* policy,
-                                        uint32_t initial_metadata_flags_mask,
-                                        uint32_t initial_metadata_flags_eq,
-                                        grpc_error* error) {
-  policy->vtable->cancel_picks_locked(policy, initial_metadata_flags_mask,
-                                      initial_metadata_flags_eq, error);
-}
-
-void grpc_lb_policy_exit_idle_locked(grpc_lb_policy* policy) {
-  policy->vtable->exit_idle_locked(policy);
-}
-
-void grpc_lb_policy_ping_one_locked(grpc_lb_policy* policy,
-                                    grpc_closure* on_initiate,
-                                    grpc_closure* on_ack) {
-  policy->vtable->ping_one_locked(policy, on_initiate, on_ack);
-}
-
-void grpc_lb_policy_notify_on_state_change_locked(
-    grpc_lb_policy* policy, grpc_connectivity_state* state,
-    grpc_closure* closure) {
-  policy->vtable->notify_on_state_change_locked(policy, state, closure);
-}
-
-grpc_connectivity_state grpc_lb_policy_check_connectivity_locked(
-    grpc_lb_policy* policy, grpc_error** connectivity_error) {
-  return policy->vtable->check_connectivity_locked(policy, connectivity_error);
-}
-
-void grpc_lb_policy_update_locked(grpc_lb_policy* policy,
-                                  const grpc_lb_policy_args* lb_policy_args) {
-  policy->vtable->update_locked(policy, lb_policy_args);
-}
-
-void grpc_lb_policy_set_reresolve_closure_locked(
-    grpc_lb_policy* policy, grpc_closure* request_reresolution) {
-  policy->vtable->set_reresolve_closure_locked(policy, request_reresolution);
-}
-
-void grpc_lb_policy_try_reresolve(grpc_lb_policy* policy,
-                                  grpc_core::TraceFlag* grpc_lb_trace,
-                                  grpc_error* error) {
-  if (policy->request_reresolution != nullptr) {
-    GRPC_CLOSURE_SCHED(policy->request_reresolution, error);
-    policy->request_reresolution = nullptr;
+void LoadBalancingPolicy::TryReresolutionLocked(
+    grpc_core::TraceFlag* grpc_lb_trace, grpc_error* error) {
+  if (request_reresolution_ != nullptr) {
+    GRPC_CLOSURE_SCHED(request_reresolution_, error);
+    request_reresolution_ = nullptr;
     if (grpc_lb_trace->enabled()) {
       gpr_log(GPR_DEBUG,
               "%s %p: scheduling re-resolution closure with error=%s.",
-              grpc_lb_trace->name(), policy, grpc_error_string(error));
+              grpc_lb_trace->name(), this, grpc_error_string(error));
     }
   } else {
-    if (grpc_lb_trace->enabled() && error == GRPC_ERROR_NONE) {
-      gpr_log(GPR_DEBUG, "%s %p: re-resolution already in progress.",
-              grpc_lb_trace->name(), policy);
+    if (grpc_lb_trace->enabled()) {
+      gpr_log(GPR_DEBUG, "%s %p: no available re-resolution closure.",
+              grpc_lb_trace->name(), this);
     }
   }
 }
+
+}  // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h
index 30660cb..c3e43e5 100644
--- a/src/core/ext/filters/client_channel/lb_policy.h
+++ b/src/core/ext/filters/client_channel/lb_policy.h
@@ -19,186 +19,183 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H
 
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
 #include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
-/** A load balancing policy: specified by a vtable and a struct (which
-    is expected to be extended to contain some parameters) */
-typedef struct grpc_lb_policy grpc_lb_policy;
-typedef struct grpc_lb_policy_vtable grpc_lb_policy_vtable;
-typedef struct grpc_lb_policy_args grpc_lb_policy_args;
-
 extern grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount;
 
-struct grpc_lb_policy {
-  const grpc_lb_policy_vtable* vtable;
-  gpr_refcount refs;
-  /* owned pointer to interested parties in load balancing decisions */
-  grpc_pollset_set* interested_parties;
-  /* combiner under which lb_policy actions take place */
-  grpc_combiner* combiner;
-  /* callback to force a re-resolution */
-  grpc_closure* request_reresolution;
-};
+namespace grpc_core {
 
-/// State used for an LB pick.
-typedef struct grpc_lb_policy_pick_state {
-  /// Initial metadata associated with the picking call.
-  grpc_metadata_batch* initial_metadata;
-  /// Bitmask used for selective cancelling. See \a
-  /// grpc_lb_policy_cancel_picks() and \a GRPC_INITIAL_METADATA_* in
-  /// grpc_types.h.
-  uint32_t initial_metadata_flags;
-  /// Storage for LB token in \a initial_metadata, or NULL if not used.
-  grpc_linked_mdelem lb_token_mdelem_storage;
-  /// Closure to run when pick is complete, if not completed synchronously.
-  grpc_closure* on_complete;
-  /// Will be set to the selected subchannel, or nullptr on failure or when
-  /// the LB policy decides to drop the call.
-  grpc_core::RefCountedPtr<grpc_core::ConnectedSubchannel> connected_subchannel;
-  /// Will be populated with context to pass to the subchannel call, if needed.
-  grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT];
-  /// Upon success, \a *user_data will be set to whatever opaque information
-  /// may need to be propagated from the LB policy, or NULL if not needed.
-  void** user_data;
-  /// Next pointer.  For internal use by LB policy.
-  struct grpc_lb_policy_pick_state* next;
-} grpc_lb_policy_pick_state;
+/// Interface for load balancing policies.
+///
+/// Note: All methods with a "Locked" suffix must be called from the
+/// combiner passed to the constructor.
+///
+/// Any I/O done by the LB policy should be done under the pollset_set
+/// returned by \a interested_parties().
+class LoadBalancingPolicy
+    : public InternallyRefCountedWithTracing<LoadBalancingPolicy> {
+ public:
+  struct Args {
+    /// The combiner under which all LB policy calls will be run.
+    /// Policy does NOT take ownership of the reference to the combiner.
+    // TODO(roth): Once we have a C++-like interface for combiners, this
+    // API should change to take a smart pointer that does pass ownership
+    // of a reference.
+    grpc_combiner* combiner = nullptr;
+    /// Used to create channels and subchannels.
+    grpc_client_channel_factory* client_channel_factory = nullptr;
+    /// Channel args from the resolver.
+    /// Note that the LB policy gets the set of addresses from the
+    /// GRPC_ARG_LB_ADDRESSES channel arg.
+    grpc_channel_args* args = nullptr;
+  };
 
-struct grpc_lb_policy_vtable {
-  void (*destroy)(grpc_lb_policy* policy);
+  /// State used for an LB pick.
+  struct PickState {
+    /// Initial metadata associated with the picking call.
+    grpc_metadata_batch* initial_metadata;
+    /// Bitmask used for selective cancelling. See
+    /// \a CancelMatchingPicksLocked() and \a GRPC_INITIAL_METADATA_* in
+    /// grpc_types.h.
+    uint32_t initial_metadata_flags;
+    /// Storage for LB token in \a initial_metadata, or nullptr if not used.
+    grpc_linked_mdelem lb_token_mdelem_storage;
+    /// Closure to run when pick is complete, if not completed synchronously.
+    grpc_closure* on_complete;
+    /// Will be set to the selected subchannel, or nullptr on failure or when
+    /// the LB policy decides to drop the call.
+    RefCountedPtr<ConnectedSubchannel> connected_subchannel;
+    /// Will be populated with context to pass to the subchannel call, if
+    /// needed.
+    grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT];
+    /// Upon success, \a *user_data will be set to whatever opaque information
+    /// may need to be propagated from the LB policy, or nullptr if not needed.
+    // TODO(roth): As part of revamping our metadata APIs, try to find a
+    // way to clean this up and C++-ify it.
+    void** user_data;
+    /// Next pointer.  For internal use by LB policy.
+    PickState* next;
+  };
 
-  /// \see grpc_lb_policy_shutdown_locked().
-  void (*shutdown_locked)(grpc_lb_policy* policy, grpc_lb_policy* new_policy);
+  // Not copyable nor movable.
+  LoadBalancingPolicy(const LoadBalancingPolicy&) = delete;
+  LoadBalancingPolicy& operator=(const LoadBalancingPolicy&) = delete;
 
-  /** \see grpc_lb_policy_pick */
-  int (*pick_locked)(grpc_lb_policy* policy, grpc_lb_policy_pick_state* pick);
+  /// Updates the policy with a new set of \a args from the resolver.
+  /// Note that the LB policy gets the set of addresses from the
+  /// GRPC_ARG_LB_ADDRESSES channel arg.
+  virtual void UpdateLocked(const grpc_channel_args& args) GRPC_ABSTRACT;
 
-  /** \see grpc_lb_policy_cancel_pick */
-  void (*cancel_pick_locked)(grpc_lb_policy* policy,
-                             grpc_lb_policy_pick_state* pick,
+  /// Finds an appropriate subchannel for a call, based on data in \a pick.
+  /// \a pick must remain alive until the pick is complete.
+  ///
+  /// If the pick succeeds and a result is known immediately, returns true.
+  /// Otherwise, \a pick->on_complete will be invoked once the pick is
+  /// complete with its error argument set to indicate success or failure.
+  virtual bool PickLocked(PickState* pick) GRPC_ABSTRACT;
+
+  /// Cancels \a pick.
+  /// The \a on_complete callback of the pending pick will be invoked with
+  /// \a pick->connected_subchannel set to null.
+  virtual void CancelPickLocked(PickState* pick,
+                                grpc_error* error) GRPC_ABSTRACT;
+
+  /// Cancels all pending picks for which their \a initial_metadata_flags (as
+  /// given in the call to \a PickLocked()) matches
+  /// \a initial_metadata_flags_eq when ANDed with
+  /// \a initial_metadata_flags_mask.
+  virtual void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                         uint32_t initial_metadata_flags_eq,
+                                         grpc_error* error) GRPC_ABSTRACT;
+
+  /// Requests a notification when the connectivity state of the policy
+  /// changes from \a *state.  When that happens, sets \a *state to the
+  /// new state and schedules \a closure.
+  virtual void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                         grpc_closure* closure) GRPC_ABSTRACT;
+
+  /// Returns the policy's current connectivity state.  Sets \a error to
+  /// the associated error, if any.
+  virtual grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) GRPC_ABSTRACT;
+
+  /// Hands off pending picks to \a new_policy.
+  virtual void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy)
+      GRPC_ABSTRACT;
+
+  /// Performs a connected subchannel ping via \a ConnectedSubchannel::Ping()
+  /// against one of the connected subchannels managed by the policy.
+  /// Note: This is intended only for use in tests.
+  virtual void PingOneLocked(grpc_closure* on_initiate,
+                             grpc_closure* on_ack) GRPC_ABSTRACT;
+
+  /// Tries to enter a READY connectivity state.
+  /// TODO(roth): As part of restructuring how we handle IDLE state,
+  /// consider whether this method is still needed.
+  virtual void ExitIdleLocked() GRPC_ABSTRACT;
+
+  void Orphan() override {
+    // Invoke ShutdownAndUnrefLocked() inside of the combiner.
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(&LoadBalancingPolicy::ShutdownAndUnrefLocked, this,
+                            grpc_combiner_scheduler(combiner_)),
+        GRPC_ERROR_NONE);
+  }
+
+  /// Sets the re-resolution closure to \a request_reresolution.
+  void SetReresolutionClosureLocked(grpc_closure* request_reresolution) {
+    GPR_ASSERT(request_reresolution_ == nullptr);
+    request_reresolution_ = request_reresolution;
+  }
+
+  grpc_pollset_set* interested_parties() const { return interested_parties_; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  explicit LoadBalancingPolicy(const Args& args);
+  virtual ~LoadBalancingPolicy();
+
+  grpc_combiner* combiner() const { return combiner_; }
+  grpc_client_channel_factory* client_channel_factory() const {
+    return client_channel_factory_;
+  }
+
+  /// Shuts down the policy.  Any pending picks that have not been
+  /// handed off to a new policy via HandOffPendingPicksLocked() will be
+  /// failed.
+  virtual void ShutdownLocked() GRPC_ABSTRACT;
+
+  /// Tries to request a re-resolution.
+  void TryReresolutionLocked(grpc_core::TraceFlag* grpc_lb_trace,
                              grpc_error* error);
 
-  /** \see grpc_lb_policy_cancel_picks */
-  void (*cancel_picks_locked)(grpc_lb_policy* policy,
-                              uint32_t initial_metadata_flags_mask,
-                              uint32_t initial_metadata_flags_eq,
-                              grpc_error* error);
+ private:
+  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) {
+    LoadBalancingPolicy* policy = static_cast<LoadBalancingPolicy*>(arg);
+    policy->ShutdownLocked();
+    policy->Unref();
+  }
 
-  /** \see grpc_lb_policy_ping_one */
-  void (*ping_one_locked)(grpc_lb_policy* policy, grpc_closure* on_initiate,
-                          grpc_closure* on_ack);
-
-  /** Try to enter a READY connectivity state */
-  void (*exit_idle_locked)(grpc_lb_policy* policy);
-
-  /** check the current connectivity of the lb_policy */
-  grpc_connectivity_state (*check_connectivity_locked)(
-      grpc_lb_policy* policy, grpc_error** connectivity_error);
-
-  /** call notify when the connectivity state of a channel changes from *state.
-      Updates *state with the new state of the policy. Calling with a NULL \a
-      state cancels the subscription.  */
-  void (*notify_on_state_change_locked)(grpc_lb_policy* policy,
-                                        grpc_connectivity_state* state,
-                                        grpc_closure* closure);
-
-  void (*update_locked)(grpc_lb_policy* policy,
-                        const grpc_lb_policy_args* args);
-
-  /** \see grpc_lb_policy_set_reresolve_closure */
-  void (*set_reresolve_closure_locked)(grpc_lb_policy* policy,
-                                       grpc_closure* request_reresolution);
+  /// Combiner under which LB policy actions take place.
+  grpc_combiner* combiner_;
+  /// Client channel factory, used to create channels and subchannels.
+  grpc_client_channel_factory* client_channel_factory_;
+  /// Owned pointer to interested parties in load balancing decisions.
+  grpc_pollset_set* interested_parties_;
+  /// Callback to force a re-resolution.
+  grpc_closure* request_reresolution_;
 };
 
-#ifndef NDEBUG
-#define GRPC_LB_POLICY_REF(p, r) \
-  grpc_lb_policy_ref((p), __FILE__, __LINE__, (r))
-#define GRPC_LB_POLICY_UNREF(p, r) \
-  grpc_lb_policy_unref((p), __FILE__, __LINE__, (r))
-void grpc_lb_policy_ref(grpc_lb_policy* policy, const char* file, int line,
-                        const char* reason);
-void grpc_lb_policy_unref(grpc_lb_policy* policy, const char* file, int line,
-                          const char* reason);
-#else  // !NDEBUG
-#define GRPC_LB_POLICY_REF(p, r) grpc_lb_policy_ref((p))
-#define GRPC_LB_POLICY_UNREF(p, r) grpc_lb_policy_unref((p))
-void grpc_lb_policy_ref(grpc_lb_policy* policy);
-void grpc_lb_policy_unref(grpc_lb_policy* policy);
-#endif
-
-/** called by concrete implementations to initialize the base struct */
-void grpc_lb_policy_init(grpc_lb_policy* policy,
-                         const grpc_lb_policy_vtable* vtable,
-                         grpc_combiner* combiner);
-
-/// Shuts down \a policy.
-/// If \a new_policy is non-null, any pending picks will be restarted
-/// on that policy; otherwise, they will be failed.
-void grpc_lb_policy_shutdown_locked(grpc_lb_policy* policy,
-                                    grpc_lb_policy* new_policy);
-
-/** Finds an appropriate subchannel for a call, based on data in \a pick.
-    \a pick must remain alive until the pick is complete.
-
-    If the pick succeeds and a result is known immediately, a non-zero
-    value will be returned.  Otherwise, \a pick->on_complete will be invoked
-    once the pick is complete with its error argument set to indicate
-    success or failure.
-
-    Any IO should be done under the \a interested_parties \a grpc_pollset_set
-    in the \a grpc_lb_policy struct. */
-int grpc_lb_policy_pick_locked(grpc_lb_policy* policy,
-                               grpc_lb_policy_pick_state* pick);
-
-/** Perform a connected subchannel ping (see \a
-   grpc_core::ConnectedSubchannel::Ping)
-    against one of the connected subchannels managed by \a policy. */
-void grpc_lb_policy_ping_one_locked(grpc_lb_policy* policy,
-                                    grpc_closure* on_initiate,
-                                    grpc_closure* on_ack);
-
-/** Cancel picks for \a pick.
-    The \a on_complete callback of the pending picks will be invoked with \a
-    *target set to NULL. */
-void grpc_lb_policy_cancel_pick_locked(grpc_lb_policy* policy,
-                                       grpc_lb_policy_pick_state* pick,
-                                       grpc_error* error);
-
-/** Cancel all pending picks for which their \a initial_metadata_flags (as given
-    in the call to \a grpc_lb_policy_pick) matches \a initial_metadata_flags_eq
-    when AND'd with \a initial_metadata_flags_mask */
-void grpc_lb_policy_cancel_picks_locked(grpc_lb_policy* policy,
-                                        uint32_t initial_metadata_flags_mask,
-                                        uint32_t initial_metadata_flags_eq,
-                                        grpc_error* error);
-
-/** Try to enter a READY connectivity state */
-void grpc_lb_policy_exit_idle_locked(grpc_lb_policy* policy);
-
-/* Call notify when the connectivity state of a channel changes from \a *state.
- * Updates \a *state with the new state of the policy */
-void grpc_lb_policy_notify_on_state_change_locked(
-    grpc_lb_policy* policy, grpc_connectivity_state* state,
-    grpc_closure* closure);
-
-grpc_connectivity_state grpc_lb_policy_check_connectivity_locked(
-    grpc_lb_policy* policy, grpc_error** connectivity_error);
-
-/** Update \a policy with \a lb_policy_args. */
-void grpc_lb_policy_update_locked(grpc_lb_policy* policy,
-                                  const grpc_lb_policy_args* lb_policy_args);
-
-/** Set the re-resolution closure to \a request_reresolution. */
-void grpc_lb_policy_set_reresolve_closure_locked(
-    grpc_lb_policy* policy, grpc_closure* request_reresolution);
-
-/** Try to request a re-resolution. It's NOT a public API; it's only for use by
-    the LB policy implementations. */
-void grpc_lb_policy_try_reresolve(grpc_lb_policy* policy,
-                                  grpc_core::TraceFlag* grpc_lb_trace,
-                                  grpc_error* error);
+}  // namespace grpc_core
 
 #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H */
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
index 1a3a1f0..18ef1f6 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
 
 #include <grpc/support/atm.h>
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
index 04de7a0..838e2ef 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_client_load_reporting_filter;
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
index 04bf798..cb39e42 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -16,68 +16,50 @@
  *
  */
 
-/** Implementation of the gRPC LB policy.
- *
- * This policy takes as input a set of resolved addresses {a1..an} for which the
- * LB set was set (it's the resolver's responsibility to ensure this). That is
- * to say, {a1..an} represent a collection of LB servers.
- *
- * An internal channel (\a glb_lb_policy.lb_channel) is created over {a1..an}.
- * This channel behaves just like a regular channel. In particular, the
- * constructed URI over the addresses a1..an will use the default pick first
- * policy to select from this list of LB server backends.
- *
- * The first time the policy gets a request for a pick, a ping, or to exit the
- * idle state, \a query_for_backends_locked() is called. This function sets up
- * and initiates the internal communication with the LB server. In particular,
- * it's responsible for instantiating the internal *streaming* call to the LB
- * server (whichever address from {a1..an} pick-first chose). This call is
- * serviced by two callbacks, \a lb_on_server_status_received and \a
- * lb_on_response_received. The former will be called when the call to the LB
- * server completes. This can happen if the LB server closes the connection or
- * if this policy itself cancels the call (for example because it's shutting
- * down). If the internal call times out, the usual behavior of pick-first
- * applies, continuing to pick from the list {a1..an}.
- *
- * Upon sucesss, the incoming \a LoadBalancingResponse is processed by \a
- * res_recv. An invalid one results in the termination of the streaming call. A
- * new streaming call should be created if possible, failing the original call
- * otherwise. For a valid \a LoadBalancingResponse, the server list of actual
- * backends is extracted. A Round Robin policy will be created from this list.
- * There are two possible scenarios:
- *
- * 1. This is the first server list received. There was no previous instance of
- *    the Round Robin policy. \a rr_handover_locked() will instantiate the RR
- *    policy and perform all the pending operations over it.
- * 2. There's already a RR policy instance active. We need to introduce the new
- *    one build from the new serverlist, but taking care not to disrupt the
- *    operations in progress over the old RR instance. This is done by
- *    decreasing the reference count on the old policy. The moment no more
- *    references are held on the old RR policy, it'll be destroyed and \a
- *    on_rr_connectivity_changed notified with a \a GRPC_CHANNEL_SHUTDOWN
- *    state. At this point we can transition to a new RR instance safely, which
- *    is done once again via \a rr_handover_locked().
- *
- *
- * Once a RR policy instance is in place (and getting updated as described),
- * calls to for a pick, a ping or a cancellation will be serviced right away by
- * forwarding them to the RR instance. Any time there's no RR policy available
- * (ie, right after the creation of the gRPCLB policy, if an empty serverlist is
- * received, etc), pick/ping requests are added to a list of pending picks/pings
- * to be flushed and serviced as part of \a rr_handover_locked() the moment the
- * RR policy instance becomes available.
- *
- * \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the
- * high level design and details. */
+/// Implementation of the gRPC LB policy.
+///
+/// This policy takes as input a list of resolved addresses, which must
+/// include at least one balancer address.
+///
+/// An internal channel (\a lb_channel_) is created for the addresses
+/// from that are balancers.  This channel behaves just like a regular
+/// channel that uses pick_first to select from the list of balancer
+/// addresses.
+///
+/// The first time the policy gets a request for a pick, a ping, or to exit
+/// the idle state, \a StartPickingLocked() is called. This method is
+/// responsible for instantiating the internal *streaming* call to the LB
+/// server (whichever address pick_first chose).  The call will be complete
+/// when either the balancer sends status or when we cancel the call (e.g.,
+/// because we are shutting down).  In needed, we retry the call.  If we
+/// received at least one valid message from the server, a new call attempt
+/// will be made immediately; otherwise, we apply back-off delays between
+/// attempts.
+///
+/// We maintain an internal round_robin policy instance for distributing
+/// requests across backends.  Whenever we receive a new serverlist from
+/// the balancer, we update the round_robin policy with the new list of
+/// addresses.  If we cannot communicate with the balancer on startup,
+/// however, we may enter fallback mode, in which case we will populate
+/// the RR policy's addresses from the backend addresses returned by the
+/// resolver.
+///
+/// Once an RR policy instance is in place (and getting updated as described),
+/// calls for a pick, a ping, or a cancellation will be serviced right
+/// away by forwarding them to the RR instance.  Any time there's no RR
+/// policy available (i.e., right after the creation of the gRPCLB policy),
+/// pick and ping requests are added to a list of pending picks and pings
+/// to be flushed and serviced when the RR policy instance becomes available.
+///
+/// \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the
+/// high level design and details.
 
-/* TODO(dgq):
- * - Implement LB service forwarding (point 2c. in the doc's diagram).
- */
+// With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+// using that endpoint. Because of various transitive includes in uv.h,
+// including windows.h on Windows, uv.h must be included before other system
+// headers. Therefore, sockaddr.h must always be included first.
+#include <grpc/support/port_platform.h>
 
-/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
-   using that endpoint. Because of various transitive includes in uv.h,
-   including windows.h on Windows, uv.h must be included before other system
-   headers. Therefore, sockaddr.h must always be included first */
 #include "src/core/lib/iomgr/sockaddr.h"
 
 #include <inttypes.h>
@@ -93,7 +75,6 @@
 #include "src/core/ext/filters/client_channel/client_channel.h"
 #include "src/core/ext/filters/client_channel/client_channel_factory.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
-#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
@@ -108,6 +89,8 @@
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr.h"
@@ -127,330 +110,294 @@
 #define GRPC_GRPCLB_RECONNECT_JITTER 0.2
 #define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000
 
-grpc_core::TraceFlag grpc_lb_glb_trace(false, "glb");
+namespace grpc_core {
 
-struct glb_lb_policy;
+TraceFlag grpc_lb_glb_trace(false, "glb");
 
 namespace {
 
-/// Linked list of pending pick requests. It stores all information needed to
-/// eventually call (Round Robin's) pick() on them. They mainly stay pending
-/// waiting for the RR policy to be created.
-///
-/// Note that when a pick is sent to the RR policy, we inject our own
-/// on_complete callback, so that we can intercept the result before
-/// invoking the original on_complete callback.  This allows us to set the
-/// LB token metadata and add client_stats to the call context.
-/// See \a pending_pick_complete() for details.
-struct pending_pick {
-  // Our on_complete closure and the original one.
-  grpc_closure on_complete;
-  grpc_closure* original_on_complete;
-  // The original pick.
-  grpc_lb_policy_pick_state* pick;
-  // Stats for client-side load reporting. Note that this holds a
-  // reference, which must be either passed on via context or unreffed.
-  grpc_grpclb_client_stats* client_stats;
-  // The LB token associated with the pick.  This is set via user_data in
-  // the pick.
-  grpc_mdelem lb_token;
-  // The grpclb instance that created the wrapping. This instance is not owned,
-  // reference counts are untouched. It's used only for logging purposes.
-  glb_lb_policy* glb_policy;
-  // Next pending pick.
-  struct pending_pick* next;
+class GrpcLb : public LoadBalancingPolicy {
+ public:
+  GrpcLb(const grpc_lb_addresses* addresses, const Args& args);
+
+  void UpdateLocked(const grpc_channel_args& args) override;
+  bool PickLocked(PickState* pick) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) override;
+  void ExitIdleLocked() override;
+
+ private:
+  /// Linked list of pending pick requests. It stores all information needed to
+  /// eventually call (Round Robin's) pick() on them. They mainly stay pending
+  /// waiting for the RR policy to be created.
+  ///
+  /// Note that when a pick is sent to the RR policy, we inject our own
+  /// on_complete callback, so that we can intercept the result before
+  /// invoking the original on_complete callback.  This allows us to set the
+  /// LB token metadata and add client_stats to the call context.
+  /// See \a pending_pick_complete() for details.
+  struct PendingPick {
+    // The grpclb instance that created the wrapping. This instance is not
+    // owned; reference counts are untouched. It's used only for logging
+    // purposes.
+    GrpcLb* grpclb_policy;
+    // The original pick.
+    PickState* pick;
+    // Our on_complete closure and the original one.
+    grpc_closure on_complete;
+    grpc_closure* original_on_complete;
+    // The LB token associated with the pick.  This is set via user_data in
+    // the pick.
+    grpc_mdelem lb_token;
+    // Stats for client-side load reporting. Note that this holds a
+    // reference, which must be either passed on via context or unreffed.
+    grpc_grpclb_client_stats* client_stats = nullptr;
+    // Next pending pick.
+    PendingPick* next = nullptr;
+  };
+
+  /// A linked list of pending pings waiting for the RR policy to be created.
+  struct PendingPing {
+    grpc_closure* on_initiate;
+    grpc_closure* on_ack;
+    PendingPing* next = nullptr;
+  };
+
+  /// Contains a call to the LB server and all the data related to the call.
+  class BalancerCallState
+      : public InternallyRefCountedWithTracing<BalancerCallState> {
+   public:
+    explicit BalancerCallState(
+        RefCountedPtr<LoadBalancingPolicy> parent_grpclb_policy);
+
+    // It's the caller's responsibility to ensure that Orphan() is called from
+    // inside the combiner.
+    void Orphan() override;
+
+    void StartQuery();
+
+    grpc_grpclb_client_stats* client_stats() const { return client_stats_; }
+    bool seen_initial_response() const { return seen_initial_response_; }
+
+   private:
+    ~BalancerCallState();
+
+    GrpcLb* grpclb_policy() const {
+      return static_cast<GrpcLb*>(grpclb_policy_.get());
+    }
+
+    void ScheduleNextClientLoadReportLocked();
+    void SendClientLoadReportLocked();
+
+    static bool LoadReportCountersAreZero(grpc_grpclb_request* request);
+
+    static void MaybeSendClientLoadReportLocked(void* arg, grpc_error* error);
+    static void ClientLoadReportDoneLocked(void* arg, grpc_error* error);
+    static void OnInitialRequestSentLocked(void* arg, grpc_error* error);
+    static void OnBalancerMessageReceivedLocked(void* arg, grpc_error* error);
+    static void OnBalancerStatusReceivedLocked(void* arg, grpc_error* error);
+
+    // The owning LB policy.
+    RefCountedPtr<LoadBalancingPolicy> grpclb_policy_;
+
+    // The streaming call to the LB server. Always non-NULL.
+    grpc_call* lb_call_ = nullptr;
+
+    // recv_initial_metadata
+    grpc_metadata_array lb_initial_metadata_recv_;
+
+    // send_message
+    grpc_byte_buffer* send_message_payload_ = nullptr;
+    grpc_closure lb_on_initial_request_sent_;
+
+    // recv_message
+    grpc_byte_buffer* recv_message_payload_ = nullptr;
+    grpc_closure lb_on_balancer_message_received_;
+    bool seen_initial_response_ = false;
+
+    // recv_trailing_metadata
+    grpc_closure lb_on_balancer_status_received_;
+    grpc_metadata_array lb_trailing_metadata_recv_;
+    grpc_status_code lb_call_status_;
+    grpc_slice lb_call_status_details_;
+
+    // The stats for client-side load reporting associated with this LB call.
+    // Created after the first serverlist is received.
+    grpc_grpclb_client_stats* client_stats_ = nullptr;
+    grpc_millis client_stats_report_interval_ = 0;
+    grpc_timer client_load_report_timer_;
+    bool client_load_report_timer_callback_pending_ = false;
+    bool last_client_load_report_counters_were_zero_ = false;
+    bool client_load_report_is_due_ = false;
+    // The closure used for either the load report timer or the callback for
+    // completion of sending the load report.
+    grpc_closure client_load_report_closure_;
+  };
+
+  ~GrpcLb();
+
+  void ShutdownLocked() override;
+
+  // Helper function used in ctor and UpdateLocked().
+  void ProcessChannelArgsLocked(const grpc_channel_args& args);
+
+  // Methods for dealing with the balancer channel and call.
+  void StartPickingLocked();
+  void StartBalancerCallLocked();
+  static void OnFallbackTimerLocked(void* arg, grpc_error* error);
+  void StartBalancerCallRetryTimerLocked();
+  static void OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error);
+  static void OnBalancerChannelConnectivityChangedLocked(void* arg,
+                                                         grpc_error* error);
+
+  // Pending pick methods.
+  static void PendingPickSetMetadataAndContext(PendingPick* pp);
+  PendingPick* PendingPickCreate(PickState* pick);
+  void AddPendingPick(PendingPick* pp);
+  static void OnPendingPickComplete(void* arg, grpc_error* error);
+
+  // Pending ping methods.
+  void AddPendingPing(grpc_closure* on_initiate, grpc_closure* on_ack);
+
+  // Methods for dealing with the RR policy.
+  void CreateOrUpdateRoundRobinPolicyLocked();
+  grpc_channel_args* CreateRoundRobinPolicyArgsLocked();
+  void CreateRoundRobinPolicyLocked(const Args& args);
+  bool PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp);
+  void UpdateConnectivityStateFromRoundRobinPolicyLocked(
+      grpc_error* rr_state_error);
+  static void OnRoundRobinConnectivityChangedLocked(void* arg,
+                                                    grpc_error* error);
+  static void OnRoundRobinRequestReresolutionLocked(void* arg,
+                                                    grpc_error* error);
+
+  // Who the client is trying to communicate with.
+  const char* server_name_ = nullptr;
+
+  // Current channel args from the resolver.
+  grpc_channel_args* args_ = nullptr;
+
+  // Internal state.
+  bool started_picking_ = false;
+  bool shutting_down_ = false;
+  grpc_connectivity_state_tracker state_tracker_;
+
+  // The channel for communicating with the LB server.
+  grpc_channel* lb_channel_ = nullptr;
+  grpc_connectivity_state lb_channel_connectivity_;
+  grpc_closure lb_channel_on_connectivity_changed_;
+  // Are we already watching the LB channel's connectivity?
+  bool watching_lb_channel_ = false;
+  // Response generator to inject address updates into lb_channel_.
+  RefCountedPtr<FakeResolverResponseGenerator> response_generator_;
+
+  // The data associated with the current LB call. It holds a ref to this LB
+  // policy. It's initialized every time we query for backends. It's reset to
+  // NULL whenever the current LB call is no longer needed (e.g., the LB policy
+  // is shutting down, or the LB call has ended). A non-NULL lb_calld_ always
+  // contains a non-NULL lb_call_.
+  OrphanablePtr<BalancerCallState> lb_calld_;
+  // Timeout in milliseconds for the LB call. 0 means no deadline.
+  int lb_call_timeout_ms_ = 0;
+  // Balancer call retry state.
+  BackOff lb_call_backoff_;
+  bool retry_timer_callback_pending_ = false;
+  grpc_timer lb_call_retry_timer_;
+  grpc_closure lb_on_call_retry_;
+
+  // The deserialized response from the balancer. May be nullptr until one
+  // such response has arrived.
+  grpc_grpclb_serverlist* serverlist_ = nullptr;
+  // Index into serverlist for next pick.
+  // If the server at this index is a drop, we return a drop.
+  // Otherwise, we delegate to the RR policy.
+  size_t serverlist_index_ = 0;
+
+  // Timeout in milliseconds for before using fallback backend addresses.
+  // 0 means not using fallback.
+  int lb_fallback_timeout_ms_ = 0;
+  // The backend addresses from the resolver.
+  grpc_lb_addresses* fallback_backend_addresses_ = nullptr;
+  // Fallback timer.
+  bool fallback_timer_callback_pending_ = false;
+  grpc_timer lb_fallback_timer_;
+  grpc_closure lb_on_fallback_;
+
+  // Pending picks and pings that are waiting on the RR policy's connectivity.
+  PendingPick* pending_picks_ = nullptr;
+  PendingPing* pending_pings_ = nullptr;
+
+  // The RR policy to use for the backends.
+  OrphanablePtr<LoadBalancingPolicy> rr_policy_;
+  grpc_connectivity_state rr_connectivity_state_;
+  grpc_closure on_rr_connectivity_changed_;
+  grpc_closure on_rr_request_reresolution_;
 };
 
-/// A linked list of pending pings waiting for the RR policy to be created.
-struct pending_ping {
-  grpc_closure* on_initiate;
-  grpc_closure* on_ack;
-  struct pending_ping* next;
-};
+//
+// serverlist parsing code
+//
 
-}  // namespace
-
-typedef struct glb_lb_call_data {
-  struct glb_lb_policy* glb_policy;
-  // TODO(juanlishen): c++ize this struct.
-  gpr_refcount refs;
-
-  /** The streaming call to the LB server. Always non-NULL. */
-  grpc_call* lb_call;
-
-  /** The initial metadata received from the LB server. */
-  grpc_metadata_array lb_initial_metadata_recv;
-
-  /** The message sent to the LB server. It's used to query for backends (the
-   * value may vary if the LB server indicates a redirect) or send client load
-   * report. */
-  grpc_byte_buffer* send_message_payload;
-  /** The callback after the initial request is sent. */
-  grpc_closure lb_on_sent_initial_request;
-
-  /** The response received from the LB server, if any. */
-  grpc_byte_buffer* recv_message_payload;
-  /** The callback to process the response received from the LB server. */
-  grpc_closure lb_on_response_received;
-  bool seen_initial_response;
-
-  /** The callback to process the status received from the LB server, which
-   * signals the end of the LB call. */
-  grpc_closure lb_on_server_status_received;
-  /** The trailing metadata from the LB server. */
-  grpc_metadata_array lb_trailing_metadata_recv;
-  /** The call status code and details. */
-  grpc_status_code lb_call_status;
-  grpc_slice lb_call_status_details;
-
-  /** The stats for client-side load reporting associated with this LB call.
-   * Created after the first serverlist is received. */
-  grpc_grpclb_client_stats* client_stats;
-  /** The interval and timer for next client load report. */
-  grpc_millis client_stats_report_interval;
-  grpc_timer client_load_report_timer;
-  bool client_load_report_timer_callback_pending;
-  bool last_client_load_report_counters_were_zero;
-  bool client_load_report_is_due;
-  /** The closure used for either the load report timer or the callback for
-   * completion of sending the load report. */
-  grpc_closure client_load_report_closure;
-} glb_lb_call_data;
-
-typedef struct glb_lb_policy {
-  /** Base policy: must be first. */
-  grpc_lb_policy base;
-
-  /** Who the client is trying to communicate with. */
-  const char* server_name;
-
-  /** Channel related data that will be propagated to the internal RR policy. */
-  grpc_client_channel_factory* cc_factory;
-  grpc_channel_args* args;
-
-  /** Timeout in milliseconds for before using fallback backend addresses.
-   * 0 means not using fallback. */
-  int lb_fallback_timeout_ms;
-
-  /** The channel for communicating with the LB server. */
-  grpc_channel* lb_channel;
-
-  /** The data associated with the current LB call. It holds a ref to this LB
-   * policy. It's initialized every time we query for backends. It's reset to
-   * NULL whenever the current LB call is no longer needed (e.g., the LB policy
-   * is shutting down, or the LB call has ended). A non-NULL lb_calld always
-   * contains a non-NULL lb_call. */
-  glb_lb_call_data* lb_calld;
-
-  /** response generator to inject address updates into \a lb_channel */
-  grpc_core::RefCountedPtr<grpc_core::FakeResolverResponseGenerator>
-      response_generator;
-
-  /** the RR policy to use of the backend servers returned by the LB server */
-  grpc_lb_policy* rr_policy;
-
-  grpc_closure on_rr_connectivity_changed;
-  grpc_connectivity_state rr_connectivity_state;
-
-  bool started_picking;
-
-  /** our connectivity state tracker */
-  grpc_connectivity_state_tracker state_tracker;
-
-  /** connectivity state of the LB channel */
-  grpc_connectivity_state lb_channel_connectivity;
-
-  /** stores the deserialized response from the LB. May be nullptr until one
-   * such response has arrived. */
-  grpc_grpclb_serverlist* serverlist;
-
-  /** Index into serverlist for next pick.
-   * If the server at this index is a drop, we return a drop.
-   * Otherwise, we delegate to the RR policy. */
-  size_t serverlist_index;
-
-  /** stores the backend addresses from the resolver */
-  grpc_lb_addresses* fallback_backend_addresses;
-
-  /** list of picks that are waiting on RR's policy connectivity */
-  pending_pick* pending_picks;
-
-  /** list of pings that are waiting on RR's policy connectivity */
-  pending_ping* pending_pings;
-
-  bool shutting_down;
-
-  /** are we already watching the LB channel's connectivity? */
-  bool watching_lb_channel;
-
-  /** is the callback associated with \a lb_call_retry_timer pending? */
-  bool retry_timer_callback_pending;
-
-  /** is the callback associated with \a lb_fallback_timer pending? */
-  bool fallback_timer_callback_pending;
-
-  /** called upon changes to the LB channel's connectivity. */
-  grpc_closure lb_channel_on_connectivity_changed;
-
-  /************************************************************/
-  /*  client data associated with the LB server communication */
-  /************************************************************/
-
-  /** LB call retry backoff state */
-  grpc_core::ManualConstructor<grpc_core::BackOff> lb_call_backoff;
-
-  /** timeout in milliseconds for the LB call. 0 means no deadline. */
-  int lb_call_timeout_ms;
-
-  /** LB call retry timer */
-  grpc_timer lb_call_retry_timer;
-  /** LB call retry timer callback */
-  grpc_closure lb_on_call_retry;
-
-  /** LB fallback timer */
-  grpc_timer lb_fallback_timer;
-  /** LB fallback timer callback */
-  grpc_closure lb_on_fallback;
-} glb_lb_policy;
-
-static void glb_lb_call_data_ref(glb_lb_call_data* lb_calld,
-                                 const char* reason) {
-  gpr_ref_non_zero(&lb_calld->refs);
-  if (grpc_lb_glb_trace.enabled()) {
-    const gpr_atm count = gpr_atm_acq_load(&lb_calld->refs.count);
-    gpr_log(GPR_DEBUG, "[%s %p] lb_calld %p REF %lu->%lu (%s)",
-            grpc_lb_glb_trace.name(), lb_calld->glb_policy, lb_calld,
-            static_cast<unsigned long>(count - 1),
-            static_cast<unsigned long>(count), reason);
+// vtable for LB tokens in grpc_lb_addresses
+void* lb_token_copy(void* token) {
+  return token == nullptr
+             ? nullptr
+             : (void*)GRPC_MDELEM_REF(grpc_mdelem{(uintptr_t)token}).payload;
+}
+void lb_token_destroy(void* token) {
+  if (token != nullptr) {
+    GRPC_MDELEM_UNREF(grpc_mdelem{(uintptr_t)token});
   }
 }
-
-static void glb_lb_call_data_unref(glb_lb_call_data* lb_calld,
-                                   const char* reason) {
-  const bool done = gpr_unref(&lb_calld->refs);
-  if (grpc_lb_glb_trace.enabled()) {
-    const gpr_atm count = gpr_atm_acq_load(&lb_calld->refs.count);
-    gpr_log(GPR_DEBUG, "[%s %p] lb_calld %p UNREF %lu->%lu (%s)",
-            grpc_lb_glb_trace.name(), lb_calld->glb_policy, lb_calld,
-            static_cast<unsigned long>(count + 1),
-            static_cast<unsigned long>(count), reason);
-  }
-  if (done) {
-    GPR_ASSERT(lb_calld->lb_call != nullptr);
-    grpc_call_unref(lb_calld->lb_call);
-    grpc_metadata_array_destroy(&lb_calld->lb_initial_metadata_recv);
-    grpc_metadata_array_destroy(&lb_calld->lb_trailing_metadata_recv);
-    grpc_byte_buffer_destroy(lb_calld->send_message_payload);
-    grpc_byte_buffer_destroy(lb_calld->recv_message_payload);
-    grpc_slice_unref_internal(lb_calld->lb_call_status_details);
-    if (lb_calld->client_stats != nullptr) {
-      grpc_grpclb_client_stats_unref(lb_calld->client_stats);
-    }
-    GRPC_LB_POLICY_UNREF(&lb_calld->glb_policy->base, "lb_calld");
-    gpr_free(lb_calld);
-  }
+int lb_token_cmp(void* token1, void* token2) {
+  if (token1 > token2) return 1;
+  if (token1 < token2) return -1;
+  return 0;
 }
+const grpc_lb_user_data_vtable lb_token_vtable = {
+    lb_token_copy, lb_token_destroy, lb_token_cmp};
 
-static void lb_call_data_shutdown(glb_lb_policy* glb_policy) {
-  GPR_ASSERT(glb_policy->lb_calld != nullptr);
-  GPR_ASSERT(glb_policy->lb_calld->lb_call != nullptr);
-  // lb_on_server_status_received will complete the cancellation and clean up.
-  grpc_call_cancel(glb_policy->lb_calld->lb_call, nullptr);
-  if (glb_policy->lb_calld->client_load_report_timer_callback_pending) {
-    grpc_timer_cancel(&glb_policy->lb_calld->client_load_report_timer);
-  }
-  glb_policy->lb_calld = nullptr;
-}
-
-/* add lb_token of selected subchannel (address) to the call's initial
- * metadata */
-static grpc_error* initial_metadata_add_lb_token(
-    grpc_metadata_batch* initial_metadata,
-    grpc_linked_mdelem* lb_token_mdelem_storage, grpc_mdelem lb_token) {
-  GPR_ASSERT(lb_token_mdelem_storage != nullptr);
-  GPR_ASSERT(!GRPC_MDISNULL(lb_token));
-  return grpc_metadata_batch_add_tail(initial_metadata, lb_token_mdelem_storage,
-                                      lb_token);
-}
-
-static void destroy_client_stats(void* arg) {
-  grpc_grpclb_client_stats_unref(static_cast<grpc_grpclb_client_stats*>(arg));
-}
-
-static void pending_pick_set_metadata_and_context(pending_pick* pp) {
-  /* if connected_subchannel is nullptr, no pick has been made by the RR
-   * policy (e.g., all addresses failed to connect). There won't be any
-   * user_data/token available */
-  if (pp->pick->connected_subchannel != nullptr) {
-    if (!GRPC_MDISNULL(pp->lb_token)) {
-      initial_metadata_add_lb_token(pp->pick->initial_metadata,
-                                    &pp->pick->lb_token_mdelem_storage,
-                                    GRPC_MDELEM_REF(pp->lb_token));
-    } else {
-      gpr_log(GPR_ERROR,
-              "[grpclb %p] No LB token for connected subchannel pick %p",
-              pp->glb_policy, pp->pick);
-      abort();
-    }
-    // Pass on client stats via context. Passes ownership of the reference.
-    if (pp->client_stats != nullptr) {
-      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].value =
-          pp->client_stats;
-      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].destroy =
-          destroy_client_stats;
-    }
-  } else {
-    if (pp->client_stats != nullptr) {
-      grpc_grpclb_client_stats_unref(pp->client_stats);
+// Returns the backend addresses extracted from the given addresses.
+grpc_lb_addresses* ExtractBackendAddresses(const grpc_lb_addresses* addresses) {
+  // First pass: count the number of backend addresses.
+  size_t num_backends = 0;
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (!addresses->addresses[i].is_balancer) {
+      ++num_backends;
     }
   }
+  // Second pass: actually populate the addresses and (empty) LB tokens.
+  grpc_lb_addresses* backend_addresses =
+      grpc_lb_addresses_create(num_backends, &lb_token_vtable);
+  size_t num_copied = 0;
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (addresses->addresses[i].is_balancer) continue;
+    const grpc_resolved_address* addr = &addresses->addresses[i].address;
+    grpc_lb_addresses_set_address(backend_addresses, num_copied, &addr->addr,
+                                  addr->len, false /* is_balancer */,
+                                  nullptr /* balancer_name */,
+                                  (void*)GRPC_MDELEM_LB_TOKEN_EMPTY.payload);
+    ++num_copied;
+  }
+  return backend_addresses;
 }
 
-/* The \a on_complete closure passed as part of the pick requires keeping a
- * reference to its associated round robin instance. We wrap this closure in
- * order to unref the round robin instance upon its invocation */
-static void pending_pick_complete(void* arg, grpc_error* error) {
-  pending_pick* pp = static_cast<pending_pick*>(arg);
-  pending_pick_set_metadata_and_context(pp);
-  GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error));
-  gpr_free(pp);
-}
-
-static pending_pick* pending_pick_create(glb_lb_policy* glb_policy,
-                                         grpc_lb_policy_pick_state* pick) {
-  pending_pick* pp = static_cast<pending_pick*>(gpr_zalloc(sizeof(*pp)));
-  pp->pick = pick;
-  pp->glb_policy = glb_policy;
-  GRPC_CLOSURE_INIT(&pp->on_complete, pending_pick_complete, pp,
-                    grpc_schedule_on_exec_ctx);
-  pp->original_on_complete = pick->on_complete;
-  pp->pick->on_complete = &pp->on_complete;
-  return pp;
-}
-
-static void pending_pick_add(pending_pick** root, pending_pick* new_pp) {
-  new_pp->next = *root;
-  *root = new_pp;
-}
-
-static void pending_ping_add(pending_ping** root, grpc_closure* on_initiate,
-                             grpc_closure* on_ack) {
-  pending_ping* pping = static_cast<pending_ping*>(gpr_zalloc(sizeof(*pping)));
-  pping->on_initiate = on_initiate;
-  pping->on_ack = on_ack;
-  pping->next = *root;
-  *root = pping;
-}
-
-static bool is_server_valid(const grpc_grpclb_server* server, size_t idx,
-                            bool log) {
+bool IsServerValid(const grpc_grpclb_server* server, size_t idx, bool log) {
   if (server->drop) return false;
   const grpc_grpclb_ip_address* ip = &server->ip_address;
   if (server->port >> 16 != 0) {
     if (log) {
       gpr_log(GPR_ERROR,
               "Invalid port '%d' at index %lu of serverlist. Ignoring.",
-              server->port, static_cast<unsigned long>(idx));
+              server->port, (unsigned long)idx);
     }
     return false;
   }
@@ -459,65 +406,43 @@
       gpr_log(GPR_ERROR,
               "Expected IP to be 4 or 16 bytes, got %d at index %lu of "
               "serverlist. Ignoring",
-              ip->size, static_cast<unsigned long>(idx));
+              ip->size, (unsigned long)idx);
     }
     return false;
   }
   return true;
 }
 
-/* vtable for LB tokens in grpc_lb_addresses. */
-static void* lb_token_copy(void* token) {
-  return token == nullptr
-             ? nullptr
-             : (void*)GRPC_MDELEM_REF(grpc_mdelem{(uintptr_t)token}).payload;
-}
-static void lb_token_destroy(void* token) {
-  if (token != nullptr) {
-    GRPC_MDELEM_UNREF(grpc_mdelem{(uintptr_t)token});
-  }
-}
-static int lb_token_cmp(void* token1, void* token2) {
-  if (token1 > token2) return 1;
-  if (token1 < token2) return -1;
-  return 0;
-}
-static const grpc_lb_user_data_vtable lb_token_vtable = {
-    lb_token_copy, lb_token_destroy, lb_token_cmp};
-
-static void parse_server(const grpc_grpclb_server* server,
-                         grpc_resolved_address* addr) {
+void ParseServer(const grpc_grpclb_server* server,
+                 grpc_resolved_address* addr) {
   memset(addr, 0, sizeof(*addr));
   if (server->drop) return;
-  const uint16_t netorder_port = htons(static_cast<uint16_t>(server->port));
+  const uint16_t netorder_port = htons((uint16_t)server->port);
   /* the addresses are given in binary format (a in(6)_addr struct) in
    * server->ip_address.bytes. */
   const grpc_grpclb_ip_address* ip = &server->ip_address;
   if (ip->size == 4) {
     addr->len = sizeof(struct sockaddr_in);
-    struct sockaddr_in* addr4 =
-        reinterpret_cast<struct sockaddr_in*>(&addr->addr);
+    struct sockaddr_in* addr4 = (struct sockaddr_in*)&addr->addr;
     addr4->sin_family = AF_INET;
     memcpy(&addr4->sin_addr, ip->bytes, ip->size);
     addr4->sin_port = netorder_port;
   } else if (ip->size == 16) {
     addr->len = sizeof(struct sockaddr_in6);
-    struct sockaddr_in6* addr6 =
-        reinterpret_cast<struct sockaddr_in6*>(&addr->addr);
+    struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr->addr;
     addr6->sin6_family = AF_INET6;
     memcpy(&addr6->sin6_addr, ip->bytes, ip->size);
     addr6->sin6_port = netorder_port;
   }
 }
 
-/* Returns addresses extracted from \a serverlist. */
-static grpc_lb_addresses* process_serverlist_locked(
-    const grpc_grpclb_serverlist* serverlist) {
+// Returns addresses extracted from \a serverlist.
+grpc_lb_addresses* ProcessServerlist(const grpc_grpclb_serverlist* serverlist) {
   size_t num_valid = 0;
   /* first pass: count how many are valid in order to allocate the necessary
    * memory in a single block */
   for (size_t i = 0; i < serverlist->num_servers; ++i) {
-    if (is_server_valid(serverlist->servers[i], i, true)) ++num_valid;
+    if (IsServerValid(serverlist->servers[i], i, true)) ++num_valid;
   }
   grpc_lb_addresses* lb_addresses =
       grpc_lb_addresses_create(num_valid, &lb_token_vtable);
@@ -529,11 +454,11 @@
   size_t addr_idx = 0;
   for (size_t sl_idx = 0; sl_idx < serverlist->num_servers; ++sl_idx) {
     const grpc_grpclb_server* server = serverlist->servers[sl_idx];
-    if (!is_server_valid(serverlist->servers[sl_idx], sl_idx, false)) continue;
+    if (!IsServerValid(serverlist->servers[sl_idx], sl_idx, false)) continue;
     GPR_ASSERT(addr_idx < num_valid);
     /* address processing */
     grpc_resolved_address addr;
-    parse_server(server, &addr);
+    ParseServer(server, &addr);
     /* lb token processing */
     void* user_data;
     if (server->has_load_balance_token) {
@@ -564,37 +489,1283 @@
   return lb_addresses;
 }
 
-/* Returns the backend addresses extracted from the given addresses */
-static grpc_lb_addresses* extract_backend_addresses_locked(
-    const grpc_lb_addresses* addresses) {
-  /* first pass: count the number of backend addresses */
-  size_t num_backends = 0;
-  for (size_t i = 0; i < addresses->num_addresses; ++i) {
-    if (!addresses->addresses[i].is_balancer) {
-      ++num_backends;
-    }
-  }
-  /* second pass: actually populate the addresses and (empty) LB tokens */
-  grpc_lb_addresses* backend_addresses =
-      grpc_lb_addresses_create(num_backends, &lb_token_vtable);
-  size_t num_copied = 0;
-  for (size_t i = 0; i < addresses->num_addresses; ++i) {
-    if (addresses->addresses[i].is_balancer) continue;
-    const grpc_resolved_address* addr = &addresses->addresses[i].address;
-    grpc_lb_addresses_set_address(backend_addresses, num_copied, &addr->addr,
-                                  addr->len, false /* is_balancer */,
-                                  nullptr /* balancer_name */,
-                                  (void*)GRPC_MDELEM_LB_TOKEN_EMPTY.payload);
-    ++num_copied;
-  }
-  return backend_addresses;
+//
+// GrpcLb::BalancerCallState
+//
+
+GrpcLb::BalancerCallState::BalancerCallState(
+    RefCountedPtr<LoadBalancingPolicy> parent_grpclb_policy)
+    : InternallyRefCountedWithTracing<BalancerCallState>(&grpc_lb_glb_trace),
+      grpclb_policy_(std::move(parent_grpclb_policy)) {
+  GPR_ASSERT(grpclb_policy_ != nullptr);
+  GPR_ASSERT(!grpclb_policy()->shutting_down_);
+  // Init the LB call. Note that the LB call will progress every time there's
+  // activity in grpclb_policy_->interested_parties(), which is comprised of
+  // the polling entities from client_channel.
+  GPR_ASSERT(grpclb_policy()->server_name_ != nullptr);
+  GPR_ASSERT(grpclb_policy()->server_name_[0] != '\0');
+  grpc_slice host =
+      grpc_slice_from_copied_string(grpclb_policy()->server_name_);
+  grpc_millis deadline =
+      grpclb_policy()->lb_call_timeout_ms_ == 0
+          ? GRPC_MILLIS_INF_FUTURE
+          : ExecCtx::Get()->Now() + grpclb_policy()->lb_call_timeout_ms_;
+  lb_call_ = grpc_channel_create_pollset_set_call(
+      grpclb_policy()->lb_channel_, nullptr, GRPC_PROPAGATE_DEFAULTS,
+      grpclb_policy_->interested_parties(),
+      GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD,
+      &host, deadline, nullptr);
+  grpc_slice_unref_internal(host);
+  // Init the LB call request payload.
+  grpc_grpclb_request* request =
+      grpc_grpclb_request_create(grpclb_policy()->server_name_);
+  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
+  send_message_payload_ =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_slice_unref_internal(request_payload_slice);
+  grpc_grpclb_request_destroy(request);
+  // Init other data associated with the LB call.
+  grpc_metadata_array_init(&lb_initial_metadata_recv_);
+  grpc_metadata_array_init(&lb_trailing_metadata_recv_);
+  GRPC_CLOSURE_INIT(&lb_on_initial_request_sent_, OnInitialRequestSentLocked,
+                    this, grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  GRPC_CLOSURE_INIT(&lb_on_balancer_message_received_,
+                    OnBalancerMessageReceivedLocked, this,
+                    grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  GRPC_CLOSURE_INIT(&lb_on_balancer_status_received_,
+                    OnBalancerStatusReceivedLocked, this,
+                    grpc_combiner_scheduler(grpclb_policy()->combiner()));
 }
 
-static void update_lb_connectivity_status_locked(
-    glb_lb_policy* glb_policy, grpc_connectivity_state rr_state,
+GrpcLb::BalancerCallState::~BalancerCallState() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  grpc_call_unref(lb_call_);
+  grpc_metadata_array_destroy(&lb_initial_metadata_recv_);
+  grpc_metadata_array_destroy(&lb_trailing_metadata_recv_);
+  grpc_byte_buffer_destroy(send_message_payload_);
+  grpc_byte_buffer_destroy(recv_message_payload_);
+  grpc_slice_unref_internal(lb_call_status_details_);
+  if (client_stats_ != nullptr) {
+    grpc_grpclb_client_stats_unref(client_stats_);
+  }
+}
+
+void GrpcLb::BalancerCallState::Orphan() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  // If we are here because grpclb_policy wants to cancel the call,
+  // lb_on_balancer_status_received_ will complete the cancellation and clean
+  // up. Otherwise, we are here because grpclb_policy has to orphan a failed
+  // call, then the following cancellation will be a no-op.
+  grpc_call_cancel(lb_call_, nullptr);
+  if (client_load_report_timer_callback_pending_) {
+    grpc_timer_cancel(&client_load_report_timer_);
+  }
+  // Note that the initial ref is hold by lb_on_balancer_status_received_
+  // instead of the caller of this function. So the corresponding unref happens
+  // in lb_on_balancer_status_received_ instead of here.
+}
+
+void GrpcLb::BalancerCallState::StartQuery() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[grpclb %p] Starting LB call (lb_calld: %p, lb_call: %p)",
+            grpclb_policy_.get(), this, lb_call_);
+  }
+  // Create the ops.
+  grpc_call_error call_error;
+  grpc_op ops[3];
+  memset(ops, 0, sizeof(ops));
+  // Op: send initial metadata.
+  grpc_op* op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // Op: send request message.
+  GPR_ASSERT(send_message_payload_ != nullptr);
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = send_message_payload_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  auto self = Ref(DEBUG_LOCATION, "on_initial_request_sent");
+  self.release();
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_initial_request_sent_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+  // Op: recv initial metadata.
+  op = ops;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &lb_initial_metadata_recv_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // Op: recv response.
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &recv_message_payload_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  self = Ref(DEBUG_LOCATION, "on_message_received");
+  self.release();
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_message_received_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+  // Op: recv server status.
+  op = ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata =
+      &lb_trailing_metadata_recv_;
+  op->data.recv_status_on_client.status = &lb_call_status_;
+  op->data.recv_status_on_client.status_details = &lb_call_status_details_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // This callback signals the end of the LB call, so it relies on the initial
+  // ref instead of a new ref. When it's invoked, it's the initial ref that is
+  // unreffed.
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_status_received_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+};
+
+void GrpcLb::BalancerCallState::ScheduleNextClientLoadReportLocked() {
+  const grpc_millis next_client_load_report_time =
+      ExecCtx::Get()->Now() + client_stats_report_interval_;
+  GRPC_CLOSURE_INIT(&client_load_report_closure_,
+                    MaybeSendClientLoadReportLocked, this,
+                    grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  grpc_timer_init(&client_load_report_timer_, next_client_load_report_time,
+                  &client_load_report_closure_);
+  client_load_report_timer_callback_pending_ = true;
+}
+
+void GrpcLb::BalancerCallState::MaybeSendClientLoadReportLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  lb_calld->client_load_report_timer_callback_pending_ = false;
+  if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) {
+    lb_calld->Unref(DEBUG_LOCATION, "client_load_report");
+    return;
+  }
+  // If we've already sent the initial request, then we can go ahead and send
+  // the load report. Otherwise, we need to wait until the initial request has
+  // been sent to send this (see OnInitialRequestSentLocked()).
+  if (lb_calld->send_message_payload_ == nullptr) {
+    lb_calld->SendClientLoadReportLocked();
+  } else {
+    lb_calld->client_load_report_is_due_ = true;
+  }
+}
+
+bool GrpcLb::BalancerCallState::LoadReportCountersAreZero(
+    grpc_grpclb_request* request) {
+  grpc_grpclb_dropped_call_counts* drop_entries =
+      static_cast<grpc_grpclb_dropped_call_counts*>(
+          request->client_stats.calls_finished_with_drop.arg);
+  return request->client_stats.num_calls_started == 0 &&
+         request->client_stats.num_calls_finished == 0 &&
+         request->client_stats.num_calls_finished_with_client_failed_to_send ==
+             0 &&
+         request->client_stats.num_calls_finished_known_received == 0 &&
+         (drop_entries == nullptr || drop_entries->num_entries == 0);
+}
+
+void GrpcLb::BalancerCallState::SendClientLoadReportLocked() {
+  // Construct message payload.
+  GPR_ASSERT(send_message_payload_ == nullptr);
+  grpc_grpclb_request* request =
+      grpc_grpclb_load_report_request_create_locked(client_stats_);
+  // Skip client load report if the counters were all zero in the last
+  // report and they are still zero in this one.
+  if (LoadReportCountersAreZero(request)) {
+    if (last_client_load_report_counters_were_zero_) {
+      grpc_grpclb_request_destroy(request);
+      ScheduleNextClientLoadReportLocked();
+      return;
+    }
+    last_client_load_report_counters_were_zero_ = true;
+  } else {
+    last_client_load_report_counters_were_zero_ = false;
+  }
+  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
+  send_message_payload_ =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_slice_unref_internal(request_payload_slice);
+  grpc_grpclb_request_destroy(request);
+  // Send the report.
+  grpc_op op;
+  memset(&op, 0, sizeof(op));
+  op.op = GRPC_OP_SEND_MESSAGE;
+  op.data.send_message.send_message = send_message_payload_;
+  GRPC_CLOSURE_INIT(&client_load_report_closure_, ClientLoadReportDoneLocked,
+                    this, grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  grpc_call_error call_error = grpc_call_start_batch_and_execute(
+      lb_call_, &op, 1, &client_load_report_closure_);
+  if (call_error != GRPC_CALL_OK) {
+    gpr_log(GPR_ERROR, "[grpclb %p] call_error=%d", grpclb_policy_.get(),
+            call_error);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
+  }
+}
+
+void GrpcLb::BalancerCallState::ClientLoadReportDoneLocked(void* arg,
+                                                           grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  grpc_byte_buffer_destroy(lb_calld->send_message_payload_);
+  lb_calld->send_message_payload_ = nullptr;
+  if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) {
+    lb_calld->Unref(DEBUG_LOCATION, "client_load_report");
+    return;
+  }
+  lb_calld->ScheduleNextClientLoadReportLocked();
+}
+
+void GrpcLb::BalancerCallState::OnInitialRequestSentLocked(void* arg,
+                                                           grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  grpc_byte_buffer_destroy(lb_calld->send_message_payload_);
+  lb_calld->send_message_payload_ = nullptr;
+  // If we attempted to send a client load report before the initial request was
+  // sent (and this lb_calld is still in use), send the load report now.
+  if (lb_calld->client_load_report_is_due_ &&
+      lb_calld == lb_calld->grpclb_policy()->lb_calld_.get()) {
+    lb_calld->SendClientLoadReportLocked();
+    lb_calld->client_load_report_is_due_ = false;
+  }
+  lb_calld->Unref(DEBUG_LOCATION, "on_initial_request_sent");
+}
+
+void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  // Empty payload means the LB call was cancelled.
+  if (lb_calld != grpclb_policy->lb_calld_.get() ||
+      lb_calld->recv_message_payload_ == nullptr) {
+    lb_calld->Unref(DEBUG_LOCATION, "on_message_received");
+    return;
+  }
+  grpc_byte_buffer_reader bbr;
+  grpc_byte_buffer_reader_init(&bbr, lb_calld->recv_message_payload_);
+  grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  grpc_byte_buffer_destroy(lb_calld->recv_message_payload_);
+  lb_calld->recv_message_payload_ = nullptr;
+  grpc_grpclb_initial_response* initial_response;
+  grpc_grpclb_serverlist* serverlist;
+  if (!lb_calld->seen_initial_response_ &&
+      (initial_response = grpc_grpclb_initial_response_parse(response_slice)) !=
+          nullptr) {
+    // Have NOT seen initial response, look for initial response.
+    if (initial_response->has_client_stats_report_interval) {
+      lb_calld->client_stats_report_interval_ = GPR_MAX(
+          GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
+                              &initial_response->client_stats_report_interval));
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[grpclb %p] Received initial LB response message; "
+                "client load reporting interval = %" PRIdPTR " milliseconds",
+                grpclb_policy, lb_calld->client_stats_report_interval_);
+      }
+    } else if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] Received initial LB response message; client load "
+              "reporting NOT enabled",
+              grpclb_policy);
+    }
+    grpc_grpclb_initial_response_destroy(initial_response);
+    lb_calld->seen_initial_response_ = true;
+  } else if ((serverlist = grpc_grpclb_response_parse_serverlist(
+                  response_slice)) != nullptr) {
+    // Have seen initial response, look for serverlist.
+    GPR_ASSERT(lb_calld->lb_call_ != nullptr);
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] Serverlist with %" PRIuPTR " servers received",
+              grpclb_policy, serverlist->num_servers);
+      for (size_t i = 0; i < serverlist->num_servers; ++i) {
+        grpc_resolved_address addr;
+        ParseServer(serverlist->servers[i], &addr);
+        char* ipport;
+        grpc_sockaddr_to_string(&ipport, &addr, false);
+        gpr_log(GPR_INFO, "[grpclb %p] Serverlist[%" PRIuPTR "]: %s",
+                grpclb_policy, i, ipport);
+        gpr_free(ipport);
+      }
+    }
+    /* update serverlist */
+    if (serverlist->num_servers > 0) {
+      // Start sending client load report only after we start using the
+      // serverlist returned from the current LB call.
+      if (lb_calld->client_stats_report_interval_ > 0 &&
+          lb_calld->client_stats_ == nullptr) {
+        lb_calld->client_stats_ = grpc_grpclb_client_stats_create();
+        // TODO(roth): We currently track this ref manually.  Once the
+        // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+        // with the callback.
+        auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report");
+        self.release();
+        lb_calld->ScheduleNextClientLoadReportLocked();
+      }
+      if (grpc_grpclb_serverlist_equals(grpclb_policy->serverlist_,
+                                        serverlist)) {
+        if (grpc_lb_glb_trace.enabled()) {
+          gpr_log(GPR_INFO,
+                  "[grpclb %p] Incoming server list identical to current, "
+                  "ignoring.",
+                  grpclb_policy);
+        }
+        grpc_grpclb_destroy_serverlist(serverlist);
+      } else { /* new serverlist */
+        if (grpclb_policy->serverlist_ != nullptr) {
+          /* dispose of the old serverlist */
+          grpc_grpclb_destroy_serverlist(grpclb_policy->serverlist_);
+        } else {
+          /* or dispose of the fallback */
+          grpc_lb_addresses_destroy(grpclb_policy->fallback_backend_addresses_);
+          grpclb_policy->fallback_backend_addresses_ = nullptr;
+          if (grpclb_policy->fallback_timer_callback_pending_) {
+            grpc_timer_cancel(&grpclb_policy->lb_fallback_timer_);
+          }
+        }
+        // and update the copy in the GrpcLb instance. This
+        // serverlist instance will be destroyed either upon the next
+        // update or when the GrpcLb instance is destroyed.
+        grpclb_policy->serverlist_ = serverlist;
+        grpclb_policy->serverlist_index_ = 0;
+        grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked();
+      }
+    } else {
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO, "[grpclb %p] Received empty server list, ignoring.",
+                grpclb_policy);
+      }
+      grpc_grpclb_destroy_serverlist(serverlist);
+    }
+  } else {
+    // No valid initial response or serverlist found.
+    gpr_log(GPR_ERROR,
+            "[grpclb %p] Invalid LB response received: '%s'. Ignoring.",
+            grpclb_policy,
+            grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
+  }
+  grpc_slice_unref_internal(response_slice);
+  if (!grpclb_policy->shutting_down_) {
+    // Keep listening for serverlist updates.
+    grpc_op op;
+    memset(&op, 0, sizeof(op));
+    op.op = GRPC_OP_RECV_MESSAGE;
+    op.data.recv_message.recv_message = &lb_calld->recv_message_payload_;
+    op.flags = 0;
+    op.reserved = nullptr;
+    // Reuse the "OnBalancerMessageReceivedLocked" ref taken in StartQuery().
+    const grpc_call_error call_error = grpc_call_start_batch_and_execute(
+        lb_calld->lb_call_, &op, 1,
+        &lb_calld->lb_on_balancer_message_received_);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
+  } else {
+    lb_calld->Unref(DEBUG_LOCATION, "on_message_received+grpclb_shutdown");
+  }
+}
+
+void GrpcLb::BalancerCallState::OnBalancerStatusReceivedLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  GPR_ASSERT(lb_calld->lb_call_ != nullptr);
+  if (grpc_lb_glb_trace.enabled()) {
+    char* status_details =
+        grpc_slice_to_c_string(lb_calld->lb_call_status_details_);
+    gpr_log(GPR_INFO,
+            "[grpclb %p] Status from LB server received. Status = %d, details "
+            "= '%s', (lb_calld: %p, lb_call: %p), error '%s'",
+            grpclb_policy, lb_calld->lb_call_status_, status_details, lb_calld,
+            lb_calld->lb_call_, grpc_error_string(error));
+    gpr_free(status_details);
+  }
+  grpclb_policy->TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_NONE);
+  // If this lb_calld is still in use, this call ended because of a failure so
+  // we want to retry connecting. Otherwise, we have deliberately ended this
+  // call and no further action is required.
+  if (lb_calld == grpclb_policy->lb_calld_.get()) {
+    grpclb_policy->lb_calld_.reset();
+    GPR_ASSERT(!grpclb_policy->shutting_down_);
+    if (lb_calld->seen_initial_response_) {
+      // If we lose connection to the LB server, reset the backoff and restart
+      // the LB call immediately.
+      grpclb_policy->lb_call_backoff_.Reset();
+      grpclb_policy->StartBalancerCallLocked();
+    } else {
+      // If this LB call fails establishing any connection to the LB server,
+      // retry later.
+      grpclb_policy->StartBalancerCallRetryTimerLocked();
+    }
+  }
+  lb_calld->Unref(DEBUG_LOCATION, "lb_call_ended");
+}
+
+//
+// helper code for creating balancer channel
+//
+
+grpc_lb_addresses* ExtractBalancerAddresses(
+    const grpc_lb_addresses* addresses) {
+  size_t num_grpclb_addrs = 0;
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs;
+  }
+  // There must be at least one balancer address, or else the
+  // client_channel would not have chosen this LB policy.
+  GPR_ASSERT(num_grpclb_addrs > 0);
+  grpc_lb_addresses* lb_addresses =
+      grpc_lb_addresses_create(num_grpclb_addrs, nullptr);
+  size_t lb_addresses_idx = 0;
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (!addresses->addresses[i].is_balancer) continue;
+    if (addresses->addresses[i].user_data != nullptr) {
+      gpr_log(GPR_ERROR,
+              "This LB policy doesn't support user data. It will be ignored");
+    }
+    grpc_lb_addresses_set_address(
+        lb_addresses, lb_addresses_idx++, addresses->addresses[i].address.addr,
+        addresses->addresses[i].address.len, false /* is balancer */,
+        addresses->addresses[i].balancer_name, nullptr /* user data */);
+  }
+  GPR_ASSERT(num_grpclb_addrs == lb_addresses_idx);
+  return lb_addresses;
+}
+
+/* Returns the channel args for the LB channel, used to create a bidirectional
+ * stream for the reception of load balancing updates.
+ *
+ * Inputs:
+ *   - \a addresses: corresponding to the balancers.
+ *   - \a response_generator: in order to propagate updates from the resolver
+ *   above the grpclb policy.
+ *   - \a args: other args inherited from the grpclb policy. */
+grpc_channel_args* BuildBalancerChannelArgs(
+    const grpc_lb_addresses* addresses,
+    FakeResolverResponseGenerator* response_generator,
+    const grpc_channel_args* args) {
+  grpc_lb_addresses* lb_addresses = ExtractBalancerAddresses(addresses);
+  // Channel args to remove.
+  static const char* args_to_remove[] = {
+      // LB policy name, since we want to use the default (pick_first) in
+      // the LB channel.
+      GRPC_ARG_LB_POLICY_NAME,
+      // The channel arg for the server URI, since that will be different for
+      // the LB channel than for the parent channel.  The client channel
+      // factory will re-add this arg with the right value.
+      GRPC_ARG_SERVER_URI,
+      // The resolved addresses, which will be generated by the name resolver
+      // used in the LB channel.  Note that the LB channel will use the fake
+      // resolver, so this won't actually generate a query to DNS (or some
+      // other name service).  However, the addresses returned by the fake
+      // resolver will have is_balancer=false, whereas our own addresses have
+      // is_balancer=true.  We need the LB channel to return addresses with
+      // is_balancer=false so that it does not wind up recursively using the
+      // grpclb LB policy, as per the special case logic in client_channel.c.
+      GRPC_ARG_LB_ADDRESSES,
+      // The fake resolver response generator, because we are replacing it
+      // with the one from the grpclb policy, used to propagate updates to
+      // the LB channel.
+      GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
+  };
+  // Channel args to add.
+  const grpc_arg args_to_add[] = {
+      // New LB addresses.
+      // Note that we pass these in both when creating the LB channel
+      // and via the fake resolver.  The latter is what actually gets used.
+      grpc_lb_addresses_create_channel_arg(lb_addresses),
+      // The fake resolver response generator, which we use to inject
+      // address updates into the LB channel.
+      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
+          response_generator),
+  };
+  // Construct channel args.
+  grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(
+      args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), args_to_add,
+      GPR_ARRAY_SIZE(args_to_add));
+  // Make any necessary modifications for security.
+  new_args = grpc_lb_policy_grpclb_modify_lb_channel_args(new_args);
+  // Clean up.
+  grpc_lb_addresses_destroy(lb_addresses);
+  return new_args;
+}
+
+//
+// ctor and dtor
+//
+
+GrpcLb::GrpcLb(const grpc_lb_addresses* addresses,
+               const LoadBalancingPolicy::Args& args)
+    : LoadBalancingPolicy(args),
+      response_generator_(MakeRefCounted<FakeResolverResponseGenerator>()),
+      lb_call_backoff_(
+          BackOff::Options()
+              .set_initial_backoff(GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS *
+                                   1000)
+              .set_multiplier(GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER)
+              .set_jitter(GRPC_GRPCLB_RECONNECT_JITTER)
+              .set_max_backoff(GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS *
+                               1000)) {
+  // Initialization.
+  grpc_subchannel_index_ref();
+  GRPC_CLOSURE_INIT(&lb_channel_on_connectivity_changed_,
+                    &GrpcLb::OnBalancerChannelConnectivityChangedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_rr_connectivity_changed_,
+                    &GrpcLb::OnRoundRobinConnectivityChangedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_rr_request_reresolution_,
+                    &GrpcLb::OnRoundRobinRequestReresolutionLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, "grpclb");
+  // Record server name.
+  const grpc_arg* arg = grpc_channel_args_find(args.args, GRPC_ARG_SERVER_URI);
+  const char* server_uri = grpc_channel_arg_get_string(arg);
+  GPR_ASSERT(server_uri != nullptr);
+  grpc_uri* uri = grpc_uri_parse(server_uri, true);
+  GPR_ASSERT(uri->path[0] != '\0');
+  server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[grpclb %p] Will use '%s' as the server name for LB request.",
+            this, server_name_);
+  }
+  grpc_uri_destroy(uri);
+  // Record LB call timeout.
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS);
+  lb_call_timeout_ms_ = grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX});
+  // Record fallback timeout.
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS);
+  lb_fallback_timeout_ms_ = grpc_channel_arg_get_integer(
+      arg, {GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX});
+  // Process channel args.
+  ProcessChannelArgsLocked(*args.args);
+}
+
+GrpcLb::~GrpcLb() {
+  GPR_ASSERT(pending_picks_ == nullptr);
+  GPR_ASSERT(pending_pings_ == nullptr);
+  gpr_free((void*)server_name_);
+  grpc_channel_args_destroy(args_);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  if (serverlist_ != nullptr) {
+    grpc_grpclb_destroy_serverlist(serverlist_);
+  }
+  if (fallback_backend_addresses_ != nullptr) {
+    grpc_lb_addresses_destroy(fallback_backend_addresses_);
+  }
+  grpc_subchannel_index_unref();
+}
+
+void GrpcLb::ShutdownLocked() {
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
+  shutting_down_ = true;
+  lb_calld_.reset();
+  if (retry_timer_callback_pending_) {
+    grpc_timer_cancel(&lb_call_retry_timer_);
+  }
+  if (fallback_timer_callback_pending_) {
+    grpc_timer_cancel(&lb_fallback_timer_);
+  }
+  rr_policy_.reset();
+  TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_CANCELLED);
+  // We destroy the LB channel here instead of in our destructor because
+  // destroying the channel triggers a last callback to
+  // OnBalancerChannelConnectivityChangedLocked(), and we need to be
+  // alive when that callback is invoked.
+  if (lb_channel_ != nullptr) {
+    grpc_channel_destroy(lb_channel_);
+    lb_channel_ = nullptr;
+  }
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
+                              GRPC_ERROR_REF(error), "grpclb_shutdown");
+  // Clear pending picks.
+  PendingPick* pp;
+  while ((pp = pending_picks_) != nullptr) {
+    pending_picks_ = pp->next;
+    pp->pick->connected_subchannel.reset();
+    // Note: pp is deleted in this callback.
+    GRPC_CLOSURE_SCHED(&pp->on_complete, GRPC_ERROR_REF(error));
+  }
+  // Clear pending pings.
+  PendingPing* pping;
+  while ((pping = pending_pings_) != nullptr) {
+    pending_pings_ = pping->next;
+    GRPC_CLOSURE_SCHED(pping->on_initiate, GRPC_ERROR_REF(error));
+    GRPC_CLOSURE_SCHED(pping->on_ack, GRPC_ERROR_REF(error));
+    Delete(pping);
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+//
+// public methods
+//
+
+void GrpcLb::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PendingPick* pp;
+  while ((pp = pending_picks_) != nullptr) {
+    pending_picks_ = pp->next;
+    pp->pick->on_complete = pp->original_on_complete;
+    pp->pick->user_data = nullptr;
+    if (new_policy->PickLocked(pp->pick)) {
+      // Synchronous return; schedule closure.
+      GRPC_CLOSURE_SCHED(pp->pick->on_complete, GRPC_ERROR_NONE);
+    }
+    Delete(pp);
+  }
+}
+
+// Cancel a specific pending pick.
+//
+// A grpclb pick progresses as follows:
+// - If there's a Round Robin policy (rr_policy_) available, it'll be
+//   handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From
+//   that point onwards, it'll be RR's responsibility. For cancellations, that
+//   implies the pick needs also be cancelled by the RR instance.
+// - Otherwise, without an RR instance, picks stay pending at this policy's
+//   level (grpclb), inside the pending_picks_ list. To cancel these,
+//   we invoke the completion closure and set the pick's connected
+//   subchannel to nullptr right here.
+void GrpcLb::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PendingPick* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PendingPick* next = pp->next;
+    if (pp->pick == pick) {
+      pick->connected_subchannel.reset();
+      // Note: pp is deleted in this callback.
+      GRPC_CLOSURE_SCHED(&pp->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  if (rr_policy_ != nullptr) {
+    rr_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Cancel all pending picks.
+//
+// A grpclb pick progresses as follows:
+// - If there's a Round Robin policy (rr_policy_) available, it'll be
+//   handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From
+//   that point onwards, it'll be RR's responsibility. For cancellations, that
+//   implies the pick needs also be cancelled by the RR instance.
+// - Otherwise, without an RR instance, picks stay pending at this policy's
+//   level (grpclb), inside the pending_picks_ list. To cancel these,
+//   we invoke the completion closure and set the pick's connected
+//   subchannel to nullptr right here.
+void GrpcLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                       uint32_t initial_metadata_flags_eq,
+                                       grpc_error* error) {
+  PendingPick* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PendingPick* next = pp->next;
+    if ((pp->pick->initial_metadata_flags & initial_metadata_flags_mask) ==
+        initial_metadata_flags_eq) {
+      // Note: pp is deleted in this callback.
+      GRPC_CLOSURE_SCHED(&pp->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  if (rr_policy_ != nullptr) {
+    rr_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask,
+                                          initial_metadata_flags_eq,
+                                          GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void GrpcLb::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+}
+
+bool GrpcLb::PickLocked(PickState* pick) {
+  PendingPick* pp = PendingPickCreate(pick);
+  bool pick_done = false;
+  if (rr_policy_ != nullptr) {
+    const grpc_connectivity_state rr_connectivity_state =
+        rr_policy_->CheckConnectivityLocked(nullptr);
+    // The RR policy may have transitioned to SHUTDOWN but the callback
+    // registered to capture this event (on_rr_connectivity_changed_) may not
+    // have been invoked yet. We need to make sure we aren't trying to pick
+    // from an RR policy instance that's in shutdown.
+    if (rr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) {
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[grpclb %p] NOT picking from from RR %p: RR conn state=%s",
+                this, rr_policy_.get(),
+                grpc_connectivity_state_name(rr_connectivity_state));
+      }
+      AddPendingPick(pp);
+      pick_done = false;
+    } else {  // RR not in shutdown
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO, "[grpclb %p] about to PICK from RR %p", this,
+                rr_policy_.get());
+      }
+      pick_done = PickFromRoundRobinPolicyLocked(false /* force_async */, pp);
+    }
+  } else {  // rr_policy_ == NULL
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_DEBUG,
+              "[grpclb %p] No RR policy. Adding to grpclb's pending picks",
+              this);
+    }
+    AddPendingPick(pp);
+    if (!started_picking_) {
+      StartPickingLocked();
+    }
+    pick_done = false;
+  }
+  return pick_done;
+}
+
+void GrpcLb::PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) {
+  if (rr_policy_ != nullptr) {
+    rr_policy_->PingOneLocked(on_initiate, on_ack);
+  } else {
+    AddPendingPing(on_initiate, on_ack);
+    if (!started_picking_) {
+      StartPickingLocked();
+    }
+  }
+}
+
+grpc_connectivity_state GrpcLb::CheckConnectivityLocked(
+    grpc_error** connectivity_error) {
+  return grpc_connectivity_state_get(&state_tracker_, connectivity_error);
+}
+
+void GrpcLb::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                       grpc_closure* notify) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
+                                                 notify);
+}
+
+void GrpcLb::ProcessChannelArgsLocked(const grpc_channel_args& args) {
+  const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
+  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
+    // Ignore this update.
+    gpr_log(
+        GPR_ERROR,
+        "[grpclb %p] No valid LB addresses channel arg in update, ignoring.",
+        this);
+    return;
+  }
+  const grpc_lb_addresses* addresses =
+      static_cast<const grpc_lb_addresses*>(arg->value.pointer.p);
+  // Update fallback address list.
+  if (fallback_backend_addresses_ != nullptr) {
+    grpc_lb_addresses_destroy(fallback_backend_addresses_);
+  }
+  fallback_backend_addresses_ = ExtractBackendAddresses(addresses);
+  // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
+  // since we use this to trigger the client_load_reporting filter.
+  static const char* args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
+  grpc_arg new_arg = grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_LB_POLICY_NAME, (char*)"grpclb");
+  grpc_channel_args_destroy(args_);
+  args_ = grpc_channel_args_copy_and_add_and_remove(
+      &args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
+  // Construct args for balancer channel.
+  grpc_channel_args* lb_channel_args =
+      BuildBalancerChannelArgs(addresses, response_generator_.get(), &args);
+  // Create balancer channel if needed.
+  if (lb_channel_ == nullptr) {
+    char* uri_str;
+    gpr_asprintf(&uri_str, "fake:///%s", server_name_);
+    lb_channel_ = grpc_client_channel_factory_create_channel(
+        client_channel_factory(), uri_str,
+        GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, lb_channel_args);
+    GPR_ASSERT(lb_channel_ != nullptr);
+    gpr_free(uri_str);
+  }
+  // Propagate updates to the LB channel (pick_first) through the fake
+  // resolver.
+  response_generator_->SetResponse(lb_channel_args);
+  grpc_channel_args_destroy(lb_channel_args);
+}
+
+void GrpcLb::UpdateLocked(const grpc_channel_args& args) {
+  ProcessChannelArgsLocked(args);
+  // If fallback is configured and the RR policy already exists, update
+  // it with the new fallback addresses.
+  if (lb_fallback_timeout_ms_ > 0 && rr_policy_ != nullptr) {
+    CreateOrUpdateRoundRobinPolicyLocked();
+  }
+  // Start watching the LB channel connectivity for connection, if not
+  // already doing so.
+  if (!watching_lb_channel_) {
+    lb_channel_connectivity_ = grpc_channel_check_connectivity_state(
+        lb_channel_, true /* try to connect */);
+    grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
+        grpc_channel_get_channel_stack(lb_channel_));
+    GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+    watching_lb_channel_ = true;
+    // TODO(roth): We currently track this ref manually.  Once the
+    // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+    // with the callback.
+    auto self = Ref(DEBUG_LOCATION, "watch_lb_channel_connectivity");
+    self.release();
+    grpc_client_channel_watch_connectivity_state(
+        client_channel_elem,
+        grpc_polling_entity_create_from_pollset_set(interested_parties()),
+        &lb_channel_connectivity_, &lb_channel_on_connectivity_changed_,
+        nullptr);
+  }
+}
+
+//
+// code for balancer channel and call
+//
+
+void GrpcLb::StartPickingLocked() {
+  // Start a timer to fall back.
+  if (lb_fallback_timeout_ms_ > 0 && serverlist_ == nullptr &&
+      !fallback_timer_callback_pending_) {
+    grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_;
+    // TODO(roth): We currently track this ref manually.  Once the
+    // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+    // with the callback.
+    auto self = Ref(DEBUG_LOCATION, "on_fallback_timer");
+    self.release();
+    GRPC_CLOSURE_INIT(&lb_on_fallback_, &GrpcLb::OnFallbackTimerLocked, this,
+                      grpc_combiner_scheduler(combiner()));
+    fallback_timer_callback_pending_ = true;
+    grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_);
+  }
+  started_picking_ = true;
+  StartBalancerCallLocked();
+}
+
+void GrpcLb::StartBalancerCallLocked() {
+  GPR_ASSERT(lb_channel_ != nullptr);
+  if (shutting_down_) return;
+  // Init the LB call data.
+  GPR_ASSERT(lb_calld_ == nullptr);
+  lb_calld_ = MakeOrphanable<BalancerCallState>(Ref());
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[grpclb %p] Query for backends (lb_channel: %p, lb_calld: %p)",
+            this, lb_channel_, lb_calld_.get());
+  }
+  lb_calld_->StartQuery();
+}
+
+void GrpcLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  grpclb_policy->fallback_timer_callback_pending_ = false;
+  // If we receive a serverlist after the timer fires but before this callback
+  // actually runs, don't fall back.
+  if (grpclb_policy->serverlist_ == nullptr && !grpclb_policy->shutting_down_ &&
+      error == GRPC_ERROR_NONE) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] Falling back to use backends from resolver",
+              grpclb_policy);
+    }
+    GPR_ASSERT(grpclb_policy->fallback_backend_addresses_ != nullptr);
+    grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked();
+  }
+  grpclb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer");
+}
+
+void GrpcLb::StartBalancerCallRetryTimerLocked() {
+  grpc_millis next_try = lb_call_backoff_.NextAttemptTime();
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "[grpclb %p] Connection to LB server lost...", this);
+    grpc_millis timeout = next_try - ExecCtx::Get()->Now();
+    if (timeout > 0) {
+      gpr_log(GPR_DEBUG,
+              "[grpclb %p] ... retry_timer_active in %" PRIuPTR "ms.", this,
+              timeout);
+    } else {
+      gpr_log(GPR_DEBUG, "[grpclb %p] ... retry_timer_active immediately.",
+              this);
+    }
+  }
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  auto self = Ref(DEBUG_LOCATION, "on_balancer_call_retry_timer");
+  self.release();
+  GRPC_CLOSURE_INIT(&lb_on_call_retry_, &GrpcLb::OnBalancerCallRetryTimerLocked,
+                    this, grpc_combiner_scheduler(combiner()));
+  retry_timer_callback_pending_ = true;
+  grpc_timer_init(&lb_call_retry_timer_, next_try, &lb_on_call_retry_);
+}
+
+void GrpcLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  grpclb_policy->retry_timer_callback_pending_ = false;
+  if (!grpclb_policy->shutting_down_ && error == GRPC_ERROR_NONE &&
+      grpclb_policy->lb_calld_ == nullptr) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server",
+              grpclb_policy);
+    }
+    grpclb_policy->StartBalancerCallLocked();
+  }
+  grpclb_policy->Unref(DEBUG_LOCATION, "on_balancer_call_retry_timer");
+}
+
+// Invoked as part of the update process. It continues watching the LB channel
+// until it shuts down or becomes READY. It's invoked even if the LB channel
+// stayed READY throughout the update (for example if the update is identical).
+void GrpcLb::OnBalancerChannelConnectivityChangedLocked(void* arg,
+                                                        grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  if (grpclb_policy->shutting_down_) goto done;
+  // Re-initialize the lb_call. This should also take care of updating the
+  // embedded RR policy. Note that the current RR policy, if any, will stay in
+  // effect until an update from the new lb_call is received.
+  switch (grpclb_policy->lb_channel_connectivity_) {
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_TRANSIENT_FAILURE: {
+      // Keep watching the LB channel.
+      grpc_channel_element* client_channel_elem =
+          grpc_channel_stack_last_element(
+              grpc_channel_get_channel_stack(grpclb_policy->lb_channel_));
+      GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+      grpc_client_channel_watch_connectivity_state(
+          client_channel_elem,
+          grpc_polling_entity_create_from_pollset_set(
+              grpclb_policy->interested_parties()),
+          &grpclb_policy->lb_channel_connectivity_,
+          &grpclb_policy->lb_channel_on_connectivity_changed_, nullptr);
+      break;
+    }
+      // The LB channel may be IDLE because it's shut down before the update.
+      // Restart the LB call to kick the LB channel into gear.
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_READY:
+      grpclb_policy->lb_calld_.reset();
+      if (grpclb_policy->started_picking_) {
+        if (grpclb_policy->retry_timer_callback_pending_) {
+          grpc_timer_cancel(&grpclb_policy->lb_call_retry_timer_);
+        }
+        grpclb_policy->lb_call_backoff_.Reset();
+        grpclb_policy->StartBalancerCallLocked();
+      }
+      // Fall through.
+    case GRPC_CHANNEL_SHUTDOWN:
+    done:
+      grpclb_policy->watching_lb_channel_ = false;
+      grpclb_policy->Unref(DEBUG_LOCATION,
+                           "watch_lb_channel_connectivity_cb_shutdown");
+  }
+}
+
+//
+// PendingPick
+//
+
+// Adds lb_token of selected subchannel (address) to the call's initial
+// metadata.
+grpc_error* AddLbTokenToInitialMetadata(
+    grpc_mdelem lb_token, grpc_linked_mdelem* lb_token_mdelem_storage,
+    grpc_metadata_batch* initial_metadata) {
+  GPR_ASSERT(lb_token_mdelem_storage != nullptr);
+  GPR_ASSERT(!GRPC_MDISNULL(lb_token));
+  return grpc_metadata_batch_add_tail(initial_metadata, lb_token_mdelem_storage,
+                                      lb_token);
+}
+
+// Destroy function used when embedding client stats in call context.
+void DestroyClientStats(void* arg) {
+  grpc_grpclb_client_stats_unref(static_cast<grpc_grpclb_client_stats*>(arg));
+}
+
+void GrpcLb::PendingPickSetMetadataAndContext(PendingPick* pp) {
+  /* if connected_subchannel is nullptr, no pick has been made by the RR
+   * policy (e.g., all addresses failed to connect). There won't be any
+   * user_data/token available */
+  if (pp->pick->connected_subchannel != nullptr) {
+    if (!GRPC_MDISNULL(pp->lb_token)) {
+      AddLbTokenToInitialMetadata(GRPC_MDELEM_REF(pp->lb_token),
+                                  &pp->pick->lb_token_mdelem_storage,
+                                  pp->pick->initial_metadata);
+    } else {
+      gpr_log(GPR_ERROR,
+              "[grpclb %p] No LB token for connected subchannel pick %p",
+              pp->grpclb_policy, pp->pick);
+      abort();
+    }
+    // Pass on client stats via context. Passes ownership of the reference.
+    if (pp->client_stats != nullptr) {
+      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].value =
+          pp->client_stats;
+      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].destroy =
+          DestroyClientStats;
+    }
+  } else {
+    if (pp->client_stats != nullptr) {
+      grpc_grpclb_client_stats_unref(pp->client_stats);
+    }
+  }
+}
+
+/* The \a on_complete closure passed as part of the pick requires keeping a
+ * reference to its associated round robin instance. We wrap this closure in
+ * order to unref the round robin instance upon its invocation */
+void GrpcLb::OnPendingPickComplete(void* arg, grpc_error* error) {
+  PendingPick* pp = static_cast<PendingPick*>(arg);
+  PendingPickSetMetadataAndContext(pp);
+  GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error));
+  Delete(pp);
+}
+
+GrpcLb::PendingPick* GrpcLb::PendingPickCreate(PickState* pick) {
+  PendingPick* pp = New<PendingPick>();
+  pp->grpclb_policy = this;
+  pp->pick = pick;
+  GRPC_CLOSURE_INIT(&pp->on_complete, &GrpcLb::OnPendingPickComplete, pp,
+                    grpc_schedule_on_exec_ctx);
+  pp->original_on_complete = pick->on_complete;
+  pick->on_complete = &pp->on_complete;
+  return pp;
+}
+
+void GrpcLb::AddPendingPick(PendingPick* pp) {
+  pp->next = pending_picks_;
+  pending_picks_ = pp;
+}
+
+//
+// PendingPing
+//
+
+void GrpcLb::AddPendingPing(grpc_closure* on_initiate, grpc_closure* on_ack) {
+  PendingPing* pping = New<PendingPing>();
+  pping->on_initiate = on_initiate;
+  pping->on_ack = on_ack;
+  pping->next = pending_pings_;
+  pending_pings_ = pping;
+}
+
+//
+// code for interacting with the RR policy
+//
+
+// Performs a pick over \a rr_policy_. Given that a pick can return
+// immediately (ignoring its completion callback), we need to perform the
+// cleanups this callback would otherwise be responsible for.
+// If \a force_async is true, then we will manually schedule the
+// completion callback even if the pick is available immediately.
+bool GrpcLb::PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp) {
+  // Check for drops if we are not using fallback backend addresses.
+  if (serverlist_ != nullptr) {
+    // Look at the index into the serverlist to see if we should drop this call.
+    grpc_grpclb_server* server = serverlist_->servers[serverlist_index_++];
+    if (serverlist_index_ == serverlist_->num_servers) {
+      serverlist_index_ = 0;  // Wrap-around.
+    }
+    if (server->drop) {
+      // Update client load reporting stats to indicate the number of
+      // dropped calls.  Note that we have to do this here instead of in
+      // the client_load_reporting filter, because we do not create a
+      // subchannel call (and therefore no client_load_reporting filter)
+      // for dropped calls.
+      if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
+        grpc_grpclb_client_stats_add_call_dropped_locked(
+            server->load_balance_token, lb_calld_->client_stats());
+      }
+      if (force_async) {
+        GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE);
+        Delete(pp);
+        return false;
+      }
+      Delete(pp);
+      return true;
+    }
+  }
+  // Set client_stats and user_data.
+  if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
+    pp->client_stats = grpc_grpclb_client_stats_ref(lb_calld_->client_stats());
+  }
+  GPR_ASSERT(pp->pick->user_data == nullptr);
+  pp->pick->user_data = (void**)&pp->lb_token;
+  // Pick via the RR policy.
+  bool pick_done = rr_policy_->PickLocked(pp->pick);
+  if (pick_done) {
+    PendingPickSetMetadataAndContext(pp);
+    if (force_async) {
+      GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE);
+      pick_done = false;
+    }
+    Delete(pp);
+  }
+  // else, the pending pick will be registered and taken care of by the
+  // pending pick list inside the RR policy.  Eventually,
+  // OnPendingPickComplete() will be called, which will (among other
+  // things) add the LB token to the call's initial metadata.
+  return pick_done;
+}
+
+void GrpcLb::CreateRoundRobinPolicyLocked(const Args& args) {
+  GPR_ASSERT(rr_policy_ == nullptr);
+  rr_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+      "round_robin", args);
+  if (rr_policy_ == nullptr) {
+    gpr_log(GPR_ERROR, "[grpclb %p] Failure creating a RoundRobin policy",
+            this);
+    return;
+  }
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
+  auto self = Ref(DEBUG_LOCATION, "on_rr_reresolution_requested");
+  self.release();
+  rr_policy_->SetReresolutionClosureLocked(&on_rr_request_reresolution_);
+  grpc_error* rr_state_error = nullptr;
+  rr_connectivity_state_ = rr_policy_->CheckConnectivityLocked(&rr_state_error);
+  // Connectivity state is a function of the RR policy updated/created.
+  UpdateConnectivityStateFromRoundRobinPolicyLocked(rr_state_error);
+  // Add the gRPC LB's interested_parties pollset_set to that of the newly
+  // created RR policy. This will make the RR policy progress upon activity on
+  // gRPC LB, which in turn is tied to the application's call.
+  grpc_pollset_set_add_pollset_set(rr_policy_->interested_parties(),
+                                   interested_parties());
+  // Subscribe to changes to the connectivity of the new RR.
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
+  self = Ref(DEBUG_LOCATION, "on_rr_connectivity_changed");
+  self.release();
+  rr_policy_->NotifyOnStateChangeLocked(&rr_connectivity_state_,
+                                        &on_rr_connectivity_changed_);
+  rr_policy_->ExitIdleLocked();
+  // Send pending picks to RR policy.
+  PendingPick* pp;
+  while ((pp = pending_picks_)) {
+    pending_picks_ = pp->next;
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] Pending pick about to (async) PICK from RR %p", this,
+              rr_policy_.get());
+    }
+    PickFromRoundRobinPolicyLocked(true /* force_async */, pp);
+  }
+  // Send pending pings to RR policy.
+  PendingPing* pping;
+  while ((pping = pending_pings_)) {
+    pending_pings_ = pping->next;
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO, "[grpclb %p] Pending ping about to PING from RR %p",
+              this, rr_policy_.get());
+    }
+    rr_policy_->PingOneLocked(pping->on_initiate, pping->on_ack);
+    Delete(pping);
+  }
+}
+
+grpc_channel_args* GrpcLb::CreateRoundRobinPolicyArgsLocked() {
+  grpc_lb_addresses* addresses;
+  if (serverlist_ != nullptr) {
+    GPR_ASSERT(serverlist_->num_servers > 0);
+    addresses = ProcessServerlist(serverlist_);
+  } else {
+    // If CreateOrUpdateRoundRobinPolicyLocked() is invoked when we haven't
+    // received any serverlist from the balancer, we use the fallback backends
+    // returned by the resolver. Note that the fallback backend list may be
+    // empty, in which case the new round_robin policy will keep the requested
+    // picks pending.
+    GPR_ASSERT(fallback_backend_addresses_ != nullptr);
+    addresses = grpc_lb_addresses_copy(fallback_backend_addresses_);
+  }
+  GPR_ASSERT(addresses != nullptr);
+  // Replace the LB addresses in the channel args that we pass down to
+  // the subchannel.
+  static const char* keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES};
+  const grpc_arg arg = grpc_lb_addresses_create_channel_arg(addresses);
+  grpc_channel_args* args = grpc_channel_args_copy_and_add_and_remove(
+      args_, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg, 1);
+  grpc_lb_addresses_destroy(addresses);
+  return args;
+}
+
+void GrpcLb::CreateOrUpdateRoundRobinPolicyLocked() {
+  if (shutting_down_) return;
+  grpc_channel_args* args = CreateRoundRobinPolicyArgsLocked();
+  GPR_ASSERT(args != nullptr);
+  if (rr_policy_ != nullptr) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "[grpclb %p] Updating RR policy %p", this,
+              rr_policy_.get());
+    }
+    rr_policy_->UpdateLocked(*args);
+  } else {
+    LoadBalancingPolicy::Args lb_policy_args;
+    lb_policy_args.combiner = combiner();
+    lb_policy_args.client_channel_factory = client_channel_factory();
+    lb_policy_args.args = args;
+    CreateRoundRobinPolicyLocked(lb_policy_args);
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_DEBUG, "[grpclb %p] Created new RR policy %p", this,
+              rr_policy_.get());
+    }
+  }
+  grpc_channel_args_destroy(args);
+}
+
+void GrpcLb::OnRoundRobinRequestReresolutionLocked(void* arg,
+                                                   grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  if (grpclb_policy->shutting_down_ || error != GRPC_ERROR_NONE) {
+    grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_reresolution_requested");
+    return;
+  }
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(
+        GPR_DEBUG,
+        "[grpclb %p] Re-resolution requested from the internal RR policy (%p).",
+        grpclb_policy, grpclb_policy->rr_policy_.get());
+  }
+  // If we are talking to a balancer, we expect to get updated addresses form
+  // the balancer, so we can ignore the re-resolution request from the RR
+  // policy. Otherwise, handle the re-resolution request using the
+  // grpclb policy's original re-resolution closure.
+  if (grpclb_policy->lb_calld_ == nullptr ||
+      !grpclb_policy->lb_calld_->seen_initial_response()) {
+    grpclb_policy->TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_NONE);
+  }
+  // Give back the wrapper closure to the RR policy.
+  grpclb_policy->rr_policy_->SetReresolutionClosureLocked(
+      &grpclb_policy->on_rr_request_reresolution_);
+}
+
+void GrpcLb::UpdateConnectivityStateFromRoundRobinPolicyLocked(
     grpc_error* rr_state_error) {
   const grpc_connectivity_state curr_glb_state =
-      grpc_connectivity_state_check(&glb_policy->state_tracker);
+      grpc_connectivity_state_check(&state_tracker_);
   /* The new connectivity status is a function of the previous one and the new
    * input coming from the status of the RR policy.
    *
@@ -624,7 +1795,7 @@
    *
    *  (*) This function mustn't be called during shutting down. */
   GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN);
-  switch (rr_state) {
+  switch (rr_connectivity_state_) {
     case GRPC_CHANNEL_TRANSIENT_FAILURE:
     case GRPC_CHANNEL_SHUTDOWN:
       GPR_ASSERT(rr_state_error != GRPC_ERROR_NONE);
@@ -638,1290 +1809,69 @@
     gpr_log(
         GPR_INFO,
         "[grpclb %p] Setting grpclb's state to %s from new RR policy %p state.",
-        glb_policy, grpc_connectivity_state_name(rr_state),
-        glb_policy->rr_policy);
+        this, grpc_connectivity_state_name(rr_connectivity_state_),
+        rr_policy_.get());
   }
-  grpc_connectivity_state_set(&glb_policy->state_tracker, rr_state,
+  grpc_connectivity_state_set(&state_tracker_, rr_connectivity_state_,
                               rr_state_error,
                               "update_lb_connectivity_status_locked");
 }
 
-/* Perform a pick over \a glb_policy->rr_policy. Given that a pick can return
- * immediately (ignoring its completion callback), we need to perform the
- * cleanups this callback would otherwise be responsible for.
- * If \a force_async is true, then we will manually schedule the
- * completion callback even if the pick is available immediately. */
-static bool pick_from_internal_rr_locked(glb_lb_policy* glb_policy,
-                                         bool force_async, pending_pick* pp) {
-  // Check for drops if we are not using fallback backend addresses.
-  if (glb_policy->serverlist != nullptr) {
-    // Look at the index into the serverlist to see if we should drop this call.
-    grpc_grpclb_server* server =
-        glb_policy->serverlist->servers[glb_policy->serverlist_index++];
-    if (glb_policy->serverlist_index == glb_policy->serverlist->num_servers) {
-      glb_policy->serverlist_index = 0;  // Wrap-around.
-    }
-    if (server->drop) {
-      // Update client load reporting stats to indicate the number of
-      // dropped calls.  Note that we have to do this here instead of in
-      // the client_load_reporting filter, because we do not create a
-      // subchannel call (and therefore no client_load_reporting filter)
-      // for dropped calls.
-      if (glb_policy->lb_calld != nullptr &&
-          glb_policy->lb_calld->client_stats != nullptr) {
-        grpc_grpclb_client_stats_add_call_dropped_locked(
-            server->load_balance_token, glb_policy->lb_calld->client_stats);
-      }
-      if (force_async) {
-        GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE);
-        gpr_free(pp);
-        return false;
-      }
-      gpr_free(pp);
-      return true;
-    }
-  }
-  // Set client_stats and user_data.
-  if (glb_policy->lb_calld != nullptr &&
-      glb_policy->lb_calld->client_stats != nullptr) {
-    pp->client_stats =
-        grpc_grpclb_client_stats_ref(glb_policy->lb_calld->client_stats);
-  }
-  GPR_ASSERT(pp->pick->user_data == nullptr);
-  pp->pick->user_data = reinterpret_cast<void**>(&pp->lb_token);
-  // Pick via the RR policy.
-  bool pick_done = grpc_lb_policy_pick_locked(glb_policy->rr_policy, pp->pick);
-  if (pick_done) {
-    pending_pick_set_metadata_and_context(pp);
-    if (force_async) {
-      GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE);
-      pick_done = false;
-    }
-    gpr_free(pp);
-  }
-  /* else, the pending pick will be registered and taken care of by the
-   * pending pick list inside the RR policy (glb_policy->rr_policy).
-   * Eventually, wrapped_on_complete will be called, which will -among other
-   * things- add the LB token to the call's initial metadata */
-  return pick_done;
-}
-
-static grpc_lb_policy_args* lb_policy_args_create(glb_lb_policy* glb_policy) {
-  grpc_lb_addresses* addresses;
-  if (glb_policy->serverlist != nullptr) {
-    GPR_ASSERT(glb_policy->serverlist->num_servers > 0);
-    addresses = process_serverlist_locked(glb_policy->serverlist);
-  } else {
-    // If rr_handover_locked() is invoked when we haven't received any
-    // serverlist from the balancer, we use the fallback backends returned by
-    // the resolver. Note that the fallback backend list may be empty, in which
-    // case the new round_robin policy will keep the requested picks pending.
-    GPR_ASSERT(glb_policy->fallback_backend_addresses != nullptr);
-    addresses = grpc_lb_addresses_copy(glb_policy->fallback_backend_addresses);
-  }
-  GPR_ASSERT(addresses != nullptr);
-  grpc_lb_policy_args* args =
-      static_cast<grpc_lb_policy_args*>(gpr_zalloc(sizeof(*args)));
-  args->client_channel_factory = glb_policy->cc_factory;
-  args->combiner = glb_policy->base.combiner;
-  // Replace the LB addresses in the channel args that we pass down to
-  // the subchannel.
-  static const char* keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES};
-  const grpc_arg arg = grpc_lb_addresses_create_channel_arg(addresses);
-  args->args = grpc_channel_args_copy_and_add_and_remove(
-      glb_policy->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg,
-      1);
-  grpc_lb_addresses_destroy(addresses);
-  return args;
-}
-
-static void lb_policy_args_destroy(grpc_lb_policy_args* args) {
-  grpc_channel_args_destroy(args->args);
-  gpr_free(args);
-}
-
-static void on_rr_connectivity_changed_locked(void* arg, grpc_error* error);
-static void create_rr_locked(glb_lb_policy* glb_policy,
-                             grpc_lb_policy_args* args) {
-  GPR_ASSERT(glb_policy->rr_policy == nullptr);
-
-  grpc_lb_policy* new_rr_policy = grpc_lb_policy_create("round_robin", args);
-  if (new_rr_policy == nullptr) {
-    gpr_log(GPR_ERROR,
-            "[grpclb %p] Failure creating a RoundRobin policy for serverlist "
-            "update with %" PRIuPTR
-            " entries. The previous RR instance (%p), if any, will continue to "
-            "be used. Future updates from the LB will attempt to create new "
-            "instances.",
-            glb_policy, glb_policy->serverlist->num_servers,
-            glb_policy->rr_policy);
+void GrpcLb::OnRoundRobinConnectivityChangedLocked(void* arg,
+                                                   grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  if (grpclb_policy->shutting_down_) {
+    grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_connectivity_changed");
     return;
   }
-  grpc_lb_policy_set_reresolve_closure_locked(
-      new_rr_policy, glb_policy->base.request_reresolution);
-  glb_policy->base.request_reresolution = nullptr;
-  glb_policy->rr_policy = new_rr_policy;
-  grpc_error* rr_state_error = nullptr;
-  glb_policy->rr_connectivity_state = grpc_lb_policy_check_connectivity_locked(
-      glb_policy->rr_policy, &rr_state_error);
-  /* Connectivity state is a function of the RR policy updated/created */
-  update_lb_connectivity_status_locked(
-      glb_policy, glb_policy->rr_connectivity_state, rr_state_error);
-  /* Add the gRPC LB's interested_parties pollset_set to that of the newly
-   * created RR policy. This will make the RR policy progress upon activity on
-   * gRPC LB, which in turn is tied to the application's call */
-  grpc_pollset_set_add_pollset_set(glb_policy->rr_policy->interested_parties,
-                                   glb_policy->base.interested_parties);
-  GRPC_CLOSURE_INIT(&glb_policy->on_rr_connectivity_changed,
-                    on_rr_connectivity_changed_locked, glb_policy,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  /* Subscribe to changes to the connectivity of the new RR */
-  GRPC_LB_POLICY_REF(&glb_policy->base, "glb_rr_connectivity_cb");
-  grpc_lb_policy_notify_on_state_change_locked(
-      glb_policy->rr_policy, &glb_policy->rr_connectivity_state,
-      &glb_policy->on_rr_connectivity_changed);
-  grpc_lb_policy_exit_idle_locked(glb_policy->rr_policy);
-  // Send pending picks to RR policy.
-  pending_pick* pp;
-  while ((pp = glb_policy->pending_picks)) {
-    glb_policy->pending_picks = pp->next;
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_INFO,
-              "[grpclb %p] Pending pick about to (async) PICK from RR %p",
-              glb_policy, glb_policy->rr_policy);
-    }
-    pick_from_internal_rr_locked(glb_policy, true /* force_async */, pp);
-  }
-  // Send pending pings to RR policy.
-  pending_ping* pping;
-  while ((pping = glb_policy->pending_pings)) {
-    glb_policy->pending_pings = pping->next;
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_INFO, "[grpclb %p] Pending ping about to PING from RR %p",
-              glb_policy, glb_policy->rr_policy);
-    }
-    grpc_lb_policy_ping_one_locked(glb_policy->rr_policy, pping->on_initiate,
-                                   pping->on_ack);
-    gpr_free(pping);
-  }
+  grpclb_policy->UpdateConnectivityStateFromRoundRobinPolicyLocked(
+      GRPC_ERROR_REF(error));
+  // Resubscribe. Reuse the "on_rr_connectivity_changed" ref.
+  grpclb_policy->rr_policy_->NotifyOnStateChangeLocked(
+      &grpclb_policy->rr_connectivity_state_,
+      &grpclb_policy->on_rr_connectivity_changed_);
 }
 
-/* glb_policy->rr_policy may be nullptr (initial handover) */
-static void rr_handover_locked(glb_lb_policy* glb_policy) {
-  if (glb_policy->shutting_down) return;
-  grpc_lb_policy_args* args = lb_policy_args_create(glb_policy);
-  GPR_ASSERT(args != nullptr);
-  if (glb_policy->rr_policy != nullptr) {
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_DEBUG, "[grpclb %p] Updating RR policy %p", glb_policy,
-              glb_policy->rr_policy);
-    }
-    grpc_lb_policy_update_locked(glb_policy->rr_policy, args);
-  } else {
-    create_rr_locked(glb_policy, args);
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_DEBUG, "[grpclb %p] Created new RR policy %p", glb_policy,
-              glb_policy->rr_policy);
-    }
-  }
-  lb_policy_args_destroy(args);
-}
-
-static void on_rr_connectivity_changed_locked(void* arg, grpc_error* error) {
-  glb_lb_policy* glb_policy = static_cast<glb_lb_policy*>(arg);
-  if (glb_policy->shutting_down) {
-    GRPC_LB_POLICY_UNREF(&glb_policy->base, "glb_rr_connectivity_cb");
-    return;
-  }
-  if (glb_policy->rr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) {
-    /* An RR policy that has transitioned into the SHUTDOWN connectivity state
-     * should not be considered for picks or updates: the SHUTDOWN state is a
-     * sink, policies can't transition back from it. .*/
-    GRPC_LB_POLICY_UNREF(glb_policy->rr_policy, "rr_connectivity_shutdown");
-    glb_policy->rr_policy = nullptr;
-    GRPC_LB_POLICY_UNREF(&glb_policy->base, "glb_rr_connectivity_cb");
-    return;
-  }
-  /* rr state != SHUTDOWN && !glb_policy->shutting down: biz as usual */
-  update_lb_connectivity_status_locked(
-      glb_policy, glb_policy->rr_connectivity_state, GRPC_ERROR_REF(error));
-  /* Resubscribe. Reuse the "glb_rr_connectivity_cb" ref. */
-  grpc_lb_policy_notify_on_state_change_locked(
-      glb_policy->rr_policy, &glb_policy->rr_connectivity_state,
-      &glb_policy->on_rr_connectivity_changed);
-}
-
-static void destroy_balancer_name(void* balancer_name) {
-  gpr_free(balancer_name);
-}
-
-static grpc_slice_hash_table_entry targets_info_entry_create(
-    const char* address, const char* balancer_name) {
-  grpc_slice_hash_table_entry entry;
-  entry.key = grpc_slice_from_copied_string(address);
-  entry.value = gpr_strdup(balancer_name);
-  return entry;
-}
-
-static int balancer_name_cmp_fn(void* a, void* b) {
-  const char* a_str = static_cast<const char*>(a);
-  const char* b_str = static_cast<const char*>(b);
-  return strcmp(a_str, b_str);
-}
-
-/* Returns the channel args for the LB channel, used to create a bidirectional
- * stream for the reception of load balancing updates.
- *
- * Inputs:
- *   - \a addresses: corresponding to the balancers.
- *   - \a response_generator: in order to propagate updates from the resolver
- *   above the grpclb policy.
- *   - \a args: other args inherited from the grpclb policy. */
-static grpc_channel_args* build_lb_channel_args(
-    const grpc_lb_addresses* addresses,
-    grpc_core::FakeResolverResponseGenerator* response_generator,
-    const grpc_channel_args* args) {
-  size_t num_grpclb_addrs = 0;
-  for (size_t i = 0; i < addresses->num_addresses; ++i) {
-    if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs;
-  }
-  /* All input addresses come from a resolver that claims they are LB services.
-   * It's the resolver's responsibility to make sure this policy is only
-   * instantiated and used in that case. Otherwise, something has gone wrong. */
-  GPR_ASSERT(num_grpclb_addrs > 0);
-  grpc_lb_addresses* lb_addresses =
-      grpc_lb_addresses_create(num_grpclb_addrs, nullptr);
-  grpc_slice_hash_table_entry* targets_info_entries =
-      static_cast<grpc_slice_hash_table_entry*>(
-          gpr_zalloc(sizeof(*targets_info_entries) * num_grpclb_addrs));
-
-  size_t lb_addresses_idx = 0;
-  for (size_t i = 0; i < addresses->num_addresses; ++i) {
-    if (!addresses->addresses[i].is_balancer) continue;
-    if (addresses->addresses[i].user_data != nullptr) {
-      gpr_log(GPR_ERROR,
-              "This LB policy doesn't support user data. It will be ignored");
-    }
-    char* addr_str;
-    GPR_ASSERT(grpc_sockaddr_to_string(
-                   &addr_str, &addresses->addresses[i].address, true) > 0);
-    targets_info_entries[lb_addresses_idx] = targets_info_entry_create(
-        addr_str, addresses->addresses[i].balancer_name);
-    gpr_free(addr_str);
-
-    grpc_lb_addresses_set_address(
-        lb_addresses, lb_addresses_idx++, addresses->addresses[i].address.addr,
-        addresses->addresses[i].address.len, false /* is balancer */,
-        addresses->addresses[i].balancer_name, nullptr /* user data */);
-  }
-  GPR_ASSERT(num_grpclb_addrs == lb_addresses_idx);
-  grpc_slice_hash_table* targets_info =
-      grpc_slice_hash_table_create(num_grpclb_addrs, targets_info_entries,
-                                   destroy_balancer_name, balancer_name_cmp_fn);
-  gpr_free(targets_info_entries);
-
-  grpc_channel_args* lb_channel_args =
-      grpc_lb_policy_grpclb_build_lb_channel_args(targets_info,
-                                                  response_generator, args);
-
-  grpc_arg lb_channel_addresses_arg =
-      grpc_lb_addresses_create_channel_arg(lb_addresses);
-
-  grpc_channel_args* result = grpc_channel_args_copy_and_add(
-      lb_channel_args, &lb_channel_addresses_arg, 1);
-  grpc_slice_hash_table_unref(targets_info);
-  grpc_channel_args_destroy(lb_channel_args);
-  grpc_lb_addresses_destroy(lb_addresses);
-  return result;
-}
-
-static void glb_destroy(grpc_lb_policy* pol) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  GPR_ASSERT(glb_policy->pending_picks == nullptr);
-  GPR_ASSERT(glb_policy->pending_pings == nullptr);
-  gpr_free((void*)glb_policy->server_name);
-  grpc_channel_args_destroy(glb_policy->args);
-  grpc_connectivity_state_destroy(&glb_policy->state_tracker);
-  if (glb_policy->serverlist != nullptr) {
-    grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
-  }
-  if (glb_policy->fallback_backend_addresses != nullptr) {
-    grpc_lb_addresses_destroy(glb_policy->fallback_backend_addresses);
-  }
-  // TODO(roth): Remove this once the LB policy becomes a C++ object.
-  glb_policy->response_generator.reset();
-  grpc_subchannel_index_unref();
-  gpr_free(glb_policy);
-}
-
-static void glb_shutdown_locked(grpc_lb_policy* pol,
-                                grpc_lb_policy* new_policy) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
-  glb_policy->shutting_down = true;
-  if (glb_policy->lb_calld != nullptr) {
-    lb_call_data_shutdown(glb_policy);
-  }
-  if (glb_policy->retry_timer_callback_pending) {
-    grpc_timer_cancel(&glb_policy->lb_call_retry_timer);
-  }
-  if (glb_policy->fallback_timer_callback_pending) {
-    grpc_timer_cancel(&glb_policy->lb_fallback_timer);
-  }
-  if (glb_policy->rr_policy != nullptr) {
-    grpc_lb_policy_shutdown_locked(glb_policy->rr_policy, nullptr);
-    GRPC_LB_POLICY_UNREF(glb_policy->rr_policy, "glb_shutdown");
-  } else {
-    grpc_lb_policy_try_reresolve(pol, &grpc_lb_glb_trace, GRPC_ERROR_CANCELLED);
-  }
-  // We destroy the LB channel here because
-  // glb_lb_channel_on_connectivity_changed_cb needs a valid glb_policy
-  // instance.  Destroying the lb channel in glb_destroy would likely result in
-  // a callback invocation without a valid glb_policy arg.
-  if (glb_policy->lb_channel != nullptr) {
-    grpc_channel_destroy(glb_policy->lb_channel);
-    glb_policy->lb_channel = nullptr;
-  }
-  grpc_connectivity_state_set(&glb_policy->state_tracker, GRPC_CHANNEL_SHUTDOWN,
-                              GRPC_ERROR_REF(error), "glb_shutdown");
-  // Clear pending picks.
-  pending_pick* pp = glb_policy->pending_picks;
-  glb_policy->pending_picks = nullptr;
-  while (pp != nullptr) {
-    pending_pick* next = pp->next;
-    if (new_policy != nullptr) {
-      // Hand pick over to new policy.
-      if (pp->client_stats != nullptr) {
-        grpc_grpclb_client_stats_unref(pp->client_stats);
-      }
-      pp->pick->on_complete = pp->original_on_complete;
-      if (grpc_lb_policy_pick_locked(new_policy, pp->pick)) {
-        // Synchronous return; schedule callback.
-        GRPC_CLOSURE_SCHED(pp->pick->on_complete, GRPC_ERROR_NONE);
-      }
-      gpr_free(pp);
-    } else {
-      pp->pick->connected_subchannel.reset();
-      GRPC_CLOSURE_SCHED(&pp->on_complete, GRPC_ERROR_REF(error));
-    }
-    pp = next;
-  }
-  // Clear pending pings.
-  pending_ping* pping = glb_policy->pending_pings;
-  glb_policy->pending_pings = nullptr;
-  while (pping != nullptr) {
-    pending_ping* next = pping->next;
-    GRPC_CLOSURE_SCHED(pping->on_initiate, GRPC_ERROR_REF(error));
-    GRPC_CLOSURE_SCHED(pping->on_ack, GRPC_ERROR_REF(error));
-    gpr_free(pping);
-    pping = next;
-  }
-  GRPC_ERROR_UNREF(error);
-}
-
-// Cancel a specific pending pick.
 //
-// A grpclb pick progresses as follows:
-// - If there's a Round Robin policy (glb_policy->rr_policy) available, it'll be
-//   handed over to the RR policy (in create_rr_locked()). From that point
-//   onwards, it'll be RR's responsibility. For cancellations, that implies the
-//   pick needs also be cancelled by the RR instance.
-// - Otherwise, without an RR instance, picks stay pending at this policy's
-//   level (grpclb), inside the glb_policy->pending_picks list. To cancel these,
-//   we invoke the completion closure and set *target to nullptr right here.
-static void glb_cancel_pick_locked(grpc_lb_policy* pol,
-                                   grpc_lb_policy_pick_state* pick,
-                                   grpc_error* error) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  pending_pick* pp = glb_policy->pending_picks;
-  glb_policy->pending_picks = nullptr;
-  while (pp != nullptr) {
-    pending_pick* next = pp->next;
-    if (pp->pick == pick) {
-      pick->connected_subchannel.reset();
-      GRPC_CLOSURE_SCHED(&pp->on_complete,
-                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                             "Pick Cancelled", &error, 1));
-    } else {
-      pp->next = glb_policy->pending_picks;
-      glb_policy->pending_picks = pp;
-    }
-    pp = next;
-  }
-  if (glb_policy->rr_policy != nullptr) {
-    grpc_lb_policy_cancel_pick_locked(glb_policy->rr_policy, pick,
-                                      GRPC_ERROR_REF(error));
-  }
-  GRPC_ERROR_UNREF(error);
-}
-
-// Cancel all pending picks.
+// factory
 //
-// A grpclb pick progresses as follows:
-// - If there's a Round Robin policy (glb_policy->rr_policy) available, it'll be
-//   handed over to the RR policy (in create_rr_locked()). From that point
-//   onwards, it'll be RR's responsibility. For cancellations, that implies the
-//   pick needs also be cancelled by the RR instance.
-// - Otherwise, without an RR instance, picks stay pending at this policy's
-//   level (grpclb), inside the glb_policy->pending_picks list. To cancel these,
-//   we invoke the completion closure and set *target to nullptr right here.
-static void glb_cancel_picks_locked(grpc_lb_policy* pol,
-                                    uint32_t initial_metadata_flags_mask,
-                                    uint32_t initial_metadata_flags_eq,
-                                    grpc_error* error) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  pending_pick* pp = glb_policy->pending_picks;
-  glb_policy->pending_picks = nullptr;
-  while (pp != nullptr) {
-    pending_pick* next = pp->next;
-    if ((pp->pick->initial_metadata_flags & initial_metadata_flags_mask) ==
-        initial_metadata_flags_eq) {
-      GRPC_CLOSURE_SCHED(&pp->on_complete,
-                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                             "Pick Cancelled", &error, 1));
-    } else {
-      pp->next = glb_policy->pending_picks;
-      glb_policy->pending_picks = pp;
+
+class GrpcLbFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    /* Count the number of gRPC-LB addresses. There must be at least one. */
+    const grpc_arg* arg =
+        grpc_channel_args_find(args.args, GRPC_ARG_LB_ADDRESSES);
+    if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
+      return nullptr;
     }
-    pp = next;
-  }
-  if (glb_policy->rr_policy != nullptr) {
-    grpc_lb_policy_cancel_picks_locked(
-        glb_policy->rr_policy, initial_metadata_flags_mask,
-        initial_metadata_flags_eq, GRPC_ERROR_REF(error));
-  }
-  GRPC_ERROR_UNREF(error);
-}
-
-static void lb_on_fallback_timer_locked(void* arg, grpc_error* error);
-static void query_for_backends_locked(glb_lb_policy* glb_policy);
-static void start_picking_locked(glb_lb_policy* glb_policy) {
-  /* start a timer to fall back */
-  if (glb_policy->lb_fallback_timeout_ms > 0 &&
-      glb_policy->serverlist == nullptr &&
-      !glb_policy->fallback_timer_callback_pending) {
-    grpc_millis deadline =
-        grpc_core::ExecCtx::Get()->Now() + glb_policy->lb_fallback_timeout_ms;
-    GRPC_LB_POLICY_REF(&glb_policy->base, "grpclb_fallback_timer");
-    GRPC_CLOSURE_INIT(&glb_policy->lb_on_fallback, lb_on_fallback_timer_locked,
-                      glb_policy,
-                      grpc_combiner_scheduler(glb_policy->base.combiner));
-    glb_policy->fallback_timer_callback_pending = true;
-    grpc_timer_init(&glb_policy->lb_fallback_timer, deadline,
-                    &glb_policy->lb_on_fallback);
-  }
-  glb_policy->started_picking = true;
-  glb_policy->lb_call_backoff->Reset();
-  query_for_backends_locked(glb_policy);
-}
-
-static void glb_exit_idle_locked(grpc_lb_policy* pol) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  if (!glb_policy->started_picking) {
-    start_picking_locked(glb_policy);
-  }
-}
-
-static int glb_pick_locked(grpc_lb_policy* pol,
-                           grpc_lb_policy_pick_state* pick) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  pending_pick* pp = pending_pick_create(glb_policy, pick);
-  bool pick_done = false;
-  if (glb_policy->rr_policy != nullptr) {
-    const grpc_connectivity_state rr_connectivity_state =
-        grpc_lb_policy_check_connectivity_locked(glb_policy->rr_policy,
-                                                 nullptr);
-    // The glb_policy->rr_policy may have transitioned to SHUTDOWN but the
-    // callback registered to capture this event
-    // (on_rr_connectivity_changed_locked) may not have been invoked yet. We
-    // need to make sure we aren't trying to pick from a RR policy instance
-    // that's in shutdown.
-    if (rr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) {
-      if (grpc_lb_glb_trace.enabled()) {
-        gpr_log(GPR_INFO,
-                "[grpclb %p] NOT picking from from RR %p: RR conn state=%s",
-                glb_policy, glb_policy->rr_policy,
-                grpc_connectivity_state_name(rr_connectivity_state));
-      }
-      pending_pick_add(&glb_policy->pending_picks, pp);
-      pick_done = false;
-    } else {  // RR not in shutdown
-      if (grpc_lb_glb_trace.enabled()) {
-        gpr_log(GPR_INFO, "[grpclb %p] about to PICK from RR %p", glb_policy,
-                glb_policy->rr_policy);
-      }
-      pick_done =
-          pick_from_internal_rr_locked(glb_policy, false /* force_async */, pp);
+    grpc_lb_addresses* addresses =
+        static_cast<grpc_lb_addresses*>(arg->value.pointer.p);
+    size_t num_grpclb_addrs = 0;
+    for (size_t i = 0; i < addresses->num_addresses; ++i) {
+      if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs;
     }
-  } else {  // glb_policy->rr_policy == NULL
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_DEBUG,
-              "[grpclb %p] No RR policy. Adding to grpclb's pending picks",
-              glb_policy);
-    }
-    pending_pick_add(&glb_policy->pending_picks, pp);
-    if (!glb_policy->started_picking) {
-      start_picking_locked(glb_policy);
-    }
-    pick_done = false;
+    if (num_grpclb_addrs == 0) return nullptr;
+    return OrphanablePtr<LoadBalancingPolicy>(New<GrpcLb>(addresses, args));
   }
-  return pick_done;
-}
 
-static grpc_connectivity_state glb_check_connectivity_locked(
-    grpc_lb_policy* pol, grpc_error** connectivity_error) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  return grpc_connectivity_state_get(&glb_policy->state_tracker,
-                                     connectivity_error);
-}
+  const char* name() const override { return "grpclb"; }
+};
 
-static void glb_ping_one_locked(grpc_lb_policy* pol, grpc_closure* on_initiate,
-                                grpc_closure* on_ack) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  if (glb_policy->rr_policy) {
-    grpc_lb_policy_ping_one_locked(glb_policy->rr_policy, on_initiate, on_ack);
-  } else {
-    pending_ping_add(&glb_policy->pending_pings, on_initiate, on_ack);
-    if (!glb_policy->started_picking) {
-      start_picking_locked(glb_policy);
-    }
-  }
-}
+}  // namespace
 
-static void glb_notify_on_state_change_locked(grpc_lb_policy* pol,
-                                              grpc_connectivity_state* current,
-                                              grpc_closure* notify) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(pol);
-  grpc_connectivity_state_notify_on_state_change(&glb_policy->state_tracker,
-                                                 current, notify);
-}
+}  // namespace grpc_core
 
-static void lb_call_on_retry_timer_locked(void* arg, grpc_error* error) {
-  glb_lb_policy* glb_policy = static_cast<glb_lb_policy*>(arg);
-  glb_policy->retry_timer_callback_pending = false;
-  if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE &&
-      glb_policy->lb_calld == nullptr) {
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server", glb_policy);
-    }
-    query_for_backends_locked(glb_policy);
-  }
-  GRPC_LB_POLICY_UNREF(&glb_policy->base, "grpclb_retry_timer");
-}
+//
+// Plugin registration
+//
 
-static void start_lb_call_retry_timer_locked(glb_lb_policy* glb_policy) {
-  grpc_millis next_try = glb_policy->lb_call_backoff->NextAttemptTime();
-  if (grpc_lb_glb_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "[grpclb %p] Connection to LB server lost...",
-            glb_policy);
-    grpc_millis timeout = next_try - grpc_core::ExecCtx::Get()->Now();
-    if (timeout > 0) {
-      gpr_log(GPR_DEBUG,
-              "[grpclb %p] ... retry_timer_active in %" PRIuPTR "ms.",
-              glb_policy, timeout);
-    } else {
-      gpr_log(GPR_DEBUG, "[grpclb %p] ... retry_timer_active immediately.",
-              glb_policy);
-    }
-  }
-  GRPC_LB_POLICY_REF(&glb_policy->base, "grpclb_retry_timer");
-  GRPC_CLOSURE_INIT(&glb_policy->lb_on_call_retry,
-                    lb_call_on_retry_timer_locked, glb_policy,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  glb_policy->retry_timer_callback_pending = true;
-  grpc_timer_init(&glb_policy->lb_call_retry_timer, next_try,
-                  &glb_policy->lb_on_call_retry);
-}
-
-static void maybe_send_client_load_report_locked(void* arg, grpc_error* error);
-
-static void schedule_next_client_load_report(glb_lb_call_data* lb_calld) {
-  const grpc_millis next_client_load_report_time =
-      grpc_core::ExecCtx::Get()->Now() + lb_calld->client_stats_report_interval;
-  GRPC_CLOSURE_INIT(
-      &lb_calld->client_load_report_closure,
-      maybe_send_client_load_report_locked, lb_calld,
-      grpc_combiner_scheduler(lb_calld->glb_policy->base.combiner));
-  grpc_timer_init(&lb_calld->client_load_report_timer,
-                  next_client_load_report_time,
-                  &lb_calld->client_load_report_closure);
-  lb_calld->client_load_report_timer_callback_pending = true;
-}
-
-static void client_load_report_done_locked(void* arg, grpc_error* error) {
-  glb_lb_call_data* lb_calld = static_cast<glb_lb_call_data*>(arg);
-  glb_lb_policy* glb_policy = lb_calld->glb_policy;
-  grpc_byte_buffer_destroy(lb_calld->send_message_payload);
-  lb_calld->send_message_payload = nullptr;
-  if (error != GRPC_ERROR_NONE || lb_calld != glb_policy->lb_calld) {
-    glb_lb_call_data_unref(lb_calld, "client_load_report");
-    return;
-  }
-  schedule_next_client_load_report(lb_calld);
-}
-
-static bool load_report_counters_are_zero(grpc_grpclb_request* request) {
-  grpc_grpclb_dropped_call_counts* drop_entries =
-      static_cast<grpc_grpclb_dropped_call_counts*>(
-          request->client_stats.calls_finished_with_drop.arg);
-  return request->client_stats.num_calls_started == 0 &&
-         request->client_stats.num_calls_finished == 0 &&
-         request->client_stats.num_calls_finished_with_client_failed_to_send ==
-             0 &&
-         request->client_stats.num_calls_finished_known_received == 0 &&
-         (drop_entries == nullptr || drop_entries->num_entries == 0);
-}
-
-static void send_client_load_report_locked(glb_lb_call_data* lb_calld) {
-  glb_lb_policy* glb_policy = lb_calld->glb_policy;
-  // Construct message payload.
-  GPR_ASSERT(lb_calld->send_message_payload == nullptr);
-  grpc_grpclb_request* request =
-      grpc_grpclb_load_report_request_create_locked(lb_calld->client_stats);
-  // Skip client load report if the counters were all zero in the last
-  // report and they are still zero in this one.
-  if (load_report_counters_are_zero(request)) {
-    if (lb_calld->last_client_load_report_counters_were_zero) {
-      grpc_grpclb_request_destroy(request);
-      schedule_next_client_load_report(lb_calld);
-      return;
-    }
-    lb_calld->last_client_load_report_counters_were_zero = true;
-  } else {
-    lb_calld->last_client_load_report_counters_were_zero = false;
-  }
-  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
-  lb_calld->send_message_payload =
-      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
-  grpc_slice_unref_internal(request_payload_slice);
-  grpc_grpclb_request_destroy(request);
-  // Send the report.
-  grpc_op op;
-  memset(&op, 0, sizeof(op));
-  op.op = GRPC_OP_SEND_MESSAGE;
-  op.data.send_message.send_message = lb_calld->send_message_payload;
-  GRPC_CLOSURE_INIT(&lb_calld->client_load_report_closure,
-                    client_load_report_done_locked, lb_calld,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  grpc_call_error call_error = grpc_call_start_batch_and_execute(
-      lb_calld->lb_call, &op, 1, &lb_calld->client_load_report_closure);
-  if (call_error != GRPC_CALL_OK) {
-    gpr_log(GPR_ERROR, "[grpclb %p] call_error=%d", glb_policy, call_error);
-    GPR_ASSERT(GRPC_CALL_OK == call_error);
-  }
-}
-
-static void maybe_send_client_load_report_locked(void* arg, grpc_error* error) {
-  glb_lb_call_data* lb_calld = static_cast<glb_lb_call_data*>(arg);
-  glb_lb_policy* glb_policy = lb_calld->glb_policy;
-  lb_calld->client_load_report_timer_callback_pending = false;
-  if (error != GRPC_ERROR_NONE || lb_calld != glb_policy->lb_calld) {
-    glb_lb_call_data_unref(lb_calld, "client_load_report");
-    return;
-  }
-  // If we've already sent the initial request, then we can go ahead and send
-  // the load report. Otherwise, we need to wait until the initial request has
-  // been sent to send this (see lb_on_sent_initial_request_locked()).
-  if (lb_calld->send_message_payload == nullptr) {
-    send_client_load_report_locked(lb_calld);
-  } else {
-    lb_calld->client_load_report_is_due = true;
-  }
-}
-
-static void lb_on_sent_initial_request_locked(void* arg, grpc_error* error);
-static void lb_on_server_status_received_locked(void* arg, grpc_error* error);
-static void lb_on_response_received_locked(void* arg, grpc_error* error);
-static glb_lb_call_data* lb_call_data_create_locked(glb_lb_policy* glb_policy) {
-  GPR_ASSERT(!glb_policy->shutting_down);
-  // Init the LB call. Note that the LB call will progress every time there's
-  // activity in glb_policy->base.interested_parties, which is comprised of the
-  // polling entities from client_channel.
-  GPR_ASSERT(glb_policy->server_name != nullptr);
-  GPR_ASSERT(glb_policy->server_name[0] != '\0');
-  grpc_slice host = grpc_slice_from_copied_string(glb_policy->server_name);
-  grpc_millis deadline =
-      glb_policy->lb_call_timeout_ms == 0
-          ? GRPC_MILLIS_INF_FUTURE
-          : grpc_core::ExecCtx::Get()->Now() + glb_policy->lb_call_timeout_ms;
-  glb_lb_call_data* lb_calld =
-      static_cast<glb_lb_call_data*>(gpr_zalloc(sizeof(*lb_calld)));
-  lb_calld->lb_call = grpc_channel_create_pollset_set_call(
-      glb_policy->lb_channel, nullptr, GRPC_PROPAGATE_DEFAULTS,
-      glb_policy->base.interested_parties,
-      GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD,
-      &host, deadline, nullptr);
-  grpc_slice_unref_internal(host);
-  // Init the LB call request payload.
-  grpc_grpclb_request* request =
-      grpc_grpclb_request_create(glb_policy->server_name);
-  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
-  lb_calld->send_message_payload =
-      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
-  grpc_slice_unref_internal(request_payload_slice);
-  grpc_grpclb_request_destroy(request);
-  // Init other data associated with the LB call.
-  lb_calld->glb_policy = glb_policy;
-  gpr_ref_init(&lb_calld->refs, 1);
-  grpc_metadata_array_init(&lb_calld->lb_initial_metadata_recv);
-  grpc_metadata_array_init(&lb_calld->lb_trailing_metadata_recv);
-  GRPC_CLOSURE_INIT(&lb_calld->lb_on_sent_initial_request,
-                    lb_on_sent_initial_request_locked, lb_calld,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  GRPC_CLOSURE_INIT(&lb_calld->lb_on_response_received,
-                    lb_on_response_received_locked, lb_calld,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  GRPC_CLOSURE_INIT(&lb_calld->lb_on_server_status_received,
-                    lb_on_server_status_received_locked, lb_calld,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  // Hold a ref to the glb_policy.
-  GRPC_LB_POLICY_REF(&glb_policy->base, "lb_calld");
-  return lb_calld;
-}
-
-/*
- * Auxiliary functions and LB client callbacks.
- */
-
-static void query_for_backends_locked(glb_lb_policy* glb_policy) {
-  GPR_ASSERT(glb_policy->lb_channel != nullptr);
-  if (glb_policy->shutting_down) return;
-  // Init the LB call data.
-  GPR_ASSERT(glb_policy->lb_calld == nullptr);
-  glb_policy->lb_calld = lb_call_data_create_locked(glb_policy);
-  if (grpc_lb_glb_trace.enabled()) {
-    gpr_log(GPR_INFO,
-            "[grpclb %p] Query for backends (lb_channel: %p, lb_calld: %p, "
-            "lb_call: %p)",
-            glb_policy, glb_policy->lb_channel, glb_policy->lb_calld,
-            glb_policy->lb_calld->lb_call);
-  }
-  GPR_ASSERT(glb_policy->lb_calld->lb_call != nullptr);
-  // Create the ops.
-  grpc_call_error call_error;
-  grpc_op ops[3];
-  memset(ops, 0, sizeof(ops));
-  // Op: send initial metadata.
-  grpc_op* op = ops;
-  op->op = GRPC_OP_SEND_INITIAL_METADATA;
-  op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  // Op: send request message.
-  GPR_ASSERT(glb_policy->lb_calld->send_message_payload != nullptr);
-  op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message.send_message =
-      glb_policy->lb_calld->send_message_payload;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  glb_lb_call_data_ref(glb_policy->lb_calld,
-                       "lb_on_sent_initial_request_locked");
-  call_error = grpc_call_start_batch_and_execute(
-      glb_policy->lb_calld->lb_call, ops, static_cast<size_t>(op - ops),
-      &glb_policy->lb_calld->lb_on_sent_initial_request);
-  GPR_ASSERT(GRPC_CALL_OK == call_error);
-  // Op: recv initial metadata.
-  op = ops;
-  op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata.recv_initial_metadata =
-      &glb_policy->lb_calld->lb_initial_metadata_recv;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  // Op: recv response.
-  op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message.recv_message =
-      &glb_policy->lb_calld->recv_message_payload;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  glb_lb_call_data_ref(glb_policy->lb_calld, "lb_on_response_received_locked");
-  call_error = grpc_call_start_batch_and_execute(
-      glb_policy->lb_calld->lb_call, ops, static_cast<size_t>(op - ops),
-      &glb_policy->lb_calld->lb_on_response_received);
-  GPR_ASSERT(GRPC_CALL_OK == call_error);
-  // Op: recv server status.
-  op = ops;
-  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
-  op->data.recv_status_on_client.trailing_metadata =
-      &glb_policy->lb_calld->lb_trailing_metadata_recv;
-  op->data.recv_status_on_client.status = &glb_policy->lb_calld->lb_call_status;
-  op->data.recv_status_on_client.status_details =
-      &glb_policy->lb_calld->lb_call_status_details;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  // This callback signals the end of the LB call, so it relies on the initial
-  // ref instead of a new ref. When it's invoked, it's the initial ref that is
-  // unreffed.
-  call_error = grpc_call_start_batch_and_execute(
-      glb_policy->lb_calld->lb_call, ops, static_cast<size_t>(op - ops),
-      &glb_policy->lb_calld->lb_on_server_status_received);
-  GPR_ASSERT(GRPC_CALL_OK == call_error);
-}
-
-static void lb_on_sent_initial_request_locked(void* arg, grpc_error* error) {
-  glb_lb_call_data* lb_calld = static_cast<glb_lb_call_data*>(arg);
-  grpc_byte_buffer_destroy(lb_calld->send_message_payload);
-  lb_calld->send_message_payload = nullptr;
-  // If we attempted to send a client load report before the initial request was
-  // sent (and this lb_calld is still in use), send the load report now.
-  if (lb_calld->client_load_report_is_due &&
-      lb_calld == lb_calld->glb_policy->lb_calld) {
-    send_client_load_report_locked(lb_calld);
-    lb_calld->client_load_report_is_due = false;
-  }
-  glb_lb_call_data_unref(lb_calld, "lb_on_sent_initial_request_locked");
-}
-
-static void lb_on_response_received_locked(void* arg, grpc_error* error) {
-  glb_lb_call_data* lb_calld = static_cast<glb_lb_call_data*>(arg);
-  glb_lb_policy* glb_policy = lb_calld->glb_policy;
-  // Empty payload means the LB call was cancelled.
-  if (lb_calld != glb_policy->lb_calld ||
-      lb_calld->recv_message_payload == nullptr) {
-    glb_lb_call_data_unref(lb_calld, "lb_on_response_received_locked");
-    return;
-  }
-  grpc_op ops[2];
-  memset(ops, 0, sizeof(ops));
-  grpc_op* op = ops;
-  glb_policy->lb_call_backoff->Reset();
-  grpc_byte_buffer_reader bbr;
-  grpc_byte_buffer_reader_init(&bbr, lb_calld->recv_message_payload);
-  grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
-  grpc_byte_buffer_reader_destroy(&bbr);
-  grpc_byte_buffer_destroy(lb_calld->recv_message_payload);
-  lb_calld->recv_message_payload = nullptr;
-  grpc_grpclb_initial_response* initial_response;
-  grpc_grpclb_serverlist* serverlist;
-  if (!lb_calld->seen_initial_response &&
-      (initial_response = grpc_grpclb_initial_response_parse(response_slice)) !=
-          nullptr) {
-    // Have NOT seen initial response, look for initial response.
-    if (initial_response->has_client_stats_report_interval) {
-      lb_calld->client_stats_report_interval = GPR_MAX(
-          GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
-                              &initial_response->client_stats_report_interval));
-      if (grpc_lb_glb_trace.enabled()) {
-        gpr_log(GPR_INFO,
-                "[grpclb %p] Received initial LB response message; "
-                "client load reporting interval = %" PRIdPTR " milliseconds",
-                glb_policy, lb_calld->client_stats_report_interval);
-      }
-    } else if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_INFO,
-              "[grpclb %p] Received initial LB response message; client load "
-              "reporting NOT enabled",
-              glb_policy);
-    }
-    grpc_grpclb_initial_response_destroy(initial_response);
-    lb_calld->seen_initial_response = true;
-  } else if ((serverlist = grpc_grpclb_response_parse_serverlist(
-                  response_slice)) != nullptr) {
-    // Have seen initial response, look for serverlist.
-    GPR_ASSERT(lb_calld->lb_call != nullptr);
-    if (grpc_lb_glb_trace.enabled()) {
-      gpr_log(GPR_INFO,
-              "[grpclb %p] Serverlist with %" PRIuPTR " servers received",
-              glb_policy, serverlist->num_servers);
-      for (size_t i = 0; i < serverlist->num_servers; ++i) {
-        grpc_resolved_address addr;
-        parse_server(serverlist->servers[i], &addr);
-        char* ipport;
-        grpc_sockaddr_to_string(&ipport, &addr, false);
-        gpr_log(GPR_INFO, "[grpclb %p] Serverlist[%" PRIuPTR "]: %s",
-                glb_policy, i, ipport);
-        gpr_free(ipport);
-      }
-    }
-    /* update serverlist */
-    if (serverlist->num_servers > 0) {
-      // Start sending client load report only after we start using the
-      // serverlist returned from the current LB call.
-      if (lb_calld->client_stats_report_interval > 0 &&
-          lb_calld->client_stats == nullptr) {
-        lb_calld->client_stats = grpc_grpclb_client_stats_create();
-        glb_lb_call_data_ref(lb_calld, "client_load_report");
-        schedule_next_client_load_report(lb_calld);
-      }
-      if (grpc_grpclb_serverlist_equals(glb_policy->serverlist, serverlist)) {
-        if (grpc_lb_glb_trace.enabled()) {
-          gpr_log(GPR_INFO,
-                  "[grpclb %p] Incoming server list identical to current, "
-                  "ignoring.",
-                  glb_policy);
-        }
-        grpc_grpclb_destroy_serverlist(serverlist);
-      } else { /* new serverlist */
-        if (glb_policy->serverlist != nullptr) {
-          /* dispose of the old serverlist */
-          grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
-        } else {
-          /* or dispose of the fallback */
-          grpc_lb_addresses_destroy(glb_policy->fallback_backend_addresses);
-          glb_policy->fallback_backend_addresses = nullptr;
-          if (glb_policy->fallback_timer_callback_pending) {
-            grpc_timer_cancel(&glb_policy->lb_fallback_timer);
-            glb_policy->fallback_timer_callback_pending = false;
-          }
-        }
-        /* and update the copy in the glb_lb_policy instance. This
-         * serverlist instance will be destroyed either upon the next
-         * update or in glb_destroy() */
-        glb_policy->serverlist = serverlist;
-        glb_policy->serverlist_index = 0;
-        rr_handover_locked(glb_policy);
-      }
-    } else {
-      if (grpc_lb_glb_trace.enabled()) {
-        gpr_log(GPR_INFO, "[grpclb %p] Received empty server list, ignoring.",
-                glb_policy);
-      }
-      grpc_grpclb_destroy_serverlist(serverlist);
-    }
-  } else {
-    // No valid initial response or serverlist found.
-    gpr_log(GPR_ERROR,
-            "[grpclb %p] Invalid LB response received: '%s'. Ignoring.",
-            glb_policy,
-            grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
-  }
-  grpc_slice_unref_internal(response_slice);
-  if (!glb_policy->shutting_down) {
-    // Keep listening for serverlist updates.
-    op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message.recv_message = &lb_calld->recv_message_payload;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    // Reuse the "lb_on_response_received_locked" ref taken in
-    // query_for_backends_locked().
-    const grpc_call_error call_error = grpc_call_start_batch_and_execute(
-        lb_calld->lb_call, ops, static_cast<size_t>(op - ops),
-        &lb_calld->lb_on_response_received);
-    GPR_ASSERT(GRPC_CALL_OK == call_error);
-  } else {
-    glb_lb_call_data_unref(lb_calld,
-                           "lb_on_response_received_locked+glb_shutdown");
-  }
-}
-
-static void lb_on_server_status_received_locked(void* arg, grpc_error* error) {
-  glb_lb_call_data* lb_calld = static_cast<glb_lb_call_data*>(arg);
-  glb_lb_policy* glb_policy = lb_calld->glb_policy;
-  GPR_ASSERT(lb_calld->lb_call != nullptr);
-  if (grpc_lb_glb_trace.enabled()) {
-    char* status_details =
-        grpc_slice_to_c_string(lb_calld->lb_call_status_details);
-    gpr_log(GPR_INFO,
-            "[grpclb %p] Status from LB server received. Status = %d, details "
-            "= '%s', (lb_calld: %p, lb_call: %p), error '%s'",
-            lb_calld->glb_policy, lb_calld->lb_call_status, status_details,
-            lb_calld, lb_calld->lb_call, grpc_error_string(error));
-    gpr_free(status_details);
-  }
-  // If this lb_calld is still in use, this call ended because of a failure so
-  // we want to retry connecting. Otherwise, we have deliberately ended this
-  // call and no further action is required.
-  if (lb_calld == glb_policy->lb_calld) {
-    glb_policy->lb_calld = nullptr;
-    if (lb_calld->client_load_report_timer_callback_pending) {
-      grpc_timer_cancel(&lb_calld->client_load_report_timer);
-    }
-    GPR_ASSERT(!glb_policy->shutting_down);
-    if (lb_calld->seen_initial_response) {
-      // If we lose connection to the LB server, reset the backoff and restart
-      // the LB call immediately.
-      glb_policy->lb_call_backoff->Reset();
-      query_for_backends_locked(glb_policy);
-    } else {
-      // If this LB call fails establishing any connection to the LB server,
-      // retry later.
-      start_lb_call_retry_timer_locked(glb_policy);
-    }
-  }
-  glb_lb_call_data_unref(lb_calld, "lb_call_ended");
-}
-
-static void lb_on_fallback_timer_locked(void* arg, grpc_error* error) {
-  glb_lb_policy* glb_policy = static_cast<glb_lb_policy*>(arg);
-  glb_policy->fallback_timer_callback_pending = false;
-  /* If we receive a serverlist after the timer fires but before this callback
-   * actually runs, don't fall back. */
-  if (glb_policy->serverlist == nullptr) {
-    if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) {
-      if (grpc_lb_glb_trace.enabled()) {
-        gpr_log(GPR_INFO,
-                "[grpclb %p] Falling back to use backends from resolver",
-                glb_policy);
-      }
-      GPR_ASSERT(glb_policy->fallback_backend_addresses != nullptr);
-      rr_handover_locked(glb_policy);
-    }
-  }
-  GRPC_LB_POLICY_UNREF(&glb_policy->base, "grpclb_fallback_timer");
-}
-
-static void fallback_update_locked(glb_lb_policy* glb_policy,
-                                   const grpc_lb_addresses* addresses) {
-  GPR_ASSERT(glb_policy->fallback_backend_addresses != nullptr);
-  grpc_lb_addresses_destroy(glb_policy->fallback_backend_addresses);
-  glb_policy->fallback_backend_addresses =
-      extract_backend_addresses_locked(addresses);
-  if (glb_policy->lb_fallback_timeout_ms > 0 &&
-      glb_policy->rr_policy != nullptr) {
-    rr_handover_locked(glb_policy);
-  }
-}
-
-static void glb_update_locked(grpc_lb_policy* policy,
-                              const grpc_lb_policy_args* args) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(policy);
-  const grpc_arg* arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
-  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
-    if (glb_policy->lb_channel == nullptr) {
-      // If we don't have a current channel to the LB, go into TRANSIENT
-      // FAILURE.
-      grpc_connectivity_state_set(
-          &glb_policy->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
-          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
-          "glb_update_missing");
-    } else {
-      // otherwise, keep using the current LB channel (ignore this update).
-      gpr_log(
-          GPR_ERROR,
-          "[grpclb %p] No valid LB addresses channel arg in update, ignoring.",
-          glb_policy);
-    }
-    return;
-  }
-  const grpc_lb_addresses* addresses =
-      static_cast<const grpc_lb_addresses*>(arg->value.pointer.p);
-  // If a non-empty serverlist hasn't been received from the balancer,
-  // propagate the update to fallback_backend_addresses.
-  if (glb_policy->serverlist == nullptr) {
-    fallback_update_locked(glb_policy, addresses);
-  }
-  GPR_ASSERT(glb_policy->lb_channel != nullptr);
-  // Propagate updates to the LB channel (pick_first) through the fake
-  // resolver.
-  grpc_channel_args* lb_channel_args = build_lb_channel_args(
-      addresses, glb_policy->response_generator.get(), args->args);
-  glb_policy->response_generator->SetResponse(lb_channel_args);
-  grpc_channel_args_destroy(lb_channel_args);
-  // Start watching the LB channel connectivity for connection, if not
-  // already doing so.
-  if (!glb_policy->watching_lb_channel) {
-    glb_policy->lb_channel_connectivity = grpc_channel_check_connectivity_state(
-        glb_policy->lb_channel, true /* try to connect */);
-    grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
-        grpc_channel_get_channel_stack(glb_policy->lb_channel));
-    GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
-    glb_policy->watching_lb_channel = true;
-    GRPC_LB_POLICY_REF(&glb_policy->base, "watch_lb_channel_connectivity");
-    grpc_client_channel_watch_connectivity_state(
-        client_channel_elem,
-        grpc_polling_entity_create_from_pollset_set(
-            glb_policy->base.interested_parties),
-        &glb_policy->lb_channel_connectivity,
-        &glb_policy->lb_channel_on_connectivity_changed, nullptr);
-  }
-}
-
-// Invoked as part of the update process. It continues watching the LB channel
-// until it shuts down or becomes READY. It's invoked even if the LB channel
-// stayed READY throughout the update (for example if the update is identical).
-static void glb_lb_channel_on_connectivity_changed_cb(void* arg,
-                                                      grpc_error* error) {
-  glb_lb_policy* glb_policy = static_cast<glb_lb_policy*>(arg);
-  if (glb_policy->shutting_down) goto done;
-  // Re-initialize the lb_call. This should also take care of updating the
-  // embedded RR policy. Note that the current RR policy, if any, will stay in
-  // effect until an update from the new lb_call is received.
-  switch (glb_policy->lb_channel_connectivity) {
-    case GRPC_CHANNEL_CONNECTING:
-    case GRPC_CHANNEL_TRANSIENT_FAILURE: {
-      // Keep watching the LB channel.
-      grpc_channel_element* client_channel_elem =
-          grpc_channel_stack_last_element(
-              grpc_channel_get_channel_stack(glb_policy->lb_channel));
-      GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
-      grpc_client_channel_watch_connectivity_state(
-          client_channel_elem,
-          grpc_polling_entity_create_from_pollset_set(
-              glb_policy->base.interested_parties),
-          &glb_policy->lb_channel_connectivity,
-          &glb_policy->lb_channel_on_connectivity_changed, nullptr);
-      break;
-    }
-      // The LB channel may be IDLE because it's shut down before the update.
-      // Restart the LB call to kick the LB channel into gear.
-    case GRPC_CHANNEL_IDLE:
-    case GRPC_CHANNEL_READY:
-      if (glb_policy->lb_calld != nullptr) {
-        lb_call_data_shutdown(glb_policy);
-      }
-      if (glb_policy->started_picking) {
-        if (glb_policy->retry_timer_callback_pending) {
-          grpc_timer_cancel(&glb_policy->lb_call_retry_timer);
-        }
-        glb_policy->lb_call_backoff->Reset();
-        query_for_backends_locked(glb_policy);
-      }
-      // Fall through.
-    case GRPC_CHANNEL_SHUTDOWN:
-    done:
-      glb_policy->watching_lb_channel = false;
-      GRPC_LB_POLICY_UNREF(&glb_policy->base,
-                           "watch_lb_channel_connectivity_cb_shutdown");
-  }
-}
-
-static void glb_set_reresolve_closure_locked(
-    grpc_lb_policy* policy, grpc_closure* request_reresolution) {
-  glb_lb_policy* glb_policy = reinterpret_cast<glb_lb_policy*>(policy);
-  GPR_ASSERT(!glb_policy->shutting_down);
-  GPR_ASSERT(glb_policy->base.request_reresolution == nullptr);
-  if (glb_policy->rr_policy != nullptr) {
-    grpc_lb_policy_set_reresolve_closure_locked(glb_policy->rr_policy,
-                                                request_reresolution);
-  } else {
-    glb_policy->base.request_reresolution = request_reresolution;
-  }
-}
-
-/* Code wiring the policy with the rest of the core */
-static const grpc_lb_policy_vtable glb_lb_policy_vtable = {
-    glb_destroy,
-    glb_shutdown_locked,
-    glb_pick_locked,
-    glb_cancel_pick_locked,
-    glb_cancel_picks_locked,
-    glb_ping_one_locked,
-    glb_exit_idle_locked,
-    glb_check_connectivity_locked,
-    glb_notify_on_state_change_locked,
-    glb_update_locked,
-    glb_set_reresolve_closure_locked};
-
-static grpc_lb_policy* glb_create(grpc_lb_policy_factory* factory,
-                                  grpc_lb_policy_args* args) {
-  /* Count the number of gRPC-LB addresses. There must be at least one. */
-  const grpc_arg* arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
-  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
-    return nullptr;
-  }
-  grpc_lb_addresses* addresses =
-      static_cast<grpc_lb_addresses*>(arg->value.pointer.p);
-  size_t num_grpclb_addrs = 0;
-  for (size_t i = 0; i < addresses->num_addresses; ++i) {
-    if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs;
-  }
-  if (num_grpclb_addrs == 0) return nullptr;
-
-  glb_lb_policy* glb_policy =
-      static_cast<glb_lb_policy*>(gpr_zalloc(sizeof(*glb_policy)));
-
-  /* Get server name. */
-  arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI);
-  GPR_ASSERT(arg != nullptr);
-  GPR_ASSERT(arg->type == GRPC_ARG_STRING);
-  grpc_uri* uri = grpc_uri_parse(arg->value.string, true);
-  GPR_ASSERT(uri->path[0] != '\0');
-  glb_policy->server_name =
-      gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
-  if (grpc_lb_glb_trace.enabled()) {
-    gpr_log(GPR_INFO,
-            "[grpclb %p] Will use '%s' as the server name for LB request.",
-            glb_policy, glb_policy->server_name);
-  }
-  grpc_uri_destroy(uri);
-
-  glb_policy->cc_factory = args->client_channel_factory;
-  GPR_ASSERT(glb_policy->cc_factory != nullptr);
-
-  arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS);
-  glb_policy->lb_call_timeout_ms =
-      grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX});
-
-  arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS);
-  glb_policy->lb_fallback_timeout_ms = grpc_channel_arg_get_integer(
-      arg, {GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX});
-
-  // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
-  // since we use this to trigger the client_load_reporting filter.
-  grpc_arg new_arg = grpc_channel_arg_string_create(
-      (char*)GRPC_ARG_LB_POLICY_NAME, (char*)"grpclb");
-  static const char* args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
-  glb_policy->args = grpc_channel_args_copy_and_add_and_remove(
-      args->args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
-
-  /* Extract the backend addresses (may be empty) from the resolver for
-   * fallback. */
-  glb_policy->fallback_backend_addresses =
-      extract_backend_addresses_locked(addresses);
-
-  /* Create a client channel over them to communicate with a LB service */
-  glb_policy->response_generator =
-      grpc_core::MakeRefCounted<grpc_core::FakeResolverResponseGenerator>();
-  grpc_channel_args* lb_channel_args = build_lb_channel_args(
-      addresses, glb_policy->response_generator.get(), args->args);
-  char* uri_str;
-  gpr_asprintf(&uri_str, "fake:///%s", glb_policy->server_name);
-  glb_policy->lb_channel = grpc_lb_policy_grpclb_create_lb_channel(
-      uri_str, args->client_channel_factory, lb_channel_args);
-
-  /* Propagate initial resolution */
-  glb_policy->response_generator->SetResponse(lb_channel_args);
-  grpc_channel_args_destroy(lb_channel_args);
-  gpr_free(uri_str);
-  if (glb_policy->lb_channel == nullptr) {
-    gpr_free((void*)glb_policy->server_name);
-    grpc_channel_args_destroy(glb_policy->args);
-    gpr_free(glb_policy);
-    return nullptr;
-  }
-  grpc_subchannel_index_ref();
-  GRPC_CLOSURE_INIT(&glb_policy->lb_channel_on_connectivity_changed,
-                    glb_lb_channel_on_connectivity_changed_cb, glb_policy,
-                    grpc_combiner_scheduler(args->combiner));
-  grpc_lb_policy_init(&glb_policy->base, &glb_lb_policy_vtable, args->combiner);
-  grpc_connectivity_state_init(&glb_policy->state_tracker, GRPC_CHANNEL_IDLE,
-                               "grpclb");
-  // Init LB call backoff option.
-  grpc_core::BackOff::Options backoff_options;
-  backoff_options
-      .set_initial_backoff(GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS * 1000)
-      .set_multiplier(GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER)
-      .set_jitter(GRPC_GRPCLB_RECONNECT_JITTER)
-      .set_max_backoff(GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
-  glb_policy->lb_call_backoff.Init(backoff_options);
-  return &glb_policy->base;
-}
-
-static void glb_factory_ref(grpc_lb_policy_factory* factory) {}
-
-static void glb_factory_unref(grpc_lb_policy_factory* factory) {}
-
-static const grpc_lb_policy_factory_vtable glb_factory_vtable = {
-    glb_factory_ref, glb_factory_unref, glb_create, "grpclb"};
-
-static grpc_lb_policy_factory glb_lb_policy_factory = {&glb_factory_vtable};
-
-grpc_lb_policy_factory* grpc_glb_lb_factory_create() {
-  return &glb_lb_policy_factory;
-}
-
-/* Plugin registration */
+namespace {
 
 // Only add client_load_reporting filter if the grpclb LB policy is used.
-static bool maybe_add_client_load_reporting_filter(
-    grpc_channel_stack_builder* builder, void* arg) {
+bool maybe_add_client_load_reporting_filter(grpc_channel_stack_builder* builder,
+                                            void* arg) {
   const grpc_channel_args* args =
       grpc_channel_stack_builder_get_channel_arguments(builder);
   const grpc_arg* channel_arg =
@@ -1929,14 +1879,18 @@
   if (channel_arg != nullptr && channel_arg->type == GRPC_ARG_STRING &&
       strcmp(channel_arg->value.string, "grpclb") == 0) {
     return grpc_channel_stack_builder_append_filter(
-        builder, static_cast<const grpc_channel_filter*>(arg), nullptr,
-        nullptr);
+        builder, (const grpc_channel_filter*)arg, nullptr, nullptr);
   }
   return true;
 }
 
+}  // namespace
+
 void grpc_lb_policy_grpclb_init() {
-  grpc_register_lb_policy(grpc_glb_lb_factory_create());
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::GrpcLbFactory>()));
   grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_client_load_reporting_filter,
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
deleted file mode 100644
index 0a2edb0..0000000
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *
- * Copyright 2016 gRPC authors.
- *
- * 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 GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H
-#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H
-
-#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
-
-/** Returns a load balancing factory for the glb policy, which tries to connect
- * to a load balancing server to decide the next successfully connected
- * subchannel to pick. */
-grpc_lb_policy_factory* grpc_glb_lb_factory_create();
-
-#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H */
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc
index 013fb12..fd873f0 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc
@@ -16,57 +16,11 @@
  *
  */
 
-#include <grpc/support/alloc.h>
-#include <grpc/support/string_util.h>
+#include <grpc/support/port_platform.h>
 
-#include "src/core/ext/filters/client_channel/client_channel.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/gpr/string.h"
-#include "src/core/lib/iomgr/sockaddr_utils.h"
 
-grpc_channel* grpc_lb_policy_grpclb_create_lb_channel(
-    const char* lb_service_target_addresses,
-    grpc_client_channel_factory* client_channel_factory,
+grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args(
     grpc_channel_args* args) {
-  grpc_channel* lb_channel = grpc_client_channel_factory_create_channel(
-      client_channel_factory, lb_service_target_addresses,
-      GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, args);
-  return lb_channel;
-}
-
-grpc_channel_args* grpc_lb_policy_grpclb_build_lb_channel_args(
-    grpc_slice_hash_table* targets_info,
-    grpc_core::FakeResolverResponseGenerator* response_generator,
-    const grpc_channel_args* args) {
-  const grpc_arg to_add[] = {
-      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
-          response_generator)};
-  /* We remove:
-   *
-   * - The channel arg for the LB policy name, since we want to use the default
-   *   (pick_first) in this case.
-   *
-   * - The channel arg for the resolved addresses, since that will be generated
-   *   by the name resolver used in the LB channel.  Note that the LB channel
-   *   will use the fake resolver, so this won't actually generate a query
-   *   to DNS (or some other name service).  However, the addresses returned by
-   *   the fake resolver will have is_balancer=false, whereas our own
-   *   addresses have is_balancer=true.  We need the LB channel to return
-   *   addresses with is_balancer=false so that it does not wind up recursively
-   *   using the grpclb LB policy, as per the special case logic in
-   *   client_channel.c.
-   *
-   * - The channel arg for the server URI, since that will be different for the
-   *   LB channel than for the parent channel (the client channel factory will
-   *   re-add this arg with the right value).
-   *
-   * - The fake resolver generator, because we are replacing it with the one
-   *   from the grpclb policy, used to propagate updates to the LB channel. */
-  static const char* keys_to_remove[] = {
-      GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI,
-      GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR};
-  return grpc_channel_args_copy_and_add_and_remove(
-      args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), to_add,
-      GPR_ARRAY_SIZE(to_add));
+  return args;
 }
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
index 2e34e3c..825065a 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
@@ -19,26 +19,18 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
-#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
-#include "src/core/lib/slice/slice_hash_table.h"
 
-/** Create the channel used for communicating with an LB service.
- * Note that an LB *service* may be comprised of several LB *servers*.
- *
- * \a lb_service_target_addresses is the target URI containing the addresses
- * from resolving the LB service's name (eg, ipv4:10.0.0.1:1234,10.2.3.4:9876).
- * \a client_channel_factory will be used for the creation of the LB channel,
- * alongside the channel args passed in \a args. */
-grpc_channel* grpc_lb_policy_grpclb_create_lb_channel(
-    const char* lb_service_target_addresses,
-    grpc_client_channel_factory* client_channel_factory,
+/// Makes any necessary modifications to \a args for use in the grpclb
+/// balancer channel.
+///
+/// Takes ownership of \a args.
+///
+/// Caller takes ownership of the returned args.
+grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args(
     grpc_channel_args* args);
 
-grpc_channel_args* grpc_lb_policy_grpclb_build_lb_channel_args(
-    grpc_slice_hash_table* targets_info,
-    grpc_core::FakeResolverResponseGenerator* response_generator,
-    const grpc_channel_args* args);
-
 #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H \
         */
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc
index 5e615ad..441efd5 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc
@@ -16,85 +16,93 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
+
+#include <string.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/filters/client_channel/client_channel.h"
-#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/lb_targets_info.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
 #include "src/core/lib/slice/slice_internal.h"
 
-grpc_channel* grpc_lb_policy_grpclb_create_lb_channel(
-    const char* lb_service_target_addresses,
-    grpc_client_channel_factory* client_channel_factory,
+namespace grpc_core {
+namespace {
+
+int BalancerNameCmp(const grpc_core::UniquePtr<char>& a,
+                    const grpc_core::UniquePtr<char>& b) {
+  return strcmp(a.get(), b.get());
+}
+
+RefCountedPtr<TargetAuthorityTable> CreateTargetAuthorityTable(
+    grpc_lb_addresses* addresses) {
+  TargetAuthorityTable::Entry* target_authority_entries =
+      static_cast<TargetAuthorityTable::Entry*>(gpr_zalloc(
+          sizeof(*target_authority_entries) * addresses->num_addresses));
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    char* addr_str;
+    GPR_ASSERT(grpc_sockaddr_to_string(
+                   &addr_str, &addresses->addresses[i].address, true) > 0);
+    target_authority_entries[i].key = grpc_slice_from_copied_string(addr_str);
+    target_authority_entries[i].value.reset(
+        gpr_strdup(addresses->addresses[i].balancer_name));
+    gpr_free(addr_str);
+  }
+  RefCountedPtr<TargetAuthorityTable> target_authority_table =
+      TargetAuthorityTable::Create(addresses->num_addresses,
+                                   target_authority_entries, BalancerNameCmp);
+  gpr_free(target_authority_entries);
+  return target_authority_table;
+}
+
+}  // namespace
+}  // namespace grpc_core
+
+grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args(
     grpc_channel_args* args) {
-  grpc_channel_args* new_args = args;
+  const char* args_to_remove[1];
+  size_t num_args_to_remove = 0;
+  grpc_arg args_to_add[2];
+  size_t num_args_to_add = 0;
+  // Add arg for targets info table.
+  const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_LB_ADDRESSES);
+  GPR_ASSERT(arg != nullptr);
+  GPR_ASSERT(arg->type == GRPC_ARG_POINTER);
+  grpc_lb_addresses* addresses =
+      static_cast<grpc_lb_addresses*>(arg->value.pointer.p);
+  grpc_core::RefCountedPtr<grpc_core::TargetAuthorityTable>
+      target_authority_table = grpc_core::CreateTargetAuthorityTable(addresses);
+  args_to_add[num_args_to_add++] =
+      grpc_core::CreateTargetAuthorityTableChannelArg(
+          target_authority_table.get());
+  // Substitute the channel credentials with a version without call
+  // credentials: the load balancer is not necessarily trusted to handle
+  // bearer token credentials.
   grpc_channel_credentials* channel_credentials =
       grpc_channel_credentials_find_in_args(args);
+  grpc_channel_credentials* creds_sans_call_creds = nullptr;
   if (channel_credentials != nullptr) {
-    /* Substitute the channel credentials with a version without call
-     * credentials: the load balancer is not necessarily trusted to handle
-     * bearer token credentials */
-    static const char* keys_to_remove[] = {GRPC_ARG_CHANNEL_CREDENTIALS};
-    grpc_channel_credentials* creds_sans_call_creds =
+    creds_sans_call_creds =
         grpc_channel_credentials_duplicate_without_call_credentials(
             channel_credentials);
     GPR_ASSERT(creds_sans_call_creds != nullptr);
-    grpc_arg args_to_add[] = {
-        grpc_channel_credentials_to_arg(creds_sans_call_creds)};
-    /* Create the new set of channel args */
-    new_args = grpc_channel_args_copy_and_add_and_remove(
-        args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add,
-        GPR_ARRAY_SIZE(args_to_add));
+    args_to_remove[num_args_to_remove++] = GRPC_ARG_CHANNEL_CREDENTIALS;
+    args_to_add[num_args_to_add++] =
+        grpc_channel_credentials_to_arg(creds_sans_call_creds);
+  }
+  grpc_channel_args* result = grpc_channel_args_copy_and_add_and_remove(
+      args, args_to_remove, num_args_to_remove, args_to_add, num_args_to_add);
+  // Clean up.
+  grpc_channel_args_destroy(args);
+  if (creds_sans_call_creds != nullptr) {
     grpc_channel_credentials_unref(creds_sans_call_creds);
   }
-  grpc_channel* lb_channel = grpc_client_channel_factory_create_channel(
-      client_channel_factory, lb_service_target_addresses,
-      GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, new_args);
-  if (channel_credentials != nullptr) {
-    grpc_channel_args_destroy(new_args);
-  }
-  return lb_channel;
-}
-
-grpc_channel_args* grpc_lb_policy_grpclb_build_lb_channel_args(
-    grpc_slice_hash_table* targets_info,
-    grpc_core::FakeResolverResponseGenerator* response_generator,
-    const grpc_channel_args* args) {
-  const grpc_arg to_add[] = {
-      grpc_lb_targets_info_create_channel_arg(targets_info),
-      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
-          response_generator)};
-  /* We remove:
-   *
-   * - The channel arg for the LB policy name, since we want to use the default
-   *   (pick_first) in this case.
-   *
-   * - The channel arg for the resolved addresses, since that will be generated
-   *   by the name resolver used in the LB channel.  Note that the LB channel
-   *   will use the fake resolver, so this won't actually generate a query
-   *   to DNS (or some other name service).  However, the addresses returned by
-   *   the fake resolver will have is_balancer=false, whereas our own
-   *   addresses have is_balancer=true.  We need the LB channel to return
-   *   addresses with is_balancer=false so that it does not wind up recursively
-   *   using the grpclb LB policy, as per the special case logic in
-   *   client_channel.c.
-   *
-   * - The channel arg for the server URI, since that will be different for the
-   *   LB channel than for the parent channel (the client channel factory will
-   *   re-add this arg with the right value).
-   *
-   * - The fake resolver generator, because we are replacing it with the one
-   *   from the grpclb policy, used to propagate updates to the LB channel. */
-  static const char* keys_to_remove[] = {
-      GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI,
-      GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR};
-  /* Add the targets info table to be used for secure naming */
-  return grpc_channel_args_copy_and_add_and_remove(
-      args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), to_add,
-      GPR_ARRAY_SIZE(to_add));
+  return result;
 }
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
index 0b5a798..dfbaead 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
 
 #include <string.h>
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
index d4b9d06..c971e56 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/impl/codegen/grpc_types.h>
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
index c388b6b..7ef3bcf 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
 #include "third_party/nanopb/pb_decode.h"
 #include "third_party/nanopb/pb_encode.h"
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
index ccb0212..d4270f2 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice_buffer.h>
 
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
index 1485f7c..9090c34 100644
--- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -29,194 +31,225 @@
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
-grpc_core::TraceFlag grpc_lb_pick_first_trace(false, "pick_first");
+namespace grpc_core {
 
-typedef struct {
-  /** base policy: must be first */
-  grpc_lb_policy base;
+TraceFlag grpc_lb_pick_first_trace(false, "pick_first");
+
+namespace {
+
+//
+// pick_first LB policy
+//
+
+class PickFirst : public LoadBalancingPolicy {
+ public:
+  explicit PickFirst(const Args& args);
+
+  void UpdateLocked(const grpc_channel_args& args) override;
+  bool PickLocked(PickState* pick) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) override;
+  void ExitIdleLocked() override;
+
+ private:
+  ~PickFirst();
+
+  void ShutdownLocked() override;
+
+  void StartPickingLocked();
+  void DestroyUnselectedSubchannelsLocked();
+
+  static void OnConnectivityChangedLocked(void* arg, grpc_error* error);
+
+  void SubchannelListRefForConnectivityWatch(
+      grpc_lb_subchannel_list* subchannel_list, const char* reason);
+  void SubchannelListUnrefForConnectivityWatch(
+      grpc_lb_subchannel_list* subchannel_list, const char* reason);
+
   /** all our subchannels */
-  grpc_lb_subchannel_list* subchannel_list;
+  grpc_lb_subchannel_list* subchannel_list_ = nullptr;
   /** latest pending subchannel list */
-  grpc_lb_subchannel_list* latest_pending_subchannel_list;
+  grpc_lb_subchannel_list* latest_pending_subchannel_list_ = nullptr;
   /** selected subchannel in \a subchannel_list */
-  grpc_lb_subchannel_data* selected;
+  grpc_lb_subchannel_data* selected_ = nullptr;
   /** have we started picking? */
-  bool started_picking;
+  bool started_picking_ = false;
   /** are we shut down? */
-  bool shutdown;
+  bool shutdown_ = false;
   /** list of picks that are waiting on connectivity */
-  grpc_lb_policy_pick_state* pending_picks;
+  PickState* pending_picks_ = nullptr;
   /** our connectivity state tracker */
-  grpc_connectivity_state_tracker state_tracker;
-} pick_first_lb_policy;
+  grpc_connectivity_state_tracker state_tracker_;
+};
 
-static void pf_destroy(grpc_lb_policy* pol) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  GPR_ASSERT(p->subchannel_list == nullptr);
-  GPR_ASSERT(p->latest_pending_subchannel_list == nullptr);
-  GPR_ASSERT(p->pending_picks == nullptr);
-  grpc_connectivity_state_destroy(&p->state_tracker);
-  gpr_free(p);
-  grpc_subchannel_index_unref();
+PickFirst::PickFirst(const Args& args) : LoadBalancingPolicy(args) {
+  GPR_ASSERT(args.client_channel_factory != nullptr);
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
+                               "pick_first");
   if (grpc_lb_pick_first_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "Pick First %p destroyed.", (void*)p);
+    gpr_log(GPR_DEBUG, "Pick First %p created.", this);
+  }
+  UpdateLocked(*args.args);
+  grpc_subchannel_index_ref();
+}
+
+PickFirst::~PickFirst() {
+  if (grpc_lb_pick_first_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "Destroying Pick First %p", this);
+  }
+  GPR_ASSERT(subchannel_list_ == nullptr);
+  GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
+  GPR_ASSERT(pending_picks_ == nullptr);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  grpc_subchannel_index_unref();
+}
+
+void PickFirst::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    if (new_policy->PickLocked(pick)) {
+      // Synchronous return, schedule closure.
+      GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
+    }
   }
 }
 
-static void pf_shutdown_locked(grpc_lb_policy* pol,
-                               grpc_lb_policy* new_policy) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
+void PickFirst::ShutdownLocked() {
   grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
   if (grpc_lb_pick_first_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "Pick First %p Shutting down", p);
+    gpr_log(GPR_DEBUG, "Pick First %p Shutting down", this);
   }
-  p->shutdown = true;
-  grpc_lb_policy_pick_state* pick;
-  while ((pick = p->pending_picks) != nullptr) {
-    p->pending_picks = pick->next;
-    if (new_policy != nullptr) {
-      // Hand off to new LB policy.
-      if (grpc_lb_policy_pick_locked(new_policy, pick)) {
-        // Synchronous return, schedule closure.
-        GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
-      }
-    } else {
-      pick->connected_subchannel.reset();
-      GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error));
-    }
+  shutdown_ = true;
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    pick->connected_subchannel.reset();
+    GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error));
   }
-  grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_SHUTDOWN,
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
                               GRPC_ERROR_REF(error), "shutdown");
-  if (p->subchannel_list != nullptr) {
-    grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+  if (subchannel_list_ != nullptr) {
+    grpc_lb_subchannel_list_shutdown_and_unref(subchannel_list_, "pf_shutdown");
+    subchannel_list_ = nullptr;
+  }
+  if (latest_pending_subchannel_list_ != nullptr) {
+    grpc_lb_subchannel_list_shutdown_and_unref(latest_pending_subchannel_list_,
                                                "pf_shutdown");
-    p->subchannel_list = nullptr;
+    latest_pending_subchannel_list_ = nullptr;
   }
-  if (p->latest_pending_subchannel_list != nullptr) {
-    grpc_lb_subchannel_list_shutdown_and_unref(
-        p->latest_pending_subchannel_list, "pf_shutdown");
-    p->latest_pending_subchannel_list = nullptr;
-  }
-  grpc_lb_policy_try_reresolve(&p->base, &grpc_lb_pick_first_trace,
-                               GRPC_ERROR_CANCELLED);
+  TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_CANCELLED);
   GRPC_ERROR_UNREF(error);
 }
 
-static void pf_cancel_pick_locked(grpc_lb_policy* pol,
-                                  grpc_lb_policy_pick_state* pick,
-                                  grpc_error* error) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  grpc_lb_policy_pick_state* pp = p->pending_picks;
-  p->pending_picks = nullptr;
+void PickFirst::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PickState* pp = pending_picks_;
+  pending_picks_ = nullptr;
   while (pp != nullptr) {
-    grpc_lb_policy_pick_state* next = pp->next;
+    PickState* next = pp->next;
     if (pp == pick) {
       pick->connected_subchannel.reset();
       GRPC_CLOSURE_SCHED(pick->on_complete,
                          GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
                              "Pick Cancelled", &error, 1));
     } else {
-      pp->next = p->pending_picks;
-      p->pending_picks = pp;
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
     }
     pp = next;
   }
   GRPC_ERROR_UNREF(error);
 }
 
-static void pf_cancel_picks_locked(grpc_lb_policy* pol,
-                                   uint32_t initial_metadata_flags_mask,
-                                   uint32_t initial_metadata_flags_eq,
-                                   grpc_error* error) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  grpc_lb_policy_pick_state* pick = p->pending_picks;
-  p->pending_picks = nullptr;
+void PickFirst::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                          uint32_t initial_metadata_flags_eq,
+                                          grpc_error* error) {
+  PickState* pick = pending_picks_;
+  pending_picks_ = nullptr;
   while (pick != nullptr) {
-    grpc_lb_policy_pick_state* next = pick->next;
+    PickState* next = pick->next;
     if ((pick->initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
       GRPC_CLOSURE_SCHED(pick->on_complete,
                          GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
                              "Pick Cancelled", &error, 1));
     } else {
-      pick->next = p->pending_picks;
-      p->pending_picks = pick;
+      pick->next = pending_picks_;
+      pending_picks_ = pick;
     }
     pick = next;
   }
   GRPC_ERROR_UNREF(error);
 }
 
-static void start_picking_locked(pick_first_lb_policy* p) {
-  p->started_picking = true;
-  if (p->subchannel_list != nullptr &&
-      p->subchannel_list->num_subchannels > 0) {
-    p->subchannel_list->checking_subchannel = 0;
-    for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) {
-      if (p->subchannel_list->subchannels[i].subchannel != nullptr) {
-        grpc_lb_subchannel_list_ref_for_connectivity_watch(
-            p->subchannel_list, "connectivity_watch+start_picking");
+void PickFirst::StartPickingLocked() {
+  started_picking_ = true;
+  if (subchannel_list_ != nullptr && subchannel_list_->num_subchannels > 0) {
+    subchannel_list_->checking_subchannel = 0;
+    for (size_t i = 0; i < subchannel_list_->num_subchannels; ++i) {
+      if (subchannel_list_->subchannels[i].subchannel != nullptr) {
+        SubchannelListRefForConnectivityWatch(
+            subchannel_list_, "connectivity_watch+start_picking");
         grpc_lb_subchannel_data_start_connectivity_watch(
-            &p->subchannel_list->subchannels[i]);
+            &subchannel_list_->subchannels[i]);
         break;
       }
     }
   }
 }
 
-static void pf_exit_idle_locked(grpc_lb_policy* pol) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  if (!p->started_picking) {
-    start_picking_locked(p);
+void PickFirst::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
   }
 }
 
-static int pf_pick_locked(grpc_lb_policy* pol,
-                          grpc_lb_policy_pick_state* pick) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
+bool PickFirst::PickLocked(PickState* pick) {
   // If we have a selected subchannel already, return synchronously.
-  if (p->selected != nullptr) {
-    pick->connected_subchannel = p->selected->connected_subchannel;
-    return 1;
+  if (selected_ != nullptr) {
+    pick->connected_subchannel = selected_->connected_subchannel;
+    return true;
   }
   // No subchannel selected yet, so handle asynchronously.
-  if (!p->started_picking) {
-    start_picking_locked(p);
+  if (!started_picking_) {
+    StartPickingLocked();
   }
-  pick->next = p->pending_picks;
-  p->pending_picks = pick;
-  return 0;
+  pick->next = pending_picks_;
+  pending_picks_ = pick;
+  return false;
 }
 
-static void destroy_unselected_subchannels_locked(pick_first_lb_policy* p) {
-  for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) {
-    grpc_lb_subchannel_data* sd = &p->subchannel_list->subchannels[i];
-    if (p->selected != sd) {
+void PickFirst::DestroyUnselectedSubchannelsLocked() {
+  for (size_t i = 0; i < subchannel_list_->num_subchannels; ++i) {
+    grpc_lb_subchannel_data* sd = &subchannel_list_->subchannels[i];
+    if (selected_ != sd) {
       grpc_lb_subchannel_data_unref_subchannel(sd,
                                                "selected_different_subchannel");
     }
   }
 }
 
-static grpc_connectivity_state pf_check_connectivity_locked(
-    grpc_lb_policy* pol, grpc_error** error) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  return grpc_connectivity_state_get(&p->state_tracker, error);
+grpc_connectivity_state PickFirst::CheckConnectivityLocked(grpc_error** error) {
+  return grpc_connectivity_state_get(&state_tracker_, error);
 }
 
-static void pf_notify_on_state_change_locked(grpc_lb_policy* pol,
-                                             grpc_connectivity_state* current,
-                                             grpc_closure* notify) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  grpc_connectivity_state_notify_on_state_change(&p->state_tracker, current,
+void PickFirst::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                          grpc_closure* notify) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
                                                  notify);
 }
 
-static void pf_ping_one_locked(grpc_lb_policy* pol, grpc_closure* on_initiate,
-                               grpc_closure* on_ack) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(pol);
-  if (p->selected) {
-    p->selected->connected_subchannel->Ping(on_initiate, on_ack);
+void PickFirst::PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) {
+  if (selected_ != nullptr) {
+    selected_->connected_subchannel->Ping(on_initiate, on_ack);
   } else {
     GRPC_CLOSURE_SCHED(on_initiate,
                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Not connected"));
@@ -225,18 +258,31 @@
   }
 }
 
-static void pf_connectivity_changed_locked(void* arg, grpc_error* error);
+void PickFirst::SubchannelListRefForConnectivityWatch(
+    grpc_lb_subchannel_list* subchannel_list, const char* reason) {
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is ready and the subchannel_list code has been
+  // converted to a C++ API, find a way to hold the RefCountedPtr<>
+  // somewhere (maybe in the subchannel_data object) instead of doing
+  // this manually.
+  auto self = Ref(DEBUG_LOCATION, reason);
+  self.release();
+  grpc_lb_subchannel_list_ref(subchannel_list, reason);
+}
 
-static void pf_update_locked(grpc_lb_policy* policy,
-                             const grpc_lb_policy_args* args) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(policy);
-  const grpc_arg* arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
+void PickFirst::SubchannelListUnrefForConnectivityWatch(
+    grpc_lb_subchannel_list* subchannel_list, const char* reason) {
+  Unref(DEBUG_LOCATION, reason);
+  grpc_lb_subchannel_list_unref(subchannel_list, reason);
+}
+
+void PickFirst::UpdateLocked(const grpc_channel_args& args) {
+  const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
   if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
-    if (p->subchannel_list == nullptr) {
+    if (subchannel_list_ == nullptr) {
       // If we don't have a current subchannel list, go into TRANSIENT FAILURE.
       grpc_connectivity_state_set(
-          &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
           GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
           "pf_update_missing");
     } else {
@@ -244,77 +290,78 @@
       gpr_log(GPR_ERROR,
               "No valid LB addresses channel arg for Pick First %p update, "
               "ignoring.",
-              (void*)p);
+              this);
     }
     return;
   }
   const grpc_lb_addresses* addresses =
-      static_cast<const grpc_lb_addresses*>(arg->value.pointer.p);
+      (const grpc_lb_addresses*)arg->value.pointer.p;
   if (grpc_lb_pick_first_trace.enabled()) {
-    gpr_log(GPR_INFO, "Pick First %p received update with %lu addresses",
-            (void*)p, static_cast<unsigned long>(addresses->num_addresses));
+    gpr_log(GPR_INFO,
+            "Pick First %p received update with %" PRIuPTR " addresses", this,
+            addresses->num_addresses);
   }
   grpc_lb_subchannel_list* subchannel_list = grpc_lb_subchannel_list_create(
-      &p->base, &grpc_lb_pick_first_trace, addresses, args,
-      pf_connectivity_changed_locked);
+      this, &grpc_lb_pick_first_trace, addresses, combiner(),
+      client_channel_factory(), args, &PickFirst::OnConnectivityChangedLocked);
   if (subchannel_list->num_subchannels == 0) {
     // Empty update or no valid subchannels. Unsubscribe from all current
     // subchannels and put the channel in TRANSIENT_FAILURE.
     grpc_connectivity_state_set(
-        &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+        &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
         "pf_update_empty");
-    if (p->subchannel_list != nullptr) {
-      grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+    if (subchannel_list_ != nullptr) {
+      grpc_lb_subchannel_list_shutdown_and_unref(subchannel_list_,
                                                  "sl_shutdown_empty_update");
     }
-    p->subchannel_list = subchannel_list;  // Empty list.
-    p->selected = nullptr;
+    subchannel_list_ = subchannel_list;  // Empty list.
+    selected_ = nullptr;
     return;
   }
-  if (p->selected == nullptr) {
+  if (selected_ == nullptr) {
     // We don't yet have a selected subchannel, so replace the current
     // subchannel list immediately.
-    if (p->subchannel_list != nullptr) {
-      grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+    if (subchannel_list_ != nullptr) {
+      grpc_lb_subchannel_list_shutdown_and_unref(subchannel_list_,
                                                  "pf_update_before_selected");
     }
-    p->subchannel_list = subchannel_list;
+    subchannel_list_ = subchannel_list;
   } else {
     // We do have a selected subchannel.
     // Check if it's present in the new list.  If so, we're done.
     for (size_t i = 0; i < subchannel_list->num_subchannels; ++i) {
       grpc_lb_subchannel_data* sd = &subchannel_list->subchannels[i];
-      if (sd->subchannel == p->selected->subchannel) {
+      if (sd->subchannel == selected_->subchannel) {
         // The currently selected subchannel is in the update: we are done.
         if (grpc_lb_pick_first_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "Pick First %p found already selected subchannel %p "
                   "at update index %" PRIuPTR " of %" PRIuPTR "; update done",
-                  p, p->selected->subchannel, i,
+                  this, selected_->subchannel, i,
                   subchannel_list->num_subchannels);
         }
-        if (p->selected->connected_subchannel != nullptr) {
-          sd->connected_subchannel = p->selected->connected_subchannel;
+        if (selected_->connected_subchannel != nullptr) {
+          sd->connected_subchannel = selected_->connected_subchannel;
         }
-        p->selected = sd;
-        if (p->subchannel_list != nullptr) {
+        selected_ = sd;
+        if (subchannel_list_ != nullptr) {
           grpc_lb_subchannel_list_shutdown_and_unref(
-              p->subchannel_list, "pf_update_includes_selected");
+              subchannel_list_, "pf_update_includes_selected");
         }
-        p->subchannel_list = subchannel_list;
-        destroy_unselected_subchannels_locked(p);
-        grpc_lb_subchannel_list_ref_for_connectivity_watch(
+        subchannel_list_ = subchannel_list;
+        DestroyUnselectedSubchannelsLocked();
+        SubchannelListRefForConnectivityWatch(
             subchannel_list, "connectivity_watch+replace_selected");
         grpc_lb_subchannel_data_start_connectivity_watch(sd);
         // If there was a previously pending update (which may or may
         // not have contained the currently selected subchannel), drop
         // it, so that it doesn't override what we've done here.
-        if (p->latest_pending_subchannel_list != nullptr) {
+        if (latest_pending_subchannel_list_ != nullptr) {
           grpc_lb_subchannel_list_shutdown_and_unref(
-              p->latest_pending_subchannel_list,
+              latest_pending_subchannel_list_,
               "pf_update_includes_selected+outdated");
-          p->latest_pending_subchannel_list = nullptr;
+          latest_pending_subchannel_list_ = nullptr;
         }
         return;
       }
@@ -323,84 +370,81 @@
     // pending subchannel list to the new subchannel list.  We will wait
     // for it to report READY before swapping it into the current
     // subchannel list.
-    if (p->latest_pending_subchannel_list != nullptr) {
+    if (latest_pending_subchannel_list_ != nullptr) {
       if (grpc_lb_pick_first_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "Pick First %p Shutting down latest pending subchannel list "
                 "%p, about to be replaced by newer latest %p",
-                (void*)p, (void*)p->latest_pending_subchannel_list,
-                (void*)subchannel_list);
+                this, latest_pending_subchannel_list_, subchannel_list);
       }
       grpc_lb_subchannel_list_shutdown_and_unref(
-          p->latest_pending_subchannel_list, "sl_outdated_dont_smash");
+          latest_pending_subchannel_list_, "sl_outdated_dont_smash");
     }
-    p->latest_pending_subchannel_list = subchannel_list;
+    latest_pending_subchannel_list_ = subchannel_list;
   }
   // If we've started picking, start trying to connect to the first
   // subchannel in the new list.
-  if (p->started_picking) {
-    grpc_lb_subchannel_list_ref_for_connectivity_watch(
-        subchannel_list, "connectivity_watch+update");
+  if (started_picking_) {
+    SubchannelListRefForConnectivityWatch(subchannel_list,
+                                          "connectivity_watch+update");
     grpc_lb_subchannel_data_start_connectivity_watch(
         &subchannel_list->subchannels[0]);
   }
 }
 
-static void pf_connectivity_changed_locked(void* arg, grpc_error* error) {
+void PickFirst::OnConnectivityChangedLocked(void* arg, grpc_error* error) {
   grpc_lb_subchannel_data* sd = static_cast<grpc_lb_subchannel_data*>(arg);
-  pick_first_lb_policy* p =
-      reinterpret_cast<pick_first_lb_policy*>(sd->subchannel_list->policy);
+  PickFirst* p = static_cast<PickFirst*>(sd->subchannel_list->policy);
   if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "Pick First %p connectivity changed for subchannel %p (%" PRIuPTR
             " of %" PRIuPTR
-            "), subchannel_list %p: state=%s p->shutdown=%d "
+            "), subchannel_list %p: state=%s p->shutdown_=%d "
             "sd->subchannel_list->shutting_down=%d error=%s",
-            (void*)p, (void*)sd->subchannel,
-            sd->subchannel_list->checking_subchannel,
-            sd->subchannel_list->num_subchannels, (void*)sd->subchannel_list,
+            p, sd->subchannel, sd->subchannel_list->checking_subchannel,
+            sd->subchannel_list->num_subchannels, sd->subchannel_list,
             grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe),
-            p->shutdown, sd->subchannel_list->shutting_down,
+            p->shutdown_, sd->subchannel_list->shutting_down,
             grpc_error_string(error));
   }
   // If the policy is shutting down, unref and return.
-  if (p->shutdown) {
+  if (p->shutdown_) {
     grpc_lb_subchannel_data_stop_connectivity_watch(sd);
     grpc_lb_subchannel_data_unref_subchannel(sd, "pf_shutdown");
-    grpc_lb_subchannel_list_unref_for_connectivity_watch(sd->subchannel_list,
-                                                         "pf_shutdown");
+    p->SubchannelListUnrefForConnectivityWatch(sd->subchannel_list,
+                                               "pf_shutdown");
     return;
   }
   // If the subchannel list is shutting down, stop watching.
   if (sd->subchannel_list->shutting_down || error == GRPC_ERROR_CANCELLED) {
     grpc_lb_subchannel_data_stop_connectivity_watch(sd);
     grpc_lb_subchannel_data_unref_subchannel(sd, "pf_sl_shutdown");
-    grpc_lb_subchannel_list_unref_for_connectivity_watch(sd->subchannel_list,
-                                                         "pf_sl_shutdown");
+    p->SubchannelListUnrefForConnectivityWatch(sd->subchannel_list,
+                                               "pf_sl_shutdown");
     return;
   }
   // If we're still here, the notification must be for a subchannel in
   // either the current or latest pending subchannel lists.
-  GPR_ASSERT(sd->subchannel_list == p->subchannel_list ||
-             sd->subchannel_list == p->latest_pending_subchannel_list);
+  GPR_ASSERT(sd->subchannel_list == p->subchannel_list_ ||
+             sd->subchannel_list == p->latest_pending_subchannel_list_);
   // Update state.
   sd->curr_connectivity_state = sd->pending_connectivity_state_unsafe;
   // Handle updates for the currently selected subchannel.
-  if (p->selected == sd) {
+  if (p->selected_ == sd) {
     // If the new state is anything other than READY and there is a
     // pending update, switch to the pending update.
     if (sd->curr_connectivity_state != GRPC_CHANNEL_READY &&
-        p->latest_pending_subchannel_list != nullptr) {
-      p->selected = nullptr;
+        p->latest_pending_subchannel_list_ != nullptr) {
+      p->selected_ = nullptr;
       grpc_lb_subchannel_data_stop_connectivity_watch(sd);
-      grpc_lb_subchannel_list_unref_for_connectivity_watch(
+      p->SubchannelListUnrefForConnectivityWatch(
           sd->subchannel_list, "selected_not_ready+switch_to_update");
       grpc_lb_subchannel_list_shutdown_and_unref(
-          p->subchannel_list, "selected_not_ready+switch_to_update");
-      p->subchannel_list = p->latest_pending_subchannel_list;
-      p->latest_pending_subchannel_list = nullptr;
+          p->subchannel_list_, "selected_not_ready+switch_to_update");
+      p->subchannel_list_ = p->latest_pending_subchannel_list_;
+      p->latest_pending_subchannel_list_ = nullptr;
       grpc_connectivity_state_set(
-          &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          &p->state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
           GRPC_ERROR_REF(error), "selected_not_ready+switch_to_update");
     } else {
       // TODO(juanlishen): we re-resolve when the selected subchannel goes to
@@ -411,21 +455,20 @@
       GPR_ASSERT(sd->curr_connectivity_state != GRPC_CHANNEL_SHUTDOWN);
       if (sd->curr_connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
         // If the selected channel goes bad, request a re-resolution.
-        grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_IDLE,
+        grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_IDLE,
                                     GRPC_ERROR_NONE,
                                     "selected_changed+reresolve");
-        p->started_picking = false;
-        grpc_lb_policy_try_reresolve(&p->base, &grpc_lb_pick_first_trace,
-                                     GRPC_ERROR_NONE);
-        // in transient failure. Rely on re-resolution to recover.
-        p->selected = nullptr;
+        p->started_picking_ = false;
+        p->TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_NONE);
+        // In transient failure. Rely on re-resolution to recover.
+        p->selected_ = nullptr;
         grpc_lb_subchannel_data_stop_connectivity_watch(sd);
-        grpc_lb_subchannel_list_unref_for_connectivity_watch(
-            sd->subchannel_list, "pf_selected_shutdown");
+        p->SubchannelListUnrefForConnectivityWatch(sd->subchannel_list,
+                                                   "pf_selected_shutdown");
         grpc_lb_subchannel_data_unref_subchannel(
             sd, "pf_selected_shutdown");  // Unrefs connected subchannel
       } else {
-        grpc_connectivity_state_set(&p->state_tracker,
+        grpc_connectivity_state_set(&p->state_tracker_,
                                     sd->curr_connectivity_state,
                                     GRPC_ERROR_REF(error), "selected_changed");
         // Renew notification.
@@ -436,45 +479,45 @@
   }
   // If we get here, there are two possible cases:
   // 1. We do not currently have a selected subchannel, and the update is
-  //    for a subchannel in p->subchannel_list that we're trying to
+  //    for a subchannel in p->subchannel_list_ that we're trying to
   //    connect to.  The goal here is to find a subchannel that we can
   //    select.
   // 2. We do currently have a selected subchannel, and the update is
-  //    for a subchannel in p->latest_pending_subchannel_list.  The
+  //    for a subchannel in p->latest_pending_subchannel_list_.  The
   //    goal here is to find a subchannel from the update that we can
   //    select in place of the current one.
   switch (sd->curr_connectivity_state) {
     case GRPC_CHANNEL_READY: {
-      // Case 2.  Promote p->latest_pending_subchannel_list to
-      // p->subchannel_list.
+      // Case 2.  Promote p->latest_pending_subchannel_list_ to
+      // p->subchannel_list_.
       sd->connected_subchannel =
           grpc_subchannel_get_connected_subchannel(sd->subchannel);
-      if (sd->subchannel_list == p->latest_pending_subchannel_list) {
-        GPR_ASSERT(p->subchannel_list != nullptr);
-        grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+      if (sd->subchannel_list == p->latest_pending_subchannel_list_) {
+        GPR_ASSERT(p->subchannel_list_ != nullptr);
+        grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list_,
                                                    "finish_update");
-        p->subchannel_list = p->latest_pending_subchannel_list;
-        p->latest_pending_subchannel_list = nullptr;
+        p->subchannel_list_ = p->latest_pending_subchannel_list_;
+        p->latest_pending_subchannel_list_ = nullptr;
       }
       // Cases 1 and 2.
-      grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_READY,
+      grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_READY,
                                   GRPC_ERROR_NONE, "connecting_ready");
-      p->selected = sd;
+      p->selected_ = sd;
       if (grpc_lb_pick_first_trace.enabled()) {
-        gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", (void*)p,
-                (void*)sd->subchannel);
+        gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", p,
+                sd->subchannel);
       }
       // Drop all other subchannels, since we are now connected.
-      destroy_unselected_subchannels_locked(p);
+      p->DestroyUnselectedSubchannelsLocked();
       // Update any calls that were waiting for a pick.
-      grpc_lb_policy_pick_state* pick;
-      while ((pick = p->pending_picks)) {
-        p->pending_picks = pick->next;
-        pick->connected_subchannel = p->selected->connected_subchannel;
+      PickState* pick;
+      while ((pick = p->pending_picks_)) {
+        p->pending_picks_ = pick->next;
+        pick->connected_subchannel = p->selected_->connected_subchannel;
         if (grpc_lb_pick_first_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "Servicing pending pick with selected subchannel %p",
-                  (void*)p->selected);
+                  p->selected_);
         }
         GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
       }
@@ -494,9 +537,9 @@
       // Case 1: Only set state to TRANSIENT_FAILURE if we've tried
       // all subchannels.
       if (sd->subchannel_list->checking_subchannel == 0 &&
-          sd->subchannel_list == p->subchannel_list) {
+          sd->subchannel_list == p->subchannel_list_) {
         grpc_connectivity_state_set(
-            &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+            &p->state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
             GRPC_ERROR_REF(error), "connecting_transient_failure");
       }
       // Reuses the connectivity refs from the previous watch.
@@ -506,8 +549,8 @@
     case GRPC_CHANNEL_CONNECTING:
     case GRPC_CHANNEL_IDLE: {
       // Only update connectivity state in case 1.
-      if (sd->subchannel_list == p->subchannel_list) {
-        grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_CONNECTING,
+      if (sd->subchannel_list == p->subchannel_list_) {
+        grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_CONNECTING,
                                     GRPC_ERROR_REF(error),
                                     "connecting_changed");
       }
@@ -520,60 +563,29 @@
   }
 }
 
-static void pf_set_reresolve_closure_locked(
-    grpc_lb_policy* policy, grpc_closure* request_reresolution) {
-  pick_first_lb_policy* p = reinterpret_cast<pick_first_lb_policy*>(policy);
-  GPR_ASSERT(!p->shutdown);
-  GPR_ASSERT(policy->request_reresolution == nullptr);
-  policy->request_reresolution = request_reresolution;
-}
+//
+// factory
+//
 
-static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = {
-    pf_destroy,
-    pf_shutdown_locked,
-    pf_pick_locked,
-    pf_cancel_pick_locked,
-    pf_cancel_picks_locked,
-    pf_ping_one_locked,
-    pf_exit_idle_locked,
-    pf_check_connectivity_locked,
-    pf_notify_on_state_change_locked,
-    pf_update_locked,
-    pf_set_reresolve_closure_locked};
-
-static void pick_first_factory_ref(grpc_lb_policy_factory* factory) {}
-
-static void pick_first_factory_unref(grpc_lb_policy_factory* factory) {}
-
-static grpc_lb_policy* create_pick_first(grpc_lb_policy_factory* factory,
-                                         grpc_lb_policy_args* args) {
-  GPR_ASSERT(args->client_channel_factory != nullptr);
-  pick_first_lb_policy* p =
-      static_cast<pick_first_lb_policy*>(gpr_zalloc(sizeof(*p)));
-  if (grpc_lb_pick_first_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "Pick First %p created.", (void*)p);
+class PickFirstFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    return OrphanablePtr<LoadBalancingPolicy>(New<PickFirst>(args));
   }
-  pf_update_locked(&p->base, args);
-  grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable, args->combiner);
-  grpc_subchannel_index_ref();
-  return &p->base;
-}
 
-static const grpc_lb_policy_factory_vtable pick_first_factory_vtable = {
-    pick_first_factory_ref, pick_first_factory_unref, create_pick_first,
-    "pick_first"};
+  const char* name() const override { return "pick_first"; }
+};
 
-static grpc_lb_policy_factory pick_first_lb_policy_factory = {
-    &pick_first_factory_vtable};
+}  // namespace
 
-static grpc_lb_policy_factory* pick_first_lb_factory_create() {
-  return &pick_first_lb_policy_factory;
-}
-
-/* Plugin registration */
+}  // namespace grpc_core
 
 void grpc_lb_policy_pick_first_init() {
-  grpc_register_lb_policy(pick_first_lb_factory_create());
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::PickFirstFactory>()));
 }
 
 void grpc_lb_policy_pick_first_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
index cefd0d8..e534131 100644
--- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
@@ -24,6 +24,8 @@
  * updates. Note however that updates will start picking from the beginning of
  * the updated list. */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -40,34 +42,94 @@
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-grpc_core::TraceFlag grpc_lb_round_robin_trace(false, "round_robin");
+namespace grpc_core {
 
-typedef struct round_robin_lb_policy {
-  /** base policy: must be first */
-  grpc_lb_policy base;
+TraceFlag grpc_lb_round_robin_trace(false, "round_robin");
 
-  grpc_lb_subchannel_list* subchannel_list;
+namespace {
 
+//
+// round_robin LB policy
+//
+
+class RoundRobin : public LoadBalancingPolicy {
+ public:
+  explicit RoundRobin(const Args& args);
+
+  void UpdateLocked(const grpc_channel_args& args) override;
+  bool PickLocked(PickState* pick) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) override;
+  void ExitIdleLocked() override;
+
+ private:
+  ~RoundRobin();
+
+  void ShutdownLocked() override;
+
+  void StartPickingLocked();
+  size_t GetNextReadySubchannelIndexLocked();
+  void UpdateLastReadySubchannelIndexLocked(size_t last_ready_index);
+  void UpdateConnectivityStatusLocked(grpc_lb_subchannel_data* sd,
+                                      grpc_error* error);
+
+  static void OnConnectivityChangedLocked(void* arg, grpc_error* error);
+
+  void SubchannelListRefForConnectivityWatch(
+      grpc_lb_subchannel_list* subchannel_list, const char* reason);
+  void SubchannelListUnrefForConnectivityWatch(
+      grpc_lb_subchannel_list* subchannel_list, const char* reason);
+
+  /** list of subchannels */
+  grpc_lb_subchannel_list* subchannel_list_ = nullptr;
   /** have we started picking? */
-  bool started_picking;
+  bool started_picking_ = false;
   /** are we shutting down? */
-  bool shutdown;
+  bool shutdown_ = false;
   /** List of picks that are waiting on connectivity */
-  grpc_lb_policy_pick_state* pending_picks;
-
+  PickState* pending_picks_ = nullptr;
   /** our connectivity state tracker */
-  grpc_connectivity_state_tracker state_tracker;
-
+  grpc_connectivity_state_tracker state_tracker_;
   /** Index into subchannels for last pick. */
-  size_t last_ready_subchannel_index;
-
+  size_t last_ready_subchannel_index_ = 0;
   /** Latest version of the subchannel list.
    * Subchannel connectivity callbacks will only promote updated subchannel
    * lists if they equal \a latest_pending_subchannel_list. In other words,
    * racing callbacks that reference outdated subchannel lists won't perform any
    * update. */
-  grpc_lb_subchannel_list* latest_pending_subchannel_list;
-} round_robin_lb_policy;
+  grpc_lb_subchannel_list* latest_pending_subchannel_list_ = nullptr;
+};
+
+RoundRobin::RoundRobin(const Args& args) : LoadBalancingPolicy(args) {
+  GPR_ASSERT(args.client_channel_factory != nullptr);
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
+                               "round_robin");
+  UpdateLocked(*args.args);
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "[RR %p] Created with %" PRIuPTR " subchannels", this,
+            subchannel_list_->num_subchannels);
+  }
+  grpc_subchannel_index_ref();
+}
+
+RoundRobin::~RoundRobin() {
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "[RR %p] Destroying Round Robin policy", this);
+  }
+  GPR_ASSERT(subchannel_list_ == nullptr);
+  GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
+  GPR_ASSERT(pending_picks_ == nullptr);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  grpc_subchannel_index_unref();
+}
 
 /** Returns the index into p->subchannel_list->subchannels of the next
  * subchannel in READY state, or p->subchannel_list->num_subchannels if no
@@ -75,195 +137,190 @@
  *
  * Note that this function does *not* update p->last_ready_subchannel_index.
  * The caller must do that if it returns a pick. */
-static size_t get_next_ready_subchannel_index_locked(
-    const round_robin_lb_policy* p) {
-  GPR_ASSERT(p->subchannel_list != nullptr);
+size_t RoundRobin::GetNextReadySubchannelIndexLocked() {
+  GPR_ASSERT(subchannel_list_ != nullptr);
   if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_INFO,
-            "[RR %p] getting next ready subchannel (out of %lu), "
-            "last_ready_subchannel_index=%lu",
-            (void*)p,
-            static_cast<unsigned long>(p->subchannel_list->num_subchannels),
-            static_cast<unsigned long>(p->last_ready_subchannel_index));
+            "[RR %p] getting next ready subchannel (out of %" PRIuPTR
+            "), "
+            "last_ready_subchannel_index=%" PRIuPTR,
+            this, subchannel_list_->num_subchannels,
+            last_ready_subchannel_index_);
   }
-  for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) {
-    const size_t index = (i + p->last_ready_subchannel_index + 1) %
-                         p->subchannel_list->num_subchannels;
+  for (size_t i = 0; i < subchannel_list_->num_subchannels; ++i) {
+    const size_t index = (i + last_ready_subchannel_index_ + 1) %
+                         subchannel_list_->num_subchannels;
     if (grpc_lb_round_robin_trace.enabled()) {
       gpr_log(
           GPR_DEBUG,
-          "[RR %p] checking subchannel %p, subchannel_list %p, index %lu: "
-          "state=%s",
-          (void*)p, (void*)p->subchannel_list->subchannels[index].subchannel,
-          (void*)p->subchannel_list, static_cast<unsigned long>(index),
+          "[RR %p] checking subchannel %p, subchannel_list %p, index %" PRIuPTR
+          ": state=%s",
+          this, subchannel_list_->subchannels[index].subchannel,
+          subchannel_list_, index,
           grpc_connectivity_state_name(
-              p->subchannel_list->subchannels[index].curr_connectivity_state));
+              subchannel_list_->subchannels[index].curr_connectivity_state));
     }
-    if (p->subchannel_list->subchannels[index].curr_connectivity_state ==
+    if (subchannel_list_->subchannels[index].curr_connectivity_state ==
         GRPC_CHANNEL_READY) {
       if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(GPR_DEBUG,
-                "[RR %p] found next ready subchannel (%p) at index %lu of "
-                "subchannel_list %p",
-                (void*)p,
-                (void*)p->subchannel_list->subchannels[index].subchannel,
-                static_cast<unsigned long>(index), (void*)p->subchannel_list);
+                "[RR %p] found next ready subchannel (%p) at index %" PRIuPTR
+                " of subchannel_list %p",
+                this, subchannel_list_->subchannels[index].subchannel, index,
+                subchannel_list_);
       }
       return index;
     }
   }
   if (grpc_lb_round_robin_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "[RR %p] no subchannels in ready state", (void*)p);
+    gpr_log(GPR_DEBUG, "[RR %p] no subchannels in ready state", this);
   }
-  return p->subchannel_list->num_subchannels;
+  return subchannel_list_->num_subchannels;
 }
 
-// Sets p->last_ready_subchannel_index to last_ready_index.
-static void update_last_ready_subchannel_index_locked(round_robin_lb_policy* p,
-                                                      size_t last_ready_index) {
-  GPR_ASSERT(last_ready_index < p->subchannel_list->num_subchannels);
-  p->last_ready_subchannel_index = last_ready_index;
+// Sets last_ready_subchannel_index_ to last_ready_index.
+void RoundRobin::UpdateLastReadySubchannelIndexLocked(size_t last_ready_index) {
+  GPR_ASSERT(last_ready_index < subchannel_list_->num_subchannels);
+  last_ready_subchannel_index_ = last_ready_index;
   if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG,
-            "[RR %p] setting last_ready_subchannel_index=%lu (SC %p, CSC %p)",
-            (void*)p, static_cast<unsigned long>(last_ready_index),
-            (void*)p->subchannel_list->subchannels[last_ready_index].subchannel,
-            (void*)p->subchannel_list->subchannels[last_ready_index]
+            "[RR %p] setting last_ready_subchannel_index=%" PRIuPTR
+            " (SC %p, CSC %p)",
+            this, last_ready_index,
+            subchannel_list_->subchannels[last_ready_index].subchannel,
+            subchannel_list_->subchannels[last_ready_index]
                 .connected_subchannel.get());
   }
 }
 
-static void rr_destroy(grpc_lb_policy* pol) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  if (grpc_lb_round_robin_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "[RR %p] Destroying Round Robin policy at %p",
-            (void*)pol, (void*)pol);
-  }
-  GPR_ASSERT(p->subchannel_list == nullptr);
-  GPR_ASSERT(p->latest_pending_subchannel_list == nullptr);
-  grpc_connectivity_state_destroy(&p->state_tracker);
-  grpc_subchannel_index_unref();
-  gpr_free(p);
-}
-
-static void rr_shutdown_locked(grpc_lb_policy* pol,
-                               grpc_lb_policy* new_policy) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
-  if (grpc_lb_round_robin_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "[RR %p] Shutting down", p);
-  }
-  p->shutdown = true;
-  grpc_lb_policy_pick_state* pick;
-  while ((pick = p->pending_picks) != nullptr) {
-    p->pending_picks = pick->next;
-    if (new_policy != nullptr) {
-      // Hand off to new LB policy.
-      if (grpc_lb_policy_pick_locked(new_policy, pick)) {
-        // Synchronous return; schedule callback.
-        GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
-      }
-    } else {
-      pick->connected_subchannel.reset();
-      GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error));
+void RoundRobin::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    if (new_policy->PickLocked(pick)) {
+      // Synchronous return, schedule closure.
+      GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
     }
   }
-  grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_SHUTDOWN,
+}
+
+void RoundRobin::ShutdownLocked() {
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "[RR %p] Shutting down", this);
+  }
+  shutdown_ = true;
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    pick->connected_subchannel.reset();
+    GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error));
+  }
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
                               GRPC_ERROR_REF(error), "rr_shutdown");
-  if (p->subchannel_list != nullptr) {
-    grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+  if (subchannel_list_ != nullptr) {
+    grpc_lb_subchannel_list_shutdown_and_unref(subchannel_list_,
                                                "sl_shutdown_rr_shutdown");
-    p->subchannel_list = nullptr;
+    subchannel_list_ = nullptr;
   }
-  if (p->latest_pending_subchannel_list != nullptr) {
+  if (latest_pending_subchannel_list_ != nullptr) {
     grpc_lb_subchannel_list_shutdown_and_unref(
-        p->latest_pending_subchannel_list, "sl_shutdown_pending_rr_shutdown");
-    p->latest_pending_subchannel_list = nullptr;
+        latest_pending_subchannel_list_, "sl_shutdown_pending_rr_shutdown");
+    latest_pending_subchannel_list_ = nullptr;
   }
-  grpc_lb_policy_try_reresolve(&p->base, &grpc_lb_round_robin_trace,
-                               GRPC_ERROR_CANCELLED);
+  TryReresolutionLocked(&grpc_lb_round_robin_trace, GRPC_ERROR_CANCELLED);
   GRPC_ERROR_UNREF(error);
 }
 
-static void rr_cancel_pick_locked(grpc_lb_policy* pol,
-                                  grpc_lb_policy_pick_state* pick,
-                                  grpc_error* error) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  grpc_lb_policy_pick_state* pp = p->pending_picks;
-  p->pending_picks = nullptr;
+void RoundRobin::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PickState* pp = pending_picks_;
+  pending_picks_ = nullptr;
   while (pp != nullptr) {
-    grpc_lb_policy_pick_state* next = pp->next;
+    PickState* next = pp->next;
     if (pp == pick) {
       pick->connected_subchannel.reset();
       GRPC_CLOSURE_SCHED(pick->on_complete,
                          GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                             "Pick cancelled", &error, 1));
+                             "Pick Cancelled", &error, 1));
     } else {
-      pp->next = p->pending_picks;
-      p->pending_picks = pp;
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
     }
     pp = next;
   }
   GRPC_ERROR_UNREF(error);
 }
 
-static void rr_cancel_picks_locked(grpc_lb_policy* pol,
-                                   uint32_t initial_metadata_flags_mask,
-                                   uint32_t initial_metadata_flags_eq,
-                                   grpc_error* error) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  grpc_lb_policy_pick_state* pick = p->pending_picks;
-  p->pending_picks = nullptr;
+void RoundRobin::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                           uint32_t initial_metadata_flags_eq,
+                                           grpc_error* error) {
+  PickState* pick = pending_picks_;
+  pending_picks_ = nullptr;
   while (pick != nullptr) {
-    grpc_lb_policy_pick_state* next = pick->next;
+    PickState* next = pick->next;
     if ((pick->initial_metadata_flags & initial_metadata_flags_mask) ==
         initial_metadata_flags_eq) {
       pick->connected_subchannel.reset();
       GRPC_CLOSURE_SCHED(pick->on_complete,
                          GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                             "Pick cancelled", &error, 1));
+                             "Pick Cancelled", &error, 1));
     } else {
-      pick->next = p->pending_picks;
-      p->pending_picks = pick;
+      pick->next = pending_picks_;
+      pending_picks_ = pick;
     }
     pick = next;
   }
   GRPC_ERROR_UNREF(error);
 }
 
-static void start_picking_locked(round_robin_lb_policy* p) {
-  p->started_picking = true;
-  for (size_t i = 0; i < p->subchannel_list->num_subchannels; i++) {
-    if (p->subchannel_list->subchannels[i].subchannel != nullptr) {
-      grpc_lb_subchannel_list_ref_for_connectivity_watch(p->subchannel_list,
-                                                         "connectivity_watch");
+void RoundRobin::SubchannelListRefForConnectivityWatch(
+    grpc_lb_subchannel_list* subchannel_list, const char* reason) {
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is ready and the subchannel_list code has been
+  // converted to a C++ API, find a way to hold the RefCountedPtr<>
+  // somewhere (maybe in the subchannel_data object) instead of doing
+  // this manually.
+  auto self = Ref(DEBUG_LOCATION, reason);
+  self.release();
+  grpc_lb_subchannel_list_ref(subchannel_list, reason);
+}
+
+void RoundRobin::SubchannelListUnrefForConnectivityWatch(
+    grpc_lb_subchannel_list* subchannel_list, const char* reason) {
+  Unref(DEBUG_LOCATION, reason);
+  grpc_lb_subchannel_list_unref(subchannel_list, reason);
+}
+
+void RoundRobin::StartPickingLocked() {
+  started_picking_ = true;
+  for (size_t i = 0; i < subchannel_list_->num_subchannels; i++) {
+    if (subchannel_list_->subchannels[i].subchannel != nullptr) {
+      SubchannelListRefForConnectivityWatch(subchannel_list_,
+                                            "connectivity_watch");
       grpc_lb_subchannel_data_start_connectivity_watch(
-          &p->subchannel_list->subchannels[i]);
+          &subchannel_list_->subchannels[i]);
     }
   }
 }
 
-static void rr_exit_idle_locked(grpc_lb_policy* pol) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  if (!p->started_picking) {
-    start_picking_locked(p);
+void RoundRobin::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
   }
 }
 
-static int rr_pick_locked(grpc_lb_policy* pol,
-                          grpc_lb_policy_pick_state* pick) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
+bool RoundRobin::PickLocked(PickState* pick) {
   if (grpc_lb_round_robin_trace.enabled()) {
-    gpr_log(GPR_INFO, "[RR %p] Trying to pick (shutdown: %d)", pol,
-            p->shutdown);
+    gpr_log(GPR_DEBUG, "[RR %p] Trying to pick (shutdown: %d)", this,
+            shutdown_);
   }
-  GPR_ASSERT(!p->shutdown);
-  if (p->subchannel_list != nullptr) {
-    const size_t next_ready_index = get_next_ready_subchannel_index_locked(p);
-    if (next_ready_index < p->subchannel_list->num_subchannels) {
+  GPR_ASSERT(!shutdown_);
+  if (subchannel_list_ != nullptr) {
+    const size_t next_ready_index = GetNextReadySubchannelIndexLocked();
+    if (next_ready_index < subchannel_list_->num_subchannels) {
       /* readily available, report right away */
       grpc_lb_subchannel_data* sd =
-          &p->subchannel_list->subchannels[next_ready_index];
+          &subchannel_list_->subchannels[next_ready_index];
       pick->connected_subchannel = sd->connected_subchannel;
       if (pick->user_data != nullptr) {
         *pick->user_data = sd->user_data;
@@ -273,24 +330,24 @@
             GPR_DEBUG,
             "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, "
             "index %" PRIuPTR ")",
-            p, sd->subchannel, pick->connected_subchannel.get(),
+            this, sd->subchannel, pick->connected_subchannel.get(),
             sd->subchannel_list, next_ready_index);
       }
       /* only advance the last picked pointer if the selection was used */
-      update_last_ready_subchannel_index_locked(p, next_ready_index);
-      return 1;
+      UpdateLastReadySubchannelIndexLocked(next_ready_index);
+      return true;
     }
   }
   /* no pick currently available. Save for later in list of pending picks */
-  if (!p->started_picking) {
-    start_picking_locked(p);
+  if (!started_picking_) {
+    StartPickingLocked();
   }
-  pick->next = p->pending_picks;
-  p->pending_picks = pick;
-  return 0;
+  pick->next = pending_picks_;
+  pending_picks_ = pick;
+  return false;
 }
 
-static void update_state_counters_locked(grpc_lb_subchannel_data* sd) {
+void UpdateStateCountersLocked(grpc_lb_subchannel_data* sd) {
   grpc_lb_subchannel_list* subchannel_list = sd->subchannel_list;
   GPR_ASSERT(sd->prev_connectivity_state != GRPC_CHANNEL_SHUTDOWN);
   GPR_ASSERT(sd->curr_connectivity_state != GRPC_CHANNEL_SHUTDOWN);
@@ -318,8 +375,8 @@
  * (the grpc_lb_subchannel_data associated with the updated subchannel) and the
  * subchannel list \a sd belongs to (sd->subchannel_list). \a error will be used
  * only if the policy transitions to state TRANSIENT_FAILURE. */
-static void update_lb_connectivity_status_locked(grpc_lb_subchannel_data* sd,
-                                                 grpc_error* error) {
+void RoundRobin::UpdateConnectivityStatusLocked(grpc_lb_subchannel_data* sd,
+                                                grpc_error* error) {
   /* In priority order. The first rule to match terminates the search (ie, if we
    * are on rule n, all previous rules were unfulfilled).
    *
@@ -335,64 +392,61 @@
    *           subchannel_list->num_subchannels.
    */
   grpc_lb_subchannel_list* subchannel_list = sd->subchannel_list;
-  round_robin_lb_policy* p =
-      reinterpret_cast<round_robin_lb_policy*>(subchannel_list->policy);
   GPR_ASSERT(sd->curr_connectivity_state != GRPC_CHANNEL_IDLE);
   if (subchannel_list->num_ready > 0) {
     /* 1) READY */
-    grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_READY,
+    grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_READY,
                                 GRPC_ERROR_NONE, "rr_ready");
   } else if (sd->curr_connectivity_state == GRPC_CHANNEL_CONNECTING) {
     /* 2) CONNECTING */
-    grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_CONNECTING,
+    grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_CONNECTING,
                                 GRPC_ERROR_NONE, "rr_connecting");
   } else if (subchannel_list->num_transient_failures ==
              subchannel_list->num_subchannels) {
     /* 3) TRANSIENT_FAILURE */
-    grpc_connectivity_state_set(&p->state_tracker,
-                                GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                GRPC_ERROR_REF(error), "rr_transient_failure");
+    grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+                                GRPC_ERROR_REF(error),
+                                "rr_exhausted_subchannels");
   }
   GRPC_ERROR_UNREF(error);
 }
 
-static void rr_connectivity_changed_locked(void* arg, grpc_error* error) {
+void RoundRobin::OnConnectivityChangedLocked(void* arg, grpc_error* error) {
   grpc_lb_subchannel_data* sd = static_cast<grpc_lb_subchannel_data*>(arg);
-  round_robin_lb_policy* p =
-      reinterpret_cast<round_robin_lb_policy*>(sd->subchannel_list->policy);
+  RoundRobin* p = static_cast<RoundRobin*>(sd->subchannel_list->policy);
   if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(
         GPR_DEBUG,
         "[RR %p] connectivity changed for subchannel %p, subchannel_list %p: "
         "prev_state=%s new_state=%s p->shutdown=%d "
         "sd->subchannel_list->shutting_down=%d error=%s",
-        (void*)p, (void*)sd->subchannel, (void*)sd->subchannel_list,
+        p, sd->subchannel, sd->subchannel_list,
         grpc_connectivity_state_name(sd->prev_connectivity_state),
         grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe),
-        p->shutdown, sd->subchannel_list->shutting_down,
+        p->shutdown_, sd->subchannel_list->shutting_down,
         grpc_error_string(error));
   }
   GPR_ASSERT(sd->subchannel != nullptr);
   // If the policy is shutting down, unref and return.
-  if (p->shutdown) {
+  if (p->shutdown_) {
     grpc_lb_subchannel_data_stop_connectivity_watch(sd);
     grpc_lb_subchannel_data_unref_subchannel(sd, "rr_shutdown");
-    grpc_lb_subchannel_list_unref_for_connectivity_watch(sd->subchannel_list,
-                                                         "rr_shutdown");
+    p->SubchannelListUnrefForConnectivityWatch(sd->subchannel_list,
+                                               "rr_shutdown");
     return;
   }
   // If the subchannel list is shutting down, stop watching.
   if (sd->subchannel_list->shutting_down || error == GRPC_ERROR_CANCELLED) {
     grpc_lb_subchannel_data_stop_connectivity_watch(sd);
     grpc_lb_subchannel_data_unref_subchannel(sd, "rr_sl_shutdown");
-    grpc_lb_subchannel_list_unref_for_connectivity_watch(sd->subchannel_list,
-                                                         "rr_sl_shutdown");
+    p->SubchannelListUnrefForConnectivityWatch(sd->subchannel_list,
+                                               "rr_sl_shutdown");
     return;
   }
   // If we're still here, the notification must be for a subchannel in
   // either the current or latest pending subchannel lists.
-  GPR_ASSERT(sd->subchannel_list == p->subchannel_list ||
-             sd->subchannel_list == p->latest_pending_subchannel_list);
+  GPR_ASSERT(sd->subchannel_list == p->subchannel_list_ ||
+             sd->subchannel_list == p->latest_pending_subchannel_list_);
   GPR_ASSERT(sd->pending_connectivity_state_unsafe != GRPC_CHANNEL_SHUTDOWN);
   // Now that we're inside the combiner, copy the pending connectivity
   // state (which was set by the connectivity state watcher) to
@@ -409,8 +463,7 @@
                 "Requesting re-resolution",
                 p, sd->subchannel);
       }
-      grpc_lb_policy_try_reresolve(&p->base, &grpc_lb_round_robin_trace,
-                                   GRPC_ERROR_NONE);
+      p->TryReresolutionLocked(&grpc_lb_round_robin_trace, GRPC_ERROR_NONE);
       break;
     }
     case GRPC_CHANNEL_READY: {
@@ -418,49 +471,47 @@
         sd->connected_subchannel =
             grpc_subchannel_get_connected_subchannel(sd->subchannel);
       }
-      if (sd->subchannel_list != p->subchannel_list) {
-        // promote sd->subchannel_list to p->subchannel_list.
+      if (sd->subchannel_list != p->subchannel_list_) {
+        // promote sd->subchannel_list to p->subchannel_list_.
         // sd->subchannel_list must be equal to
-        // p->latest_pending_subchannel_list because we have already filtered
+        // p->latest_pending_subchannel_list_ because we have already filtered
         // for sds belonging to outdated subchannel lists.
-        GPR_ASSERT(sd->subchannel_list == p->latest_pending_subchannel_list);
+        GPR_ASSERT(sd->subchannel_list == p->latest_pending_subchannel_list_);
         GPR_ASSERT(!sd->subchannel_list->shutting_down);
         if (grpc_lb_round_robin_trace.enabled()) {
-          const unsigned long num_subchannels =
-              p->subchannel_list != nullptr
-                  ? static_cast<unsigned long>(
-                        p->subchannel_list->num_subchannels)
+          const size_t num_subchannels =
+              p->subchannel_list_ != nullptr
+                  ? p->subchannel_list_->num_subchannels
                   : 0;
           gpr_log(GPR_DEBUG,
-                  "[RR %p] phasing out subchannel list %p (size %lu) in favor "
-                  "of %p (size %lu)",
-                  p, p->subchannel_list, num_subchannels, sd->subchannel_list,
+                  "[RR %p] phasing out subchannel list %p (size %" PRIuPTR
+                  ") in favor of %p (size %" PRIuPTR ")",
+                  p, p->subchannel_list_, num_subchannels, sd->subchannel_list,
                   num_subchannels);
         }
-        if (p->subchannel_list != nullptr) {
+        if (p->subchannel_list_ != nullptr) {
           // dispose of the current subchannel_list
-          grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+          grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list_,
                                                      "sl_phase_out_shutdown");
         }
-        p->subchannel_list = p->latest_pending_subchannel_list;
-        p->latest_pending_subchannel_list = nullptr;
+        p->subchannel_list_ = p->latest_pending_subchannel_list_;
+        p->latest_pending_subchannel_list_ = nullptr;
       }
       /* at this point we know there's at least one suitable subchannel. Go
        * ahead and pick one and notify the pending suitors in
-       * p->pending_picks. This preemptively replicates rr_pick()'s actions.
-       */
-      const size_t next_ready_index = get_next_ready_subchannel_index_locked(p);
-      GPR_ASSERT(next_ready_index < p->subchannel_list->num_subchannels);
+       * p->pending_picks. This preemptively replicates rr_pick()'s actions. */
+      const size_t next_ready_index = p->GetNextReadySubchannelIndexLocked();
+      GPR_ASSERT(next_ready_index < p->subchannel_list_->num_subchannels);
       grpc_lb_subchannel_data* selected =
-          &p->subchannel_list->subchannels[next_ready_index];
-      if (p->pending_picks != nullptr) {
+          &p->subchannel_list_->subchannels[next_ready_index];
+      if (p->pending_picks_ != nullptr) {
         // if the selected subchannel is going to be used for the pending
         // picks, update the last picked pointer
-        update_last_ready_subchannel_index_locked(p, next_ready_index);
+        p->UpdateLastReadySubchannelIndexLocked(next_ready_index);
       }
-      grpc_lb_policy_pick_state* pick;
-      while ((pick = p->pending_picks)) {
-        p->pending_picks = pick->next;
+      PickState* pick;
+      while ((pick = p->pending_picks_)) {
+        p->pending_picks_ = pick->next;
         pick->connected_subchannel = selected->connected_subchannel;
         if (pick->user_data != nullptr) {
           *pick->user_data = selected->user_data;
@@ -468,10 +519,9 @@
         if (grpc_lb_round_robin_trace.enabled()) {
           gpr_log(GPR_DEBUG,
                   "[RR %p] Fulfilling pending pick. Target <-- subchannel %p "
-                  "(subchannel_list %p, index %lu)",
-                  (void*)p, (void*)selected->subchannel,
-                  (void*)p->subchannel_list,
-                  static_cast<unsigned long>(next_ready_index));
+                  "(subchannel_list %p, index %" PRIuPTR ")",
+                  p, selected->subchannel, p->subchannel_list_,
+                  next_ready_index);
         }
         GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
       }
@@ -482,40 +532,34 @@
     case GRPC_CHANNEL_CONNECTING:
     case GRPC_CHANNEL_IDLE:;  // fallthrough
   }
-  // Update state counters and new overall state.
-  update_state_counters_locked(sd);
+  // Update state counters.
+  UpdateStateCountersLocked(sd);
   // Only update connectivity based on the selected subchannel list.
-  if (sd->subchannel_list == p->subchannel_list) {
-    update_lb_connectivity_status_locked(sd, GRPC_ERROR_REF(error));
+  if (sd->subchannel_list == p->subchannel_list_) {
+    p->UpdateConnectivityStatusLocked(sd, GRPC_ERROR_REF(error));
   }
   // Renew notification.
   grpc_lb_subchannel_data_start_connectivity_watch(sd);
 }
 
-static grpc_connectivity_state rr_check_connectivity_locked(
-    grpc_lb_policy* pol, grpc_error** error) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  return grpc_connectivity_state_get(&p->state_tracker, error);
+grpc_connectivity_state RoundRobin::CheckConnectivityLocked(
+    grpc_error** error) {
+  return grpc_connectivity_state_get(&state_tracker_, error);
 }
 
-static void rr_notify_on_state_change_locked(grpc_lb_policy* pol,
-                                             grpc_connectivity_state* current,
-                                             grpc_closure* notify) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  grpc_connectivity_state_notify_on_state_change(&p->state_tracker, current,
+void RoundRobin::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                           grpc_closure* notify) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
                                                  notify);
 }
 
-static void rr_ping_one_locked(grpc_lb_policy* pol, grpc_closure* on_initiate,
+void RoundRobin::PingOneLocked(grpc_closure* on_initiate,
                                grpc_closure* on_ack) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(pol);
-  const size_t next_ready_index = get_next_ready_subchannel_index_locked(p);
-  if (next_ready_index < p->subchannel_list->num_subchannels) {
+  const size_t next_ready_index = GetNextReadySubchannelIndexLocked();
+  if (next_ready_index < subchannel_list_->num_subchannels) {
     grpc_lb_subchannel_data* selected =
-        &p->subchannel_list->subchannels[next_ready_index];
-    grpc_core::RefCountedPtr<grpc_core::ConnectedSubchannel> target =
-        selected->connected_subchannel;
-    target->Ping(on_initiate, on_ack);
+        &subchannel_list_->subchannels[next_ready_index];
+    selected->connected_subchannel->Ping(on_initiate, on_ack);
   } else {
     GRPC_CLOSURE_SCHED(on_initiate, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                                         "Round Robin not connected"));
@@ -524,45 +568,41 @@
   }
 }
 
-static void rr_update_locked(grpc_lb_policy* policy,
-                             const grpc_lb_policy_args* args) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(policy);
-  const grpc_arg* arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
+void RoundRobin::UpdateLocked(const grpc_channel_args& args) {
+  const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
   if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
-    gpr_log(GPR_ERROR, "[RR %p] update provided no addresses; ignoring", p);
+    gpr_log(GPR_ERROR, "[RR %p] update provided no addresses; ignoring", this);
     // If we don't have a current subchannel list, go into TRANSIENT_FAILURE.
     // Otherwise, keep using the current subchannel list (ignore this update).
-    if (p->subchannel_list == nullptr) {
+    if (subchannel_list_ == nullptr) {
       grpc_connectivity_state_set(
-          &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
           GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
           "rr_update_missing");
     }
     return;
   }
-  grpc_lb_addresses* addresses =
-      static_cast<grpc_lb_addresses*>(arg->value.pointer.p);
+  grpc_lb_addresses* addresses = (grpc_lb_addresses*)arg->value.pointer.p;
   if (grpc_lb_round_robin_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "[RR %p] received update with %" PRIuPTR " addresses", p,
-            addresses->num_addresses);
+    gpr_log(GPR_DEBUG, "[RR %p] received update with %" PRIuPTR " addresses",
+            this, addresses->num_addresses);
   }
   grpc_lb_subchannel_list* subchannel_list = grpc_lb_subchannel_list_create(
-      &p->base, &grpc_lb_round_robin_trace, addresses, args,
-      rr_connectivity_changed_locked);
+      this, &grpc_lb_round_robin_trace, addresses, combiner(),
+      client_channel_factory(), args, &RoundRobin::OnConnectivityChangedLocked);
   if (subchannel_list->num_subchannels == 0) {
     grpc_connectivity_state_set(
-        &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+        &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
         "rr_update_empty");
-    if (p->subchannel_list != nullptr) {
-      grpc_lb_subchannel_list_shutdown_and_unref(p->subchannel_list,
+    if (subchannel_list_ != nullptr) {
+      grpc_lb_subchannel_list_shutdown_and_unref(subchannel_list_,
                                                  "sl_shutdown_empty_update");
     }
-    p->subchannel_list = subchannel_list;  // empty list
+    subchannel_list_ = subchannel_list;  // empty list
     return;
   }
-  if (p->started_picking) {
+  if (started_picking_) {
     for (size_t i = 0; i < subchannel_list->num_subchannels; ++i) {
       const grpc_connectivity_state subchannel_state =
           grpc_subchannel_check_connectivity(
@@ -587,96 +627,61 @@
         ++subchannel_list->num_transient_failures;
       }
     }
-    if (p->latest_pending_subchannel_list != nullptr) {
+    if (latest_pending_subchannel_list_ != nullptr) {
       if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "[RR %p] Shutting down latest pending subchannel list %p, "
                 "about to be replaced by newer latest %p",
-                (void*)p, (void*)p->latest_pending_subchannel_list,
-                (void*)subchannel_list);
+                this, latest_pending_subchannel_list_, subchannel_list);
       }
       grpc_lb_subchannel_list_shutdown_and_unref(
-          p->latest_pending_subchannel_list, "sl_outdated");
+          latest_pending_subchannel_list_, "sl_outdated");
     }
-    p->latest_pending_subchannel_list = subchannel_list;
+    latest_pending_subchannel_list_ = subchannel_list;
     for (size_t i = 0; i < subchannel_list->num_subchannels; ++i) {
       /* Watch every new subchannel. A subchannel list becomes active the
        * moment one of its subchannels is READY. At that moment, we swap
        * p->subchannel_list for sd->subchannel_list, provided the subchannel
        * list is still valid (ie, isn't shutting down) */
-      grpc_lb_subchannel_list_ref_for_connectivity_watch(subchannel_list,
-                                                         "connectivity_watch");
+      SubchannelListRefForConnectivityWatch(subchannel_list,
+                                            "connectivity_watch");
       grpc_lb_subchannel_data_start_connectivity_watch(
           &subchannel_list->subchannels[i]);
     }
   } else {
     // The policy isn't picking yet. Save the update for later, disposing of
     // previous version if any.
-    if (p->subchannel_list != nullptr) {
+    if (subchannel_list_ != nullptr) {
       grpc_lb_subchannel_list_shutdown_and_unref(
-          p->subchannel_list, "rr_update_before_started_picking");
+          subchannel_list_, "rr_update_before_started_picking");
     }
-    p->subchannel_list = subchannel_list;
+    subchannel_list_ = subchannel_list;
   }
 }
 
-static void rr_set_reresolve_closure_locked(
-    grpc_lb_policy* policy, grpc_closure* request_reresolution) {
-  round_robin_lb_policy* p = reinterpret_cast<round_robin_lb_policy*>(policy);
-  GPR_ASSERT(!p->shutdown);
-  GPR_ASSERT(policy->request_reresolution == nullptr);
-  policy->request_reresolution = request_reresolution;
-}
+//
+// factory
+//
 
-static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = {
-    rr_destroy,
-    rr_shutdown_locked,
-    rr_pick_locked,
-    rr_cancel_pick_locked,
-    rr_cancel_picks_locked,
-    rr_ping_one_locked,
-    rr_exit_idle_locked,
-    rr_check_connectivity_locked,
-    rr_notify_on_state_change_locked,
-    rr_update_locked,
-    rr_set_reresolve_closure_locked};
-
-static void round_robin_factory_ref(grpc_lb_policy_factory* factory) {}
-
-static void round_robin_factory_unref(grpc_lb_policy_factory* factory) {}
-
-static grpc_lb_policy* round_robin_create(grpc_lb_policy_factory* factory,
-                                          grpc_lb_policy_args* args) {
-  GPR_ASSERT(args->client_channel_factory != nullptr);
-  round_robin_lb_policy* p =
-      static_cast<round_robin_lb_policy*>(gpr_zalloc(sizeof(*p)));
-  grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable, args->combiner);
-  grpc_subchannel_index_ref();
-  grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE,
-                               "round_robin");
-  rr_update_locked(&p->base, args);
-  if (grpc_lb_round_robin_trace.enabled()) {
-    gpr_log(GPR_DEBUG, "[RR %p] Created with %lu subchannels", (void*)p,
-            static_cast<unsigned long>(p->subchannel_list->num_subchannels));
+class RoundRobinFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    return OrphanablePtr<LoadBalancingPolicy>(New<RoundRobin>(args));
   }
-  return &p->base;
-}
 
-static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = {
-    round_robin_factory_ref, round_robin_factory_unref, round_robin_create,
-    "round_robin"};
+  const char* name() const override { return "round_robin"; }
+};
 
-static grpc_lb_policy_factory round_robin_lb_policy_factory = {
-    &round_robin_factory_vtable};
+}  // namespace
 
-static grpc_lb_policy_factory* round_robin_lb_factory_create() {
-  return &round_robin_lb_policy_factory;
-}
-
-/* Plugin registration */
+}  // namespace grpc_core
 
 void grpc_lb_policy_round_robin_init() {
-  grpc_register_lb_policy(round_robin_lb_factory_create());
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::RoundRobinFactory>()));
 }
 
 void grpc_lb_policy_round_robin_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
index e35c5e8..79cb64c 100644
--- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -67,7 +69,7 @@
   }
   sd->connectivity_notification_pending = true;
   grpc_subchannel_notify_on_state_change(
-      sd->subchannel, sd->subchannel_list->policy->interested_parties,
+      sd->subchannel, sd->subchannel_list->policy->interested_parties(),
       &sd->pending_connectivity_state_unsafe,
       &sd->connectivity_changed_closure);
 }
@@ -88,9 +90,10 @@
 }
 
 grpc_lb_subchannel_list* grpc_lb_subchannel_list_create(
-    grpc_lb_policy* p, grpc_core::TraceFlag* tracer,
-    const grpc_lb_addresses* addresses, const grpc_lb_policy_args* args,
-    grpc_iomgr_cb_func connectivity_changed_cb) {
+    grpc_core::LoadBalancingPolicy* p, grpc_core::TraceFlag* tracer,
+    const grpc_lb_addresses* addresses, grpc_combiner* combiner,
+    grpc_client_channel_factory* client_channel_factory,
+    const grpc_channel_args& args, grpc_iomgr_cb_func connectivity_changed_cb) {
   grpc_lb_subchannel_list* subchannel_list =
       static_cast<grpc_lb_subchannel_list*>(
           gpr_zalloc(sizeof(*subchannel_list)));
@@ -118,12 +121,11 @@
     grpc_arg addr_arg =
         grpc_create_subchannel_address_arg(&addresses->addresses[i].address);
     grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(
-        args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg,
-        1);
+        &args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg, 1);
     gpr_free(addr_arg.value.string);
     sc_args.args = new_args;
     grpc_subchannel* subchannel = grpc_client_channel_factory_create_subchannel(
-        args->client_channel_factory, &sc_args);
+        client_channel_factory, &sc_args);
     grpc_channel_args_destroy(new_args);
     if (subchannel == nullptr) {
       // Subchannel could not be created.
@@ -154,7 +156,7 @@
     sd->subchannel = subchannel;
     GRPC_CLOSURE_INIT(&sd->connectivity_changed_closure,
                       connectivity_changed_cb, sd,
-                      grpc_combiner_scheduler(args->combiner));
+                      grpc_combiner_scheduler(combiner));
     // We assume that the current state is IDLE.  If not, we'll get a
     // callback telling us that.
     sd->prev_connectivity_state = GRPC_CHANNEL_IDLE;
@@ -212,18 +214,6 @@
   }
 }
 
-void grpc_lb_subchannel_list_ref_for_connectivity_watch(
-    grpc_lb_subchannel_list* subchannel_list, const char* reason) {
-  GRPC_LB_POLICY_REF(subchannel_list->policy, reason);
-  grpc_lb_subchannel_list_ref(subchannel_list, reason);
-}
-
-void grpc_lb_subchannel_list_unref_for_connectivity_watch(
-    grpc_lb_subchannel_list* subchannel_list, const char* reason) {
-  GRPC_LB_POLICY_UNREF(subchannel_list->policy, reason);
-  grpc_lb_subchannel_list_unref(subchannel_list, reason);
-}
-
 static void subchannel_data_cancel_connectivity_watch(
     grpc_lb_subchannel_data* sd, const char* reason) {
   if (sd->subchannel_list->tracer->enabled()) {
diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
index 91537f3..6889d59 100644
--- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
+++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 #include "src/core/ext/filters/client_channel/subchannel.h"
 #include "src/core/lib/debug/trace.h"
@@ -82,7 +84,7 @@
 
 struct grpc_lb_subchannel_list {
   /** backpointer to owning policy */
-  grpc_lb_policy* policy;
+  grpc_core::LoadBalancingPolicy* policy;
 
   grpc_core::TraceFlag* tracer;
 
@@ -115,9 +117,10 @@
 };
 
 grpc_lb_subchannel_list* grpc_lb_subchannel_list_create(
-    grpc_lb_policy* p, grpc_core::TraceFlag* tracer,
-    const grpc_lb_addresses* addresses, const grpc_lb_policy_args* args,
-    grpc_iomgr_cb_func connectivity_changed_cb);
+    grpc_core::LoadBalancingPolicy* p, grpc_core::TraceFlag* tracer,
+    const grpc_lb_addresses* addresses, grpc_combiner* combiner,
+    grpc_client_channel_factory* client_channel_factory,
+    const grpc_channel_args& args, grpc_iomgr_cb_func connectivity_changed_cb);
 
 void grpc_lb_subchannel_list_ref(grpc_lb_subchannel_list* subchannel_list,
                                  const char* reason);
@@ -125,13 +128,6 @@
 void grpc_lb_subchannel_list_unref(grpc_lb_subchannel_list* subchannel_list,
                                    const char* reason);
 
-/// Takes and releases refs needed for a connectivity notification.
-/// This includes a ref to subchannel_list and a weak ref to the LB policy.
-void grpc_lb_subchannel_list_ref_for_connectivity_watch(
-    grpc_lb_subchannel_list* subchannel_list, const char* reason);
-void grpc_lb_subchannel_list_unref_for_connectivity_watch(
-    grpc_lb_subchannel_list* subchannel_list, const char* reason);
-
 /// Mark subchannel_list as discarded. Unsubscribes all its subchannels. The
 /// connectivity state notification callback will ultimately unref it.
 void grpc_lb_subchannel_list_shutdown_and_unref(
diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.cc b/src/core/ext/filters/client_channel/lb_policy_factory.cc
index f2a800b..80646a1 100644
--- a/src/core/ext/filters/client_channel/lb_policy_factory.cc
+++ b/src/core/ext/filters/client_channel/lb_policy_factory.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -151,17 +153,3 @@
     return nullptr;
   return static_cast<grpc_lb_addresses*>(lb_addresses_arg->value.pointer.p);
 }
-
-void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) {
-  factory->vtable->ref(factory);
-}
-
-void grpc_lb_policy_factory_unref(grpc_lb_policy_factory* factory) {
-  factory->vtable->unref(factory);
-}
-
-grpc_lb_policy* grpc_lb_policy_factory_create_lb_policy(
-    grpc_lb_policy_factory* factory, grpc_lb_policy_args* args) {
-  if (factory == nullptr) return nullptr;
-  return factory->vtable->create_lb_policy(factory, args);
-}
diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.h b/src/core/ext/filters/client_channel/lb_policy_factory.h
index 9da231b..b8bbd32 100644
--- a/src/core/ext/filters/client_channel/lb_policy_factory.h
+++ b/src/core/ext/filters/client_channel/lb_policy_factory.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 
@@ -26,21 +28,20 @@
 #include "src/core/ext/filters/client_channel/lb_policy.h"
 #include "src/core/ext/filters/client_channel/uri_parser.h"
 
+//
+// representation of an LB address
+//
+
 // Channel arg key for grpc_lb_addresses.
 #define GRPC_ARG_LB_ADDRESSES "grpc.lb_addresses"
 
-typedef struct grpc_lb_policy_factory grpc_lb_policy_factory;
-typedef struct grpc_lb_policy_factory_vtable grpc_lb_policy_factory_vtable;
-
-struct grpc_lb_policy_factory {
-  const grpc_lb_policy_factory_vtable* vtable;
-};
-
 /** A resolved address alongside any LB related information associated with it.
  * \a user_data, if not NULL, contains opaque data meant to be consumed by the
  * gRPC LB policy. Note that no all LB policies support \a user_data as input.
  * Those who don't will simply ignore it and will correspondingly return NULL in
  * their namesake pick() output argument. */
+// TODO(roth): Once we figure out a better way of handling user_data in
+// LB policies, convert these structs to C++ classes.
 typedef struct grpc_lb_address {
   grpc_resolved_address address;
   bool is_balancer;
@@ -101,30 +102,27 @@
 grpc_lb_addresses* grpc_lb_addresses_find_channel_arg(
     const grpc_channel_args* channel_args);
 
-/** Arguments passed to LB policies. */
-struct grpc_lb_policy_args {
-  grpc_client_channel_factory* client_channel_factory;
-  grpc_channel_args* args;
-  grpc_combiner* combiner;
+//
+// LB policy factory
+//
+
+namespace grpc_core {
+
+class LoadBalancingPolicyFactory {
+ public:
+  /// Returns a new LB policy instance.
+  virtual OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const GRPC_ABSTRACT;
+
+  /// Returns the LB policy name that this factory provides.
+  /// Caller does NOT take ownership of result.
+  virtual const char* name() const GRPC_ABSTRACT;
+
+  virtual ~LoadBalancingPolicyFactory() {}
+
+  GRPC_ABSTRACT_BASE_CLASS
 };
 
-struct grpc_lb_policy_factory_vtable {
-  void (*ref)(grpc_lb_policy_factory* factory);
-  void (*unref)(grpc_lb_policy_factory* factory);
-
-  /** Implementation of grpc_lb_policy_factory_create_lb_policy */
-  grpc_lb_policy* (*create_lb_policy)(grpc_lb_policy_factory* factory,
-                                      grpc_lb_policy_args* args);
-
-  /** Name for the LB policy this factory implements */
-  const char* name;
-};
-
-void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory);
-void grpc_lb_policy_factory_unref(grpc_lb_policy_factory* factory);
-
-/** Create a lb_policy instance. */
-grpc_lb_policy* grpc_lb_policy_factory_create_lb_policy(
-    grpc_lb_policy_factory* factory, grpc_lb_policy_args* args);
+}  // namespace grpc_core
 
 #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H */
diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.cc b/src/core/ext/filters/client_channel/lb_policy_registry.cc
index 8414504..d651b11 100644
--- a/src/core/ext/filters/client_channel/lb_policy_registry.cc
+++ b/src/core/ext/filters/client_channel/lb_policy_registry.cc
@@ -16,55 +16,82 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 
 #include <string.h>
 
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
 
-#define MAX_POLICIES 10
+namespace grpc_core {
 
-static grpc_lb_policy_factory* g_all_of_the_lb_policies[MAX_POLICIES];
-static int g_number_of_lb_policies = 0;
+namespace {
 
-void grpc_lb_policy_registry_init(void) { g_number_of_lb_policies = 0; }
+class RegistryState {
+ public:
+  RegistryState() {}
 
-void grpc_lb_policy_registry_shutdown(void) {
-  int i;
-  for (i = 0; i < g_number_of_lb_policies; i++) {
-    grpc_lb_policy_factory_unref(g_all_of_the_lb_policies[i]);
-  }
-}
-
-void grpc_register_lb_policy(grpc_lb_policy_factory* factory) {
-  int i;
-  for (i = 0; i < g_number_of_lb_policies; i++) {
-    GPR_ASSERT(0 != gpr_stricmp(factory->vtable->name,
-                                g_all_of_the_lb_policies[i]->vtable->name));
-  }
-  GPR_ASSERT(g_number_of_lb_policies != MAX_POLICIES);
-  grpc_lb_policy_factory_ref(factory);
-  g_all_of_the_lb_policies[g_number_of_lb_policies++] = factory;
-}
-
-static grpc_lb_policy_factory* lookup_factory(const char* name) {
-  int i;
-
-  if (name == nullptr) return nullptr;
-
-  for (i = 0; i < g_number_of_lb_policies; i++) {
-    if (0 == gpr_stricmp(name, g_all_of_the_lb_policies[i]->vtable->name)) {
-      return g_all_of_the_lb_policies[i];
+  void RegisterLoadBalancingPolicyFactory(
+      UniquePtr<LoadBalancingPolicyFactory> factory) {
+    for (size_t i = 0; i < factories_.size(); ++i) {
+      GPR_ASSERT(strcmp(factories_[i]->name(), factory->name()) != 0);
     }
+    factories_.push_back(std::move(factory));
   }
 
-  return nullptr;
+  LoadBalancingPolicyFactory* GetLoadBalancingPolicyFactory(
+      const char* name) const {
+    for (size_t i = 0; i < factories_.size(); ++i) {
+      if (strcmp(name, factories_[i]->name()) == 0) {
+        return factories_[i].get();
+      }
+    }
+    return nullptr;
+  }
+
+ private:
+  InlinedVector<UniquePtr<LoadBalancingPolicyFactory>, 10> factories_;
+};
+
+RegistryState* g_state = nullptr;
+
+}  // namespace
+
+//
+// LoadBalancingPolicyRegistry::Builder
+//
+
+void LoadBalancingPolicyRegistry::Builder::InitRegistry() {
+  if (g_state == nullptr) g_state = New<RegistryState>();
 }
 
-grpc_lb_policy* grpc_lb_policy_create(const char* name,
-                                      grpc_lb_policy_args* args) {
-  grpc_lb_policy_factory* factory = lookup_factory(name);
-  grpc_lb_policy* lb_policy =
-      grpc_lb_policy_factory_create_lb_policy(factory, args);
-  return lb_policy;
+void LoadBalancingPolicyRegistry::Builder::ShutdownRegistry() {
+  Delete(g_state);
+  g_state = nullptr;
 }
+
+void LoadBalancingPolicyRegistry::Builder::RegisterLoadBalancingPolicyFactory(
+    UniquePtr<LoadBalancingPolicyFactory> factory) {
+  InitRegistry();
+  g_state->RegisterLoadBalancingPolicyFactory(std::move(factory));
+}
+
+//
+// LoadBalancingPolicyRegistry
+//
+
+OrphanablePtr<LoadBalancingPolicy>
+LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+    const char* name, const LoadBalancingPolicy::Args& args) {
+  GPR_ASSERT(g_state != nullptr);
+  // Find factory.
+  LoadBalancingPolicyFactory* factory =
+      g_state->GetLoadBalancingPolicyFactory(name);
+  if (factory == nullptr) return nullptr;  // Specified name not found.
+  // Create policy via factory.
+  return factory->CreateLoadBalancingPolicy(args);
+}
+
+}  // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.h b/src/core/ext/filters/client_channel/lb_policy_registry.h
index 5aff793..2283d84 100644
--- a/src/core/ext/filters/client_channel/lb_policy_registry.h
+++ b/src/core/ext/filters/client_channel/lb_policy_registry.h
@@ -19,22 +19,37 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/orphanable.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
-/** Initialize the registry and set \a default_factory as the factory to be
- * returned when no name is provided in a lookup */
-void grpc_lb_policy_registry_init(void);
-void grpc_lb_policy_registry_shutdown(void);
+namespace grpc_core {
 
-/** Register a LB policy factory. */
-void grpc_register_lb_policy(grpc_lb_policy_factory* factory);
+class LoadBalancingPolicyRegistry {
+ public:
+  /// Methods used to create and populate the LoadBalancingPolicyRegistry.
+  /// NOT THREAD SAFE -- to be used only during global gRPC
+  /// initialization and shutdown.
+  class Builder {
+   public:
+    /// Global initialization and shutdown hooks.
+    static void InitRegistry();
+    static void ShutdownRegistry();
 
-/** Create a \a grpc_lb_policy instance.
- *
- * If \a name is NULL, the default factory from \a grpc_lb_policy_registry_init
- * will be returned. */
-grpc_lb_policy* grpc_lb_policy_create(const char* name,
-                                      grpc_lb_policy_args* args);
+    /// Registers an LB policy factory.  The factory will be used to create an
+    /// LB policy whose name matches that of the factory.
+    static void RegisterLoadBalancingPolicyFactory(
+        UniquePtr<LoadBalancingPolicyFactory> factory);
+  };
+
+  /// Creates an LB policy of the type specified by \a name.
+  static OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const char* name, const LoadBalancingPolicy::Args& args);
+};
+
+}  // namespace grpc_core
 
 #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H */
diff --git a/src/core/ext/filters/client_channel/method_params.cc b/src/core/ext/filters/client_channel/method_params.cc
new file mode 100644
index 0000000..374b87e
--- /dev/null
+++ b/src/core/ext/filters/client_channel/method_params.cc
@@ -0,0 +1,178 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/method_params.h"
+#include "src/core/ext/filters/client_channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+
+// As per the retry design, we do not allow more than 5 retry attempts.
+#define MAX_MAX_RETRY_ATTEMPTS 5
+
+namespace grpc_core {
+namespace internal {
+
+namespace {
+
+bool ParseWaitForReady(
+    grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) {
+  if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
+    return false;
+  }
+  *wait_for_ready = field->type == GRPC_JSON_TRUE
+                        ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE
+                        : ClientChannelMethodParams::WAIT_FOR_READY_FALSE;
+  return true;
+}
+
+// Parses a JSON field of the form generated for a google.proto.Duration
+// proto message, as per:
+//   https://developers.google.com/protocol-buffers/docs/proto3#json
+bool ParseDuration(grpc_json* field, grpc_millis* duration) {
+  if (field->type != GRPC_JSON_STRING) return false;
+  size_t len = strlen(field->value);
+  if (field->value[len - 1] != 's') return false;
+  UniquePtr<char> buf(gpr_strdup(field->value));
+  *(buf.get() + len - 1) = '\0';  // Remove trailing 's'.
+  char* decimal_point = strchr(buf.get(), '.');
+  int nanos = 0;
+  if (decimal_point != nullptr) {
+    *decimal_point = '\0';
+    nanos = gpr_parse_nonnegative_int(decimal_point + 1);
+    if (nanos == -1) {
+      return false;
+    }
+    int num_digits = static_cast<int>(strlen(decimal_point + 1));
+    if (num_digits > 9) {  // We don't accept greater precision than nanos.
+      return false;
+    }
+    for (int i = 0; i < (9 - num_digits); ++i) {
+      nanos *= 10;
+    }
+  }
+  int seconds =
+      decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get());
+  if (seconds == -1) return false;
+  *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
+  return true;
+}
+
+UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy(
+    grpc_json* field) {
+  auto retry_policy = MakeUnique<ClientChannelMethodParams::RetryPolicy>();
+  if (field->type != GRPC_JSON_OBJECT) return nullptr;
+  for (grpc_json* sub_field = field->child; sub_field != nullptr;
+       sub_field = sub_field->next) {
+    if (sub_field->key == nullptr) return nullptr;
+    if (strcmp(sub_field->key, "maxAttempts") == 0) {
+      if (retry_policy->max_attempts != 0) return nullptr;  // Duplicate.
+      if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+      retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value);
+      if (retry_policy->max_attempts <= 1) return nullptr;
+      if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
+        gpr_log(GPR_ERROR,
+                "service config: clamped retryPolicy.maxAttempts at %d",
+                MAX_MAX_RETRY_ATTEMPTS);
+        retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
+      }
+    } else if (strcmp(sub_field->key, "initialBackoff") == 0) {
+      if (retry_policy->initial_backoff > 0) return nullptr;  // Duplicate.
+      if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) {
+        return nullptr;
+      }
+      if (retry_policy->initial_backoff == 0) return nullptr;
+    } else if (strcmp(sub_field->key, "maxBackoff") == 0) {
+      if (retry_policy->max_backoff > 0) return nullptr;  // Duplicate.
+      if (!ParseDuration(sub_field, &retry_policy->max_backoff)) {
+        return nullptr;
+      }
+      if (retry_policy->max_backoff == 0) return nullptr;
+    } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) {
+      if (retry_policy->backoff_multiplier != 0) return nullptr;  // Duplicate.
+      if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+      if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) !=
+          1) {
+        return nullptr;
+      }
+      if (retry_policy->backoff_multiplier <= 0) return nullptr;
+    } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) {
+      if (!retry_policy->retryable_status_codes.Empty()) {
+        return nullptr;  // Duplicate.
+      }
+      if (sub_field->type != GRPC_JSON_ARRAY) return nullptr;
+      for (grpc_json* element = sub_field->child; element != nullptr;
+           element = element->next) {
+        if (element->type != GRPC_JSON_STRING) return nullptr;
+        grpc_status_code status;
+        if (!grpc_status_code_from_string(element->value, &status)) {
+          return nullptr;
+        }
+        retry_policy->retryable_status_codes.Add(status);
+      }
+      if (retry_policy->retryable_status_codes.Empty()) return nullptr;
+    }
+  }
+  // Make sure required fields are set.
+  if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
+      retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 ||
+      retry_policy->retryable_status_codes.Empty()) {
+    return nullptr;
+  }
+  return retry_policy;
+}
+
+}  // namespace
+
+RefCountedPtr<ClientChannelMethodParams>
+ClientChannelMethodParams::CreateFromJson(const grpc_json* json) {
+  RefCountedPtr<ClientChannelMethodParams> method_params =
+      MakeRefCounted<ClientChannelMethodParams>();
+  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+    if (field->key == nullptr) continue;
+    if (strcmp(field->key, "waitForReady") == 0) {
+      if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) {
+        return nullptr;  // Duplicate.
+      }
+      if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) {
+        return nullptr;
+      }
+    } else if (strcmp(field->key, "timeout") == 0) {
+      if (method_params->timeout_ > 0) return nullptr;  // Duplicate.
+      if (!ParseDuration(field, &method_params->timeout_)) return nullptr;
+    } else if (strcmp(field->key, "retryPolicy") == 0) {
+      if (method_params->retry_policy_ != nullptr) {
+        return nullptr;  // Duplicate.
+      }
+      method_params->retry_policy_ = ParseRetryPolicy(field);
+      if (method_params->retry_policy_ == nullptr) return nullptr;
+    }
+  }
+  return method_params;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/method_params.h b/src/core/ext/filters/client_channel/method_params.h
new file mode 100644
index 0000000..48ece29
--- /dev/null
+++ b/src/core/ext/filters/client_channel/method_params.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/status_util.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/exec_ctx.h"  // for grpc_millis
+#include "src/core/lib/json/json.h"
+
+namespace grpc_core {
+namespace internal {
+
+class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> {
+ public:
+  enum WaitForReady {
+    WAIT_FOR_READY_UNSET = 0,
+    WAIT_FOR_READY_FALSE,
+    WAIT_FOR_READY_TRUE
+  };
+
+  struct RetryPolicy {
+    int max_attempts = 0;
+    grpc_millis initial_backoff = 0;
+    grpc_millis max_backoff = 0;
+    float backoff_multiplier = 0;
+    StatusCodeSet retryable_status_codes;
+  };
+
+  /// Creates a method_parameters object from \a json.
+  /// Intended for use with ServiceConfig::CreateMethodConfigTable().
+  static RefCountedPtr<ClientChannelMethodParams> CreateFromJson(
+      const grpc_json* json);
+
+  grpc_millis timeout() const { return timeout_; }
+  WaitForReady wait_for_ready() const { return wait_for_ready_; }
+  const RetryPolicy* retry_policy() const { return retry_policy_.get(); }
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* grpc_core::New(Args&&... args);
+
+  ClientChannelMethodParams() {}
+  virtual ~ClientChannelMethodParams() {}
+
+  grpc_millis timeout_ = 0;
+  WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET;
+  UniquePtr<RetryPolicy> retry_policy_;
+};
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H */
diff --git a/src/core/ext/filters/client_channel/parse_address.cc b/src/core/ext/filters/client_channel/parse_address.cc
index 473c754..e78dc99 100644
--- a/src/core/ext/filters/client_channel/parse_address.cc
+++ b/src/core/ext/filters/client_channel/parse_address.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 
diff --git a/src/core/ext/filters/client_channel/parse_address.h b/src/core/ext/filters/client_channel/parse_address.h
index ca0a0d1..9a88b66 100644
--- a/src/core/ext/filters/client_channel/parse_address.h
+++ b/src/core/ext/filters/client_channel/parse_address.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 #include "src/core/ext/filters/client_channel/uri_parser.h"
diff --git a/src/core/ext/filters/client_channel/proxy_mapper.cc b/src/core/ext/filters/client_channel/proxy_mapper.cc
index be85cfc..c4da067 100644
--- a/src/core/ext/filters/client_channel/proxy_mapper.cc
+++ b/src/core/ext/filters/client_channel/proxy_mapper.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/proxy_mapper.h"
 
 void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable,
diff --git a/src/core/ext/filters/client_channel/proxy_mapper.h b/src/core/ext/filters/client_channel/proxy_mapper.h
index ce3e65e..634b0ed 100644
--- a/src/core/ext/filters/client_channel/proxy_mapper.h
+++ b/src/core/ext/filters/client_channel/proxy_mapper.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/impl/codegen/grpc_types.h>
diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.cc b/src/core/ext/filters/client_channel/proxy_mapper_registry.cc
index b42597e..a02a5f5 100644
--- a/src/core/ext/filters/client_channel/proxy_mapper_registry.cc
+++ b/src/core/ext/filters/client_channel/proxy_mapper_registry.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
 
 #include <string.h>
diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.h b/src/core/ext/filters/client_channel/proxy_mapper_registry.h
index 2ad6c04..326b582 100644
--- a/src/core/ext/filters/client_channel/proxy_mapper_registry.h
+++ b/src/core/ext/filters/client_channel/proxy_mapper_registry.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/proxy_mapper.h"
 
 void grpc_proxy_mapper_registry_init();
diff --git a/src/core/ext/filters/client_channel/resolver.cc b/src/core/ext/filters/client_channel/resolver.cc
index 860c2ee..cd11eeb 100644
--- a/src/core/ext/filters/client_channel/resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/iomgr/combiner.h"
 
diff --git a/src/core/ext/filters/client_channel/resolver.h b/src/core/ext/filters/client_channel/resolver.h
index 62fcb49..1685a6c 100644
--- a/src/core/ext/filters/client_channel/resolver.h
+++ b/src/core/ext/filters/client_channel/resolver.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/gprpp/abstract.h"
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
index 0442b1e..aa93e5d 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
@@ -17,6 +17,7 @@
  */
 
 #include <grpc/support/port_platform.h>
+
 #if GRPC_ARES == 1 && !defined(GRPC_UV)
 
 #include <limits.h>
@@ -294,7 +295,7 @@
     size_t num_args_to_add = 0;
     new_args[num_args_to_add++] =
         grpc_lb_addresses_create_channel_arg(r->lb_addresses_);
-    grpc_service_config* service_config = nullptr;
+    grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config;
     char* service_config_string = nullptr;
     if (r->service_config_json_ != nullptr) {
       service_config_string = ChooseServiceConfig(r->service_config_json_);
@@ -305,10 +306,11 @@
         args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG;
         new_args[num_args_to_add++] = grpc_channel_arg_string_create(
             (char*)GRPC_ARG_SERVICE_CONFIG, service_config_string);
-        service_config = grpc_service_config_create(service_config_string);
+        service_config =
+            grpc_core::ServiceConfig::Create(service_config_string);
         if (service_config != nullptr) {
           const char* lb_policy_name =
-              grpc_service_config_get_lb_policy_name(service_config);
+              service_config->GetLoadBalancingPolicyName();
           if (lb_policy_name != nullptr) {
             args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME;
             new_args[num_args_to_add++] = grpc_channel_arg_string_create(
@@ -321,7 +323,6 @@
     result = grpc_channel_args_copy_and_add_and_remove(
         r->channel_args_, args_to_remove, num_args_to_remove, new_args,
         num_args_to_add);
-    if (service_config != nullptr) grpc_service_config_destroy(service_config);
     gpr_free(service_config_string);
     grpc_lb_addresses_destroy(r->lb_addresses_);
     // Reset backoff state so that we start from the beginning when the
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
index ba7dad6..0bc13e3 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <ares.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/pollset_set.h"
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
index 10bc8f6..b604f2b 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
@@ -16,6 +16,7 @@
  *
  */
 #include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 #if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET)
 
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
index 82b5545..71b06eb 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
@@ -17,6 +17,7 @@
  */
 
 #include <grpc/support/port_platform.h>
+
 #if GRPC_ARES == 1 && !defined(GRPC_UV)
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
index 86d870e..bda9cd1 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/iomgr.h"
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
index a184cf2..5096e48 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
@@ -17,6 +17,7 @@
  */
 
 #include <grpc/support/port_platform.h>
+
 #if GRPC_ARES != 1 || defined(GRPC_UV)
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
index b01e608..4d8958f 100644
--- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
@@ -17,6 +17,8 @@
 // This is similar to the sockaddr resolver, except that it supports a
 // bunch of query args that are useful for dependency injection in tests.
 
+#include <grpc/support/port_platform.h>
+
 #include <limits.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -24,7 +26,6 @@
 #include <string.h>
 
 #include <grpc/support/alloc.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
index d42811d..858f358 100644
--- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
+++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
@@ -17,6 +17,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 #include "src/core/ext/filters/client_channel/uri_parser.h"
 #include "src/core/lib/channel/channel_args.h"
diff --git a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
index 966b9fd..f74ac5a 100644
--- a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
@@ -16,13 +16,14 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <grpc/support/alloc.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
diff --git a/src/core/ext/filters/client_channel/resolver_factory.h b/src/core/ext/filters/client_channel/resolver_factory.h
index f9b9501..ee3cfee 100644
--- a/src/core/ext/filters/client_channel/resolver_factory.h
+++ b/src/core/ext/filters/client_channel/resolver_factory.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/filters/client_channel/resolver.h"
diff --git a/src/core/ext/filters/client_channel/resolver_registry.cc b/src/core/ext/filters/client_channel/resolver_registry.cc
index 036e81d..91c0267 100644
--- a/src/core/ext/filters/client_channel/resolver_registry.cc
+++ b/src/core/ext/filters/client_channel/resolver_registry.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 
 #include <string.h>
diff --git a/src/core/ext/filters/client_channel/resolver_registry.h b/src/core/ext/filters/client_channel/resolver_registry.h
index 260336d..d6ec681 100644
--- a/src/core/ext/filters/client_channel/resolver_registry.h
+++ b/src/core/ext/filters/client_channel/resolver_registry.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/resolver_factory.h"
 #include "src/core/lib/gprpp/inlined_vector.h"
 #include "src/core/lib/gprpp/memory.h"
diff --git a/src/core/ext/filters/client_channel/retry_throttle.cc b/src/core/ext/filters/client_channel/retry_throttle.cc
index a98e278..45de666 100644
--- a/src/core/ext/filters/client_channel/retry_throttle.cc
+++ b/src/core/ext/filters/client_channel/retry_throttle.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/retry_throttle.h"
 
 #include <limits.h>
@@ -38,7 +40,7 @@
   int milli_token_ratio;
   gpr_atm milli_tokens;
   // A pointer to the replacement for this grpc_server_retry_throttle_data
-  // entry.  If non-NULL, then this entry is stale and must not be used.
+  // entry.  If non-nullptr, then this entry is stale and must not be used.
   // We hold a reference to the replacement.
   gpr_atm replacement;
 };
@@ -56,6 +58,7 @@
 
 bool grpc_server_retry_throttle_data_record_failure(
     grpc_server_retry_throttle_data* throttle_data) {
+  if (throttle_data == nullptr) return true;
   // First, check if we are stale and need to be replaced.
   get_replacement_throttle_data_if_needed(&throttle_data);
   // We decrement milli_tokens by 1000 (1 token) for each failure.
@@ -70,6 +73,7 @@
 
 void grpc_server_retry_throttle_data_record_success(
     grpc_server_retry_throttle_data* throttle_data) {
+  if (throttle_data == nullptr) return;
   // First, check if we are stale and need to be replaced.
   get_replacement_throttle_data_if_needed(&throttle_data);
   // We increment milli_tokens by milli_token_ratio for each success.
diff --git a/src/core/ext/filters/client_channel/retry_throttle.h b/src/core/ext/filters/client_channel/retry_throttle.h
index bf99297..0505fc2 100644
--- a/src/core/ext/filters/client_channel/retry_throttle.h
+++ b/src/core/ext/filters/client_channel/retry_throttle.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 /// Tracks retry throttling data for an individual server name.
diff --git a/src/core/ext/filters/client_channel/status_util.cc b/src/core/ext/filters/client_channel/status_util.cc
new file mode 100644
index 0000000..11f732a
--- /dev/null
+++ b/src/core/ext/filters/client_channel/status_util.cc
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/status_util.h"
+
+#include "src/core/lib/gpr/useful.h"
+
+typedef struct {
+  const char* str;
+  grpc_status_code status;
+} status_string_entry;
+
+static const status_string_entry g_status_string_entries[] = {
+    {"OK", GRPC_STATUS_OK},
+    {"CANCELLED", GRPC_STATUS_CANCELLED},
+    {"UNKNOWN", GRPC_STATUS_UNKNOWN},
+    {"INVALID_ARGUMENT", GRPC_STATUS_INVALID_ARGUMENT},
+    {"DEADLINE_EXCEEDED", GRPC_STATUS_DEADLINE_EXCEEDED},
+    {"NOT_FOUND", GRPC_STATUS_NOT_FOUND},
+    {"ALREADY_EXISTS", GRPC_STATUS_ALREADY_EXISTS},
+    {"PERMISSION_DENIED", GRPC_STATUS_PERMISSION_DENIED},
+    {"UNAUTHENTICATED", GRPC_STATUS_UNAUTHENTICATED},
+    {"RESOURCE_EXHAUSTED", GRPC_STATUS_RESOURCE_EXHAUSTED},
+    {"FAILED_PRECONDITION", GRPC_STATUS_FAILED_PRECONDITION},
+    {"ABORTED", GRPC_STATUS_ABORTED},
+    {"OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE},
+    {"UNIMPLEMENTED", GRPC_STATUS_UNIMPLEMENTED},
+    {"INTERNAL", GRPC_STATUS_INTERNAL},
+    {"UNAVAILABLE", GRPC_STATUS_UNAVAILABLE},
+    {"DATA_LOSS", GRPC_STATUS_DATA_LOSS},
+};
+
+bool grpc_status_code_from_string(const char* status_str,
+                                  grpc_status_code* status) {
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(g_status_string_entries); ++i) {
+    if (strcmp(status_str, g_status_string_entries[i].str) == 0) {
+      *status = g_status_string_entries[i].status;
+      return true;
+    }
+  }
+  return false;
+}
+
+const char* grpc_status_code_to_string(grpc_status_code status) {
+  switch (status) {
+    case GRPC_STATUS_OK:
+      return "OK";
+    case GRPC_STATUS_CANCELLED:
+      return "CANCELLED";
+    case GRPC_STATUS_UNKNOWN:
+      return "UNKNOWN";
+    case GRPC_STATUS_INVALID_ARGUMENT:
+      return "INVALID_ARGUMENT";
+    case GRPC_STATUS_DEADLINE_EXCEEDED:
+      return "DEADLINE_EXCEEDED";
+    case GRPC_STATUS_NOT_FOUND:
+      return "NOT_FOUND";
+    case GRPC_STATUS_ALREADY_EXISTS:
+      return "ALREADY_EXISTS";
+    case GRPC_STATUS_PERMISSION_DENIED:
+      return "PERMISSION_DENIED";
+    case GRPC_STATUS_UNAUTHENTICATED:
+      return "UNAUTHENTICATED";
+    case GRPC_STATUS_RESOURCE_EXHAUSTED:
+      return "RESOURCE_EXHAUSTED";
+    case GRPC_STATUS_FAILED_PRECONDITION:
+      return "FAILED_PRECONDITION";
+    case GRPC_STATUS_ABORTED:
+      return "ABORTED";
+    case GRPC_STATUS_OUT_OF_RANGE:
+      return "OUT_OF_RANGE";
+    case GRPC_STATUS_UNIMPLEMENTED:
+      return "UNIMPLEMENTED";
+    case GRPC_STATUS_INTERNAL:
+      return "INTERNAL";
+    case GRPC_STATUS_UNAVAILABLE:
+      return "UNAVAILABLE";
+    case GRPC_STATUS_DATA_LOSS:
+      return "DATA_LOSS";
+    default:
+      return "UNKNOWN";
+  }
+}
diff --git a/src/core/ext/filters/client_channel/status_util.h b/src/core/ext/filters/client_channel/status_util.h
new file mode 100644
index 0000000..e018709
--- /dev/null
+++ b/src/core/ext/filters/client_channel/status_util.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_STATUS_UTIL_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_STATUS_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/status.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+/// If \a status_str is a valid status string, sets \a status to the
+/// corresponding status value and returns true.
+bool grpc_status_code_from_string(const char* status_str,
+                                  grpc_status_code* status);
+
+/// Returns the string form of \a status, or "UNKNOWN" if invalid.
+const char* grpc_status_code_to_string(grpc_status_code status);
+
+namespace grpc_core {
+namespace internal {
+
+/// A set of grpc_status_code values.
+class StatusCodeSet {
+ public:
+  bool Empty() const { return status_code_mask_ == 0; }
+
+  void Add(grpc_status_code status) { status_code_mask_ |= (1 << status); }
+
+  bool Contains(grpc_status_code status) const {
+    return status_code_mask_ & (1 << status);
+  }
+
+ private:
+  int status_code_mask_ = 0;  // A bitfield of status codes in the set.
+};
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_STATUS_UTIL_H */
diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc
index 179e3f2..cae7cc3 100644
--- a/src/core/ext/filters/client_channel/subchannel.cc
+++ b/src/core/ext/filters/client_channel/subchannel.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/subchannel.h"
 
 #include <inttypes.h>
@@ -657,7 +659,6 @@
 static void subchannel_call_destroy(void* call, grpc_error* error) {
   GPR_TIMER_SCOPE("grpc_subchannel_call_unref.destroy", 0);
   grpc_subchannel_call* c = static_cast<grpc_subchannel_call*>(call);
-  GPR_ASSERT(c->schedule_closure_after_destroy != nullptr);
   grpc_core::ConnectedSubchannel* connection = c->connection;
   grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c), nullptr,
                           c->schedule_closure_after_destroy);
@@ -671,9 +672,10 @@
   call->schedule_closure_after_destroy = closure;
 }
 
-void grpc_subchannel_call_ref(
+grpc_subchannel_call* grpc_subchannel_call_ref(
     grpc_subchannel_call* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
   GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON);
+  return c;
 }
 
 void grpc_subchannel_call_unref(
@@ -703,6 +705,13 @@
   return subchannel->key;
 }
 
+void* grpc_connected_subchannel_call_get_parent_data(
+    grpc_subchannel_call* subchannel_call) {
+  grpc_channel_stack* chanstk = subchannel_call->connection->channel_stack();
+  return (char*)subchannel_call + sizeof(grpc_subchannel_call) +
+         chanstk->call_stack_size;
+}
+
 grpc_call_stack* grpc_subchannel_call_get_call_stack(
     grpc_subchannel_call* subchannel_call) {
   return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call);
@@ -728,9 +737,9 @@
 const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args) {
   const grpc_arg* addr_arg =
       grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_ADDRESS);
-  GPR_ASSERT(addr_arg != nullptr);  // Should have been set by LB policy.
-  GPR_ASSERT(addr_arg->type == GRPC_ARG_STRING);
-  return addr_arg->value.string;
+  const char* addr_str = grpc_channel_arg_get_string(addr_arg);
+  GPR_ASSERT(addr_str != nullptr);  // Should have been set by LB policy.
+  return addr_str;
 }
 
 grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address* addr) {
@@ -774,8 +783,8 @@
 grpc_error* ConnectedSubchannel::CreateCall(const CallArgs& args,
                                             grpc_subchannel_call** call) {
   *call = static_cast<grpc_subchannel_call*>(gpr_arena_alloc(
-      args.arena,
-      sizeof(grpc_subchannel_call) + channel_stack_->call_stack_size));
+      args.arena, sizeof(grpc_subchannel_call) +
+                      channel_stack_->call_stack_size + args.parent_data_size));
   grpc_call_stack* callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call);
   RefCountedPtr<ConnectedSubchannel> connection =
       Ref(DEBUG_LOCATION, "subchannel_call");
diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h
index d2b45ae..e23aec1 100644
--- a/src/core/ext/filters/client_channel/subchannel.h
+++ b/src/core/ext/filters/client_channel/subchannel.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/connector.h"
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/gpr/arena.h"
@@ -79,6 +81,7 @@
     gpr_arena* arena;
     grpc_call_context_element* context;
     grpc_call_combiner* call_combiner;
+    size_t parent_data_size;
   };
 
   explicit ConnectedSubchannel(grpc_channel_stack* channel_stack);
@@ -107,11 +110,17 @@
     grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
 void grpc_subchannel_weak_unref(
     grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
-void grpc_subchannel_call_ref(
+grpc_subchannel_call* grpc_subchannel_call_ref(
     grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
 void grpc_subchannel_call_unref(
     grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
 
+/** Returns a pointer to the parent data associated with \a subchannel_call.
+    The data will be of the size specified in \a parent_data_size
+    field of the args passed to \a grpc_connected_subchannel_create_call(). */
+void* grpc_connected_subchannel_call_get_parent_data(
+    grpc_subchannel_call* subchannel_call);
+
 /** poll the current connectivity state of a channel */
 grpc_connectivity_state grpc_subchannel_check_connectivity(
     grpc_subchannel* channel, grpc_error** error);
diff --git a/src/core/ext/filters/client_channel/subchannel_index.cc b/src/core/ext/filters/client_channel/subchannel_index.cc
index d1dc5ee..cb02b1a 100644
--- a/src/core/ext/filters/client_channel/subchannel_index.cc
+++ b/src/core/ext/filters/client_channel/subchannel_index.cc
@@ -16,6 +16,8 @@
 //
 //
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/subchannel_index.h"
 
 #include <stdbool.h>
diff --git a/src/core/ext/filters/client_channel/subchannel_index.h b/src/core/ext/filters/client_channel/subchannel_index.h
index bd160a3..a7dae9d 100644
--- a/src/core/ext/filters/client_channel/subchannel_index.h
+++ b/src/core/ext/filters/client_channel/subchannel_index.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/subchannel.h"
 
 /** \file Provides an index of active subchannels so that they can be
diff --git a/src/core/ext/filters/client_channel/uri_parser.cc b/src/core/ext/filters/client_channel/uri_parser.cc
index cd07a6f..0572034 100644
--- a/src/core/ext/filters/client_channel/uri_parser.cc
+++ b/src/core/ext/filters/client_channel/uri_parser.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/uri_parser.h"
 
 #include <string.h>
@@ -23,7 +25,6 @@
 #include <grpc/slice_buffer.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/gpr/string.h"
diff --git a/src/core/ext/filters/client_channel/uri_parser.h b/src/core/ext/filters/client_channel/uri_parser.h
index 24ff06c..1966da9 100644
--- a/src/core/ext/filters/client_channel/uri_parser.h
+++ b/src/core/ext/filters/client_channel/uri_parser.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H
 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 
diff --git a/src/core/ext/filters/deadline/deadline_filter.cc b/src/core/ext/filters/deadline/deadline_filter.cc
index 76c1204..dda3b61 100644
--- a/src/core/ext/filters/deadline/deadline_filter.cc
+++ b/src/core/ext/filters/deadline/deadline_filter.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/deadline/deadline_filter.h"
 
 #include <stdbool.h>
diff --git a/src/core/ext/filters/deadline/deadline_filter.h b/src/core/ext/filters/deadline/deadline_filter.h
index 4de817e..13207cb 100644
--- a/src/core/ext/filters/deadline/deadline_filter.h
+++ b/src/core/ext/filters/deadline/deadline_filter.h
@@ -17,6 +17,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/iomgr/timer.h"
 
diff --git a/src/core/ext/filters/http/client/http_client_filter.cc b/src/core/ext/filters/http/client/http_client_filter.cc
index 80643f8..58aefd1 100644
--- a/src/core/ext/filters/http/client/http_client_filter.cc
+++ b/src/core/ext/filters/http/client/http_client_filter.cc
@@ -15,11 +15,13 @@
  *
  */
 
-#include "src/core/ext/filters/http/client/http_client_filter.h"
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <string.h>
+#include "src/core/ext/filters/http/client/http_client_filter.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/b64.h"
diff --git a/src/core/ext/filters/http/client/http_client_filter.h b/src/core/ext/filters/http/client/http_client_filter.h
index ec8177c..b7cef33 100644
--- a/src/core/ext/filters/http/client/http_client_filter.h
+++ b/src/core/ext/filters/http/client/http_client_filter.h
@@ -18,6 +18,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 /* Processes metadata on the client side for HTTP2 transports */
diff --git a/src/core/ext/filters/http/http_filters_plugin.cc b/src/core/ext/filters/http/http_filters_plugin.cc
index 56fe1e5..f03fa01 100644
--- a/src/core/ext/filters/http/http_filters_plugin.cc
+++ b/src/core/ext/filters/http/http_filters_plugin.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include "src/core/ext/filters/http/client/http_client_filter.h"
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
index 73220a0..efe0085 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.cc
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <assert.h>
 #include <string.h>
 
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.h b/src/core/ext/filters/http/message_compress/message_compress_filter.h
index 6220791..e163e3c 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.h
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/compression_types.h>
 
 #include "src/core/lib/channel/channel_stack.h"
diff --git a/src/core/ext/filters/http/server/http_server_filter.cc b/src/core/ext/filters/http/server/http_server_filter.cc
index 63bc2bd..57ec8dc 100644
--- a/src/core/ext/filters/http/server/http_server_filter.cc
+++ b/src/core/ext/filters/http/server/http_server_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/http/server/http_server_filter.h"
 
 #include <grpc/support/alloc.h>
@@ -52,7 +54,6 @@
   grpc_closure* recv_message_ready;
   grpc_closure* on_complete;
   grpc_byte_stream** pp_recv_message;
-  grpc_slice_buffer read_slice_buffer;
   grpc_slice_buffer_stream read_stream;
 
   /** Receive closures are chained: we inject this closure as the on_done_recv
@@ -224,13 +225,15 @@
 
       /* decode payload from query and add to the slice buffer to be returned */
       const int k_url_safe = 1;
+      grpc_slice_buffer read_slice_buffer;
+      grpc_slice_buffer_init(&read_slice_buffer);
       grpc_slice_buffer_add(
-          &calld->read_slice_buffer,
+          &read_slice_buffer,
           grpc_base64_decode_with_len(
               reinterpret_cast<const char*> GRPC_SLICE_START_PTR(query_slice),
               GRPC_SLICE_LENGTH(query_slice), k_url_safe));
-      grpc_slice_buffer_stream_init(&calld->read_stream,
-                                    &calld->read_slice_buffer, 0);
+      grpc_slice_buffer_stream_init(&calld->read_stream, &read_slice_buffer, 0);
+      grpc_slice_buffer_destroy_internal(&read_slice_buffer);
       calld->seen_path_with_query = true;
       grpc_slice_unref_internal(query_slice);
     } else {
@@ -393,7 +396,6 @@
                     grpc_schedule_on_exec_ctx);
   GRPC_CLOSURE_INIT(&calld->hs_recv_message_ready, hs_recv_message_ready, elem,
                     grpc_schedule_on_exec_ctx);
-  grpc_slice_buffer_init(&calld->read_slice_buffer);
   return GRPC_ERROR_NONE;
 }
 
@@ -402,7 +404,9 @@
                               const grpc_call_final_info* final_info,
                               grpc_closure* ignored) {
   call_data* calld = static_cast<call_data*>(elem->call_data);
-  grpc_slice_buffer_destroy_internal(&calld->read_slice_buffer);
+  if (calld->seen_path_with_query && !calld->payload_bin_delivered) {
+    grpc_byte_stream_destroy(&calld->read_stream.base);
+  }
 }
 
 /* Constructor for channel_data */
diff --git a/src/core/ext/filters/http/server/http_server_filter.h b/src/core/ext/filters/http/server/http_server_filter.h
index c0f678a..4eb130b 100644
--- a/src/core/ext/filters/http/server/http_server_filter.h
+++ b/src/core/ext/filters/http/server/http_server_filter.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 /* Processes metadata on the client side for HTTP2 transports */
diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
index 79b144a..0d349e2 100644
--- a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
+++ b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/load_reporting.h>
diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.h b/src/core/ext/filters/load_reporting/server_load_reporting_filter.h
index 1baee5e..b459a8e 100644
--- a/src/core/ext/filters/load_reporting/server_load_reporting_filter.h
+++ b/src/core/ext/filters/load_reporting/server_load_reporting_filter.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h"
 #include "src/core/lib/channel/channel_stack.h"
 
diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h
index 4b694d3..c20aaa7 100644
--- a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h
+++ b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_PLUGIN_H
 #define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_PLUGIN_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/channel/channel_stack.h"
diff --git a/src/core/ext/filters/max_age/max_age_filter.cc b/src/core/ext/filters/max_age/max_age_filter.cc
index abb9d69..1fe8288 100644
--- a/src/core/ext/filters/max_age/max_age_filter.cc
+++ b/src/core/ext/filters/max_age/max_age_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/max_age/max_age_filter.h"
 
 #include <limits.h>
@@ -368,6 +370,9 @@
        max_idle_timer, and prevent max_idle_timer from being started in the
        future. */
     increase_call_count(chand);
+    if (gpr_atm_acq_load(&chand->idle_state) == MAX_IDLE_STATE_SEEN_EXIT_IDLE) {
+      grpc_timer_cancel(&chand->max_idle_timer);
+    }
   }
 }
 
diff --git a/src/core/ext/filters/max_age/max_age_filter.h b/src/core/ext/filters/max_age/max_age_filter.h
index 68fb4a4..9893222 100644
--- a/src/core/ext/filters/max_age/max_age_filter.h
+++ b/src/core/ext/filters/max_age/max_age_filter.h
@@ -17,6 +17,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_max_age_filter;
diff --git a/src/core/ext/filters/message_size/message_size_filter.cc b/src/core/ext/filters/message_size/message_size_filter.cc
index 49d9ae6..b1b14dd 100644
--- a/src/core/ext/filters/message_size/message_size_filter.cc
+++ b/src/core/ext/filters/message_size/message_size_filter.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/message_size/message_size_filter.h"
 
 #include <limits.h>
@@ -27,6 +29,8 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_stack_builder.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/lib/transport/service_config.h"
 
@@ -35,27 +39,29 @@
   int max_recv_size;
 } message_size_limits;
 
-typedef struct {
-  gpr_refcount refs;
-  message_size_limits limits;
-} refcounted_message_size_limits;
+namespace grpc_core {
+namespace {
 
-static void* refcounted_message_size_limits_ref(void* value) {
-  refcounted_message_size_limits* limits =
-      static_cast<refcounted_message_size_limits*>(value);
-  gpr_ref(&limits->refs);
-  return value;
-}
+class MessageSizeLimits : public RefCounted<MessageSizeLimits> {
+ public:
+  static RefCountedPtr<MessageSizeLimits> CreateFromJson(const grpc_json* json);
 
-static void refcounted_message_size_limits_unref(void* value) {
-  refcounted_message_size_limits* limits =
-      static_cast<refcounted_message_size_limits*>(value);
-  if (gpr_unref(&limits->refs)) {
-    gpr_free(value);
+  const message_size_limits& limits() const { return limits_; }
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* grpc_core::New(Args&&... args);
+
+  MessageSizeLimits(int max_send_size, int max_recv_size) {
+    limits_.max_send_size = max_send_size;
+    limits_.max_recv_size = max_recv_size;
   }
-}
 
-static void* refcounted_message_size_limits_create_from_json(
+  message_size_limits limits_;
+};
+
+RefCountedPtr<MessageSizeLimits> MessageSizeLimits::CreateFromJson(
     const grpc_json* json) {
   int max_request_message_bytes = -1;
   int max_response_message_bytes = -1;
@@ -77,16 +83,15 @@
       if (max_response_message_bytes == -1) return nullptr;
     }
   }
-  refcounted_message_size_limits* value =
-      static_cast<refcounted_message_size_limits*>(
-          gpr_malloc(sizeof(refcounted_message_size_limits)));
-  gpr_ref_init(&value->refs, 1);
-  value->limits.max_send_size = max_request_message_bytes;
-  value->limits.max_recv_size = max_response_message_bytes;
-  return value;
+  return MakeRefCounted<MessageSizeLimits>(max_request_message_bytes,
+                                           max_response_message_bytes);
 }
 
+}  // namespace
+}  // namespace grpc_core
+
 namespace {
+
 struct call_data {
   grpc_call_combiner* call_combiner;
   message_size_limits limits;
@@ -103,8 +108,11 @@
 struct channel_data {
   message_size_limits limits;
   // Maps path names to refcounted_message_size_limits structs.
-  grpc_slice_hash_table* method_limit_table;
+  grpc_core::RefCountedPtr<grpc_core::SliceHashTable<
+      grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits>>>
+      method_limit_table;
 };
+
 }  // namespace
 
 // Callback invoked when we receive a message.  Here we check the max
@@ -183,20 +191,19 @@
   // size to the receive limit.
   calld->limits = chand->limits;
   if (chand->method_limit_table != nullptr) {
-    refcounted_message_size_limits* limits =
-        static_cast<refcounted_message_size_limits*>(
-            grpc_method_config_table_get(chand->method_limit_table,
-                                         args->path));
+    grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits> limits =
+        grpc_core::ServiceConfig::MethodConfigTableLookup(
+            *chand->method_limit_table, args->path);
     if (limits != nullptr) {
-      if (limits->limits.max_send_size >= 0 &&
-          (limits->limits.max_send_size < calld->limits.max_send_size ||
+      if (limits->limits().max_send_size >= 0 &&
+          (limits->limits().max_send_size < calld->limits.max_send_size ||
            calld->limits.max_send_size < 0)) {
-        calld->limits.max_send_size = limits->limits.max_send_size;
+        calld->limits.max_send_size = limits->limits().max_send_size;
       }
-      if (limits->limits.max_recv_size >= 0 &&
-          (limits->limits.max_recv_size < calld->limits.max_recv_size ||
+      if (limits->limits().max_recv_size >= 0 &&
+          (limits->limits().max_recv_size < calld->limits.max_recv_size ||
            calld->limits.max_recv_size < 0)) {
-        calld->limits.max_recv_size = limits->limits.max_recv_size;
+        calld->limits.max_recv_size = limits->limits().max_recv_size;
       }
     }
   }
@@ -249,17 +256,13 @@
   // Get method config table from channel args.
   const grpc_arg* channel_arg =
       grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG);
-  if (channel_arg != nullptr) {
-    GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
-    grpc_service_config* service_config =
-        grpc_service_config_create(channel_arg->value.string);
+  const char* service_config_str = grpc_channel_arg_get_string(channel_arg);
+  if (service_config_str != nullptr) {
+    grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config =
+        grpc_core::ServiceConfig::Create(service_config_str);
     if (service_config != nullptr) {
-      chand->method_limit_table =
-          grpc_service_config_create_method_config_table(
-              service_config, refcounted_message_size_limits_create_from_json,
-              refcounted_message_size_limits_ref,
-              refcounted_message_size_limits_unref);
-      grpc_service_config_destroy(service_config);
+      chand->method_limit_table = service_config->CreateMethodConfigTable(
+          grpc_core::MessageSizeLimits::CreateFromJson);
     }
   }
   return GRPC_ERROR_NONE;
@@ -268,7 +271,7 @@
 // Destructor for channel_data.
 static void destroy_channel_elem(grpc_channel_element* elem) {
   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
-  grpc_slice_hash_table_unref(chand->method_limit_table);
+  chand->method_limit_table.reset();
 }
 
 const grpc_channel_filter grpc_message_size_filter = {
diff --git a/src/core/ext/filters/message_size/message_size_filter.h b/src/core/ext/filters/message_size/message_size_filter.h
index d3667f7..f66636e 100644
--- a/src/core/ext/filters/message_size/message_size_filter.h
+++ b/src/core/ext/filters/message_size/message_size_filter.h
@@ -17,6 +17,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_message_size_filter;
diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc
index 3092ed2..bed1004 100644
--- a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc
+++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h"
 
 #include <string.h>
diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h
index 9dae4f0..94d20f0 100644
--- a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h
+++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h
@@ -17,6 +17,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H
 #define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_workaround_cronet_compression_filter;
diff --git a/src/core/ext/filters/workarounds/workaround_utils.cc b/src/core/ext/filters/workarounds/workaround_utils.cc
index 850ed75..4dabe89 100644
--- a/src/core/ext/filters/workarounds/workaround_utils.cc
+++ b/src/core/ext/filters/workarounds/workaround_utils.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/workarounds/workaround_utils.h"
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/ext/filters/workarounds/workaround_utils.h b/src/core/ext/filters/workarounds/workaround_utils.h
index d6ef5e8..f172ccc 100644
--- a/src/core/ext/filters/workarounds/workaround_utils.h
+++ b/src/core/ext/filters/workarounds/workaround_utils.h
@@ -17,6 +17,8 @@
 #ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H
 #define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/workaround_list.h>
 
 #include "src/core/lib/transport/metadata.h"
diff --git a/src/core/ext/transport/chttp2/alpn/alpn.cc b/src/core/ext/transport/chttp2/alpn/alpn.cc
index 5c4bfd0..1fdab76 100644
--- a/src/core/ext/transport/chttp2/alpn/alpn.cc
+++ b/src/core/ext/transport/chttp2/alpn/alpn.cc
@@ -16,8 +16,10 @@
  *
  */
 
-#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
+#include "src/core/ext/transport/chttp2/alpn/alpn.h"
 
 #include "src/core/lib/gpr/useful.h"
 
diff --git a/src/core/ext/transport/chttp2/alpn/alpn.h b/src/core/ext/transport/chttp2/alpn/alpn.h
index fd7513c..0042eaf 100644
--- a/src/core/ext/transport/chttp2/alpn/alpn.h
+++ b/src/core/ext/transport/chttp2/alpn/alpn.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 /* Retuns 1 if the version is supported, 0 otherwise. */
diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/src/core/ext/transport/chttp2/client/chttp2_connector.cc
index 7eb9ebc..e7522ff 100644
--- a/src/core/ext/transport/chttp2/client/chttp2_connector.cc
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
 
 #include <grpc/grpc.h>
diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.h b/src/core/ext/transport/chttp2/client/chttp2_connector.h
index e258892..04da441 100644
--- a/src/core/ext/transport/chttp2/client/chttp2_connector.h
+++ b/src/core/ext/transport/chttp2/client/chttp2_connector.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/filters/client_channel/connector.h"
 
 grpc_connector* grpc_chttp2_connector_create();
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.cc b/src/core/ext/transport/chttp2/client/insecure/channel_create.cc
index ef1d3fb..6080036 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create.cc
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include <string.h>
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
index 0cdea5a..479f0da 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
@@ -16,10 +16,11 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/grpc_posix.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 
 #ifdef GPR_SUPPORT_CHANNELS_FROM_FD
 
diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
index 8c4025e..a82009f 100644
--- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
+++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include <string.h>
@@ -28,10 +30,11 @@
 #include "src/core/ext/filters/client_channel/uri_parser.h"
 #include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/lb_targets_info.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
 #include "src/core/lib/slice/slice_hash_table.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -63,9 +66,7 @@
   // To which address are we connecting? By default, use the server URI.
   const grpc_arg* server_uri_arg =
       grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI);
-  GPR_ASSERT(server_uri_arg != nullptr);
-  GPR_ASSERT(server_uri_arg->type == GRPC_ARG_STRING);
-  const char* server_uri_str = server_uri_arg->value.string;
+  const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg);
   GPR_ASSERT(server_uri_str != nullptr);
   grpc_uri* server_uri =
       grpc_uri_parse(server_uri_str, true /* supress errors */);
@@ -73,11 +74,11 @@
   const char* server_uri_path;
   server_uri_path =
       server_uri->path[0] == '/' ? server_uri->path + 1 : server_uri->path;
-  const grpc_slice_hash_table* targets_info =
-      grpc_lb_targets_info_find_in_args(args->args);
-  char* target_name_to_check = nullptr;
-  if (targets_info != nullptr) {  // LB channel
-    // Find the balancer name for the target.
+  const grpc_core::TargetAuthorityTable* target_authority_table =
+      grpc_core::FindTargetAuthorityTableInArgs(args->args);
+  grpc_core::UniquePtr<char> authority;
+  if (target_authority_table != nullptr) {
+    // Find the authority for the target.
     const char* target_uri_str =
         grpc_get_subchannel_address_uri_arg(args->args);
     grpc_uri* target_uri =
@@ -86,37 +87,33 @@
     if (target_uri->path[0] != '\0') {  // "path" may be empty
       const grpc_slice key = grpc_slice_from_static_string(
           target_uri->path[0] == '/' ? target_uri->path + 1 : target_uri->path);
-      const char* value = static_cast<const char*>(
-          grpc_slice_hash_table_get(targets_info, key));
-      if (value != nullptr) target_name_to_check = gpr_strdup(value);
+      const grpc_core::UniquePtr<char>* value =
+          target_authority_table->Get(key);
+      if (value != nullptr) authority.reset(gpr_strdup(value->get()));
       grpc_slice_unref_internal(key);
     }
-    if (target_name_to_check == nullptr) {
-      // If the target name to check hasn't already been set, fall back to using
-      // SERVER_URI
-      target_name_to_check = gpr_strdup(server_uri_path);
-    }
     grpc_uri_destroy(target_uri);
-  } else {  // regular channel: the secure name is the original server URI.
-    target_name_to_check = gpr_strdup(server_uri_path);
+  }
+  // If the authority hasn't already been set (either because no target
+  // authority table was present or because the target was not present
+  // in the table), fall back to using the original server URI.
+  if (authority == nullptr) {
+    authority.reset(gpr_strdup(server_uri_path));
   }
   grpc_uri_destroy(server_uri);
-  GPR_ASSERT(target_name_to_check != nullptr);
   grpc_channel_security_connector* subchannel_security_connector = nullptr;
   // Create the security connector using the credentials and target name.
   grpc_channel_args* new_args_from_connector = nullptr;
   const grpc_security_status security_status =
       grpc_channel_credentials_create_security_connector(
-          channel_credentials, target_name_to_check, args->args,
+          channel_credentials, authority.get(), args->args,
           &subchannel_security_connector, &new_args_from_connector);
   if (security_status != GRPC_SECURITY_OK) {
     gpr_log(GPR_ERROR,
             "Failed to create secure subchannel for secure name '%s'",
-            target_name_to_check);
-    gpr_free(target_name_to_check);
+            authority.get());
     return nullptr;
   }
-  gpr_free(target_name_to_check);
   grpc_arg new_security_connector_arg =
       grpc_security_connector_to_arg(&subchannel_security_connector->base);
 
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc
index 90b2ee1..687cc48 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.cc
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/server/chttp2_server.h"
 
 #include <grpc/grpc.h>
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.h b/src/core/ext/transport/chttp2/server/chttp2_server.h
index 7de859d..7b41972 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.h
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
index 52c42d0..822236d 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include <grpc/support/log.h>
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
index dafd4af..371e463 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
@@ -16,10 +16,11 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/grpc_posix.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 
 #ifdef GPR_SUPPORT_CHANNELS_FROM_FD
 
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
index 723af97..6689a17 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include <string.h>
diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.cc b/src/core/ext/transport/chttp2/transport/bin_decoder.cc
index 91980e6..f0f32da 100644
--- a/src/core/ext/transport/chttp2/transport/bin_decoder.cc
+++ b/src/core/ext/transport/chttp2/transport/bin_decoder.cc
@@ -16,9 +16,11 @@
  *
  */
 
-#include "src/core/ext/transport/chttp2/transport/bin_decoder.h"
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include "src/core/ext/transport/chttp2/transport/bin_decoder.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
@@ -75,6 +77,32 @@
 #define COMPOSE_OUTPUT_BYTE_2(input_ptr) \
   (uint8_t)((decode_table[input_ptr[2]] << 6) | decode_table[input_ptr[3]])
 
+// By RFC 4648, if the length of the encoded string without padding is 4n+r,
+// the length of decoded string is: 1) 3n if r = 0, 2) 3n + 1 if r = 2, 3, or
+// 3) invalid if r = 1.
+size_t grpc_chttp2_base64_infer_length_after_decode(const grpc_slice& slice) {
+  size_t len = GRPC_SLICE_LENGTH(slice);
+  const uint8_t* bytes = GRPC_SLICE_START_PTR(slice);
+  while (len > 0 && bytes[len - 1] == '=') {
+    len--;
+  }
+  if (GRPC_SLICE_LENGTH(slice) - len > 2) {
+    gpr_log(GPR_ERROR,
+            "Base64 decoding failed. Input has more than 2 paddings.");
+    return 0;
+  }
+  size_t tuples = len / 4;
+  size_t tail_case = len % 4;
+  if (tail_case == 1) {
+    gpr_log(GPR_ERROR,
+            "Base64 decoding failed. Input has a length of %zu (without"
+            " padding), which is invalid.\n",
+            len);
+    return 0;
+  }
+  return tuples * 3 + tail_xtra[tail_case];
+}
+
 bool grpc_base64_decode_partial(struct grpc_base64_decode_context* ctx) {
   size_t input_tail;
 
diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.h b/src/core/ext/transport/chttp2/transport/bin_decoder.h
index 9cb75cc..8a4d4a7 100644
--- a/src/core/ext/transport/chttp2/transport/bin_decoder.h
+++ b/src/core/ext/transport/chttp2/transport/bin_decoder.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <stdbool.h>
 
@@ -48,4 +50,7 @@
 grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
                                                  size_t output_length);
 
+/* Infer the length of decoded data from encoded data. */
+size_t grpc_chttp2_base64_infer_length_after_decode(const grpc_slice& slice);
+
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H */
diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.cc b/src/core/ext/transport/chttp2/transport/bin_encoder.cc
index e3b8adb..bad29e3 100644
--- a/src/core/ext/transport/chttp2/transport/bin_encoder.cc
+++ b/src/core/ext/transport/chttp2/transport/bin_encoder.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 
 #include <string.h>
diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.h b/src/core/ext/transport/chttp2/transport/bin_encoder.h
index 93ad0df..1b7bb15 100644
--- a/src/core/ext/transport/chttp2/transport/bin_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/bin_encoder.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 
 /* base64 encode a slice. Returns a new slice, does not take ownership of the
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
index a699081..531ea73 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gpr/env.h"
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index ad8da94..df3fb8c 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
-
 #include <grpc/support/port_platform.h>
 
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+
 #include <inttypes.h>
 #include <limits.h>
 #include <math.h>
@@ -1456,8 +1456,10 @@
       }
     }
     if (op_payload->send_initial_metadata.peer_string != nullptr) {
-      gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string,
-                        (gpr_atm)gpr_strdup(t->peer_string));
+      char* old_peer_string = (char*)gpr_atm_full_xchg(
+          op_payload->send_initial_metadata.peer_string,
+          (gpr_atm)gpr_strdup(t->peer_string));
+      gpr_free(old_peer_string);
     }
   }
 
@@ -1473,6 +1475,7 @@
       // streaming call might send another message before getting a
       // recv_message failure, breaking out of its loop, and then
       // starting recv_trailing_metadata.
+      grpc_byte_stream_destroy(op->payload->send_message.send_message);
       grpc_chttp2_complete_closure_step(
           t, s, &s->fetching_send_message_finished,
           t->is_client && s->received_trailing_metadata
@@ -1570,8 +1573,10 @@
     s->trailing_metadata_available =
         op_payload->recv_initial_metadata.trailing_metadata_available;
     if (op_payload->recv_initial_metadata.peer_string != nullptr) {
-      gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string,
-                        (gpr_atm)gpr_strdup(t->peer_string));
+      char* old_peer_string = (char*)gpr_atm_full_xchg(
+          op_payload->recv_initial_metadata.peer_string,
+          (gpr_atm)gpr_strdup(t->peer_string));
+      gpr_free(old_peer_string);
     }
     grpc_chttp2_maybe_complete_recv_initial_metadata(t, s);
   }
@@ -2092,7 +2097,10 @@
                                     GRPC_ERROR_REF(error),
                                     "send_trailing_metadata_finished");
 
-  s->fetching_send_message = nullptr;
+  if (s->fetching_send_message != nullptr) {
+    grpc_byte_stream_destroy(s->fetching_send_message);
+    s->fetching_send_message = nullptr;
+  }
   grpc_chttp2_complete_closure_step(t, s, &s->fetching_send_message_finished,
                                     GRPC_ERROR_REF(error),
                                     "fetching_send_message_finished");
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
index 34519ce..9d55b3f 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/transport/transport.h"
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc
index 45b6f97..e89c363 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.cc
+++ b/src/core/ext/transport/chttp2/transport/flow_control.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/flow_control.h"
 
 #include <inttypes.h>
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.h b/src/core/ext/transport/chttp2/transport/flow_control.h
index b580277..120fefc 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.h
+++ b/src/core/ext/transport/chttp2/transport/flow_control.h
@@ -20,6 +20,7 @@
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H
 
 #include <grpc/support/port_platform.h>
+
 #include <stdint.h>
 
 #include "src/core/ext/transport/chttp2/transport/http2_settings.h"
diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h
index dba4c00..083c007 100644
--- a/src/core/ext/transport/chttp2/transport/frame.h
+++ b/src/core/ext/transport/chttp2/transport/frame.h
@@ -19,9 +19,10 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H
 
-#include <grpc/slice.h>
 #include <grpc/support/port_platform.h>
 
+#include <grpc/slice.h>
+
 #include "src/core/lib/iomgr/error.h"
 
 /* defined in internal.h */
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.cc b/src/core/ext/transport/chttp2/transport/frame_data.cc
index 721f0a5..0d37a49 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_data.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/frame_data.h"
 
 #include <string.h>
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.h b/src/core/ext/transport/chttp2/transport/frame_data.h
index 964cc59..3efbbf9 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.h
+++ b/src/core/ext/transport/chttp2/transport/frame_data.h
@@ -21,6 +21,8 @@
 
 /* Parser for GRPC streams embedded in DATA frames */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.cc b/src/core/ext/transport/chttp2/transport/frame_goaway.cc
index 70931ed..2a1dd3c 100644
--- a/src/core/ext/transport/chttp2/transport/frame_goaway.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_goaway.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.h b/src/core/ext/transport/chttp2/transport/frame_goaway.h
index 064d39a..e17ed8d 100644
--- a/src/core/ext/transport/chttp2/transport/frame_goaway.h
+++ b/src/core/ext/transport/chttp2/transport/frame_goaway.h
@@ -19,9 +19,10 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
-#include <grpc/support/port_platform.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.cc b/src/core/ext/transport/chttp2/transport/frame_ping.cc
index 6229459..205826b 100644
--- a/src/core/ext/transport/chttp2/transport/frame_ping.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_ping.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/frame_ping.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.h b/src/core/ext/transport/chttp2/transport/frame_ping.h
index 75bacfb..8718d6a 100644
--- a/src/core/ext/transport/chttp2/transport/frame_ping.h
+++ b/src/core/ext/transport/chttp2/transport/frame_ping.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
index 7952876..4bdd430 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
index e76a3ca..bb2d34f 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc
index 737a938..9ea27dc 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/frame_settings.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.h b/src/core/ext/transport/chttp2/transport/frame_settings.h
index ce65402..df19627 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.h
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.h
@@ -19,8 +19,9 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H
 
-#include <grpc/slice.h>
 #include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/ext/transport/chttp2/transport/http2_settings.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.cc b/src/core/ext/transport/chttp2/transport/frame_window_update.cc
index 95be5b4..4b586dc 100644
--- a/src/core/ext/transport/chttp2/transport/frame_window_update.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_window_update.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/frame_window_update.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.h b/src/core/ext/transport/chttp2/transport/frame_window_update.h
index a32f1a9..30667c7 100644
--- a/src/core/ext/transport/chttp2/transport/frame_window_update.h
+++ b/src/core/ext/transport/chttp2/transport/frame_window_update.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
index 4855455..e4f3c1b 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
 
 #include <assert.h>
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index a26514c..b370932 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -19,9 +19,10 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
-#include <grpc/support/port_platform.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/metadata_batch.h"
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
index 26a38e3..fc96a8b 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
@@ -25,7 +27,6 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h
index 060bc5c..b3b8018 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h
@@ -19,9 +19,10 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
-#include <grpc/support/port_platform.h>
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 #include "src/core/ext/transport/chttp2/transport/hpack_table.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc
index d7cabbd..f050f50 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/hpack_table.h"
 
 #include <assert.h>
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h
index 189ad1c..98026a4 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.h
@@ -19,8 +19,9 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H
 
-#include <grpc/slice.h>
 #include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/transport/metadata.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.cc b/src/core/ext/transport/chttp2/transport/http2_settings.cc
index 745b165..294ee8e 100644
--- a/src/core/ext/transport/chttp2/transport/http2_settings.cc
+++ b/src/core/ext/transport/chttp2/transport/http2_settings.cc
@@ -18,6 +18,8 @@
  * Automatically generated by tools/codegen/core/gen_settings_ids.py
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/http2_settings.h"
 
 #include "src/core/lib/gpr/useful.h"
diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.h b/src/core/ext/transport/chttp2/transport/http2_settings.h
index fd15b69..07ce062 100644
--- a/src/core/ext/transport/chttp2/transport/http2_settings.h
+++ b/src/core/ext/transport/chttp2/transport/http2_settings.h
@@ -21,6 +21,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 #include <stdint.h>
 
diff --git a/src/core/ext/transport/chttp2/transport/huffsyms.cc b/src/core/ext/transport/chttp2/transport/huffsyms.cc
index f28d8cc..813e4c9 100644
--- a/src/core/ext/transport/chttp2/transport/huffsyms.cc
+++ b/src/core/ext/transport/chttp2/transport/huffsyms.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/huffsyms.h"
 
 /* Constants pulled from the HPACK spec, and converted to C using the vim
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
index 18d89f0..4d7dfd9 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
 
 #include <string.h>
@@ -67,6 +69,5 @@
 
 void grpc_chttp2_incoming_metadata_buffer_publish(
     grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch) {
-  *batch = buffer->batch;
-  grpc_metadata_batch_init(&buffer->batch);
+  grpc_metadata_batch_move(&buffer->batch, batch);
 }
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
index b84cd48..d029cf0 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/transport.h"
 
 typedef struct {
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 6b6c0b2..b9431cd 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H
 #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <assert.h>
 #include <stdbool.h>
 
diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc
index 9357fed..988380b 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.cc
+++ b/src/core/ext/transport/chttp2/transport/parsing.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
 #include <string.h>
diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.cc b/src/core/ext/transport/chttp2/transport/stream_lists.cc
index 3aad8c5..5d3ec4b 100644
--- a/src/core/ext/transport/chttp2/transport/stream_lists.cc
+++ b/src/core/ext/transport/chttp2/transport/stream_lists.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
diff --git a/src/core/ext/transport/chttp2/transport/stream_map.cc b/src/core/ext/transport/chttp2/transport/stream_map.cc
index a40eaff..f300e23 100644
--- a/src/core/ext/transport/chttp2/transport/stream_map.cc
+++ b/src/core/ext/transport/chttp2/transport/stream_map.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/stream_map.h"
 
 #include <string.h>
diff --git a/src/core/ext/transport/chttp2/transport/varint.cc b/src/core/ext/transport/chttp2/transport/varint.cc
index 621a810..d4b0178 100644
--- a/src/core/ext/transport/chttp2/transport/varint.cc
+++ b/src/core/ext/transport/chttp2/transport/varint.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/varint.h"
 
 uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value) {
diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc
index 42cba9a..7471d88 100644
--- a/src/core/ext/transport/chttp2/transport/writing.cc
+++ b/src/core/ext/transport/chttp2/transport/writing.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
 #include <limits.h>
diff --git a/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc b/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc
index 578cbb8..1a6bded 100644
--- a/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc
+++ b/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc
@@ -19,6 +19,8 @@
 /* This file has empty implementation of all the functions exposed by the cronet
 library, so we can build it in all environments */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/support/log.h>
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc
index 8904c12..ff1c1aa 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.cc
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc
@@ -16,14 +16,17 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
-#include <grpc/impl/codegen/port_platform.h>
 #include <grpc/slice_buffer.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/ext/transport/chttp2/transport/bin_decoder.h"
+#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
 #include "src/core/ext/transport/cronet/transport/cronet_transport.h"
 #include "src/core/lib/gpr/host_port.h"
@@ -393,6 +396,29 @@
   gpr_mu_unlock(&s->mu);
 }
 
+static void convert_cronet_array_to_metadata(
+    const bidirectional_stream_header_array* header_array,
+    grpc_chttp2_incoming_metadata_buffer* mds) {
+  for (size_t i = 0; i < header_array->count; i++) {
+    CRONET_LOG(GPR_DEBUG, "header key=%s, value=%s",
+               header_array->headers[i].key, header_array->headers[i].value);
+    grpc_slice key = grpc_slice_intern(
+        grpc_slice_from_static_string(header_array->headers[i].key));
+    grpc_slice value;
+    if (grpc_is_binary_header(key)) {
+      value = grpc_slice_from_static_string(header_array->headers[i].value);
+      value = grpc_slice_intern(grpc_chttp2_base64_decode_with_length(
+          value, grpc_chttp2_base64_infer_length_after_decode(value)));
+    } else {
+      value = grpc_slice_intern(
+          grpc_slice_from_static_string(header_array->headers[i].value));
+    }
+    GRPC_LOG_IF_ERROR("convert_cronet_array_to_metadata",
+                      grpc_chttp2_incoming_metadata_buffer_add(
+                          mds, grpc_mdelem_from_slices(key, value)));
+  }
+}
+
 /*
   Cronet callback
 */
@@ -517,16 +543,7 @@
          sizeof(s->state.rs.initial_metadata));
   grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.initial_metadata,
                                             s->arena);
-  for (size_t i = 0; i < headers->count; i++) {
-    GRPC_LOG_IF_ERROR("on_response_headers_received",
-                      grpc_chttp2_incoming_metadata_buffer_add(
-                          &s->state.rs.initial_metadata,
-                          grpc_mdelem_from_slices(
-                              grpc_slice_intern(grpc_slice_from_static_string(
-                                  headers->headers[i].key)),
-                              grpc_slice_intern(grpc_slice_from_static_string(
-                                  headers->headers[i].value)))));
-  }
+  convert_cronet_array_to_metadata(headers, &s->state.rs.initial_metadata);
   s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true;
   if (!(s->state.state_op_done[OP_CANCEL_ERROR] ||
         s->state.state_callback_received[OP_FAILED])) {
@@ -621,18 +638,11 @@
   s->state.rs.trailing_metadata_valid = false;
   grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.trailing_metadata,
                                             s->arena);
-  for (size_t i = 0; i < trailers->count; i++) {
-    CRONET_LOG(GPR_DEBUG, "trailer key=%s, value=%s", trailers->headers[i].key,
-               trailers->headers[i].value);
-    GRPC_LOG_IF_ERROR("on_response_trailers_received",
-                      grpc_chttp2_incoming_metadata_buffer_add(
-                          &s->state.rs.trailing_metadata,
-                          grpc_mdelem_from_slices(
-                              grpc_slice_intern(grpc_slice_from_static_string(
-                                  trailers->headers[i].key)),
-                              grpc_slice_intern(grpc_slice_from_static_string(
-                                  trailers->headers[i].value)))));
+  convert_cronet_array_to_metadata(trailers, &s->state.rs.trailing_metadata);
+  if (trailers->count > 0) {
     s->state.rs.trailing_metadata_valid = true;
+  }
+  for (size_t i = 0; i < trailers->count; i++) {
     if (0 == strcmp(trailers->headers[i].key, "grpc-status") &&
         0 != strcmp(trailers->headers[i].value, "0")) {
       s->state.fail_state = true;
@@ -721,7 +731,14 @@
     grpc_mdelem mdelem = curr->md;
     curr = curr->next;
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem));
-    char* value = grpc_slice_to_c_string(GRPC_MDVALUE(mdelem));
+    char* value;
+    if (grpc_is_binary_header(GRPC_MDKEY(mdelem))) {
+      grpc_slice wire_value = grpc_chttp2_base64_encode(GRPC_MDVALUE(mdelem));
+      value = grpc_slice_to_c_string(wire_value);
+      grpc_slice_unref(wire_value);
+    } else {
+      value = grpc_slice_to_c_string(GRPC_MDVALUE(mdelem));
+    }
     if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_SCHEME) ||
         grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_AUTHORITY)) {
       /* Cronet populates these fields on its own */
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.h b/src/core/ext/transport/cronet/transport/cronet_transport.h
index d9ff913..fb7e149 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.h
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H
 #define GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/transport.h"
 
 grpc_transport* grpc_create_cronet_transport(void* engine, const char* target,
diff --git a/src/core/ext/transport/inproc/inproc_plugin.cc b/src/core/ext/transport/inproc/inproc_plugin.cc
index 83a7d8d..8e251fa 100644
--- a/src/core/ext/transport/inproc/inproc_plugin.cc
+++ b/src/core/ext/transport/inproc/inproc_plugin.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/ext/transport/inproc/inproc_transport.h"
 #include "src/core/lib/debug/trace.h"
 
diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc
index 2022eaf..5f898bb 100644
--- a/src/core/ext/transport/inproc/inproc_transport.cc
+++ b/src/core/ext/transport/inproc/inproc_transport.cc
@@ -16,12 +16,14 @@
  *
  */
 
-#include "src/core/ext/transport/inproc/inproc_transport.h"
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 #include <string.h>
+#include "src/core/ext/transport/inproc/inproc_transport.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -480,6 +482,8 @@
     s->recv_message_op = nullptr;
   }
   if (s->send_message_op) {
+    grpc_byte_stream_destroy(
+        s->send_message_op->payload->send_message.send_message);
     complete_if_batch_end_locked(
         s, error, s->send_message_op,
         "fail_helper scheduling send-message-on-complete");
@@ -506,6 +510,14 @@
   GRPC_ERROR_UNREF(error);
 }
 
+// TODO(vjpai): It should not be necessary to drain the incoming byte
+// stream and create a new one; instead, we should simply pass the byte
+// stream from the sender directly to the receiver as-is.
+//
+// Note that fixing this will also avoid the assumption in this code
+// that the incoming byte stream's next() call will always return
+// synchronously.  That assumption is true today but may not always be
+// true in the future.
 static void message_transfer_locked(inproc_stream* sender,
                                     inproc_stream* receiver) {
   size_t remaining =
@@ -532,6 +544,8 @@
     remaining -= GRPC_SLICE_LENGTH(message_slice);
     grpc_slice_buffer_add(&receiver->recv_message, message_slice);
   } while (remaining > 0);
+  grpc_byte_stream_destroy(
+      sender->send_message_op->payload->send_message.send_message);
 
   grpc_slice_buffer_stream_init(&receiver->recv_stream, &receiver->recv_message,
                                 0);
@@ -592,6 +606,8 @@
                (s->trailing_md_sent || other->recv_trailing_md_op)) {
       // A server send will never be matched if the client is waiting
       // for trailing metadata already
+      grpc_byte_stream_destroy(
+          s->send_message_op->payload->send_message.send_message);
       complete_if_batch_end_locked(
           s, GRPC_ERROR_NONE, s->send_message_op,
           "op_state_machine scheduling send-message-on-complete");
@@ -728,6 +744,8 @@
     if ((s->trailing_md_sent || s->t->is_client) && s->send_message_op) {
       // Nothing further will try to receive from this stream, so finish off
       // any outstanding send_message op
+      grpc_byte_stream_destroy(
+          s->send_message_op->payload->send_message.send_message);
       complete_if_batch_end_locked(
           s, new_err, s->send_message_op,
           "op_state_machine scheduling send-message-on-complete");
@@ -785,6 +803,8 @@
       s->send_message_op) {
     // Nothing further will try to receive from this stream, so finish off
     // any outstanding send_message op
+    grpc_byte_stream_destroy(
+        s->send_message_op->payload->send_message.send_message);
     complete_if_batch_end_locked(
         s, new_err, s->send_message_op,
         "op_state_machine scheduling send-message-on-complete");
diff --git a/src/core/ext/transport/inproc/inproc_transport.h b/src/core/ext/transport/inproc/inproc_transport.h
index 7c0453e..049d140 100644
--- a/src/core/ext/transport/inproc/inproc_transport.h
+++ b/src/core/ext/transport/inproc/inproc_transport.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H
 #define GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/transport_impl.h"
 
 grpc_channel* grpc_inproc_channel_create(grpc_server* server,
diff --git a/src/core/lib/avl/avl.cc b/src/core/lib/avl/avl.cc
index c674d9b..ec106dd 100644
--- a/src/core/lib/avl/avl.cc
+++ b/src/core/lib/avl/avl.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/avl/avl.h"
 
 #include <assert.h>
diff --git a/src/core/lib/avl/avl.h b/src/core/lib/avl/avl.h
index df5d2e8..15a9d56 100644
--- a/src/core/lib/avl/avl.h
+++ b/src/core/lib/avl/avl.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_AVL_AVL_H
 #define GRPC_CORE_LIB_AVL_AVL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/sync.h>
 
 /** internal node of an AVL tree */
diff --git a/src/core/lib/backoff/backoff.cc b/src/core/lib/backoff/backoff.cc
index 4b74afc..e536abd 100644
--- a/src/core/lib/backoff/backoff.cc
+++ b/src/core/lib/backoff/backoff.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/backoff/backoff.h"
 
 #include <algorithm>
diff --git a/src/core/lib/backoff/backoff.h b/src/core/lib/backoff/backoff.h
index de30e52..e769d15 100644
--- a/src/core/lib/backoff/backoff.h
+++ b/src/core/lib/backoff/backoff.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_BACKOFF_BACKOFF_H
 #define GRPC_CORE_LIB_BACKOFF_BACKOFF_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/exec_ctx.h"
 
 namespace grpc_core {
diff --git a/src/core/lib/channel/channel_args.cc b/src/core/lib/channel/channel_args.cc
index 98fdfa6..66a86c2 100644
--- a/src/core/lib/channel/channel_args.cc
+++ b/src/core/lib/channel/channel_args.cc
@@ -354,6 +354,15 @@
   return arg->value.integer;
 }
 
+char* grpc_channel_arg_get_string(const grpc_arg* arg) {
+  if (arg == nullptr) return nullptr;
+  if (arg->type != GRPC_ARG_STRING) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be an string", arg->key);
+    return nullptr;
+  }
+  return arg->value.string;
+}
+
 bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value) {
   if (arg == nullptr) return default_value;
   if (arg->type != GRPC_ARG_INTEGER) {
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
index 73e9122..c0d6a17 100644
--- a/src/core/lib/channel/channel_args.h
+++ b/src/core/lib/channel/channel_args.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
 #define GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include "src/core/lib/iomgr/socket_mutator.h"
@@ -109,6 +111,11 @@
 int grpc_channel_arg_get_integer(const grpc_arg* arg,
                                  const grpc_integer_options options);
 
+/** Returns the value of \a arg if \a arg is of type GRPC_ARG_STRING.
+    Otherwise, emits a warning log, and returns nullptr.
+    If arg is nullptr, returns nullptr, and does not emit a warning. */
+char* grpc_channel_arg_get_string(const grpc_arg* arg);
+
 bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value);
 
 // Helpers for creating channel args.
diff --git a/src/core/lib/channel/channel_stack.cc b/src/core/lib/channel/channel_stack.cc
index 737d828..a9459b1 100644
--- a/src/core/lib/channel/channel_stack.cc
+++ b/src/core/lib/channel/channel_stack.cc
@@ -16,9 +16,11 @@
  *
  */
 
-#include "src/core/lib/channel/channel_stack.h"
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include "src/core/lib/channel/channel_stack.h"
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index b9f9748..4bf8218 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -33,6 +33,8 @@
    Call stacks are created by channel stacks and represent the per-call data
    for that stack. */
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 #include <grpc/grpc.h>
diff --git a/src/core/lib/channel/channel_stack_builder.cc b/src/core/lib/channel/channel_stack_builder.cc
index cae862e..8a72449 100644
--- a/src/core/lib/channel/channel_stack_builder.cc
+++ b/src/core/lib/channel/channel_stack_builder.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack_builder.h"
 
 #include <string.h>
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
index d00ddc6..c9a170b 100644
--- a/src/core/lib/channel/channel_stack_builder.h
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H
 #define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include "src/core/lib/channel/channel_args.h"
diff --git a/src/core/lib/channel/connected_channel.cc b/src/core/lib/channel/connected_channel.cc
index a16e89c..ddd3029 100644
--- a/src/core/lib/channel/connected_channel.cc
+++ b/src/core/lib/channel/connected_channel.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/connected_channel.h"
 
 #include <stdarg.h>
diff --git a/src/core/lib/channel/connected_channel.h b/src/core/lib/channel/connected_channel.h
index 91de802..faa1c73 100644
--- a/src/core/lib/channel/connected_channel.h
+++ b/src/core/lib/channel/connected_channel.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H
 #define GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack_builder.h"
 
 extern const grpc_channel_filter grpc_connected_filter;
diff --git a/src/core/lib/channel/handshaker.cc b/src/core/lib/channel/handshaker.cc
index 518e880..9b1af8d 100644
--- a/src/core/lib/channel/handshaker.cc
+++ b/src/core/lib/channel/handshaker.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/channel/handshaker.h b/src/core/lib/channel/handshaker.h
index 68e5463..dfecd81 100644
--- a/src/core/lib/channel/handshaker.h
+++ b/src/core/lib/channel/handshaker.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H
 #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/iomgr/closure.h"
diff --git a/src/core/lib/channel/handshaker_factory.cc b/src/core/lib/channel/handshaker_factory.cc
index 2380d98..4fd4363 100644
--- a/src/core/lib/channel/handshaker_factory.cc
+++ b/src/core/lib/channel/handshaker_factory.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/handshaker_factory.h"
 
 #include <grpc/support/log.h>
diff --git a/src/core/lib/channel/handshaker_factory.h b/src/core/lib/channel/handshaker_factory.h
index 8a7c015..9e36443 100644
--- a/src/core/lib/channel/handshaker_factory.h
+++ b/src/core/lib/channel/handshaker_factory.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H
 #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/channel/handshaker.h"
diff --git a/src/core/lib/channel/handshaker_registry.cc b/src/core/lib/channel/handshaker_registry.cc
index 5464cc4..eec3e1b 100644
--- a/src/core/lib/channel/handshaker_registry.cc
+++ b/src/core/lib/channel/handshaker_registry.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/handshaker_registry.h"
 
 #include <string.h>
diff --git a/src/core/lib/channel/handshaker_registry.h b/src/core/lib/channel/handshaker_registry.h
index 0b05531..b42d61f 100644
--- a/src/core/lib/channel/handshaker_registry.h
+++ b/src/core/lib/channel/handshaker_registry.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H
 #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/channel/handshaker_factory.h"
diff --git a/src/core/lib/compression/algorithm_metadata.h b/src/core/lib/compression/algorithm_metadata.h
index 7db771e..1be79e5 100644
--- a/src/core/lib/compression/algorithm_metadata.h
+++ b/src/core/lib/compression/algorithm_metadata.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H
 #define GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/compression.h>
 #include "src/core/lib/compression/compression_internal.h"
 #include "src/core/lib/transport/metadata.h"
diff --git a/src/core/lib/compression/compression.cc b/src/core/lib/compression/compression.cc
index 69d70ea..4871754 100644
--- a/src/core/lib/compression/compression.cc
+++ b/src/core/lib/compression/compression.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdlib.h>
 #include <string.h>
 
diff --git a/src/core/lib/compression/compression_internal.cc b/src/core/lib/compression/compression_internal.cc
index 36829c8..538514c 100644
--- a/src/core/lib/compression/compression_internal.cc
+++ b/src/core/lib/compression/compression_internal.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdlib.h>
 #include <string.h>
 
diff --git a/src/core/lib/compression/compression_internal.h b/src/core/lib/compression/compression_internal.h
index 72f01dd..da00736 100644
--- a/src/core/lib/compression/compression_internal.h
+++ b/src/core/lib/compression/compression_internal.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H
 #define GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/compression_types.h>
 
 #ifdef __cplusplus
diff --git a/src/core/lib/compression/message_compress.cc b/src/core/lib/compression/message_compress.cc
index 2e44551..e06454f 100644
--- a/src/core/lib/compression/message_compress.cc
+++ b/src/core/lib/compression/message_compress.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/compression/message_compress.h"
 
 #include <string.h>
diff --git a/src/core/lib/compression/message_compress.h b/src/core/lib/compression/message_compress.h
index ed9e5bf..91654e4 100644
--- a/src/core/lib/compression/message_compress.h
+++ b/src/core/lib/compression/message_compress.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H
 #define GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice_buffer.h>
 
 #include "src/core/lib/compression/compression_internal.h"
diff --git a/src/core/lib/compression/stream_compression.cc b/src/core/lib/compression/stream_compression.cc
index b4b3e52..46cb3da 100644
--- a/src/core/lib/compression/stream_compression.cc
+++ b/src/core/lib/compression/stream_compression.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
 
 #include "src/core/lib/compression/stream_compression.h"
diff --git a/src/core/lib/compression/stream_compression.h b/src/core/lib/compression/stream_compression.h
index 8322835..c80f2f8 100644
--- a/src/core/lib/compression/stream_compression.h
+++ b/src/core/lib/compression/stream_compression.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H
 #define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/slice_buffer.h>
diff --git a/src/core/lib/compression/stream_compression_gzip.cc b/src/core/lib/compression/stream_compression_gzip.cc
index 48fe99b..682f712 100644
--- a/src/core/lib/compression/stream_compression_gzip.cc
+++ b/src/core/lib/compression/stream_compression_gzip.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
diff --git a/src/core/lib/compression/stream_compression_gzip.h b/src/core/lib/compression/stream_compression_gzip.h
index 7cf49a0..740f097 100644
--- a/src/core/lib/compression/stream_compression_gzip.h
+++ b/src/core/lib/compression/stream_compression_gzip.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H
 #define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/compression/stream_compression.h"
 
 extern const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable;
diff --git a/src/core/lib/compression/stream_compression_identity.cc b/src/core/lib/compression/stream_compression_identity.cc
index 933d24b..52a6236 100644
--- a/src/core/lib/compression/stream_compression_identity.cc
+++ b/src/core/lib/compression/stream_compression_identity.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
diff --git a/src/core/lib/compression/stream_compression_identity.h b/src/core/lib/compression/stream_compression_identity.h
index 41926e9..cc77b63 100644
--- a/src/core/lib/compression/stream_compression_identity.h
+++ b/src/core/lib/compression/stream_compression_identity.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H
 #define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/compression/stream_compression.h"
 
 extern const grpc_stream_compression_vtable
diff --git a/src/core/lib/debug/stats.cc b/src/core/lib/debug/stats.cc
index 09a8628..d8ddf03 100644
--- a/src/core/lib/debug/stats.cc
+++ b/src/core/lib/debug/stats.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/stats.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/debug/stats.h b/src/core/lib/debug/stats.h
index 02eed5e..7496652 100644
--- a/src/core/lib/debug/stats.h
+++ b/src/core/lib/debug/stats.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_DEBUG_STATS_H
 #define GRPC_CORE_LIB_DEBUG_STATS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 #include "src/core/lib/debug/stats_data.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/lib/debug/stats_data.cc b/src/core/lib/debug/stats_data.cc
index e2d3a6d..309ece9 100644
--- a/src/core/lib/debug/stats_data.cc
+++ b/src/core/lib/debug/stats_data.cc
@@ -18,8 +18,10 @@
  * Automatically generated by tools/codegen/core/gen_stats_data.py
  */
 
-#include "src/core/lib/debug/stats_data.h"
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/stats.h"
+#include "src/core/lib/debug/stats_data.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h
index 4504be3..da1266a 100644
--- a/src/core/lib/debug/stats_data.h
+++ b/src/core/lib/debug/stats_data.h
@@ -21,6 +21,8 @@
 #ifndef GRPC_CORE_LIB_DEBUG_STATS_DATA_H
 #define GRPC_CORE_LIB_DEBUG_STATS_DATA_H
 
+#include <grpc/support/port_platform.h>
+
 #include <inttypes.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 
diff --git a/src/core/lib/debug/trace.cc b/src/core/lib/debug/trace.cc
index c0cefd0..b0e0f2b 100644
--- a/src/core/lib/debug/trace.cc
+++ b/src/core/lib/debug/trace.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/trace.h"
 
 #include <string.h>
diff --git a/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h
index 69ddd80..bfec92c 100644
--- a/src/core/lib/debug/trace.h
+++ b/src/core/lib/debug/trace.h
@@ -19,8 +19,9 @@
 #ifndef GRPC_CORE_LIB_DEBUG_TRACE_H
 #define GRPC_CORE_LIB_DEBUG_TRACE_H
 
-#include <grpc/support/atm.h>
 #include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
 #include <stdbool.h>
 
 void grpc_tracer_init(const char* env_var_name);
diff --git a/src/core/lib/gpr/alloc.cc b/src/core/lib/gpr/alloc.cc
index e0d2596..611e4cc 100644
--- a/src/core/lib/gpr/alloc.cc
+++ b/src/core/lib/gpr/alloc.cc
@@ -16,10 +16,11 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <stdlib.h>
 #include <string.h>
 #include "src/core/lib/profiling/timers.h"
diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc
index 2d514df..b02c5b9 100644
--- a/src/core/lib/gpr/arena.cc
+++ b/src/core/lib/gpr/arena.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/arena.h"
 
 #include <string.h>
@@ -24,6 +26,49 @@
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
 
+// Uncomment this to use a simple arena that simply allocates the
+// requested amount of memory for each call to gpr_arena_alloc().  This
+// effectively eliminates the efficiency gain of using an arena, but it
+// may be useful for debugging purposes.
+//#define SIMPLE_ARENA_FOR_DEBUGGING
+
+#ifdef SIMPLE_ARENA_FOR_DEBUGGING
+
+#include <grpc/support/sync.h>
+
+struct gpr_arena {
+  gpr_mu mu;
+  void** ptrs;
+  size_t num_ptrs;
+};
+
+gpr_arena* gpr_arena_create(size_t ignored_initial_size) {
+  gpr_arena* arena = (gpr_arena*)gpr_zalloc(sizeof(*arena));
+  gpr_mu_init(&arena->mu);
+  return arena;
+}
+
+size_t gpr_arena_destroy(gpr_arena* arena) {
+  gpr_mu_destroy(&arena->mu);
+  for (size_t i = 0; i < arena->num_ptrs; ++i) {
+    gpr_free(arena->ptrs[i]);
+  }
+  gpr_free(arena->ptrs);
+  gpr_free(arena);
+  return 1;  // Value doesn't matter, since it won't be used.
+}
+
+void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
+  gpr_mu_lock(&arena->mu);
+  arena->ptrs =
+      (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1));
+  void* retval = arena->ptrs[arena->num_ptrs++] = gpr_zalloc(size);
+  gpr_mu_unlock(&arena->mu);
+  return retval;
+}
+
+#else  // SIMPLE_ARENA_FOR_DEBUGGING
+
 // TODO(roth): We currently assume that all callers need alignment of 16
 // bytes, which may be wrong in some cases.  As part of converting the
 // arena API to C++, we should consider replacing gpr_arena_alloc() with a
@@ -103,3 +148,5 @@
                         ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
   return ptr + start - z->size_begin;
 }
+
+#endif  // SIMPLE_ARENA_FOR_DEBUGGING
diff --git a/src/core/lib/gpr/arena.h b/src/core/lib/gpr/arena.h
index 339771c..6d2a073 100644
--- a/src/core/lib/gpr/arena.h
+++ b/src/core/lib/gpr/arena.h
@@ -25,6 +25,8 @@
 #ifndef GRPC_CORE_LIB_GPR_ARENA_H
 #define GRPC_CORE_LIB_GPR_ARENA_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 typedef struct gpr_arena gpr_arena;
diff --git a/src/core/lib/gpr/atm.cc b/src/core/lib/gpr/atm.cc
index 3d0b430..649d400 100644
--- a/src/core/lib/gpr/atm.cc
+++ b/src/core/lib/gpr/atm.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 
 #include "src/core/lib/gpr/useful.h"
diff --git a/src/core/lib/gpr/cpu_linux.cc b/src/core/lib/gpr/cpu_linux.cc
index 4782f9f..fda2891 100644
--- a/src/core/lib/gpr/cpu_linux.cc
+++ b/src/core/lib/gpr/cpu_linux.cc
@@ -45,7 +45,7 @@
 #endif
   /* This must be signed. sysconf returns -1 when the number cannot be
      determined */
-  ncpus = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
+  ncpus = static_cast<int>(sysconf(_SC_NPROCESSORS_CONF));
   if (ncpus < 1) {
     gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1");
     ncpus = 1;
diff --git a/src/core/lib/gpr/cpu_posix.cc b/src/core/lib/gpr/cpu_posix.cc
index 7a77f7a..915fd49 100644
--- a/src/core/lib/gpr/cpu_posix.cc
+++ b/src/core/lib/gpr/cpu_posix.cc
@@ -37,7 +37,7 @@
 static pthread_key_t thread_id_key;
 
 static void init_ncpus() {
-  ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+  ncpus = sysconf(_SC_NPROCESSORS_CONF);
   if (ncpus < 1 || ncpus > INT32_MAX) {
     gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1");
     ncpus = 1;
diff --git a/src/core/lib/gpr/env.h b/src/core/lib/gpr/env.h
index b31e20b..aec8a31 100644
--- a/src/core/lib/gpr/env.h
+++ b/src/core/lib/gpr/env.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_ENV_H
 #define GRPC_CORE_LIB_GPR_ENV_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdio.h>
 
 /* Env utility functions */
diff --git a/src/core/lib/gpr/fork.cc b/src/core/lib/gpr/fork.cc
index 4651d22..812522b 100644
--- a/src/core/lib/gpr/fork.cc
+++ b/src/core/lib/gpr/fork.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/fork.h"
 
 #include <string.h>
diff --git a/src/core/lib/gpr/host_port.cc b/src/core/lib/gpr/host_port.cc
index 5a03a16..a34e01c 100644
--- a/src/core/lib/gpr/host_port.cc
+++ b/src/core/lib/gpr/host_port.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/host_port.h"
 
 #include <string.h>
diff --git a/src/core/lib/gpr/log.cc b/src/core/lib/gpr/log.cc
index 410096c..72787ab 100644
--- a/src/core/lib/gpr/log.cc
+++ b/src/core/lib/gpr/log.cc
@@ -16,10 +16,11 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
diff --git a/src/core/lib/gpr/mpscq.cc b/src/core/lib/gpr/mpscq.cc
index 34fc050..076a6bb 100644
--- a/src/core/lib/gpr/mpscq.cc
+++ b/src/core/lib/gpr/mpscq.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/mpscq.h"
 
 #include <grpc/support/log.h>
@@ -71,6 +73,7 @@
   gpr_mpscq_push(q, &q->stub);
   next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next);
   if (next != nullptr) {
+    *empty = false;
     q->tail = next;
     return tail;
   }
diff --git a/src/core/lib/gpr/mpscq.h b/src/core/lib/gpr/mpscq.h
index 4409c5c..6b67880 100644
--- a/src/core/lib/gpr/mpscq.h
+++ b/src/core/lib/gpr/mpscq.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_MPSCQ_H
 #define GRPC_CORE_LIB_GPR_MPSCQ_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 #include <grpc/support/sync.h>
 #include <stdbool.h>
diff --git a/src/core/lib/gpr/murmur_hash.cc b/src/core/lib/gpr/murmur_hash.cc
index 01a7290..cf25abf 100644
--- a/src/core/lib/gpr/murmur_hash.cc
+++ b/src/core/lib/gpr/murmur_hash.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/murmur_hash.h"
 
 #include <string.h>
diff --git a/src/core/lib/gpr/spinlock.h b/src/core/lib/gpr/spinlock.h
index f03be1d..9f35530 100644
--- a/src/core/lib/gpr/spinlock.h
+++ b/src/core/lib/gpr/spinlock.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_SPINLOCK_H
 #define GRPC_CORE_LIB_GPR_SPINLOCK_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 
 /* Simple spinlock. No backoff strategy, gpr_spinlock_lock is almost always
diff --git a/src/core/lib/gpr/string.cc b/src/core/lib/gpr/string.cc
index 5a16377..ef2a690 100644
--- a/src/core/lib/gpr/string.cc
+++ b/src/core/lib/gpr/string.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/string.h"
 
 #include <ctype.h>
@@ -26,7 +28,6 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/gpr/useful.h"
diff --git a/src/core/lib/gpr/string.h b/src/core/lib/gpr/string.h
index ef3a8c6..2e8a489 100644
--- a/src/core/lib/gpr/string.h
+++ b/src/core/lib/gpr/string.h
@@ -19,11 +19,11 @@
 #ifndef GRPC_CORE_LIB_GPR_STRING_H
 #define GRPC_CORE_LIB_GPR_STRING_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 #include <stddef.h>
 
-#include <grpc/support/port_platform.h>
-
 /* String utility functions */
 
 /* Flags for gpr_dump function. */
diff --git a/src/core/lib/gpr/sync.cc b/src/core/lib/gpr/sync.cc
index 347ffcd..2f18fc5 100644
--- a/src/core/lib/gpr/sync.cc
+++ b/src/core/lib/gpr/sync.cc
@@ -18,6 +18,8 @@
 
 /* Generic implementation of synchronization primitives. */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
diff --git a/src/core/lib/gpr/thd.cc b/src/core/lib/gpr/thd.cc
deleted file mode 100644
index 1139141..0000000
--- a/src/core/lib/gpr/thd.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * 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.
- *
- */
-
-/* Platform-independent features for gpr threads. */
-
-#include "src/core/lib/gpr/thd.h"
-
-#include <string.h>
-
-enum { GPR_THD_JOINABLE = 1 };
-
-gpr_thd_options gpr_thd_options_default(void) {
-  gpr_thd_options options;
-  memset(&options, 0, sizeof(options));
-  return options;
-}
-
-void gpr_thd_options_set_detached(gpr_thd_options* options) {
-  options->flags &= ~GPR_THD_JOINABLE;
-}
-
-void gpr_thd_options_set_joinable(gpr_thd_options* options) {
-  options->flags |= GPR_THD_JOINABLE;
-}
-
-int gpr_thd_options_is_detached(const gpr_thd_options* options) {
-  if (!options) return 1;
-  return (options->flags & GPR_THD_JOINABLE) == 0;
-}
-
-int gpr_thd_options_is_joinable(const gpr_thd_options* options) {
-  if (!options) return 0;
-  return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE;
-}
diff --git a/src/core/lib/gpr/thd.h b/src/core/lib/gpr/thd.h
deleted file mode 100644
index 58ce0d0..0000000
--- a/src/core/lib/gpr/thd.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * 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 GRPC_CORE_LIB_GPR_THD_H
-#define GRPC_CORE_LIB_GPR_THD_H
-/** Internal thread interface for GPR.
-
-   Types
-        gpr_thd_options   options used when creating a thread
- */
-
-#include <grpc/support/port_platform.h>
-#include <grpc/support/thd_id.h>
-#include <grpc/support/time.h>
-
-/** Thread creation options. */
-typedef struct {
-  int flags; /** Opaque field. Get and set with accessors below. */
-} gpr_thd_options;
-
-/** Create a new thread running (*thd_body)(arg) and place its thread identifier
-   in *t, and return true.  If there are insufficient resources, return false.
-   thd_name is the name of the thread for identification purposes on platforms
-   that support thread naming.
-   If options==NULL, default options are used.
-   The thread is immediately runnable, and exits when (*thd_body)() returns.  */
-int gpr_thd_new(gpr_thd_id* t, const char* thd_name,
-                void (*thd_body)(void* arg), void* arg,
-                const gpr_thd_options* options);
-
-/** Return a gpr_thd_options struct with all fields set to defaults. */
-gpr_thd_options gpr_thd_options_default(void);
-
-/** Set the thread to become detached on startup - this is the default. */
-void gpr_thd_options_set_detached(gpr_thd_options* options);
-
-/** Set the thread to become joinable - mutually exclusive with detached. */
-void gpr_thd_options_set_joinable(gpr_thd_options* options);
-
-/** Returns non-zero if the option detached is set. */
-int gpr_thd_options_is_detached(const gpr_thd_options* options);
-
-/** Returns non-zero if the option joinable is set. */
-int gpr_thd_options_is_joinable(const gpr_thd_options* options);
-
-/** Blocks until the specified thread properly terminates.
-   Calling this on a detached thread has unpredictable results. */
-void gpr_thd_join(gpr_thd_id t);
-
-/* Internal interfaces between modules within the gpr support library.  */
-void gpr_thd_init();
-
-/* Wait for all outstanding threads to finish, up to deadline */
-int gpr_await_threads(gpr_timespec deadline);
-
-#endif /* GRPC_CORE_LIB_GPR_THD_H */
diff --git a/src/core/lib/gpr/thd_posix.cc b/src/core/lib/gpr/thd_posix.cc
deleted file mode 100644
index fcd174b..0000000
--- a/src/core/lib/gpr/thd_posix.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * 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.
- *
- */
-
-/* Posix implementation for gpr threads. */
-
-#include <grpc/support/port_platform.h>
-
-#ifdef GPR_POSIX_SYNC
-
-#include "src/core/lib/gpr/thd.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/thd_id.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "src/core/lib/gpr/fork.h"
-#include "src/core/lib/gpr/useful.h"
-
-static gpr_mu g_mu;
-static gpr_cv g_cv;
-static int g_thread_count;
-static int g_awaiting_threads;
-
-struct thd_arg {
-  void (*body)(void* arg); /* body of a thread */
-  void* arg;               /* argument to a thread */
-  const char* name;        /* name of thread. Can be nullptr. */
-};
-
-static void inc_thd_count();
-static void dec_thd_count();
-
-/* Body of every thread started via gpr_thd_new. */
-static void* thread_body(void* v) {
-  struct thd_arg a = *static_cast<struct thd_arg*>(v);
-  free(v);
-  if (a.name != nullptr) {
-#if GPR_APPLE_PTHREAD_NAME
-    /* Apple supports 64 characters, and will truncate if it's longer. */
-    pthread_setname_np(a.name);
-#elif GPR_LINUX_PTHREAD_NAME
-    /* Linux supports 16 characters max, and will error if it's longer. */
-    char buf[16];
-    size_t buf_len = GPR_ARRAY_SIZE(buf) - 1;
-    strncpy(buf, a.name, buf_len);
-    buf[buf_len] = '\0';
-    pthread_setname_np(pthread_self(), buf);
-#endif  // GPR_APPLE_PTHREAD_NAME
-  }
-  (*a.body)(a.arg);
-  dec_thd_count();
-  return nullptr;
-}
-
-int gpr_thd_new(gpr_thd_id* t, const char* thd_name,
-                void (*thd_body)(void* arg), void* arg,
-                const gpr_thd_options* options) {
-  int thread_started;
-  pthread_attr_t attr;
-  pthread_t p;
-  /* don't use gpr_malloc as we may cause an infinite recursion with
-   * the profiling code */
-  struct thd_arg* a = static_cast<struct thd_arg*>(malloc(sizeof(*a)));
-  GPR_ASSERT(a != nullptr);
-  a->body = thd_body;
-  a->arg = arg;
-  a->name = thd_name;
-  inc_thd_count();
-
-  GPR_ASSERT(pthread_attr_init(&attr) == 0);
-  if (gpr_thd_options_is_detached(options)) {
-    GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) ==
-               0);
-  } else {
-    GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) ==
-               0);
-  }
-  thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0);
-  GPR_ASSERT(pthread_attr_destroy(&attr) == 0);
-  if (!thread_started) {
-    /* don't use gpr_free, as this was allocated using malloc (see above) */
-    free(a);
-    dec_thd_count();
-  }
-  *t = (gpr_thd_id)p;
-  return thread_started;
-}
-
-gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); }
-
-void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, nullptr); }
-
-/*****************************************
- * Only used when fork support is enabled
- */
-
-static void inc_thd_count() {
-  if (grpc_fork_support_enabled()) {
-    gpr_mu_lock(&g_mu);
-    g_thread_count++;
-    gpr_mu_unlock(&g_mu);
-  }
-}
-
-static void dec_thd_count() {
-  if (grpc_fork_support_enabled()) {
-    gpr_mu_lock(&g_mu);
-    g_thread_count--;
-    if (g_awaiting_threads && g_thread_count == 0) {
-      gpr_cv_signal(&g_cv);
-    }
-    gpr_mu_unlock(&g_mu);
-  }
-}
-
-void gpr_thd_init() {
-  gpr_mu_init(&g_mu);
-  gpr_cv_init(&g_cv);
-  g_thread_count = 0;
-  g_awaiting_threads = 0;
-}
-
-int gpr_await_threads(gpr_timespec deadline) {
-  gpr_mu_lock(&g_mu);
-  g_awaiting_threads = 1;
-  int res = 0;
-  if (g_thread_count > 0) {
-    res = gpr_cv_wait(&g_cv, &g_mu, deadline);
-  }
-  g_awaiting_threads = 0;
-  gpr_mu_unlock(&g_mu);
-  return res == 0;
-}
-
-#endif /* GPR_POSIX_SYNC */
diff --git a/src/core/lib/gpr/thd_windows.cc b/src/core/lib/gpr/thd_windows.cc
deleted file mode 100644
index b467bd2..0000000
--- a/src/core/lib/gpr/thd_windows.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * 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.
- *
- */
-
-/* Windows implementation for gpr threads. */
-
-#include <grpc/support/port_platform.h>
-
-#ifdef GPR_WINDOWS
-
-#include "src/core/lib/gpr/thd.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/thd_id.h>
-#include <string.h>
-
-#if defined(_MSC_VER)
-#define thread_local __declspec(thread)
-#elif defined(__GNUC__)
-#define thread_local __thread
-#else
-#error "Unknown compiler - please file a bug report"
-#endif
-
-struct thd_info {
-  void (*body)(void* arg); /* body of a thread */
-  void* arg;               /* argument to a thread */
-  HANDLE join_event;       /* if joinable, the join event */
-  int joinable;            /* true if not detached */
-};
-
-static thread_local struct thd_info* g_thd_info;
-
-/* Destroys a thread info */
-static void destroy_thread(struct thd_info* t) {
-  if (t->joinable) CloseHandle(t->join_event);
-  gpr_free(t);
-}
-
-void gpr_thd_init(void) {}
-
-/* Body of every thread started via gpr_thd_new. */
-static DWORD WINAPI thread_body(void* v) {
-  g_thd_info = (struct thd_info*)v;
-  g_thd_info->body(g_thd_info->arg);
-  if (g_thd_info->joinable) {
-    BOOL ret = SetEvent(g_thd_info->join_event);
-    GPR_ASSERT(ret);
-  } else {
-    destroy_thread(g_thd_info);
-  }
-  return 0;
-}
-
-int gpr_thd_new(gpr_thd_id* t, const char* thd_name,
-                void (*thd_body)(void* arg), void* arg,
-                const gpr_thd_options* options) {
-  HANDLE handle;
-  struct thd_info* info = (struct thd_info*)gpr_malloc(sizeof(*info));
-  info->body = thd_body;
-  info->arg = arg;
-  *t = 0;
-  if (gpr_thd_options_is_joinable(options)) {
-    info->joinable = 1;
-    info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (info->join_event == NULL) {
-      gpr_free(info);
-      return 0;
-    }
-  } else {
-    info->joinable = 0;
-  }
-  handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL);
-  if (handle == NULL) {
-    destroy_thread(info);
-  } else {
-    *t = (gpr_thd_id)info;
-    CloseHandle(handle);
-  }
-  return handle != NULL;
-}
-
-gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; }
-
-void gpr_thd_join(gpr_thd_id t) {
-  struct thd_info* info = (struct thd_info*)t;
-  DWORD ret = WaitForSingleObject(info->join_event, INFINITE);
-  GPR_ASSERT(ret == WAIT_OBJECT_0);
-  destroy_thread(info);
-}
-
-#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/gpr/time.cc b/src/core/lib/gpr/time.cc
index 39ebeb4..64c1c98 100644
--- a/src/core/lib/gpr/time.cc
+++ b/src/core/lib/gpr/time.cc
@@ -18,6 +18,8 @@
 
 /* Generic implementation of time calls. */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include <limits.h>
diff --git a/src/core/lib/gpr/time_posix.cc b/src/core/lib/gpr/time_posix.cc
index 09171c9..28836bf 100644
--- a/src/core/lib/gpr/time_posix.cc
+++ b/src/core/lib/gpr/time_posix.cc
@@ -17,6 +17,7 @@
  */
 
 #include <grpc/support/port_platform.h>
+
 #include "src/core/lib/gpr/time_precise.h"
 
 #ifdef GPR_POSIX_TIME
diff --git a/src/core/lib/gpr/time_precise.cc b/src/core/lib/gpr/time_precise.cc
index 3c7aaab..1b34fd7 100644
--- a/src/core/lib/gpr/time_precise.cc
+++ b/src/core/lib/gpr/time_precise.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include <stdio.h>
diff --git a/src/core/lib/gpr/time_precise.h b/src/core/lib/gpr/time_precise.h
index acc4ee3..a63ea9d 100644
--- a/src/core/lib/gpr/time_precise.h
+++ b/src/core/lib/gpr/time_precise.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_TIME_PRECISE_H
 #define GRPC_CORE_LIB_GPR_TIME_PRECISE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/time.h>
 
 void gpr_precise_clock_init(void);
diff --git a/src/core/lib/gpr/tls_gcc.h b/src/core/lib/gpr/tls_gcc.h
index 14c59ec..72b360b 100644
--- a/src/core/lib/gpr/tls_gcc.h
+++ b/src/core/lib/gpr/tls_gcc.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_TLS_GCC_H
 #define GRPC_CORE_LIB_GPR_TLS_GCC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/support/log.h>
diff --git a/src/core/lib/gpr/tls_msvc.h b/src/core/lib/gpr/tls_msvc.h
index a6cc417..f4b3f0f 100644
--- a/src/core/lib/gpr/tls_msvc.h
+++ b/src/core/lib/gpr/tls_msvc.h
@@ -20,6 +20,8 @@
 #define GRPC_CORE_LIB_GPR_TLS_MSVC_H
 
 /** Thread local storage based on ms visual c compiler primitives.
+#include <grpc/support/port_platform.h>
+
    #include tls.h to use this - and see that file for documentation */
 
 struct gpr_msvc_thread_local {
diff --git a/src/core/lib/gpr/tls_pthread.h b/src/core/lib/gpr/tls_pthread.h
index 9202653..a15f2f3 100644
--- a/src/core/lib/gpr/tls_pthread.h
+++ b/src/core/lib/gpr/tls_pthread.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_TLS_PTHREAD_H
 #define GRPC_CORE_LIB_GPR_TLS_PTHREAD_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h> /* for GPR_ASSERT */
 #include <pthread.h>
 
diff --git a/src/core/lib/gpr/tmpfile.h b/src/core/lib/gpr/tmpfile.h
index f47ec7a..3ce3ff5 100644
--- a/src/core/lib/gpr/tmpfile.h
+++ b/src/core/lib/gpr/tmpfile.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPR_TMPFILE_H
 #define GRPC_CORE_LIB_GPR_TMPFILE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdio.h>
 
 /* Creates a temporary file from a prefix.
diff --git a/src/core/lib/gprpp/atomic_with_atm.h b/src/core/lib/gprpp/atomic_with_atm.h
index 6abf0bc..3d0021b 100644
--- a/src/core/lib/gprpp/atomic_with_atm.h
+++ b/src/core/lib/gprpp/atomic_with_atm.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H
 #define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 
 namespace grpc_core {
diff --git a/src/core/lib/gprpp/atomic_with_std.h b/src/core/lib/gprpp/atomic_with_std.h
index 83322b8..a4ad16e 100644
--- a/src/core/lib/gprpp/atomic_with_std.h
+++ b/src/core/lib/gprpp/atomic_with_std.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H
 #define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H
 
+#include <grpc/support/port_platform.h>
+
 #include <atomic>
 
 namespace grpc_core {
diff --git a/src/core/lib/gprpp/inlined_vector.h b/src/core/lib/gprpp/inlined_vector.h
index 2ced3d7..ca95aec 100644
--- a/src/core/lib/gprpp/inlined_vector.h
+++ b/src/core/lib/gprpp/inlined_vector.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H
 #define GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H
 
+#include <grpc/support/port_platform.h>
+
 #include <cassert>
 
 #include "src/core/lib/gprpp/memory.h"
diff --git a/src/core/lib/gprpp/manual_constructor.h b/src/core/lib/gprpp/manual_constructor.h
index cee38ab..7f827ca 100644
--- a/src/core/lib/gprpp/manual_constructor.h
+++ b/src/core/lib/gprpp/manual_constructor.h
@@ -21,6 +21,8 @@
 
 // manually construct a region of memory with some type
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 #include <stdlib.h>
 #include <new>
@@ -154,7 +156,7 @@
     static_assert(
         manual_ctor_impl::is_one_of<DerivedType, DerivedTypes...>::value,
         "DerivedType must be one of the predeclared DerivedTypes");
-    GPR_ASSERT(reinterpret_cast<BaseType*>(static_cast<DerivedType*>(p)) == p);
+    GPR_ASSERT(static_cast<BaseType*>(p) == p);
   }
 
   typename std::aligned_storage<
diff --git a/src/core/lib/gprpp/memory.h b/src/core/lib/gprpp/memory.h
index 17f42f5..ba2f546 100644
--- a/src/core/lib/gprpp/memory.h
+++ b/src/core/lib/gprpp/memory.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPRPP_MEMORY_H
 #define GRPC_CORE_LIB_GPRPP_MEMORY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 
 #include <limits>
@@ -27,10 +29,15 @@
 
 namespace grpc_core {
 
+// The alignment of memory returned by gpr_malloc().
+constexpr size_t kAlignmentForDefaultAllocationInBytes = 8;
+
 // Alternative to new, since we cannot use it (for fear of libstdc++)
 template <typename T, typename... Args>
 inline T* New(Args&&... args) {
-  void* p = gpr_malloc(sizeof(T));
+  void* p = alignof(T) > kAlignmentForDefaultAllocationInBytes
+                ? gpr_malloc_aligned(sizeof(T), alignof(T))
+                : gpr_malloc(sizeof(T));
   return new (p) T(std::forward<Args>(args)...);
 }
 
@@ -38,7 +45,11 @@
 template <typename T>
 inline void Delete(T* p) {
   p->~T();
-  gpr_free(p);
+  if (alignof(T) > kAlignmentForDefaultAllocationInBytes) {
+    gpr_free_aligned(p);
+  } else {
+    gpr_free(p);
+  }
 }
 
 template <typename T>
diff --git a/src/core/lib/gprpp/orphanable.h b/src/core/lib/gprpp/orphanable.h
index 78d1b01..a5bc8d8 100644
--- a/src/core/lib/gprpp/orphanable.h
+++ b/src/core/lib/gprpp/orphanable.h
@@ -19,9 +19,12 @@
 #ifndef GRPC_CORE_LIB_GPRPP_ORPHANABLE_H
 #define GRPC_CORE_LIB_GPRPP_ORPHANABLE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
+#include <cinttypes>
 #include <memory>
 
 #include "src/core/lib/debug/trace.h"
diff --git a/src/core/lib/gprpp/ref_counted.h b/src/core/lib/gprpp/ref_counted.h
index ab589fc..46bfaf7 100644
--- a/src/core/lib/gprpp/ref_counted.h
+++ b/src/core/lib/gprpp/ref_counted.h
@@ -19,9 +19,13 @@
 #ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
 #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
+#include <cinttypes>
+
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gprpp/abstract.h"
 #include "src/core/lib/gprpp/debug_location.h"
diff --git a/src/core/lib/gprpp/ref_counted_ptr.h b/src/core/lib/gprpp/ref_counted_ptr.h
index f82ba50..388e2ec 100644
--- a/src/core/lib/gprpp/ref_counted_ptr.h
+++ b/src/core/lib/gprpp/ref_counted_ptr.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
 #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
 
+#include <grpc/support/port_platform.h>
+
 #include <utility>
 
 #include "src/core/lib/gprpp/memory.h"
@@ -31,6 +33,7 @@
 class RefCountedPtr {
  public:
   RefCountedPtr() {}
+  RefCountedPtr(std::nullptr_t) {}
 
   // If value is non-null, we take ownership of a ref to it.
   explicit RefCountedPtr(T* value) { value_ = value; }
diff --git a/src/core/lib/gprpp/thd.h b/src/core/lib/gprpp/thd.h
new file mode 100644
index 0000000..05c7ded
--- /dev/null
+++ b/src/core/lib/gprpp/thd.h
@@ -0,0 +1,135 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_GPRPP_THD_H
+#define GRPC_CORE_LIB_GPRPP_THD_H
+
+/** Internal thread interface. */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+namespace internal {
+
+/// Base class for platform-specific thread-state
+class ThreadInternalsInterface {
+ public:
+  virtual ~ThreadInternalsInterface() {}
+  virtual void Start() GRPC_ABSTRACT;
+  virtual void Join() GRPC_ABSTRACT;
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+}  // namespace internal
+
+class Thread {
+ public:
+  /// Default constructor only to allow use in structs that lack constructors
+  /// Does not produce a validly-constructed thread; must later
+  /// use placement new to construct a real thread. Does not init mu_ and cv_
+  Thread() : state_(FAKE), impl_(nullptr) {}
+
+  /// Normal constructor to create a thread with name \a thd_name,
+  /// which will execute a thread based on function \a thd_body
+  /// with argument \a arg once it is started.
+  /// The optional \a success argument indicates whether the thread
+  /// is successfully created.
+  Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+         bool* success = nullptr);
+
+  /// Move constructor for thread. After this is called, the other thread
+  /// no longer represents a living thread object
+  Thread(Thread&& other) : state_(other.state_), impl_(other.impl_) {
+    other.state_ = MOVED;
+    other.impl_ = nullptr;
+  }
+
+  /// Move assignment operator for thread. After this is called, the other
+  /// thread no longer represents a living thread object. Not allowed if this
+  /// thread actually exists
+  Thread& operator=(Thread&& other) {
+    if (this != &other) {
+      // TODO(vjpai): if we can be sure that all Thread's are actually
+      // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here.
+      // However, as long as threads come in structures that are
+      // allocated via gpr_malloc, this will not be the case, so we cannot
+      // assert it for the time being.
+      state_ = other.state_;
+      impl_ = other.impl_;
+      other.state_ = MOVED;
+      other.impl_ = nullptr;
+    }
+    return *this;
+  }
+
+  /// The destructor is strictly optional; either the thread never came to life
+  /// and the constructor itself killed it or it has already been joined and
+  /// the Join function kills it. The destructor shouldn't have to do anything.
+  ~Thread() { GPR_ASSERT(impl_ == nullptr); }
+
+  void Start() {
+    if (impl_ != nullptr) {
+      GPR_ASSERT(state_ == ALIVE);
+      state_ = STARTED;
+      impl_->Start();
+    } else {
+      GPR_ASSERT(state_ == FAILED);
+    }
+  };
+
+  void Join() {
+    if (impl_ != nullptr) {
+      impl_->Join();
+      grpc_core::Delete(impl_);
+      state_ = DONE;
+      impl_ = nullptr;
+    } else {
+      GPR_ASSERT(state_ == FAILED);
+    }
+  };
+
+  static void Init();
+  static bool AwaitAll(gpr_timespec deadline);
+
+ private:
+  Thread(const Thread&) = delete;
+  Thread& operator=(const Thread&) = delete;
+
+  /// The thread states are as follows:
+  /// FAKE -- just a dummy placeholder Thread created by the default constructor
+  /// ALIVE -- an actual thread of control exists associated with this thread
+  /// STARTED -- the thread of control has been started
+  /// DONE -- the thread of control has completed and been joined
+  /// FAILED -- the thread of control never came alive
+  /// MOVED -- contents were moved out and we're no longer tracking them
+  enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED };
+  ThreadState state_;
+  internal::ThreadInternalsInterface* impl_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_THD_H */
diff --git a/src/core/lib/gprpp/thd_posix.cc b/src/core/lib/gprpp/thd_posix.cc
new file mode 100644
index 0000000..2f6c2ed
--- /dev/null
+++ b/src/core/lib/gprpp/thd_posix.cc
@@ -0,0 +1,209 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/* Posix implementation for gpr threads. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
+#include "src/core/lib/gprpp/thd.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/lib/gpr/fork.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+namespace {
+gpr_mu g_mu;
+gpr_cv g_cv;
+int g_thread_count;
+int g_awaiting_threads;
+
+class ThreadInternalsPosix;
+struct thd_arg {
+  ThreadInternalsPosix* thread;
+  void (*body)(void* arg); /* body of a thread */
+  void* arg;               /* argument to a thread */
+  const char* name;        /* name of thread. Can be nullptr. */
+};
+
+class ThreadInternalsPosix
+    : public grpc_core::internal::ThreadInternalsInterface {
+ public:
+  ThreadInternalsPosix(const char* thd_name, void (*thd_body)(void* arg),
+                       void* arg, bool* success)
+      : started_(false) {
+    gpr_mu_init(&mu_);
+    gpr_cv_init(&ready_);
+    pthread_attr_t attr;
+    /* don't use gpr_malloc as we may cause an infinite recursion with
+     * the profiling code */
+    thd_arg* info = static_cast<thd_arg*>(malloc(sizeof(*info)));
+    GPR_ASSERT(info != nullptr);
+    info->thread = this;
+    info->body = thd_body;
+    info->arg = arg;
+    info->name = thd_name;
+    inc_thd_count();
+
+    GPR_ASSERT(pthread_attr_init(&attr) == 0);
+    GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) ==
+               0);
+
+    *success =
+        (pthread_create(&pthread_id_, &attr,
+                        [](void* v) -> void* {
+                          thd_arg arg = *static_cast<thd_arg*>(v);
+                          free(v);
+                          if (arg.name != nullptr) {
+#if GPR_APPLE_PTHREAD_NAME
+                            /* Apple supports 64 characters, and will
+                             * truncate if it's longer. */
+                            pthread_setname_np(arg.name);
+#elif GPR_LINUX_PTHREAD_NAME
+                            /* Linux supports 16 characters max, and will
+                             * error if it's longer. */
+                            char buf[16];
+                            size_t buf_len = GPR_ARRAY_SIZE(buf) - 1;
+                            strncpy(buf, arg.name, buf_len);
+                            buf[buf_len] = '\0';
+                            pthread_setname_np(pthread_self(), buf);
+#endif  // GPR_APPLE_PTHREAD_NAME
+                          }
+
+                          gpr_mu_lock(&arg.thread->mu_);
+                          while (!arg.thread->started_) {
+                            gpr_cv_wait(&arg.thread->ready_, &arg.thread->mu_,
+                                        gpr_inf_future(GPR_CLOCK_MONOTONIC));
+                          }
+                          gpr_mu_unlock(&arg.thread->mu_);
+
+                          (*arg.body)(arg.arg);
+                          dec_thd_count();
+                          return nullptr;
+                        },
+                        info) == 0);
+
+    GPR_ASSERT(pthread_attr_destroy(&attr) == 0);
+
+    if (!success) {
+      /* don't use gpr_free, as this was allocated using malloc (see above) */
+      free(info);
+      dec_thd_count();
+    }
+  };
+
+  ~ThreadInternalsPosix() override {
+    gpr_mu_destroy(&mu_);
+    gpr_cv_destroy(&ready_);
+  }
+
+  void Start() override {
+    gpr_mu_lock(&mu_);
+    started_ = true;
+    gpr_cv_signal(&ready_);
+    gpr_mu_unlock(&mu_);
+  }
+
+  void Join() override { pthread_join(pthread_id_, nullptr); }
+
+ private:
+  /*****************************************
+   * Only used when fork support is enabled
+   */
+
+  static void inc_thd_count() {
+    if (grpc_fork_support_enabled()) {
+      gpr_mu_lock(&g_mu);
+      g_thread_count++;
+      gpr_mu_unlock(&g_mu);
+    }
+  }
+
+  static void dec_thd_count() {
+    if (grpc_fork_support_enabled()) {
+      gpr_mu_lock(&g_mu);
+      g_thread_count--;
+      if (g_awaiting_threads && g_thread_count == 0) {
+        gpr_cv_signal(&g_cv);
+      }
+      gpr_mu_unlock(&g_mu);
+    }
+  }
+
+  gpr_mu mu_;
+  gpr_cv ready_;
+  bool started_;
+  pthread_t pthread_id_;
+};
+
+}  // namespace
+
+Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+               bool* success) {
+  bool outcome = false;
+  impl_ =
+      grpc_core::New<ThreadInternalsPosix>(thd_name, thd_body, arg, &outcome);
+  if (outcome) {
+    state_ = ALIVE;
+  } else {
+    state_ = FAILED;
+    grpc_core::Delete(impl_);
+    impl_ = nullptr;
+  }
+
+  if (success != nullptr) {
+    *success = outcome;
+  }
+}
+
+void Thread::Init() {
+  gpr_mu_init(&g_mu);
+  gpr_cv_init(&g_cv);
+  g_thread_count = 0;
+  g_awaiting_threads = 0;
+}
+
+bool Thread::AwaitAll(gpr_timespec deadline) {
+  gpr_mu_lock(&g_mu);
+  g_awaiting_threads = 1;
+  int res = 0;
+  while ((g_thread_count > 0) &&
+         (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0)) {
+    res = gpr_cv_wait(&g_cv, &g_mu, deadline);
+  }
+  g_awaiting_threads = 0;
+  gpr_mu_unlock(&g_mu);
+  return res == 0;
+}
+
+}  // namespace grpc_core
+
+// The following is in the external namespace as it is exposed as C89 API
+gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); }
+
+#endif /* GPR_POSIX_SYNC */
diff --git a/src/core/lib/gprpp/thd_windows.cc b/src/core/lib/gprpp/thd_windows.cc
new file mode 100644
index 0000000..59ea02f
--- /dev/null
+++ b/src/core/lib/gprpp/thd_windows.cc
@@ -0,0 +1,162 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/* Windows implementation for gpr threads. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include "src/core/lib/gprpp/thd.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd_id.h>
+#include <string.h>
+
+#include "src/core/lib/gprpp/memory.h"
+
+#if defined(_MSC_VER)
+#define thread_local __declspec(thread)
+#define WIN_LAMBDA
+#elif defined(__GNUC__)
+#define thread_local __thread
+#define WIN_LAMBDA WINAPI
+#else
+#error "Unknown compiler - please file a bug report"
+#endif
+
+namespace {
+class ThreadInternalsWindows;
+struct thd_info {
+  ThreadInternalsWindows* thread;
+  void (*body)(void* arg); /* body of a thread */
+  void* arg;               /* argument to a thread */
+  HANDLE join_event;       /* the join event */
+};
+
+thread_local struct thd_info* g_thd_info;
+
+class ThreadInternalsWindows
+    : public grpc_core::internal::ThreadInternalsInterface {
+ public:
+  ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success)
+      : started_(false) {
+    gpr_mu_init(&mu_);
+    gpr_cv_init(&ready_);
+
+    HANDLE handle;
+    info_ = (struct thd_info*)gpr_malloc(sizeof(*info_));
+    info_->thread = this;
+    info_->body = thd_body;
+    info_->arg = arg;
+
+    info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    if (info_->join_event == nullptr) {
+      gpr_free(info_);
+      *success = false;
+    } else {
+      handle = CreateThread(
+          nullptr, 64 * 1024,
+          [](void* v) WIN_LAMBDA -> DWORD {
+            g_thd_info = static_cast<thd_info*>(v);
+            gpr_mu_lock(&g_thd_info->thread->mu_);
+            while (!g_thd_info->thread->started_) {
+              gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_,
+                          gpr_inf_future(GPR_CLOCK_MONOTONIC));
+            }
+            gpr_mu_unlock(&g_thd_info->thread->mu_);
+            g_thd_info->body(g_thd_info->arg);
+            BOOL ret = SetEvent(g_thd_info->join_event);
+            GPR_ASSERT(ret);
+            return 0;
+          },
+          info_, 0, nullptr);
+      if (handle == nullptr) {
+        destroy_thread();
+        *success = false;
+      } else {
+        CloseHandle(handle);
+        *success = true;
+      }
+    }
+  }
+
+  ~ThreadInternalsWindows() override {
+    gpr_mu_destroy(&mu_);
+    gpr_cv_destroy(&ready_);
+  }
+
+  void Start() override {
+    gpr_mu_lock(&mu_);
+    started_ = true;
+    gpr_cv_signal(&ready_);
+    gpr_mu_unlock(&mu_);
+  }
+
+  void Join() override {
+    DWORD ret = WaitForSingleObject(info_->join_event, INFINITE);
+    GPR_ASSERT(ret == WAIT_OBJECT_0);
+    destroy_thread();
+  }
+
+ private:
+  void destroy_thread() {
+    CloseHandle(info_->join_event);
+    gpr_free(info_);
+  }
+
+  gpr_mu mu_;
+  gpr_cv ready_;
+  bool started_;
+  thd_info* info_;
+};
+
+}  // namespace
+
+namespace grpc_core {
+
+void Thread::Init() {}
+
+bool Thread::AwaitAll(gpr_timespec deadline) {
+  // TODO: Consider adding this if needed
+  return false;
+}
+
+Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+               bool* success) {
+  bool outcome = false;
+  impl_ = grpc_core::New<ThreadInternalsWindows>(thd_body, arg, &outcome);
+  if (outcome) {
+    state_ = ALIVE;
+  } else {
+    state_ = FAILED;
+    grpc_core::Delete(impl_);
+    impl_ = nullptr;
+  }
+
+  if (success != nullptr) {
+    *success = outcome;
+  }
+}
+
+}  // namespace grpc_core
+
+gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; }
+
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/http/format_request.cc b/src/core/lib/http/format_request.cc
index 6581bef..1712344 100644
--- a/src/core/lib/http/format_request.cc
+++ b/src/core/lib/http/format_request.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/http/format_request.h"
 
 #include <stdarg.h>
diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h
index c191965..bcc332f 100644
--- a/src/core/lib/http/format_request.h
+++ b/src/core/lib/http/format_request.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
 #define GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include "src/core/lib/http/httpcli.h"
 
diff --git a/src/core/lib/http/httpcli.cc b/src/core/lib/http/httpcli.cc
index 2cdca48..1206007 100644
--- a/src/core/lib/http/httpcli.cc
+++ b/src/core/lib/http/httpcli.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/http/httpcli.h"
 
 #include <string.h>
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 72d20cc..b073508 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_HTTP_HTTPCLI_H
 #define GRPC_CORE_LIB_HTTP_HTTPCLI_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 #include <grpc/support/time.h>
diff --git a/src/core/lib/http/httpcli_security_connector.cc b/src/core/lib/http/httpcli_security_connector.cc
index d754558..1809123 100644
--- a/src/core/lib/http/httpcli_security_connector.cc
+++ b/src/core/lib/http/httpcli_security_connector.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/http/httpcli.h"
 
 #include <string.h>
diff --git a/src/core/lib/http/parser.cc b/src/core/lib/http/parser.cc
index 724b6d1..a37fdda 100644
--- a/src/core/lib/http/parser.cc
+++ b/src/core/lib/http/parser.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/http/parser.h"
 
 #include <stdbool.h>
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 5fef448..1d2e13e 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -19,8 +19,9 @@
 #ifndef GRPC_CORE_LIB_HTTP_PARSER_H
 #define GRPC_CORE_LIB_HTTP_PARSER_H
 
-#include <grpc/slice.h>
 #include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/error.h"
 
diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc
index 3b11e02..24e11b6 100644
--- a/src/core/lib/iomgr/call_combiner.cc
+++ b/src/core/lib/iomgr/call_combiner.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/call_combiner.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h
index 4814dbf..16829e5 100644
--- a/src/core/lib/iomgr/call_combiner.h
+++ b/src/core/lib/iomgr/call_combiner.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H
 #define GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 #include <grpc/support/atm.h>
diff --git a/src/core/lib/iomgr/combiner.cc b/src/core/lib/iomgr/combiner.cc
index a7edf17..e66df03 100644
--- a/src/core/lib/iomgr/combiner.cc
+++ b/src/core/lib/iomgr/combiner.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/combiner.h"
 
 #include <assert.h>
diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h
index c62d21a..0d63e46 100644
--- a/src/core/lib/iomgr/combiner.h
+++ b/src/core/lib/iomgr/combiner.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_COMBINER_H
 #define GRPC_CORE_LIB_IOMGR_COMBINER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 #include <grpc/support/atm.h>
diff --git a/src/core/lib/iomgr/endpoint.cc b/src/core/lib/iomgr/endpoint.cc
index 9d4b102..e22c21e 100644
--- a/src/core/lib/iomgr/endpoint.cc
+++ b/src/core/lib/iomgr/endpoint.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/endpoint.h"
 
 void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices,
diff --git a/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h
index cd53099..15db164 100644
--- a/src/core/lib/iomgr/endpoint.h
+++ b/src/core/lib/iomgr/endpoint.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_H
 #define GRPC_CORE_LIB_IOMGR_ENDPOINT_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
 #include <grpc/support/time.h>
diff --git a/src/core/lib/iomgr/endpoint_pair.h b/src/core/lib/iomgr/endpoint_pair.h
index 506ffc8..08f9e3c 100644
--- a/src/core/lib/iomgr/endpoint_pair.h
+++ b/src/core/lib/iomgr/endpoint_pair.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
 #define GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/endpoint.h"
 
 typedef struct {
diff --git a/src/core/lib/iomgr/endpoint_pair_posix.cc b/src/core/lib/iomgr/endpoint_pair_posix.cc
index 3ad6b47..49850ab 100644
--- a/src/core/lib/iomgr/endpoint_pair_posix.cc
+++ b/src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/endpoint_pair_uv.cc b/src/core/lib/iomgr/endpoint_pair_uv.cc
index 128a947..b99d178 100644
--- a/src/core/lib/iomgr/endpoint_pair_uv.cc
+++ b/src/core/lib/iomgr/endpoint_pair_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/endpoint_pair_windows.cc b/src/core/lib/iomgr/endpoint_pair_windows.cc
index cc07ac0..416c9d8 100644
--- a/src/core/lib/iomgr/endpoint_pair_windows.cc
+++ b/src/core/lib/iomgr/endpoint_pair_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
index 8c72a43..f8cae4d 100644
--- a/src/core/lib/iomgr/error.h
+++ b/src/core/lib/iomgr/error.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_ERROR_H
 #define GRPC_CORE_LIB_IOMGR_ERROR_H
 
+#include <grpc/support/port_platform.h>
+
 #include <inttypes.h>
 #include <stdbool.h>
 
diff --git a/src/core/lib/iomgr/error_internal.h b/src/core/lib/iomgr/error_internal.h
index 6cb09c2..7fde347 100644
--- a/src/core/lib/iomgr/error_internal.h
+++ b/src/core/lib/iomgr/error_internal.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H
 #define GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <inttypes.h>
 #include <stdbool.h>  // TODO, do we need this?
 
diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc
index f84f3f8..3ebaf18 100644
--- a/src/core/lib/iomgr/ev_epoll1_linux.cc
+++ b/src/core/lib/iomgr/ev_epoll1_linux.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include <grpc/support/log.h>
diff --git a/src/core/lib/iomgr/ev_epoll1_linux.h b/src/core/lib/iomgr/ev_epoll1_linux.h
index 9a1b96b..ca0db72 100644
--- a/src/core/lib/iomgr/ev_epoll1_linux.h
+++ b/src/core/lib/iomgr/ev_epoll1_linux.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H
 #define GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc
index 3ad94a4..d3cbaf9 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.cc
+++ b/src/core/lib/iomgr/ev_epollex_linux.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include <grpc/support/log.h>
@@ -57,7 +59,7 @@
 //#define GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP 1
 
 #define MAX_EPOLL_EVENTS 100
-#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 1
+#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 16
 
 grpc_core::DebugOnlyTraceFlag grpc_trace_pollable_refcount(false,
                                                            "pollable_refcount");
@@ -196,6 +198,7 @@
 
 struct grpc_pollset {
   gpr_mu mu;
+  gpr_atm worker_count;
   pollable* active_pollable;
   bool kicked_without_poller;
   grpc_closure* shutdown_closure;
@@ -683,6 +686,7 @@
 
 static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
   gpr_mu_init(&pollset->mu);
+  gpr_atm_no_barrier_store(&pollset->worker_count, 0);
   pollset->active_pollable = POLLABLE_REF(g_empty_pollable, "pollset");
   pollset->kicked_without_poller = false;
   pollset->shutdown_closure = nullptr;
@@ -756,8 +760,20 @@
                                            pollable* pollable_obj, bool drain) {
   GPR_TIMER_SCOPE("pollable_process_events", 0);
   static const char* err_desc = "pollset_process_events";
+  // Use a simple heuristic to determine how many fd events to process
+  // per loop iteration.  (events/workers)
+  int handle_count = 1;
+  int worker_count = gpr_atm_no_barrier_load(&pollset->worker_count);
+  GPR_ASSERT(worker_count > 0);
+  handle_count =
+      (pollable_obj->event_count - pollable_obj->event_cursor) / worker_count;
+  if (handle_count == 0) {
+    handle_count = 1;
+  } else if (handle_count > MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) {
+    handle_count = MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL;
+  }
   grpc_error* error = GRPC_ERROR_NONE;
-  for (int i = 0; (drain || i < MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) &&
+  for (int i = 0; (drain || i < handle_count) &&
                   pollable_obj->event_cursor != pollable_obj->event_count;
        i++) {
     int n = pollable_obj->event_cursor++;
@@ -882,6 +898,7 @@
   GPR_TIMER_SCOPE("begin_worker", 0);
   bool do_poll =
       (pollset->shutdown_closure == nullptr && !pollset->already_shutdown);
+  gpr_atm_no_barrier_fetch_add(&pollset->worker_count, 1);
   if (worker_hdl != nullptr) *worker_hdl = worker;
   worker->initialized_cv = false;
   worker->kicked = false;
@@ -962,6 +979,7 @@
   if (worker->initialized_cv) {
     gpr_cv_destroy(&worker->cv);
   }
+  gpr_atm_no_barrier_fetch_add(&pollset->worker_count, -1);
 }
 
 #ifndef NDEBUG
diff --git a/src/core/lib/iomgr/ev_epollex_linux.h b/src/core/lib/iomgr/ev_epollex_linux.h
index ffa7fc7..e70ba72 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.h
+++ b/src/core/lib/iomgr/ev_epollex_linux.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H
 #define GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc
index 5c99c72..1e30f66 100644
--- a/src/core/lib/iomgr/ev_epollsig_linux.cc
+++ b/src/core/lib/iomgr/ev_epollsig_linux.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include <grpc/grpc_posix.h>
diff --git a/src/core/lib/iomgr/ev_epollsig_linux.h b/src/core/lib/iomgr/ev_epollsig_linux.h
index 48178d3..2ba2f0a 100644
--- a/src/core/lib/iomgr/ev_epollsig_linux.h
+++ b/src/core/lib/iomgr/ev_epollsig_linux.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H
 #define GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc
index 8d85a1f..436537b 100644
--- a/src/core/lib/iomgr/ev_poll_posix.cc
+++ b/src/core/lib/iomgr/ev_poll_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
@@ -36,9 +38,9 @@
 
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/murmur_hash.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/wakeup_fd_cv.h"
@@ -253,8 +255,13 @@
 } poll_result;
 
 typedef struct poll_args {
+  grpc_core::Thread poller_thd;
   gpr_cv trigger;
   int trigger_set;
+  bool harvestable;
+  gpr_cv harvest;
+  bool joinable;
+  gpr_cv join;
   struct pollfd* fds;
   nfds_t nfds;
   poll_result* result;
@@ -264,15 +271,17 @@
 
 // This is a 2-tiered cache, we mantain a hash table
 // of active poll calls, so we can wait on the result
-// of that call.  We also maintain a freelist of inactive
-// poll threads.
+// of that call.  We also maintain freelists of inactive
+// poll args and of dead poller threads.
 typedef struct poll_hash_table {
   poll_args* free_pollers;
   poll_args** active_pollers;
+  poll_args* dead_pollers;
   unsigned int size;
   unsigned int count;
 } poll_hash_table;
 
+// TODO(kpayson64): Eliminate use of global non-POD variables
 poll_hash_table poll_cache;
 grpc_cv_fd_table g_cvfds;
 
@@ -1299,6 +1308,7 @@
 
 static void run_poll(void* args);
 static void cache_poller_locked(poll_args* args);
+static void cache_harvest_locked();
 
 static void cache_insert_locked(poll_args* args) {
   uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd),
@@ -1361,6 +1371,10 @@
   poll_args* pargs =
       static_cast<poll_args*>(gpr_malloc(sizeof(struct poll_args)));
   gpr_cv_init(&pargs->trigger);
+  gpr_cv_init(&pargs->harvest);
+  gpr_cv_init(&pargs->join);
+  pargs->harvestable = false;
+  pargs->joinable = false;
   pargs->fds = fds;
   pargs->nfds = count;
   pargs->next = nullptr;
@@ -1368,11 +1382,9 @@
   pargs->trigger_set = 0;
   init_result(pargs);
   cache_poller_locked(pargs);
-  gpr_thd_id t_id;
-  gpr_thd_options opt = gpr_thd_options_default();
   gpr_ref(&g_cvfds.pollcount);
-  gpr_thd_options_set_detached(&opt);
-  GPR_ASSERT(gpr_thd_new(&t_id, "grpc_poller", &run_poll, pargs, &opt));
+  pargs->poller_thd = grpc_core::Thread("grpc_poller", &run_poll, pargs);
+  pargs->poller_thd.Start();
   return pargs;
 }
 
@@ -1437,7 +1449,33 @@
     poll_cache.free_pollers = args->next;
   }
 
-  gpr_free(args);
+  // Now move this args to the dead poller list for later join
+  if (poll_cache.dead_pollers != nullptr) {
+    poll_cache.dead_pollers->prev = args;
+  }
+  args->prev = nullptr;
+  args->next = poll_cache.dead_pollers;
+  poll_cache.dead_pollers = args;
+}
+
+static void cache_harvest_locked() {
+  while (poll_cache.dead_pollers) {
+    poll_args* args = poll_cache.dead_pollers;
+    poll_cache.dead_pollers = poll_cache.dead_pollers->next;
+    // Keep the list consistent in case new dead pollers get added when we
+    // release the lock below to wait on joining
+    if (poll_cache.dead_pollers) {
+      poll_cache.dead_pollers->prev = nullptr;
+    }
+    args->harvestable = true;
+    gpr_cv_signal(&args->harvest);
+    while (!args->joinable) {
+      gpr_cv_wait(&args->join, &g_cvfds.mu,
+                  gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    }
+    args->poller_thd.Join();
+    gpr_free(args);
+  }
 }
 
 static void decref_poll_result(poll_result* res) {
@@ -1469,6 +1507,7 @@
     poll_result* result = pargs->result;
     int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS);
     gpr_mu_lock(&g_cvfds.mu);
+    cache_harvest_locked();
     if (retval != 0) {
       result->completed = 1;
       result->retval = retval;
@@ -1488,6 +1527,7 @@
       deadline = gpr_time_add(deadline, thread_grace);
       pargs->trigger_set = 0;
       gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline);
+      cache_harvest_locked();
       if (!pargs->trigger_set) {
         cache_destroy_locked(pargs);
         break;
@@ -1496,10 +1536,15 @@
     gpr_mu_unlock(&g_cvfds.mu);
   }
 
-  // We still have the lock here
   if (gpr_unref(&g_cvfds.pollcount)) {
     gpr_cv_signal(&g_cvfds.shutdown_cv);
   }
+  while (!pargs->harvestable) {
+    gpr_cv_wait(&pargs->harvest, &g_cvfds.mu,
+                gpr_inf_future(GPR_CLOCK_MONOTONIC));
+  }
+  pargs->joinable = true;
+  gpr_cv_signal(&pargs->join);
   gpr_mu_unlock(&g_cvfds.mu);
 }
 
@@ -1512,6 +1557,7 @@
   nfds_t nsockfds = 0;
   poll_result* result = nullptr;
   gpr_mu_lock(&g_cvfds.mu);
+  cache_harvest_locked();
   pollcv = static_cast<grpc_cv_node*>(gpr_malloc(sizeof(grpc_cv_node)));
   pollcv->next = nullptr;
   gpr_cv pollcv_cv;
@@ -1575,12 +1621,14 @@
     pargs->trigger_set = 1;
     gpr_cv_signal(&pargs->trigger);
     gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline);
+    cache_harvest_locked();
     res = result->retval;
     errno = result->err;
     result->watchcount--;
     remove_cvn(&result->watchers, pollcv);
   } else if (!skip_poll) {
     gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline);
+    cache_harvest_locked();
   }
 
   idx = 0;
@@ -1637,6 +1685,7 @@
   for (unsigned int i = 0; i < poll_cache.size; i++) {
     poll_cache.active_pollers[i] = nullptr;
   }
+  poll_cache.dead_pollers = nullptr;
 
   gpr_mu_unlock(&g_cvfds.mu);
 }
@@ -1655,6 +1704,7 @@
   grpc_poll_function = g_cvfds.poll;
   gpr_free(g_cvfds.cvfds);
 
+  cache_harvest_locked();
   gpr_free(poll_cache.active_pollers);
 
   gpr_mu_unlock(&g_cvfds.mu);
diff --git a/src/core/lib/iomgr/ev_poll_posix.h b/src/core/lib/iomgr/ev_poll_posix.h
index f6bc624..ab3cd90 100644
--- a/src/core/lib/iomgr/ev_poll_posix.h
+++ b/src/core/lib/iomgr/ev_poll_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/ev_posix.h"
 
 const grpc_event_engine_vtable* grpc_init_poll_posix(bool explicit_request);
diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc
index 4280794..39ce459 100644
--- a/src/core/lib/iomgr/ev_posix.cc
+++ b/src/core/lib/iomgr/ev_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index 62f1162..6a5129a 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EV_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_EV_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include <poll.h>
 
 #include "src/core/lib/debug/trace.h"
diff --git a/src/core/lib/iomgr/ev_windows.cc b/src/core/lib/iomgr/ev_windows.cc
index 697697d..32c62b7 100644
--- a/src/core/lib/iomgr/ev_windows.cc
+++ b/src/core/lib/iomgr/ev_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc
index 1a2284f..2f544b2 100644
--- a/src/core/lib/iomgr/exec_ctx.cc
+++ b/src/core/lib/iomgr/exec_ctx.cc
@@ -16,12 +16,14 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/exec_ctx.h"
 
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/profiling/timers.h"
 
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 3d9a157..de97164 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
 #define GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 #include <grpc/support/cpu.h>
 #include <grpc/support/log.h>
diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc
index d2a0509..b017db5 100644
--- a/src/core/lib/iomgr/executor.cc
+++ b/src/core/lib/iomgr/executor.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/executor.h"
 
 #include <string.h>
@@ -27,9 +29,9 @@
 
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/spinlock.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
 #define MAX_DEPTH 2
@@ -41,7 +43,7 @@
   size_t depth;
   bool shutdown;
   bool queued_long_job;
-  gpr_thd_id id;
+  grpc_core::Thread thd;
 } thread_state;
 
 static thread_state* g_thread_state;
@@ -99,13 +101,13 @@
     for (size_t i = 0; i < g_max_threads; i++) {
       gpr_mu_init(&g_thread_state[i].mu);
       gpr_cv_init(&g_thread_state[i].cv);
+      g_thread_state[i].thd = grpc_core::Thread();
       g_thread_state[i].elems = GRPC_CLOSURE_LIST_INIT;
     }
 
-    gpr_thd_options opt = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&opt);
-    gpr_thd_new(&g_thread_state[0].id, "grpc_executor", executor_thread,
-                &g_thread_state[0], &opt);
+    g_thread_state[0].thd =
+        grpc_core::Thread("grpc_executor", executor_thread, &g_thread_state[0]);
+    g_thread_state[0].thd.Start();
   } else {
     if (cur_threads == 0) return;
     for (size_t i = 0; i < g_max_threads; i++) {
@@ -119,7 +121,7 @@
     gpr_spinlock_lock(&g_adding_thread_lock);
     gpr_spinlock_unlock(&g_adding_thread_lock);
     for (gpr_atm i = 0; i < g_cur_threads; i++) {
-      gpr_thd_join(g_thread_state[i].id);
+      g_thread_state[i].thd.Join();
     }
     gpr_atm_no_barrier_store(&g_cur_threads, 0);
     for (size_t i = 0; i < g_max_threads; i++) {
@@ -262,10 +264,10 @@
       if (cur_thread_count < g_max_threads) {
         gpr_atm_no_barrier_store(&g_cur_threads, cur_thread_count + 1);
 
-        gpr_thd_options opt = gpr_thd_options_default();
-        gpr_thd_options_set_joinable(&opt);
-        gpr_thd_new(&g_thread_state[cur_thread_count].id, "gpr_executor",
-                    executor_thread, &g_thread_state[cur_thread_count], &opt);
+        g_thread_state[cur_thread_count].thd =
+            grpc_core::Thread("grpc_executor", executor_thread,
+                              &g_thread_state[cur_thread_count]);
+        g_thread_state[cur_thread_count].thd.Start();
       }
       gpr_spinlock_unlock(&g_adding_thread_lock);
     }
diff --git a/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h
index e16f11a..68d540a 100644
--- a/src/core/lib/iomgr/executor.h
+++ b/src/core/lib/iomgr/executor.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EXECUTOR_H
 #define GRPC_CORE_LIB_IOMGR_EXECUTOR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/closure.h"
 
 typedef enum {
diff --git a/src/core/lib/iomgr/fork_posix.cc b/src/core/lib/iomgr/fork_posix.cc
index c9a65c5..f8645ab 100644
--- a/src/core/lib/iomgr/fork_posix.cc
+++ b/src/core/lib/iomgr/fork_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_FORK
@@ -27,7 +29,7 @@
 
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/fork.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/timer_manager.h"
@@ -51,7 +53,7 @@
     grpc_timer_manager_set_threading(false);
     grpc_executor_set_threading(false);
     grpc_core::ExecCtx::Get()->Flush();
-    if (!gpr_await_threads(
+    if (!grpc_core::Thread::AwaitAll(
             gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                          gpr_time_from_seconds(3, GPR_TIMESPAN)))) {
       gpr_log(GPR_ERROR, "gRPC thread still active! Cannot fork!");
diff --git a/src/core/lib/iomgr/fork_windows.cc b/src/core/lib/iomgr/fork_windows.cc
index f9986f3..798f671 100644
--- a/src/core/lib/iomgr/fork_windows.cc
+++ b/src/core/lib/iomgr/fork_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifndef GRPC_POSIX_FORK
diff --git a/src/core/lib/iomgr/gethostname_fallback.cc b/src/core/lib/iomgr/gethostname_fallback.cc
index 81e2c7a..65ae818 100644
--- a/src/core/lib/iomgr/gethostname_fallback.cc
+++ b/src/core/lib/iomgr/gethostname_fallback.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/gethostname.h"
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/gethostname_host_name_max.cc b/src/core/lib/iomgr/gethostname_host_name_max.cc
index ae95788..79f5daa 100644
--- a/src/core/lib/iomgr/gethostname_host_name_max.cc
+++ b/src/core/lib/iomgr/gethostname_host_name_max.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/gethostname.h"
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/gethostname_sysconf.cc b/src/core/lib/iomgr/gethostname_sysconf.cc
index 3d74e03..92c5de3 100644
--- a/src/core/lib/iomgr/gethostname_sysconf.cc
+++ b/src/core/lib/iomgr/gethostname_sysconf.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/gethostname.h"
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/iocp_windows.cc b/src/core/lib/iomgr/iocp_windows.cc
index 4716872..ce77231 100644
--- a/src/core/lib/iomgr/iocp_windows.cc
+++ b/src/core/lib/iomgr/iocp_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
@@ -28,7 +30,7 @@
 #include <grpc/support/log_windows.h>
 
 #include "src/core/lib/debug/stats.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/iocp_windows.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/socket_windows.h"
diff --git a/src/core/lib/iomgr/iocp_windows.h b/src/core/lib/iomgr/iocp_windows.h
index 75b0ff4..5079ea5 100644
--- a/src/core/lib/iomgr/iocp_windows.h
+++ b/src/core/lib/iomgr/iocp_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H
 #define GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/iomgr/port.h"
diff --git a/src/core/lib/iomgr/iomgr.cc b/src/core/lib/iomgr/iomgr.cc
index 70a80e1..3c2b83a 100644
--- a/src/core/lib/iomgr/iomgr.cc
+++ b/src/core/lib/iomgr/iomgr.cc
@@ -31,8 +31,8 @@
 
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
diff --git a/src/core/lib/iomgr/iomgr.h b/src/core/lib/iomgr/iomgr.h
index c7cde7e..e6d66e5 100644
--- a/src/core/lib/iomgr/iomgr.h
+++ b/src/core/lib/iomgr/iomgr.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_H
 #define GRPC_CORE_LIB_IOMGR_IOMGR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 /** Initializes the iomgr. */
diff --git a/src/core/lib/iomgr/iomgr_internal.h b/src/core/lib/iomgr/iomgr_internal.h
index 20b3cb7..644219f 100644
--- a/src/core/lib/iomgr/iomgr_internal.h
+++ b/src/core/lib/iomgr/iomgr_internal.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H
 #define GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include "src/core/lib/iomgr/iomgr.h"
diff --git a/src/core/lib/iomgr/iomgr_posix.cc b/src/core/lib/iomgr/iomgr_posix.cc
index f8f6fe2..35b8adf 100644
--- a/src/core/lib/iomgr/iomgr_posix.cc
+++ b/src/core/lib/iomgr/iomgr_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/iomgr_posix.h b/src/core/lib/iomgr/iomgr_posix.h
index f7a4af6..54ec46e 100644
--- a/src/core/lib/iomgr/iomgr_posix.h
+++ b/src/core/lib/iomgr/iomgr_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/iomgr_internal.h"
 
 #endif /* GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H */
diff --git a/src/core/lib/iomgr/iomgr_uv.cc b/src/core/lib/iomgr/iomgr_uv.cc
index c11bc28..c11c37c 100644
--- a/src/core/lib/iomgr/iomgr_uv.cc
+++ b/src/core/lib/iomgr/iomgr_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/iomgr_uv.h b/src/core/lib/iomgr/iomgr_uv.h
index a382f0a..4d62f00 100644
--- a/src/core/lib/iomgr/iomgr_uv.h
+++ b/src/core/lib/iomgr/iomgr_uv.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_UV_H
 #define GRPC_CORE_LIB_IOMGR_IOMGR_UV_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/iomgr_internal.h"
 
 #include <grpc/support/thd_id.h>
diff --git a/src/core/lib/iomgr/iomgr_windows.cc b/src/core/lib/iomgr/iomgr_windows.cc
index 6303701..8c4888c 100644
--- a/src/core/lib/iomgr/iomgr_windows.cc
+++ b/src/core/lib/iomgr/iomgr_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/is_epollexclusive_available.cc b/src/core/lib/iomgr/is_epollexclusive_available.cc
index 542cc41..036b778 100644
--- a/src/core/lib/iomgr/is_epollexclusive_available.cc
+++ b/src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include "src/core/lib/iomgr/is_epollexclusive_available.h"
@@ -37,7 +39,7 @@
   int fd = epoll_create1(EPOLL_CLOEXEC);
   if (fd < 0) {
     if (!logged_why_not) {
-      gpr_log(GPR_ERROR,
+      gpr_log(GPR_DEBUG,
               "epoll_create1 failed with error: %d. Not using epollex polling "
               "engine.",
               fd);
@@ -48,7 +50,7 @@
   int evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
   if (evfd < 0) {
     if (!logged_why_not) {
-      gpr_log(GPR_ERROR,
+      gpr_log(GPR_DEBUG,
               "eventfd failed with error: %d. Not using epollex polling "
               "engine.",
               fd);
@@ -80,7 +82,7 @@
     }
   } else {
     if (!logged_why_not) {
-      gpr_log(GPR_ERROR,
+      gpr_log(GPR_DEBUG,
               "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT succeeded. This is "
               "evidence of no EPOLLEXCLUSIVE support. Not using "
               "epollex polling engine.");
diff --git a/src/core/lib/iomgr/is_epollexclusive_available.h b/src/core/lib/iomgr/is_epollexclusive_available.h
index 9ae9c5c..8a44113 100644
--- a/src/core/lib/iomgr/is_epollexclusive_available.h
+++ b/src/core/lib/iomgr/is_epollexclusive_available.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H
 #define GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #ifdef __cplusplus
diff --git a/src/core/lib/iomgr/load_file.cc b/src/core/lib/iomgr/load_file.cc
index 7f5f642..f6431d0 100644
--- a/src/core/lib/iomgr/load_file.cc
+++ b/src/core/lib/iomgr/load_file.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/load_file.h"
 
 #include <errno.h>
diff --git a/src/core/lib/iomgr/load_file.h b/src/core/lib/iomgr/load_file.h
index a733652..1cb2b5d 100644
--- a/src/core/lib/iomgr/load_file.h
+++ b/src/core/lib/iomgr/load_file.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_LOAD_FILE_H
 #define GRPC_CORE_LIB_IOMGR_LOAD_FILE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdio.h>
 
 #include <grpc/slice.h>
diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc
index 7b194e3..5b6b79f 100644
--- a/src/core/lib/iomgr/lockfree_event.cc
+++ b/src/core/lib/iomgr/lockfree_event.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/lockfree_event.h"
 
 #include <grpc/support/log.h>
diff --git a/src/core/lib/iomgr/lockfree_event.h b/src/core/lib/iomgr/lockfree_event.h
index 3bd3fd7..83de656 100644
--- a/src/core/lib/iomgr/lockfree_event.h
+++ b/src/core/lib/iomgr/lockfree_event.h
@@ -21,6 +21,8 @@
 
 /* Lock free event notification for file descriptors */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/atm.h>
 
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/lib/iomgr/nameser.h b/src/core/lib/iomgr/nameser.h
index daed6de..22a00cd 100644
--- a/src/core/lib/iomgr/nameser.h
+++ b/src/core/lib/iomgr/nameser.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_NAMESER_H
 #define GRPC_CORE_LIB_IOMGR_NAMESER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_HAVE_ARPA_NAMESER
diff --git a/src/core/lib/iomgr/network_status_tracker.cc b/src/core/lib/iomgr/network_status_tracker.cc
index 73f8fbf..d4b7f4a 100644
--- a/src/core/lib/iomgr/network_status_tracker.cc
+++ b/src/core/lib/iomgr/network_status_tracker.cc
@@ -16,8 +16,10 @@
  *
  */
 
-#include "src/core/lib/iomgr/network_status_tracker.h"
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/network_status_tracker.h"
 
 void grpc_network_status_shutdown(void) {}
 
diff --git a/src/core/lib/iomgr/network_status_tracker.h b/src/core/lib/iomgr/network_status_tracker.h
index 32244d9..198877f 100644
--- a/src/core/lib/iomgr/network_status_tracker.h
+++ b/src/core/lib/iomgr/network_status_tracker.h
@@ -18,6 +18,8 @@
 
 #ifndef GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
 #define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/endpoint.h"
 
 void grpc_network_status_init(void);
diff --git a/src/core/lib/iomgr/polling_entity.cc b/src/core/lib/iomgr/polling_entity.cc
index 126f6f4..9f164f6 100644
--- a/src/core/lib/iomgr/polling_entity.cc
+++ b/src/core/lib/iomgr/polling_entity.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
diff --git a/src/core/lib/iomgr/polling_entity.h b/src/core/lib/iomgr/polling_entity.h
index 0102d32..a95e085 100644
--- a/src/core/lib/iomgr/polling_entity.h
+++ b/src/core/lib/iomgr/polling_entity.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H
 #define GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/iomgr/pollset_set.h"
 
diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
index 6bb3cd3..9cc3e4c 100644
--- a/src/core/lib/iomgr/pollset.h
+++ b/src/core/lib/iomgr/pollset.h
@@ -20,6 +20,7 @@
 #define GRPC_CORE_LIB_IOMGR_POLLSET_H
 
 #include <grpc/support/port_platform.h>
+
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
diff --git a/src/core/lib/iomgr/pollset_set.h b/src/core/lib/iomgr/pollset_set.h
index a94d0af..18f30aa 100644
--- a/src/core/lib/iomgr/pollset_set.h
+++ b/src/core/lib/iomgr/pollset_set.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_H
 #define GRPC_CORE_LIB_IOMGR_POLLSET_SET_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/pollset.h"
 
 /* A grpc_pollset_set is a set of pollsets that are interested in an
diff --git a/src/core/lib/iomgr/pollset_set_uv.cc b/src/core/lib/iomgr/pollset_set_uv.cc
index ac5dade..50814c1 100644
--- a/src/core/lib/iomgr/pollset_set_uv.cc
+++ b/src/core/lib/iomgr/pollset_set_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/pollset_set_windows.cc b/src/core/lib/iomgr/pollset_set_windows.cc
index 85edc9d..ff3f6a9 100644
--- a/src/core/lib/iomgr/pollset_set_windows.cc
+++ b/src/core/lib/iomgr/pollset_set_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdint.h>
 #include "src/core/lib/iomgr/port.h"
 
diff --git a/src/core/lib/iomgr/pollset_set_windows.h b/src/core/lib/iomgr/pollset_set_windows.h
index 1173f76..5ac9d18 100644
--- a/src/core/lib/iomgr/pollset_set_windows.h
+++ b/src/core/lib/iomgr/pollset_set_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H
 #define GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/pollset_set.h"
 
 #endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H */
diff --git a/src/core/lib/iomgr/pollset_uv.cc b/src/core/lib/iomgr/pollset_uv.cc
index d9e5ad8..c6a2f43 100644
--- a/src/core/lib/iomgr/pollset_uv.cc
+++ b/src/core/lib/iomgr/pollset_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/pollset_windows.cc b/src/core/lib/iomgr/pollset_windows.cc
index 240a24d..c1b83dd 100644
--- a/src/core/lib/iomgr/pollset_windows.cc
+++ b/src/core/lib/iomgr/pollset_windows.cc
@@ -16,13 +16,15 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
 
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/iocp_windows.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/pollset.h"
diff --git a/src/core/lib/iomgr/pollset_windows.h b/src/core/lib/iomgr/pollset_windows.h
index 93fe7d6..e89758c 100644
--- a/src/core/lib/iomgr/pollset_windows.h
+++ b/src/core/lib/iomgr/pollset_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H
 #define GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/iomgr/port.h"
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
index 12fc2ed..10a7822 100644
--- a/src/core/lib/iomgr/resolve_address.h
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H
 #define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/pollset_set.h"
diff --git a/src/core/lib/iomgr/resolve_address_posix.cc b/src/core/lib/iomgr/resolve_address_posix.cc
index 3dc1d87..2f68dbe 100644
--- a/src/core/lib/iomgr/resolve_address_posix.cc
+++ b/src/core/lib/iomgr/resolve_address_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 #ifdef GRPC_POSIX_SOCKET
 
@@ -33,8 +35,8 @@
 
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
diff --git a/src/core/lib/iomgr/resolve_address_uv.cc b/src/core/lib/iomgr/resolve_address_uv.cc
index 6eb6fe3..4d8ea59 100644
--- a/src/core/lib/iomgr/resolve_address_uv.cc
+++ b/src/core/lib/iomgr/resolve_address_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 #ifdef GRPC_UV
 
diff --git a/src/core/lib/iomgr/resolve_address_windows.cc b/src/core/lib/iomgr/resolve_address_windows.cc
index 8f4fd04..7a62c88 100644
--- a/src/core/lib/iomgr/resolve_address_windows.cc
+++ b/src/core/lib/iomgr/resolve_address_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 #ifdef GRPC_WINSOCK_SOCKET
 
@@ -35,7 +37,7 @@
 
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc
index c4763f3..8c42dd7 100644
--- a/src/core/lib/iomgr/resource_quota.cc
+++ b/src/core/lib/iomgr/resource_quota.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/resource_quota.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h
index 39e3aab..4e1c651 100644
--- a/src/core/lib/iomgr/resource_quota.h
+++ b/src/core/lib/iomgr/resource_quota.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
 #define GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include "src/core/lib/debug/trace.h"
diff --git a/src/core/lib/iomgr/sockaddr.h b/src/core/lib/iomgr/sockaddr.h
index 206d596..3b30da8 100644
--- a/src/core/lib/iomgr/sockaddr.h
+++ b/src/core/lib/iomgr/sockaddr.h
@@ -23,6 +23,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_H
 #define GRPC_CORE_LIB_IOMGR_SOCKADDR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/sockaddr_posix.h b/src/core/lib/iomgr/sockaddr_posix.h
index 22d57ca..83981e0 100644
--- a/src/core/lib/iomgr/sockaddr_posix.h
+++ b/src/core/lib/iomgr/sockaddr_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <netinet/in.h>
diff --git a/src/core/lib/iomgr/sockaddr_utils.cc b/src/core/lib/iomgr/sockaddr_utils.cc
index 69be871..88f9b2f 100644
--- a/src/core/lib/iomgr/sockaddr_utils.cc
+++ b/src/core/lib/iomgr/sockaddr_utils.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 
 #include <errno.h>
@@ -24,7 +26,6 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/gpr/host_port.h"
diff --git a/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h
index e3bd51a..ace54a2 100644
--- a/src/core/lib/iomgr/sockaddr_utils.h
+++ b/src/core/lib/iomgr/sockaddr_utils.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H
 #define GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/resolve_address.h"
 
 /* Returns true if addr is an IPv4-mapped IPv6 address within the
diff --git a/src/core/lib/iomgr/sockaddr_windows.h b/src/core/lib/iomgr/sockaddr_windows.h
index 20e37c9..3a4fcc9 100644
--- a/src/core/lib/iomgr/sockaddr_windows.h
+++ b/src/core/lib/iomgr/sockaddr_windows.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H
 #define GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/socket_factory_posix.cc b/src/core/lib/iomgr/socket_factory_posix.cc
index 3e696c2..1d1e36c 100644
--- a/src/core/lib/iomgr/socket_factory_posix.cc
+++ b/src/core/lib/iomgr/socket_factory_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/socket_factory_posix.h b/src/core/lib/iomgr/socket_factory_posix.h
index af57cc5..9a52f4e 100644
--- a/src/core/lib/iomgr/socket_factory_posix.h
+++ b/src/core/lib/iomgr/socket_factory_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/sync.h>
 #include "src/core/lib/iomgr/resolve_address.h"
diff --git a/src/core/lib/iomgr/socket_mutator.cc b/src/core/lib/iomgr/socket_mutator.cc
index eb219d7..b9b8eaf 100644
--- a/src/core/lib/iomgr/socket_mutator.cc
+++ b/src/core/lib/iomgr/socket_mutator.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/socket_mutator.h"
 
 #include <grpc/impl/codegen/grpc_types.h>
diff --git a/src/core/lib/iomgr/socket_mutator.h b/src/core/lib/iomgr/socket_mutator.h
index f8fd21d..6c7781c 100644
--- a/src/core/lib/iomgr/socket_mutator.h
+++ b/src/core/lib/iomgr/socket_mutator.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H
 #define GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/sync.h>
 
diff --git a/src/core/lib/iomgr/socket_utils.h b/src/core/lib/iomgr/socket_utils.h
index 9fd141b..e96eb97 100644
--- a/src/core/lib/iomgr/socket_utils.h
+++ b/src/core/lib/iomgr/socket_utils.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H
 #define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 /* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */
diff --git a/src/core/lib/iomgr/socket_utils_common_posix.cc b/src/core/lib/iomgr/socket_utils_common_posix.cc
index a0cca61..4fb6c7a 100644
--- a/src/core/lib/iomgr/socket_utils_common_posix.cc
+++ b/src/core/lib/iomgr/socket_utils_common_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
@@ -37,7 +39,6 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/gpr/host_port.h"
diff --git a/src/core/lib/iomgr/socket_utils_linux.cc b/src/core/lib/iomgr/socket_utils_linux.cc
index e8bf05c..deb7c55 100644
--- a/src/core/lib/iomgr/socket_utils_linux.cc
+++ b/src/core/lib/iomgr/socket_utils_linux.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_LINUX_SOCKETUTILS
diff --git a/src/core/lib/iomgr/socket_utils_posix.cc b/src/core/lib/iomgr/socket_utils_posix.cc
index c49cbb2..c856f64 100644
--- a/src/core/lib/iomgr/socket_utils_posix.cc
+++ b/src/core/lib/iomgr/socket_utils_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKETUTILS
diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h
index 77df420..1f50e8d 100644
--- a/src/core/lib/iomgr/socket_utils_posix.h
+++ b/src/core/lib/iomgr/socket_utils_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/resolve_address.h"
 
 #include <sys/socket.h>
diff --git a/src/core/lib/iomgr/socket_utils_uv.cc b/src/core/lib/iomgr/socket_utils_uv.cc
index 75316d8..3f650ee 100644
--- a/src/core/lib/iomgr/socket_utils_uv.cc
+++ b/src/core/lib/iomgr/socket_utils_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/socket_utils_windows.cc b/src/core/lib/iomgr/socket_utils_windows.cc
index 0482a17..5fc3b76 100644
--- a/src/core/lib/iomgr/socket_utils_windows.cc
+++ b/src/core/lib/iomgr/socket_utils_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINDOWS_SOCKETUTILS
diff --git a/src/core/lib/iomgr/socket_windows.cc b/src/core/lib/iomgr/socket_windows.cc
index 9bb6a75..2e23409 100644
--- a/src/core/lib/iomgr/socket_windows.cc
+++ b/src/core/lib/iomgr/socket_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/socket_windows.h b/src/core/lib/iomgr/socket_windows.h
index cb28f2b..3ff2c30 100644
--- a/src/core/lib/iomgr/socket_windows.h
+++ b/src/core/lib/iomgr/socket_windows.h
@@ -20,6 +20,7 @@
 #define GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H
 
 #include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/sys_epoll_wrapper.h b/src/core/lib/iomgr/sys_epoll_wrapper.h
index 3fa5357..d21d853 100644
--- a/src/core/lib/iomgr/sys_epoll_wrapper.h
+++ b/src/core/lib/iomgr/sys_epoll_wrapper.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H
 #define GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <sys/epoll.h>
 
 #ifndef EPOLLEXCLUSIVE
diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h
index 5f55d30..a6b99e6 100644
--- a/src/core/lib/iomgr/tcp_client.h
+++ b/src/core/lib/iomgr/tcp_client.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H
 #define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/iomgr/endpoint.h"
diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc
index 33a0b04..3fe2989 100644
--- a/src/core/lib/iomgr/tcp_client_posix.cc
+++ b/src/core/lib/iomgr/tcp_client_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/tcp_client_posix.h b/src/core/lib/iomgr/tcp_client_posix.h
index 57e50a6..d0168ef 100644
--- a/src/core/lib/iomgr/tcp_client_posix.h
+++ b/src/core/lib/iomgr/tcp_client_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/tcp_client.h"
diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc
index 4e9c7cc..d29d6c8 100644
--- a/src/core/lib/iomgr/tcp_client_uv.cc
+++ b/src/core/lib/iomgr/tcp_client_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc
index d46569d..70c2495 100644
--- a/src/core/lib/iomgr/tcp_client_windows.cc
+++ b/src/core/lib/iomgr/tcp_client_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc
index 50efd04..ca0046b 100644
--- a/src/core/lib/iomgr/tcp_posix.cc
+++ b/src/core/lib/iomgr/tcp_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/tcp_posix.h b/src/core/lib/iomgr/tcp_posix.h
index 4529c02..af89bd2 100644
--- a/src/core/lib/iomgr/tcp_posix.h
+++ b/src/core/lib/iomgr/tcp_posix.h
@@ -29,6 +29,8 @@
    otherwise specified.
 */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/ev_posix.h"
diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h
index 038c765..965d974 100644
--- a/src/core/lib/iomgr/tcp_server.h
+++ b/src/core/lib/iomgr/tcp_server.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
 #define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include "src/core/lib/iomgr/closure.h"
diff --git a/src/core/lib/iomgr/tcp_server_posix.cc b/src/core/lib/iomgr/tcp_server_posix.cc
index fe6108e..a609c09 100644
--- a/src/core/lib/iomgr/tcp_server_posix.cc
+++ b/src/core/lib/iomgr/tcp_server_posix.cc
@@ -21,6 +21,8 @@
 #define _GNU_SOURCE
 #endif
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/tcp_server_utils_posix.h b/src/core/lib/iomgr/tcp_server_utils_posix.h
index 6046f25..34d6813 100644
--- a/src/core/lib/iomgr/tcp_server_utils_posix.h
+++ b/src/core/lib/iomgr/tcp_server_utils_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/socket_utils_posix.h"
diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc
index be2a00a..846f9cc 100644
--- a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc
+++ b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc
index 612c258..308ff0f 100644
--- a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc
+++ b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_HAVE_IFADDRS
diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc
index 2d72b95..86ee14f 100644
--- a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc
+++ b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #if defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS)
diff --git a/src/core/lib/iomgr/tcp_server_uv.cc b/src/core/lib/iomgr/tcp_server_uv.cc
index 1ac4919..aa42376 100644
--- a/src/core/lib/iomgr/tcp_server_uv.cc
+++ b/src/core/lib/iomgr/tcp_server_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/tcp_server_windows.cc b/src/core/lib/iomgr/tcp_server_windows.cc
index 8a30dfd..6d19c1c 100644
--- a/src/core/lib/iomgr/tcp_server_windows.cc
+++ b/src/core/lib/iomgr/tcp_server_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/tcp_uv.cc b/src/core/lib/iomgr/tcp_uv.cc
index b384623..6db3217 100644
--- a/src/core/lib/iomgr/tcp_uv.cc
+++ b/src/core/lib/iomgr/tcp_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h
index fd6d190..6b1a6f7 100644
--- a/src/core/lib/iomgr/tcp_uv.h
+++ b/src/core/lib/iomgr/tcp_uv.h
@@ -29,6 +29,8 @@
    otherwise specified.
 */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/endpoint.h"
 
diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc
index 6777719..aab8edc 100644
--- a/src/core/lib/iomgr/tcp_windows.cc
+++ b/src/core/lib/iomgr/tcp_windows.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/tcp_windows.h b/src/core/lib/iomgr/tcp_windows.h
index 8578a35..161a545 100644
--- a/src/core/lib/iomgr/tcp_windows.h
+++ b/src/core/lib/iomgr/tcp_windows.h
@@ -29,6 +29,8 @@
    otherwise specified.
 */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/time_averaged_stats.cc b/src/core/lib/iomgr/time_averaged_stats.cc
index 3bddec0..6369e48 100644
--- a/src/core/lib/iomgr/time_averaged_stats.cc
+++ b/src/core/lib/iomgr/time_averaged_stats.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/time_averaged_stats.h"
 
 void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats,
diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h
index 8204985..67f1b1b 100644
--- a/src/core/lib/iomgr/timer.h
+++ b/src/core/lib/iomgr/timer.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TIMER_H
 #define GRPC_CORE_LIB_IOMGR_TIMER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_UV
@@ -27,7 +29,6 @@
 #include "src/core/lib/iomgr/timer_generic.h"
 #endif /* GRPC_UV */
 
-#include <grpc/support/port_platform.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/iomgr.h"
diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc
index 697162f..52a571f 100644
--- a/src/core/lib/iomgr/timer_generic.cc
+++ b/src/core/lib/iomgr/timer_generic.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/iomgr/timer_generic.h b/src/core/lib/iomgr/timer_generic.h
index 190381e..97a4513 100644
--- a/src/core/lib/iomgr/timer_generic.h
+++ b/src/core/lib/iomgr/timer_generic.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H
 #define GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/time.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 
diff --git a/src/core/lib/iomgr/timer_heap.cc b/src/core/lib/iomgr/timer_heap.cc
index c26896e..e5b5abf 100644
--- a/src/core/lib/iomgr/timer_heap.cc
+++ b/src/core/lib/iomgr/timer_heap.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_TIMER_USE_GENERIC
diff --git a/src/core/lib/iomgr/timer_heap.h b/src/core/lib/iomgr/timer_heap.h
index 436eef5..503365d 100644
--- a/src/core/lib/iomgr/timer_heap.h
+++ b/src/core/lib/iomgr/timer_heap.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H
 #define GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/timer.h"
 
 typedef struct {
diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc
index 94e7953..94f288a 100644
--- a/src/core/lib/iomgr/timer_manager.cc
+++ b/src/core/lib/iomgr/timer_manager.cc
@@ -16,22 +16,22 @@
  *
  */
 
-#include "src/core/lib/iomgr/timer_manager.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 
 #include <inttypes.h>
 
-#include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gpr/thd.h"
-#include "src/core/lib/iomgr/timer.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 
-typedef struct completed_thread {
-  gpr_thd_id t;
-  struct completed_thread* next;
-} completed_thread;
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+
+struct completed_thread {
+  grpc_core::Thread thd;
+  completed_thread* next;
+};
 
 extern grpc_core::TraceFlag grpc_timer_check_trace;
 
@@ -67,7 +67,7 @@
     g_completed_threads = nullptr;
     gpr_mu_unlock(&g_mu);
     while (to_gc != nullptr) {
-      gpr_thd_join(to_gc->t);
+      to_gc->thd.Join();
       completed_thread* next = to_gc->next;
       gpr_free(to_gc);
       to_gc = next;
@@ -84,18 +84,10 @@
   if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Spawn timer thread");
   }
-  gpr_thd_options opt = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&opt);
   completed_thread* ct =
       static_cast<completed_thread*>(gpr_malloc(sizeof(*ct)));
-  // The call to gpr_thd_new() has to be under the same lock used by
-  // gc_completed_threads(), particularly due to ct->t, which is written here
-  // (internally by gpr_thd_new) and read there. Otherwise it's possible for ct
-  // to leak through g_completed_threads and be freed in gc_completed_threads()
-  // before "&ct->t" is written to, causing a use-after-free.
-  gpr_mu_lock(&g_mu);
-  gpr_thd_new(&ct->t, "grpc_global_timer", timer_thread, ct, &opt);
-  gpr_mu_unlock(&g_mu);
+  ct->thd = grpc_core::Thread("grpc_global_timer", timer_thread, ct);
+  ct->thd.Start();
 }
 
 void grpc_timer_manager_tick() {
diff --git a/src/core/lib/iomgr/timer_manager.h b/src/core/lib/iomgr/timer_manager.h
index 0ba5029..3c4cdda 100644
--- a/src/core/lib/iomgr/timer_manager.h
+++ b/src/core/lib/iomgr/timer_manager.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H
 #define GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 /* Timer Manager tries to keep one thread waiting for the next timeout at all
diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc
index 5d238da..6f28f55 100644
--- a/src/core/lib/iomgr/timer_uv.cc
+++ b/src/core/lib/iomgr/timer_uv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #if GRPC_UV
diff --git a/src/core/lib/iomgr/timer_uv.h b/src/core/lib/iomgr/timer_uv.h
index 214aaa6..093b2d0 100644
--- a/src/core/lib/iomgr/timer_uv.h
+++ b/src/core/lib/iomgr/timer_uv.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_TIMER_UV_H
 #define GRPC_CORE_LIB_IOMGR_TIMER_UV_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/exec_ctx.h"
 
 struct grpc_timer {
diff --git a/src/core/lib/iomgr/udp_server.cc b/src/core/lib/iomgr/udp_server.cc
index eecb3b0..e739a5d 100644
--- a/src/core/lib/iomgr/udp_server.cc
+++ b/src/core/lib/iomgr/udp_server.cc
@@ -25,6 +25,8 @@
 #define SO_RXQ_OVFL 40
 #endif
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_SOCKET
@@ -50,6 +52,8 @@
 #include <grpc/support/time.h>
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/executor.h"
@@ -60,41 +64,102 @@
 #include "src/core/lib/iomgr/socket_utils_posix.h"
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
 
-/* one listening port */
-typedef struct grpc_udp_listener grpc_udp_listener;
-struct grpc_udp_listener {
-  int fd;
-  grpc_fd* emfd;
-  grpc_udp_server* server;
-  grpc_resolved_address addr;
-  grpc_closure read_closure;
-  grpc_closure write_closure;
+/* A listener which implements basic features of Listening on a port for
+ * I/O events*/
+class GrpcUdpListener {
+ public:
+  GrpcUdpListener(grpc_udp_server* server, int fd,
+                  const grpc_resolved_address* addr);
+  ~GrpcUdpListener();
+
+  /* Called when grpc server starts to listening on the grpc_fd. */
+  void StartListening(grpc_pollset** pollsets, size_t pollset_count,
+                      GrpcUdpHandlerFactory* handler_factory);
+
+  /* Called when data is available to read from the socket.
+   * Return true if there is more data to read from fd. */
+  void OnRead(grpc_error* error, void* do_read_arg);
+
+  /* Called when the socket is writeable. The given closure should be scheduled
+   * when the socket becomes blocked next time. */
+  void OnCanWrite(grpc_error* error, void* do_write_arg);
+
+  /* Called when the grpc_fd is about to be orphaned (and the FD closed). */
+  void OnFdAboutToOrphan();
+
+  /* Called to orphan fd of this listener.*/
+  void OrphanFd();
+
+  /* Called when this listener is going to be destroyed. */
+  void OnDestroy();
+
+  int fd() const { return fd_; }
+
+ protected:
+  grpc_fd* emfd() const { return emfd_; }
+
+  gpr_mu* mutex() { return &mutex_; }
+
+ private:
+  /* event manager callback when reads are ready */
+  static void on_read(void* arg, grpc_error* error);
+  static void on_write(void* arg, grpc_error* error);
+
+  static void do_read(void* arg, grpc_error* error);
+  static void do_write(void* arg, grpc_error* error);
+  // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback
+  // interface.
+  static void fd_notify_on_write_wrapper(void* arg, grpc_error* error);
+
+  static void shutdown_fd(void* args, grpc_error* error);
+
+  int fd_;
+  grpc_fd* emfd_;
+  grpc_udp_server* server_;
+  grpc_resolved_address addr_;
+  grpc_closure read_closure_;
+  grpc_closure write_closure_;
   // To be called when corresponding QuicGrpcServer closes all active
   // connections.
-  grpc_closure orphan_fd_closure;
-  grpc_closure destroyed_closure;
-  grpc_udp_server_read_cb read_cb;
-  grpc_udp_server_write_cb write_cb;
-  grpc_udp_server_orphan_cb orphan_cb;
-  grpc_udp_server_start_cb start_cb;
+  grpc_closure orphan_fd_closure_;
+  grpc_closure destroyed_closure_;
   // To be scheduled on another thread to actually read/write.
-  grpc_closure do_read_closure;
-  grpc_closure do_write_closure;
-  grpc_closure notify_on_write_closure;
+  grpc_closure do_read_closure_;
+  grpc_closure do_write_closure_;
+  grpc_closure notify_on_write_closure_;
   // True if orphan_cb is trigered.
-  bool orphan_notified;
+  bool orphan_notified_;
   // True if grpc_fd_notify_on_write() is called after on_write() call.
-  bool notify_on_write_armed;
+  bool notify_on_write_armed_;
   // True if fd has been shutdown.
-  bool already_shutdown;
-
-  struct grpc_udp_listener* next;
+  bool already_shutdown_;
+  // Object actually handles I/O events. Assigned in StartListening().
+  GrpcUdpHandler* udp_handler_ = nullptr;
+  // To be notified on destruction.
+  GrpcUdpHandlerFactory* handler_factory_ = nullptr;
+  // Required to access above fields.
+  gpr_mu mutex_;
 };
 
-struct shutdown_fd_args {
-  grpc_udp_listener* sp;
-  gpr_mu* server_mu;
-};
+GrpcUdpListener::GrpcUdpListener(grpc_udp_server* server, int fd,
+                                 const grpc_resolved_address* addr)
+    : fd_(fd),
+      server_(server),
+      orphan_notified_(false),
+      already_shutdown_(false) {
+  char* addr_str;
+  char* name;
+  grpc_sockaddr_to_string(&addr_str, addr, 1);
+  gpr_asprintf(&name, "udp-server-listener:%s", addr_str);
+  gpr_free(addr_str);
+  emfd_ = grpc_fd_create(fd, name);
+  memcpy(&addr_, addr, sizeof(grpc_resolved_address));
+  GPR_ASSERT(emfd_);
+  gpr_free(name);
+  gpr_mu_init(&mutex_);
+}
+
+GrpcUdpListener::~GrpcUdpListener() { gpr_mu_destroy(&mutex_); }
 
 /* the overall server */
 struct grpc_udp_server {
@@ -111,10 +176,11 @@
   /* is this server shutting down? (boolean) */
   int shutdown;
 
-  /* linked list of server ports */
-  grpc_udp_listener* head;
-  grpc_udp_listener* tail;
-  unsigned nports;
+  /* An array of listeners */
+  grpc_core::InlinedVector<GrpcUdpListener, 16> listeners;
+
+  /* factory for use to create udp listeners */
+  GrpcUdpHandlerFactory* handler_factory;
 
   /* shutdown callback */
   grpc_closure* shutdown_complete;
@@ -139,8 +205,7 @@
 }
 
 grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) {
-  grpc_udp_server* s =
-      static_cast<grpc_udp_server*>(gpr_malloc(sizeof(grpc_udp_server)));
+  grpc_udp_server* s = grpc_core::New<grpc_udp_server>();
   gpr_mu_init(&s->mu);
   s->socket_factory = get_socket_factory(args);
   if (s->socket_factory) {
@@ -149,33 +214,27 @@
   s->active_ports = 0;
   s->destroyed_ports = 0;
   s->shutdown = 0;
-  s->head = nullptr;
-  s->tail = nullptr;
-  s->nports = 0;
-
   return s;
 }
 
-static void shutdown_fd(void* args, grpc_error* error) {
-  struct shutdown_fd_args* shutdown_args =
-      static_cast<struct shutdown_fd_args*>(args);
-  grpc_udp_listener* sp = shutdown_args->sp;
-  gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd);
-  gpr_mu_lock(shutdown_args->server_mu);
-  grpc_fd_shutdown(sp->emfd, GRPC_ERROR_REF(error));
-  sp->already_shutdown = true;
-  if (!sp->notify_on_write_armed) {
+// static
+void GrpcUdpListener::shutdown_fd(void* args, grpc_error* error) {
+  if (args == nullptr) {
+    // No-op if shutdown args are null.
+    return;
+  }
+  auto sp = static_cast<GrpcUdpListener*>(args);
+  gpr_mu_lock(sp->mutex());
+  gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd_);
+  grpc_fd_shutdown(sp->emfd_, GRPC_ERROR_REF(error));
+  sp->already_shutdown_ = true;
+  if (!sp->notify_on_write_armed_) {
     // Re-arm write notification to notify listener with error. This is
     // necessary to decrement active_ports.
-    sp->notify_on_write_armed = true;
-    grpc_fd_notify_on_write(sp->emfd, &sp->write_closure);
+    sp->notify_on_write_armed_ = true;
+    grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_);
   }
-  gpr_mu_unlock(shutdown_args->server_mu);
-  gpr_free(shutdown_args);
-}
-
-static void dummy_cb(void* arg, grpc_error* error) {
-  // No-op.
+  gpr_mu_unlock(sp->mutex());
 }
 
 static void finish_shutdown(grpc_udp_server* s) {
@@ -186,24 +245,22 @@
   gpr_mu_destroy(&s->mu);
 
   gpr_log(GPR_DEBUG, "Destroy all listeners.");
-  while (s->head) {
-    grpc_udp_listener* sp = s->head;
-    s->head = sp->next;
-    gpr_free(sp);
+  for (size_t i = 0; i < s->listeners.size(); ++i) {
+    s->listeners[i].OnDestroy();
   }
 
   if (s->socket_factory) {
     grpc_socket_factory_unref(s->socket_factory);
   }
 
-  gpr_free(s);
+  grpc_core::Delete(s);
 }
 
 static void destroyed_port(void* server, grpc_error* error) {
   grpc_udp_server* s = static_cast<grpc_udp_server*>(server);
   gpr_mu_lock(&s->mu);
   s->destroyed_ports++;
-  if (s->destroyed_ports == s->nports) {
+  if (s->destroyed_ports == s->listeners.size()) {
     gpr_mu_unlock(&s->mu);
     finish_shutdown(s);
   } else {
@@ -220,35 +277,30 @@
 
   GPR_ASSERT(s->shutdown);
 
-  if (s->head) {
-    grpc_udp_listener* sp;
-    for (sp = s->head; sp; sp = sp->next) {
-      grpc_unlink_if_unix_domain_socket(&sp->addr);
-
-      GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s,
-                        grpc_schedule_on_exec_ctx);
-      if (!sp->orphan_notified) {
-        /* Call the orphan_cb to signal that the FD is about to be closed and
-         * should no longer be used. Because at this point, all listening ports
-         * have been shutdown already, no need to shutdown again.*/
-        GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, dummy_cb, sp,
-                          grpc_schedule_on_exec_ctx);
-        GPR_ASSERT(sp->orphan_cb);
-        gpr_log(GPR_DEBUG, "Orphan fd %d", sp->fd);
-        sp->orphan_cb(sp->emfd, &sp->orphan_fd_closure, sp->server->user_data);
-      }
-      grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, nullptr,
-                     false /* already_closed */, "udp_listener_shutdown");
-    }
-    gpr_mu_unlock(&s->mu);
-  } else {
+  if (s->listeners.size() == 0) {
     gpr_mu_unlock(&s->mu);
     finish_shutdown(s);
+    return;
   }
+  for (size_t i = 0; i < s->listeners.size(); ++i) {
+    s->listeners[i].OrphanFd();
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+void GrpcUdpListener::OrphanFd() {
+  gpr_log(GPR_DEBUG, "Orphan fd %d, emfd %p", fd_, emfd_);
+  grpc_unlink_if_unix_domain_socket(&addr_);
+
+  GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_,
+                    grpc_schedule_on_exec_ctx);
+  /* Because at this point, all listening sockets have been shutdown already, no
+   * need to call OnFdAboutToOrphan() to notify the handler again. */
+  grpc_fd_orphan(emfd_, &destroyed_closure_, nullptr,
+                 false /* already_closed */, "udp_listener_shutdown");
 }
 
 void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) {
-  grpc_udp_listener* sp;
   gpr_mu_lock(&s->mu);
 
   GPR_ASSERT(!s->shutdown);
@@ -259,16 +311,9 @@
   gpr_log(GPR_DEBUG, "start to destroy udp_server");
   /* shutdown all fd's */
   if (s->active_ports) {
-    for (sp = s->head; sp; sp = sp->next) {
-      GPR_ASSERT(sp->orphan_cb);
-      struct shutdown_fd_args* args =
-          static_cast<struct shutdown_fd_args*>(gpr_malloc(sizeof(*args)));
-      args->sp = sp;
-      args->server_mu = &s->mu;
-      GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, shutdown_fd, args,
-                        grpc_schedule_on_exec_ctx);
-      sp->orphan_cb(sp->emfd, &sp->orphan_fd_closure, sp->server->user_data);
-      sp->orphan_notified = true;
+    for (size_t i = 0; i < s->listeners.size(); ++i) {
+      GrpcUdpListener* sp = &s->listeners[i];
+      sp->OnFdAboutToOrphan();
     }
     gpr_mu_unlock(&s->mu);
   } else {
@@ -277,6 +322,24 @@
   }
 }
 
+void GrpcUdpListener::OnFdAboutToOrphan() {
+  gpr_mu_lock(&mutex_);
+  grpc_unlink_if_unix_domain_socket(&addr_);
+
+  GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_,
+                    grpc_schedule_on_exec_ctx);
+  if (!orphan_notified_ && udp_handler_ != nullptr) {
+    /* Singals udp_handler that the FD is about to be closed and
+     * should no longer be used. */
+    GRPC_CLOSURE_INIT(&orphan_fd_closure_, shutdown_fd, this,
+                      grpc_schedule_on_exec_ctx);
+    gpr_log(GPR_DEBUG, "fd %d about to be orphaned", fd_);
+    udp_handler_->OnFdAboutToOrphan(&orphan_fd_closure_, server_->user_data);
+    orphan_notified_ = true;
+  }
+  gpr_mu_unlock(&mutex_);
+}
+
 static int bind_socket(grpc_socket_factory* socket_factory, int sockfd,
                        const grpc_resolved_address* addr) {
   return (socket_factory != nullptr)
@@ -362,163 +425,140 @@
   return -1;
 }
 
-static void do_read(void* arg, grpc_error* error) {
-  grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg);
-  GPR_ASSERT(sp->read_cb && error == GRPC_ERROR_NONE);
+// static
+void GrpcUdpListener::do_read(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   /* TODO: the reason we hold server->mu here is merely to prevent fd
    * shutdown while we are reading. However, it blocks do_write(). Switch to
    * read lock if available. */
-  gpr_mu_lock(&sp->server->mu);
+  gpr_mu_lock(sp->mutex());
   /* Tell the registered callback that data is available to read. */
-  if (!sp->already_shutdown && sp->read_cb(sp->emfd)) {
+  if (!sp->already_shutdown_ && sp->udp_handler_->Read()) {
     /* There maybe more packets to read. Schedule read_more_cb_ closure to run
      * after finishing this event loop. */
-    GRPC_CLOSURE_SCHED(&sp->do_read_closure, GRPC_ERROR_NONE);
+    GRPC_CLOSURE_SCHED(&sp->do_read_closure_, GRPC_ERROR_NONE);
   } else {
     /* Finish reading all the packets, re-arm the notification event so we can
      * get another chance to read. Or fd already shutdown, re-arm to get a
      * notification with shutdown error. */
-    grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
+    grpc_fd_notify_on_read(sp->emfd_, &sp->read_closure_);
   }
-  gpr_mu_unlock(&sp->server->mu);
+  gpr_mu_unlock(sp->mutex());
 }
 
-/* event manager callback when reads are ready */
-static void on_read(void* arg, grpc_error* error) {
-  grpc_udp_listener* sp = static_cast<grpc_udp_listener*>(arg);
+// static
+void GrpcUdpListener::on_read(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  sp->OnRead(error, arg);
+}
 
-  gpr_mu_lock(&sp->server->mu);
+void GrpcUdpListener::OnRead(grpc_error* error, void* do_read_arg) {
   if (error != GRPC_ERROR_NONE) {
-    if (0 == --sp->server->active_ports && sp->server->shutdown) {
-      gpr_mu_unlock(&sp->server->mu);
-      deactivated_all_ports(sp->server);
+    gpr_mu_lock(&server_->mu);
+    if (0 == --server_->active_ports && server_->shutdown) {
+      gpr_mu_unlock(&server_->mu);
+      deactivated_all_ports(server_);
     } else {
-      gpr_mu_unlock(&sp->server->mu);
+      gpr_mu_unlock(&server_->mu);
     }
     return;
   }
+
   /* Read once. If there is more data to read, off load the work to another
    * thread to finish. */
-  GPR_ASSERT(sp->read_cb);
-  if (sp->read_cb(sp->emfd)) {
+  if (udp_handler_->Read()) {
     /* There maybe more packets to read. Schedule read_more_cb_ closure to run
      * after finishing this event loop. */
-    GRPC_CLOSURE_INIT(&sp->do_read_closure, do_read, arg,
+    GRPC_CLOSURE_INIT(&do_read_closure_, do_read, do_read_arg,
                       grpc_executor_scheduler(GRPC_EXECUTOR_LONG));
-    GRPC_CLOSURE_SCHED(&sp->do_read_closure, GRPC_ERROR_NONE);
+    GRPC_CLOSURE_SCHED(&do_read_closure_, GRPC_ERROR_NONE);
   } else {
     /* Finish reading all the packets, re-arm the notification event so we can
      * get another chance to read. Or fd already shutdown, re-arm to get a
      * notification with shutdown error. */
-    grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
+    grpc_fd_notify_on_read(emfd_, &read_closure_);
   }
-  gpr_mu_unlock(&sp->server->mu);
 }
 
+// static
 // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback interface.
-void fd_notify_on_write_wrapper(void* arg, grpc_error* error) {
-  grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg);
-  gpr_mu_lock(&sp->server->mu);
-  if (!sp->notify_on_write_armed) {
-    grpc_fd_notify_on_write(sp->emfd, &sp->write_closure);
-    sp->notify_on_write_armed = true;
+void GrpcUdpListener::fd_notify_on_write_wrapper(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  gpr_mu_lock(sp->mutex());
+  if (!sp->notify_on_write_armed_) {
+    grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_);
+    sp->notify_on_write_armed_ = true;
   }
-  gpr_mu_unlock(&sp->server->mu);
+  gpr_mu_unlock(sp->mutex());
 }
 
-static void do_write(void* arg, grpc_error* error) {
-  grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg);
-  gpr_mu_lock(&sp->server->mu);
-  if (sp->already_shutdown) {
+// static
+void GrpcUdpListener::do_write(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  gpr_mu_lock(sp->mutex());
+  if (sp->already_shutdown_) {
     // If fd has been shutdown, don't write any more and re-arm notification.
-    grpc_fd_notify_on_write(sp->emfd, &sp->write_closure);
+    grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_);
   } else {
-    sp->notify_on_write_armed = false;
+    sp->notify_on_write_armed_ = false;
     /* Tell the registered callback that the socket is writeable. */
-    GPR_ASSERT(sp->write_cb && error == GRPC_ERROR_NONE);
-    GRPC_CLOSURE_INIT(&sp->notify_on_write_closure, fd_notify_on_write_wrapper,
+    GPR_ASSERT(error == GRPC_ERROR_NONE);
+    GRPC_CLOSURE_INIT(&sp->notify_on_write_closure_, fd_notify_on_write_wrapper,
                       arg, grpc_schedule_on_exec_ctx);
-    sp->write_cb(sp->emfd, sp->server->user_data, &sp->notify_on_write_closure);
+    sp->udp_handler_->OnCanWrite(sp->server_->user_data,
+                                 &sp->notify_on_write_closure_);
   }
-  gpr_mu_unlock(&sp->server->mu);
+  gpr_mu_unlock(sp->mutex());
 }
 
-static void on_write(void* arg, grpc_error* error) {
-  grpc_udp_listener* sp = static_cast<grpc_udp_listener*>(arg);
+// static
+void GrpcUdpListener::on_write(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  sp->OnCanWrite(error, arg);
+}
 
-  gpr_mu_lock(&sp->server->mu);
+void GrpcUdpListener::OnCanWrite(grpc_error* error, void* do_write_arg) {
   if (error != GRPC_ERROR_NONE) {
-    if (0 == --sp->server->active_ports && sp->server->shutdown) {
-      gpr_mu_unlock(&sp->server->mu);
-      deactivated_all_ports(sp->server);
+    gpr_mu_lock(&server_->mu);
+    if (0 == --server_->active_ports && server_->shutdown) {
+      gpr_mu_unlock(&server_->mu);
+      deactivated_all_ports(server_);
     } else {
-      gpr_mu_unlock(&sp->server->mu);
+      gpr_mu_unlock(&server_->mu);
     }
     return;
   }
 
   /* Schedule actual write in another thread. */
-  GRPC_CLOSURE_INIT(&sp->do_write_closure, do_write, arg,
+  GRPC_CLOSURE_INIT(&do_write_closure_, do_write, do_write_arg,
                     grpc_executor_scheduler(GRPC_EXECUTOR_LONG));
 
-  GRPC_CLOSURE_SCHED(&sp->do_write_closure, GRPC_ERROR_NONE);
-  gpr_mu_unlock(&sp->server->mu);
+  GRPC_CLOSURE_SCHED(&do_write_closure_, GRPC_ERROR_NONE);
 }
 
 static int add_socket_to_server(grpc_udp_server* s, int fd,
                                 const grpc_resolved_address* addr,
-                                int rcv_buf_size, int snd_buf_size,
-                                grpc_udp_server_start_cb start_cb,
-                                grpc_udp_server_read_cb read_cb,
-                                grpc_udp_server_write_cb write_cb,
-                                grpc_udp_server_orphan_cb orphan_cb) {
-  grpc_udp_listener* sp;
-  int port;
-  char* addr_str;
-  char* name;
+                                int rcv_buf_size, int snd_buf_size) {
+  gpr_log(GPR_DEBUG, "add socket %d to server", fd);
 
-  port =
+  int port =
       prepare_socket(s->socket_factory, fd, addr, rcv_buf_size, snd_buf_size);
   if (port >= 0) {
-    grpc_sockaddr_to_string(&addr_str, addr, 1);
-    gpr_asprintf(&name, "udp-server-listener:%s", addr_str);
-    gpr_free(addr_str);
     gpr_mu_lock(&s->mu);
-    s->nports++;
-    sp = static_cast<grpc_udp_listener*>(gpr_malloc(sizeof(grpc_udp_listener)));
-    sp->next = nullptr;
-    if (s->head == nullptr) {
-      s->head = sp;
-    } else {
-      s->tail->next = sp;
-    }
-    s->tail = sp;
-    sp->server = s;
-    sp->fd = fd;
-    sp->emfd = grpc_fd_create(fd, name);
-    memcpy(&sp->addr, addr, sizeof(grpc_resolved_address));
-    sp->read_cb = read_cb;
-    sp->write_cb = write_cb;
-    sp->orphan_cb = orphan_cb;
-    sp->start_cb = start_cb;
-    sp->orphan_notified = false;
-    sp->already_shutdown = false;
-    GPR_ASSERT(sp->emfd);
+    s->listeners.emplace_back(s, fd, addr);
+    gpr_log(GPR_DEBUG,
+            "add socket %d to server for port %d, %zu listener(s) in total", fd,
+            port, s->listeners.size());
     gpr_mu_unlock(&s->mu);
-    gpr_free(name);
   }
-
   return port;
 }
 
 int grpc_udp_server_add_port(grpc_udp_server* s,
                              const grpc_resolved_address* addr,
                              int rcv_buf_size, int snd_buf_size,
-                             grpc_udp_server_start_cb start_cb,
-                             grpc_udp_server_read_cb read_cb,
-                             grpc_udp_server_write_cb write_cb,
-                             grpc_udp_server_orphan_cb orphan_cb) {
-  grpc_udp_listener* sp;
+                             GrpcUdpHandlerFactory* handler_factory) {
   int allocated_port1 = -1;
   int allocated_port2 = -1;
   int fd;
@@ -534,10 +574,10 @@
   /* Check if this is a wildcard port, and if so, try to keep the port the same
      as some previously created listener. */
   if (grpc_sockaddr_get_port(addr) == 0) {
-    for (sp = s->head; sp; sp = sp->next) {
+    for (size_t i = 0; i < s->listeners.size(); ++i) {
       sockname_temp.len = sizeof(struct sockaddr_storage);
       if (0 ==
-          getsockname(sp->fd,
+          getsockname(s->listeners[i].fd(),
                       reinterpret_cast<struct sockaddr*>(sockname_temp.addr),
                       reinterpret_cast<socklen_t*>(&sockname_temp.len))) {
         port = grpc_sockaddr_get_port(&sockname_temp);
@@ -557,6 +597,7 @@
     addr = &addr6_v4mapped;
   }
 
+  s->handler_factory = handler_factory;
   /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
   if (grpc_sockaddr_is_wildcard(addr, &port)) {
     grpc_sockaddr_make_wildcards(port, &wild4, &wild6);
@@ -567,8 +608,7 @@
     GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory(
         s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd));
     allocated_port1 =
-        add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size, start_cb,
-                             read_cb, write_cb, orphan_cb);
+        add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size);
     if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
       goto done;
     }
@@ -591,8 +631,7 @@
     addr = &addr4_copy;
   }
   allocated_port2 =
-      add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size, start_cb,
-                           read_cb, write_cb, orphan_cb);
+      add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size);
 
 done:
   gpr_free(allocated_addr);
@@ -600,52 +639,55 @@
 }
 
 int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index) {
-  grpc_udp_listener* sp;
-  if (port_index >= s->nports) {
+  if (port_index >= s->listeners.size()) {
     return -1;
   }
 
-  for (sp = s->head; sp && port_index != 0; sp = sp->next) {
-    --port_index;
-  }
-  GPR_ASSERT(sp);  // if this fails, our check earlier was bogus
-  return sp->fd;
+  return s->listeners[port_index].fd();
 }
 
 void grpc_udp_server_start(grpc_udp_server* s, grpc_pollset** pollsets,
                            size_t pollset_count, void* user_data) {
   gpr_log(GPR_DEBUG, "grpc_udp_server_start");
-  size_t i;
   gpr_mu_lock(&s->mu);
-  grpc_udp_listener* sp;
   GPR_ASSERT(s->active_ports == 0);
   s->pollsets = pollsets;
   s->user_data = user_data;
 
-  sp = s->head;
-  while (sp != nullptr) {
-    sp->start_cb(sp->emfd, sp->server->user_data);
-    for (i = 0; i < pollset_count; i++) {
-      grpc_pollset_add_fd(pollsets[i], sp->emfd);
-    }
-    GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp,
-                      grpc_schedule_on_exec_ctx);
-    grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
-
-    GRPC_CLOSURE_INIT(&sp->write_closure, on_write, sp,
-                      grpc_schedule_on_exec_ctx);
-    sp->notify_on_write_armed = true;
-    grpc_fd_notify_on_write(sp->emfd, &sp->write_closure);
-
-    /* Registered for both read and write callbacks: increment active_ports
-     * twice to account for this, and delay free-ing of memory until both
-     * on_read and on_write have fired. */
-    s->active_ports += 2;
-
-    sp = sp->next;
+  for (size_t i = 0; i < s->listeners.size(); ++i) {
+    s->listeners[i].StartListening(pollsets, pollset_count, s->handler_factory);
   }
 
   gpr_mu_unlock(&s->mu);
 }
 
+void GrpcUdpListener::StartListening(grpc_pollset** pollsets,
+                                     size_t pollset_count,
+                                     GrpcUdpHandlerFactory* handler_factory) {
+  gpr_mu_lock(&mutex_);
+  handler_factory_ = handler_factory;
+  udp_handler_ = handler_factory->CreateUdpHandler(emfd_, server_->user_data);
+  for (size_t i = 0; i < pollset_count; i++) {
+    grpc_pollset_add_fd(pollsets[i], emfd_);
+  }
+  GRPC_CLOSURE_INIT(&read_closure_, on_read, this, grpc_schedule_on_exec_ctx);
+  grpc_fd_notify_on_read(emfd_, &read_closure_);
+
+  GRPC_CLOSURE_INIT(&write_closure_, on_write, this, grpc_schedule_on_exec_ctx);
+  notify_on_write_armed_ = true;
+  grpc_fd_notify_on_write(emfd_, &write_closure_);
+
+  /* Registered for both read and write callbacks: increment active_ports
+   * twice to account for this, and delay free-ing of memory until both
+   * on_read and on_write have fired. */
+  server_->active_ports += 2;
+  gpr_mu_unlock(&mutex_);
+}
+
+void GrpcUdpListener::OnDestroy() {
+  if (udp_handler_ != nullptr) {
+    handler_factory_->DestroyUdpHandler(udp_handler_);
+  }
+}
+
 #endif
diff --git a/src/core/lib/iomgr/udp_server.h b/src/core/lib/iomgr/udp_server.h
index c1aa49f..4e384d2 100644
--- a/src/core/lib/iomgr/udp_server.h
+++ b/src/core/lib/iomgr/udp_server.h
@@ -19,6 +19,9 @@
 #ifndef GRPC_CORE_LIB_IOMGR_UDP_SERVER_H
 #define GRPC_CORE_LIB_IOMGR_UDP_SERVER_H
 
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/abstract.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/resolve_address.h"
@@ -30,22 +33,46 @@
 /* Forward decl of grpc_udp_server */
 typedef struct grpc_udp_server grpc_udp_server;
 
-/* Called when grpc server starts to listening on the grpc_fd. */
-typedef void (*grpc_udp_server_start_cb)(grpc_fd* emfd, void* user_data);
+/* An interface associated with a socket. udp server delivers I/O event on that
+ * socket to the subclass of this interface which is created through
+ * GrpcUdpHandlerFactory.
+ * Its implementation should do the real IO work, e.g. read packet and write. */
+class GrpcUdpHandler {
+ public:
+  GrpcUdpHandler(grpc_fd* emfd, void* user_data) {}
+  virtual ~GrpcUdpHandler() {}
 
-/* Called when data is available to read from the socket.
- * Return true if there is more data to read from fd. */
-typedef bool (*grpc_udp_server_read_cb)(grpc_fd* emfd);
+  // Interfaces to be implemented by subclasses to do the actual setup/tear down
+  // or I/O.
 
-/* Called when the socket is writeable. The given closure should be scheduled
- * when the socket becomes blocked next time. */
-typedef void (*grpc_udp_server_write_cb)(grpc_fd* emfd, void* user_data,
-                                         grpc_closure* notify_on_write_closure);
+  // Called when data is available to read from the socket. Returns true if
+  // there is more data to read after this call.
+  virtual bool Read() GRPC_ABSTRACT;
+  // Called when socket becomes write unblocked. The given closure should be
+  // scheduled when the socket becomes blocked next time.
+  virtual void OnCanWrite(void* user_data,
+                          grpc_closure* notify_on_write_closure) GRPC_ABSTRACT;
+  // Called before the gRPC FD is orphaned. Notify udp server to continue
+  // orphaning fd by scheduling the given closure, afterwards the associated fd
+  // will be closed.
+  virtual void OnFdAboutToOrphan(grpc_closure* orphan_fd_closure,
+                                 void* user_data) GRPC_ABSTRACT;
 
-/* Called when the grpc_fd is about to be orphaned (and the FD closed). */
-typedef void (*grpc_udp_server_orphan_cb)(grpc_fd* emfd,
-                                          grpc_closure* shutdown_fd_callback,
-                                          void* user_data);
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+class GrpcUdpHandlerFactory {
+ public:
+  virtual ~GrpcUdpHandlerFactory() {}
+  /* Called when start to listen on a socket.
+   * Return an instance of the implementation of GrpcUdpHandler interface which
+   * will process I/O events for this socket from now on. */
+  virtual GrpcUdpHandler* CreateUdpHandler(grpc_fd* emfd,
+                                           void* user_data) GRPC_ABSTRACT;
+  virtual void DestroyUdpHandler(GrpcUdpHandler* handler) GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
 
 /* Create a server, initially not bound to any ports */
 grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args);
@@ -69,10 +96,7 @@
 int grpc_udp_server_add_port(grpc_udp_server* s,
                              const grpc_resolved_address* addr,
                              int rcv_buf_size, int snd_buf_size,
-                             grpc_udp_server_start_cb start_cb,
-                             grpc_udp_server_read_cb read_cb,
-                             grpc_udp_server_write_cb write_cb,
-                             grpc_udp_server_orphan_cb orphan_cb);
+                             GrpcUdpHandlerFactory* handler_factory);
 
 void grpc_udp_server_destroy(grpc_udp_server* server, grpc_closure* on_done);
 
diff --git a/src/core/lib/iomgr/unix_sockets_posix.cc b/src/core/lib/iomgr/unix_sockets_posix.cc
index b603916..8d252fd 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.cc
+++ b/src/core/lib/iomgr/unix_sockets_posix.cc
@@ -15,6 +15,8 @@
  * limitations under the License.
  *
  */
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_HAVE_UNIX_SOCKET
diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h
index 1c079e6..917d032 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.h
+++ b/src/core/lib/iomgr/unix_sockets_posix.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #include <grpc/support/string_util.h>
diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.cc b/src/core/lib/iomgr/unix_sockets_posix_noop.cc
index fbd9602..dfab3e0 100644
--- a/src/core/lib/iomgr/unix_sockets_posix_noop.cc
+++ b/src/core/lib/iomgr/unix_sockets_posix_noop.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
 
 #ifndef GRPC_HAVE_UNIX_SOCKET
diff --git a/src/core/lib/iomgr/wakeup_fd_cv.cc b/src/core/lib/iomgr/wakeup_fd_cv.cc
index 41d35cb..74faa63 100644
--- a/src/core/lib/iomgr/wakeup_fd_cv.cc
+++ b/src/core/lib/iomgr/wakeup_fd_cv.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_WAKEUP_FD
@@ -30,8 +32,8 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 
 #define MAX_TABLE_RESIZE 256
 
diff --git a/src/core/lib/iomgr/wakeup_fd_cv.h b/src/core/lib/iomgr/wakeup_fd_cv.h
index 399620a..86365f0 100644
--- a/src/core/lib/iomgr/wakeup_fd_cv.h
+++ b/src/core/lib/iomgr/wakeup_fd_cv.h
@@ -33,6 +33,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H
 #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/iomgr/ev_posix.h"
diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.cc b/src/core/lib/iomgr/wakeup_fd_eventfd.cc
index 421ac55..dcf7dab 100644
--- a/src/core/lib/iomgr/wakeup_fd_eventfd.cc
+++ b/src/core/lib/iomgr/wakeup_fd_eventfd.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_LINUX_EVENTFD
diff --git a/src/core/lib/iomgr/wakeup_fd_nospecial.cc b/src/core/lib/iomgr/wakeup_fd_nospecial.cc
index c2b525a..6477892 100644
--- a/src/core/lib/iomgr/wakeup_fd_nospecial.cc
+++ b/src/core/lib/iomgr/wakeup_fd_nospecial.cc
@@ -21,6 +21,8 @@
  * systems without anything better than pipe.
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD
diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.cc b/src/core/lib/iomgr/wakeup_fd_pipe.cc
index 05d69dc..cb17390 100644
--- a/src/core/lib/iomgr/wakeup_fd_pipe.cc
+++ b/src/core/lib/iomgr/wakeup_fd_pipe.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_WAKEUP_FD
diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.h b/src/core/lib/iomgr/wakeup_fd_pipe.h
index 326a0c4..1756976 100644
--- a/src/core/lib/iomgr/wakeup_fd_pipe.h
+++ b/src/core/lib/iomgr/wakeup_fd_pipe.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H
 #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 
 extern const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable;
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.cc b/src/core/lib/iomgr/wakeup_fd_posix.cc
index e8de208..b5b8b37 100644
--- a/src/core/lib/iomgr/wakeup_fd_posix.cc
+++ b/src/core/lib/iomgr/wakeup_fd_posix.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/port.h"
 
 #ifdef GRPC_POSIX_WAKEUP_FD
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.h b/src/core/lib/iomgr/wakeup_fd_posix.h
index a9584d0..670c319 100644
--- a/src/core/lib/iomgr/wakeup_fd_posix.h
+++ b/src/core/lib/iomgr/wakeup_fd_posix.h
@@ -47,6 +47,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/error.h"
 
 void grpc_wakeup_fd_global_init(void);
diff --git a/src/core/lib/json/json.cc b/src/core/lib/json/json.cc
index adf35b9..2141db4 100644
--- a/src/core/lib/json/json.cc
+++ b/src/core/lib/json/json.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h
index bbd4302..3a62ef9 100644
--- a/src/core/lib/json/json.h
+++ b/src/core/lib/json/json.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_JSON_JSON_H
 #define GRPC_CORE_LIB_JSON_JSON_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdlib.h>
 
 #include "src/core/lib/json/json_common.h"
diff --git a/src/core/lib/json/json_reader.cc b/src/core/lib/json/json_reader.cc
index 6dadea5..819572e 100644
--- a/src/core/lib/json/json_reader.cc
+++ b/src/core/lib/json/json_reader.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include <string.h>
-
 #include <grpc/support/port_platform.h>
 
+#include <string.h>
+
 #include <grpc/support/log.h>
 
 #include "src/core/lib/json/json_reader.h"
diff --git a/src/core/lib/json/json_reader.h b/src/core/lib/json/json_reader.h
index 03185cb..78f7ad9 100644
--- a/src/core/lib/json/json_reader.h
+++ b/src/core/lib/json/json_reader.h
@@ -20,6 +20,7 @@
 #define GRPC_CORE_LIB_JSON_JSON_READER_H
 
 #include <grpc/support/port_platform.h>
+
 #include "src/core/lib/json/json_common.h"
 
 typedef enum {
diff --git a/src/core/lib/json/json_string.cc b/src/core/lib/json/json_string.cc
index 8200900..4f9175b 100644
--- a/src/core/lib/json/json_string.cc
+++ b/src/core/lib/json/json_string.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdlib.h>
 #include <string.h>
 
diff --git a/src/core/lib/json/json_writer.cc b/src/core/lib/json/json_writer.cc
index 6d442b8..7bbdccc 100644
--- a/src/core/lib/json/json_writer.cc
+++ b/src/core/lib/json/json_writer.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include <string.h>
-
 #include <grpc/support/port_platform.h>
 
+#include <string.h>
+
 #include "src/core/lib/json/json_writer.h"
 
 static void json_writer_output_char(grpc_json_writer* writer, char c) {
diff --git a/src/core/lib/json/json_writer.h b/src/core/lib/json/json_writer.h
index a4f2d4d..ba0bedd 100644
--- a/src/core/lib/json/json_writer.h
+++ b/src/core/lib/json/json_writer.h
@@ -31,6 +31,8 @@
 #ifndef GRPC_CORE_LIB_JSON_JSON_WRITER_H
 #define GRPC_CORE_LIB_JSON_JSON_WRITER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdlib.h>
 
 #include "src/core/lib/json/json_common.h"
diff --git a/src/core/lib/profiling/basic_timers.cc b/src/core/lib/profiling/basic_timers.cc
index ca6705a..43384fd 100644
--- a/src/core/lib/profiling/basic_timers.cc
+++ b/src/core/lib/profiling/basic_timers.cc
@@ -30,7 +30,7 @@
 #include <string.h>
 
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 
 typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type;
 
@@ -68,7 +68,7 @@
 static gpr_timer_log_list g_in_progress_logs;
 static gpr_timer_log_list g_done_logs;
 static int g_shutdown;
-static gpr_thd_id g_writing_thread;
+static grpc_core::Thread* g_writing_thread;
 static __thread int g_thread_id;
 static int g_next_thread_id;
 static int g_writing_enabled = 1;
@@ -182,7 +182,8 @@
   g_shutdown = 1;
   pthread_cond_signal(&g_cv);
   pthread_mutex_unlock(&g_mu);
-  gpr_thd_join(g_writing_thread);
+  g_writing_thread->Join();
+  grpc_core::Delete(g_writing_thread);
 
   gpr_log(GPR_INFO, "flushing logs");
 
@@ -201,10 +202,8 @@
 }
 
 static void init_output() {
-  gpr_thd_options options = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&options);
-  GPR_ASSERT(gpr_thd_new(&g_writing_thread, "timer_output_thread",
-                         writing_thread, NULL, &options));
+  g_writing_thread = grpc_core::New<grpc_core::Thread>("timer_output_thread",
+                                                       writing_thread, nullptr);
   atexit(finish_writing);
 }
 
diff --git a/src/core/lib/security/context/security_context.cc b/src/core/lib/security/context/security_context.cc
index c7e212b..14051a3 100644
--- a/src/core/lib/security/context/security_context.cc
+++ b/src/core/lib/security/context/security_context.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include "src/core/lib/channel/channel_args.h"
diff --git a/src/core/lib/security/context/security_context.h b/src/core/lib/security/context/security_context.h
index 34f8c24..e782e4f 100644
--- a/src/core/lib/security/context/security_context.h
+++ b/src/core/lib/security/context/security_context.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H
 #define GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/security/credentials/credentials.h"
 
diff --git a/src/core/lib/security/credentials/alts/alts_credentials.cc b/src/core/lib/security/credentials/alts/alts_credentials.cc
new file mode 100644
index 0000000..fa05d90
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/alts_credentials.cc
@@ -0,0 +1,119 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/alts/alts_credentials.h"
+
+#include <cstring>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+#include "src/core/lib/security/security_connector/alts_security_connector.h"
+
+#define GRPC_CREDENTIALS_TYPE_ALTS "Alts"
+#define GRPC_ALTS_HANDSHAKER_SERVICE_URL "metadata.google.internal:8080"
+
+static void alts_credentials_destruct(grpc_channel_credentials* creds) {
+  grpc_alts_credentials* alts_creds =
+      reinterpret_cast<grpc_alts_credentials*>(creds);
+  grpc_alts_credentials_options_destroy(alts_creds->options);
+  gpr_free(alts_creds->handshaker_service_url);
+}
+
+static void alts_server_credentials_destruct(grpc_server_credentials* creds) {
+  grpc_alts_server_credentials* alts_creds =
+      reinterpret_cast<grpc_alts_server_credentials*>(creds);
+  grpc_alts_credentials_options_destroy(alts_creds->options);
+  gpr_free(alts_creds->handshaker_service_url);
+}
+
+static grpc_security_status alts_create_security_connector(
+    grpc_channel_credentials* creds,
+    grpc_call_credentials* request_metadata_creds, const char* target_name,
+    const grpc_channel_args* args, grpc_channel_security_connector** sc,
+    grpc_channel_args** new_args) {
+  return grpc_alts_channel_security_connector_create(
+      creds, request_metadata_creds, target_name, sc);
+}
+
+static grpc_security_status alts_server_create_security_connector(
+    grpc_server_credentials* creds, grpc_server_security_connector** sc) {
+  return grpc_alts_server_security_connector_create(creds, sc);
+}
+
+static const grpc_channel_credentials_vtable alts_credentials_vtable = {
+    alts_credentials_destruct, alts_create_security_connector,
+    /*duplicate_without_call_credentials=*/nullptr};
+
+static const grpc_server_credentials_vtable alts_server_credentials_vtable = {
+    alts_server_credentials_destruct, alts_server_create_security_connector};
+
+grpc_channel_credentials* grpc_alts_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts) {
+  if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) {
+    return nullptr;
+  }
+  auto creds = static_cast<grpc_alts_credentials*>(
+      gpr_zalloc(sizeof(grpc_alts_credentials)));
+  creds->options = grpc_alts_credentials_options_copy(options);
+  creds->handshaker_service_url =
+      handshaker_service_url == nullptr
+          ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL)
+          : gpr_strdup(handshaker_service_url);
+  creds->base.type = GRPC_CREDENTIALS_TYPE_ALTS;
+  creds->base.vtable = &alts_credentials_vtable;
+  gpr_ref_init(&creds->base.refcount, 1);
+  return &creds->base;
+}
+
+grpc_server_credentials* grpc_alts_server_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts) {
+  if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) {
+    return nullptr;
+  }
+  auto creds = static_cast<grpc_alts_server_credentials*>(
+      gpr_zalloc(sizeof(grpc_alts_server_credentials)));
+  creds->options = grpc_alts_credentials_options_copy(options);
+  creds->handshaker_service_url =
+      handshaker_service_url == nullptr
+          ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL)
+          : gpr_strdup(handshaker_service_url);
+  creds->base.type = GRPC_CREDENTIALS_TYPE_ALTS;
+  creds->base.vtable = &alts_server_credentials_vtable;
+  gpr_ref_init(&creds->base.refcount, 1);
+  return &creds->base;
+}
+
+grpc_channel_credentials* grpc_alts_credentials_create(
+    const grpc_alts_credentials_options* options) {
+  return grpc_alts_credentials_create_customized(
+      options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false);
+}
+
+grpc_server_credentials* grpc_alts_server_credentials_create(
+    const grpc_alts_credentials_options* options) {
+  return grpc_alts_server_credentials_create_customized(
+      options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false);
+}
diff --git a/src/core/lib/security/credentials/alts/alts_credentials.h b/src/core/lib/security/credentials/alts/alts_credentials.h
new file mode 100644
index 0000000..621789c
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/alts_credentials.h
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+/* Main struct for grpc ALTS channel credential. */
+typedef struct grpc_alts_credentials {
+  grpc_channel_credentials base;
+  grpc_alts_credentials_options* options;
+  char* handshaker_service_url;
+} grpc_alts_credentials;
+
+/* Main struct for grpc ALTS server credential. */
+typedef struct grpc_alts_server_credentials {
+  grpc_server_credentials base;
+  grpc_alts_credentials_options* options;
+  char* handshaker_service_url;
+} grpc_alts_server_credentials;
+
+/**
+ * This method creates an ALTS channel credential object.
+ *
+ * - options: grpc ALTS credentials options instance for client.
+ *
+ * It returns the created ALTS channel credential object.
+ */
+grpc_channel_credentials* grpc_alts_credentials_create(
+    const grpc_alts_credentials_options* options);
+
+/**
+ * This method creates an ALTS server credential object.
+ *
+ * - options: grpc ALTS credentials options instance for server.
+ *
+ * It returns the created ALTS server credential object.
+ */
+grpc_server_credentials* grpc_alts_server_credentials_create(
+    const grpc_alts_credentials_options* options);
+
+/**
+ * This method creates an ALTS channel credential object with customized
+ * information provided by caller.
+ *
+ * - options: grpc ALTS credentials options instance for client.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port". If it's nullptr, the address of default metadata server will
+ *   be used.
+ * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted
+ *   mode. This mode can be enabled when we are sure ALTS is running on GCP or
+ * for testing purpose.
+ *
+ * It returns nullptr if the flag is disabled AND ALTS is not running on GCP.
+ * Otherwise, it returns the created credential object.
+ */
+
+grpc_channel_credentials* grpc_alts_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts);
+
+/**
+ * This method creates an ALTS server credential object with customized
+ * information provided by caller.
+ *
+ * - options: grpc ALTS credentials options instance for server.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port". If it's nullptr, the address of default metadata server will
+ *   be used.
+ * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted
+ *   mode. This mode can be enabled when we are sure ALTS is running on GCP or
+ * for testing purpose.
+ *
+ * It returns nullptr if the flag is disabled and ALTS is not running on GCP.
+ * Otherwise, it returns the created credential object.
+ */
+grpc_server_credentials* grpc_alts_server_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment.cc b/src/core/lib/security/credentials/alts/check_gcp_environment.cc
new file mode 100644
index 0000000..9680787
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/check_gcp_environment.cc
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+const size_t kBiosDataBufferSize = 256;
+
+static char* trim(const char* src) {
+  if (src == nullptr) {
+    return nullptr;
+  }
+  char* des = nullptr;
+  size_t start = 0, end = strlen(src) - 1;
+  /* find the last character that is not a whitespace. */
+  while (end != 0 && isspace(src[end])) {
+    end--;
+  }
+  /* find the first character that is not a whitespace. */
+  while (start < strlen(src) && isspace(src[start])) {
+    start++;
+  }
+  if (start <= end) {
+    des = static_cast<char*>(
+        gpr_zalloc(sizeof(char) * (end - start + 2 /* '\0' */)));
+    memcpy(des, src + start, end - start + 1);
+  }
+  return des;
+}
+
+namespace grpc_core {
+namespace internal {
+
+char* read_bios_file(const char* bios_file) {
+  FILE* fp = fopen(bios_file, "r");
+  if (!fp) {
+    gpr_log(GPR_ERROR, "BIOS data file cannot be opened.");
+    return nullptr;
+  }
+  char buf[kBiosDataBufferSize + 1];
+  size_t ret = fread(buf, sizeof(char), kBiosDataBufferSize, fp);
+  buf[ret] = '\0';
+  char* trimmed_buf = trim(buf);
+  fclose(fp);
+  return trimmed_buf;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment.h b/src/core/lib/security/credentials/alts/check_gcp_environment.h
new file mode 100644
index 0000000..aea4cea
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/check_gcp_environment.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * This method is a helper function that reads a file containing system bios
+ * data. Exposed for testing only.
+ *
+ * - bios_file: a file containing BIOS data used to determine GCE tenancy
+ *   information.
+ *
+ * It returns a buffer containing the data read from the file.
+ */
+char* read_bios_file(const char* bios_file);
+
+/**
+ * This method checks if system BIOS data contains Google-specific phrases.
+ * Exposed for testing only.
+ *
+ * - bios_data: a buffer containing system BIOS data.
+ *
+ * It returns true if the BIOS data contains Google-specific phrases, and false
+ * otherwise.
+ */
+bool check_bios_data(const char* bios_data);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+/**
+ * This method checks if a VM (Windows or Linux) is running within Google
+ * compute Engine (GCE) or not. It returns true if the VM is running in GCE and
+ * false otherwise.
+ */
+bool grpc_alts_is_running_on_gcp();
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H */
diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
new file mode 100644
index 0000000..7c4d7a7
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/sync.h>
+
+#include <string.h>
+
+#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google"
+#define GRPC_ALTS_EXPECT_NAME_GCE "Google Compute Engine"
+#define GRPC_ALTS_PRODUCT_NAME_FILE "/sys/class/dmi/id/product_name"
+
+static bool g_compute_engine_detection_done = false;
+static bool g_is_on_compute_engine = false;
+static gpr_mu g_mu;
+static gpr_once g_once = GPR_ONCE_INIT;
+
+namespace grpc_core {
+namespace internal {
+
+bool check_bios_data(const char* bios_data_file) {
+  char* bios_data = read_bios_file(bios_data_file);
+  bool result = (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE)) ||
+                (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GCE));
+  gpr_free(bios_data);
+  return result;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+static void init_mu(void) { gpr_mu_init(&g_mu); }
+
+bool grpc_alts_is_running_on_gcp() {
+  gpr_once_init(&g_once, init_mu);
+  gpr_mu_lock(&g_mu);
+  if (!g_compute_engine_detection_done) {
+    g_is_on_compute_engine =
+        grpc_core::internal::check_bios_data(GRPC_ALTS_PRODUCT_NAME_FILE);
+    g_compute_engine_detection_done = true;
+  }
+  gpr_mu_unlock(&g_mu);
+  return g_is_on_compute_engine;
+}
+
+#endif  // GPR_LINUX
diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
new file mode 100644
index 0000000..d97681b
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS)
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <grpc/support/log.h>
+
+bool grpc_alts_is_running_on_gcp() {
+  gpr_log(GPR_ERROR,
+          "Platforms other than Linux and Windows are not supported");
+  return false;
+}
+
+#endif  // !defined(LINUX) && !defined(GPR_WINDOWS)
diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
new file mode 100644
index 0000000..55efe0e
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <shellapi.h>
+#include <stdio.h>
+#include <tchar.h>
+#include <windows.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google"
+#define GRPC_ALTS_WINDOWS_CHECK_COMMAND "powershell.exe"
+#define GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS \
+  "(Get-WmiObject -Class Win32_BIOS).Manufacturer"
+#define GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE "windows_bios.data"
+
+const size_t kBiosDataBufferSize = 256;
+
+static bool g_compute_engine_detection_done = false;
+static bool g_is_on_compute_engine = false;
+static gpr_mu g_mu;
+static gpr_once g_once = GPR_ONCE_INIT;
+
+namespace grpc_core {
+namespace internal {
+
+bool check_bios_data(const char* bios_data_file) {
+  char* bios_data = read_bios_file(bios_data_file);
+  bool result = !strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE);
+  remove(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE);
+  gpr_free(bios_data);
+  return result;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+static void init_mu(void) { gpr_mu_init(&g_mu); }
+
+static bool run_powershell() {
+  SECURITY_ATTRIBUTES sa;
+  sa.nLength = sizeof(sa);
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = TRUE;
+  HANDLE h = CreateFile(_T(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE), GENERIC_WRITE,
+                        FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_ALWAYS,
+                        FILE_ATTRIBUTE_NORMAL, NULL);
+  if (h == INVALID_HANDLE_VALUE) {
+    gpr_log(GPR_ERROR, "CreateFile failed (%d).", GetLastError());
+    return false;
+  }
+  PROCESS_INFORMATION pi;
+  STARTUPINFO si;
+  DWORD flags = CREATE_NO_WINDOW;
+  ZeroMemory(&pi, sizeof(pi));
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  si.dwFlags |= STARTF_USESTDHANDLES;
+  si.hStdInput = NULL;
+  si.hStdError = h;
+  si.hStdOutput = h;
+  TCHAR cmd[kBiosDataBufferSize];
+  _sntprintf(cmd, kBiosDataBufferSize, _T("%s %s"),
+             _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND),
+             _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS));
+  if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si,
+                     &pi)) {
+    gpr_log(GPR_ERROR, "CreateProcess failed (%d).\n", GetLastError());
+    return false;
+  }
+  WaitForSingleObject(pi.hProcess, INFINITE);
+  CloseHandle(pi.hProcess);
+  CloseHandle(pi.hThread);
+  CloseHandle(h);
+  return true;
+}
+
+bool grpc_alts_is_running_on_gcp() {
+  gpr_once_init(&g_once, init_mu);
+  gpr_mu_lock(&g_mu);
+  if (!g_compute_engine_detection_done) {
+    g_is_on_compute_engine =
+        run_powershell() &&
+        grpc_core::internal::check_bios_data(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE);
+    g_compute_engine_detection_done = true;
+  }
+  gpr_mu_unlock(&g_mu);
+  return g_is_on_compute_engine;
+}
+
+#endif  // GPR_WINDOWS
diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
new file mode 100644
index 0000000..7d54e83
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+static grpc_alts_credentials_options* alts_client_options_copy(
+    const grpc_alts_credentials_options* options);
+
+static void alts_client_options_destroy(grpc_alts_credentials_options* options);
+
+static target_service_account* target_service_account_create(
+    const char* service_account) {
+  if (service_account == nullptr) {
+    return nullptr;
+  }
+  auto* sa = static_cast<target_service_account*>(
+      gpr_zalloc(sizeof(target_service_account)));
+  sa->data = gpr_strdup(service_account);
+  return sa;
+}
+
+bool grpc_alts_credentials_client_options_add_target_service_account(
+    grpc_alts_credentials_client_options* options,
+    const char* service_account) {
+  if (options == nullptr || service_account == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to "
+        "grpc_alts_credentials_client_options_add_target_service_account()");
+    return false;
+  }
+  target_service_account* node = target_service_account_create(service_account);
+  node->next = options->target_account_list_head;
+  options->target_account_list_head = node;
+  return true;
+}
+
+static void target_service_account_destroy(
+    target_service_account* service_account) {
+  if (service_account == nullptr) {
+    return;
+  }
+  gpr_free(service_account->data);
+  gpr_free(service_account);
+}
+
+static const grpc_alts_credentials_options_vtable vtable = {
+    alts_client_options_copy, alts_client_options_destroy};
+
+grpc_alts_credentials_options* grpc_alts_credentials_client_options_create() {
+  auto client_options = static_cast<grpc_alts_credentials_client_options*>(
+      gpr_zalloc(sizeof(grpc_alts_credentials_client_options)));
+  client_options->base.vtable = &vtable;
+  return &client_options->base;
+}
+
+static grpc_alts_credentials_options* alts_client_options_copy(
+    const grpc_alts_credentials_options* options) {
+  if (options == nullptr) {
+    return nullptr;
+  }
+  grpc_alts_credentials_options* new_options =
+      grpc_alts_credentials_client_options_create();
+  auto new_client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(new_options);
+  /* Copy target service accounts. */
+  target_service_account* prev = nullptr;
+  auto node =
+      (reinterpret_cast<const grpc_alts_credentials_client_options*>(options))
+          ->target_account_list_head;
+  while (node != nullptr) {
+    target_service_account* new_node =
+        target_service_account_create(node->data);
+    if (prev == nullptr) {
+      new_client_options->target_account_list_head = new_node;
+    } else {
+      prev->next = new_node;
+    }
+    prev = new_node;
+    node = node->next;
+  }
+  /* Copy rpc protocol versions. */
+  grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions,
+                                      &new_options->rpc_versions);
+  return new_options;
+}
+
+static void alts_client_options_destroy(
+    grpc_alts_credentials_options* options) {
+  if (options == nullptr) {
+    return;
+  }
+  auto* client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(options);
+  target_service_account* node = client_options->target_account_list_head;
+  while (node != nullptr) {
+    target_service_account* next_node = node->next;
+    target_service_account_destroy(node);
+    node = next_node;
+  }
+}
diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
new file mode 100644
index 0000000..d428171
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+grpc_alts_credentials_options* grpc_alts_credentials_options_copy(
+    const grpc_alts_credentials_options* options) {
+  if (options != nullptr && options->vtable != nullptr &&
+      options->vtable->copy != nullptr) {
+    return options->vtable->copy(options);
+  }
+  /* An error occurred. */
+  gpr_log(GPR_ERROR,
+          "Invalid arguments to grpc_alts_credentials_options_copy()");
+  return nullptr;
+}
+
+void grpc_alts_credentials_options_destroy(
+    grpc_alts_credentials_options* options) {
+  if (options != nullptr) {
+    if (options->vtable != nullptr && options->vtable->destruct != nullptr) {
+      options->vtable->destruct(options);
+    }
+    gpr_free(options);
+  }
+}
diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h
new file mode 100644
index 0000000..4e46d9f
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+/**
+ * Main interface for ALTS credentials options. The options will contain
+ * information that will be passed from grpc to TSI layer such as RPC protocol
+ * versions. ALTS client (channel) and server credentials will have their own
+ * implementation of this interface. The APIs listed in this header are
+ * thread-compatible.
+ */
+typedef struct grpc_alts_credentials_options grpc_alts_credentials_options;
+
+/* V-table for grpc_alts_credentials_options */
+typedef struct grpc_alts_credentials_options_vtable {
+  grpc_alts_credentials_options* (*copy)(
+      const grpc_alts_credentials_options* options);
+  void (*destruct)(grpc_alts_credentials_options* options);
+} grpc_alts_credentials_options_vtable;
+
+struct grpc_alts_credentials_options {
+  const struct grpc_alts_credentials_options_vtable* vtable;
+  grpc_gcp_rpc_protocol_versions rpc_versions;
+};
+
+typedef struct target_service_account {
+  struct target_service_account* next;
+  char* data;
+} target_service_account;
+
+/**
+ * Main struct for ALTS client credentials options. The options contain a
+ * a list of target service accounts (if specified) used for secure naming
+ * check.
+ */
+typedef struct grpc_alts_credentials_client_options {
+  grpc_alts_credentials_options base;
+  target_service_account* target_account_list_head;
+} grpc_alts_credentials_client_options;
+
+/**
+ * Main struct for ALTS server credentials options. The options currently
+ * do not contain any server-specific fields.
+ */
+typedef struct grpc_alts_credentials_server_options {
+  grpc_alts_credentials_options base;
+} grpc_alts_credentials_server_options;
+
+/**
+ * This method performs a deep copy on grpc_alts_credentials_options instance.
+ *
+ * - options: a grpc_alts_credentials_options instance that needs to be copied.
+ *
+ * It returns a new grpc_alts_credentials_options instance on success and NULL
+ * on failure.
+ */
+grpc_alts_credentials_options* grpc_alts_credentials_options_copy(
+    const grpc_alts_credentials_options* options);
+
+/**
+ * This method destroys a grpc_alts_credentials_options instance by
+ * de-allocating all of its occupied memory.
+ *
+ * - options: a grpc_alts_credentials_options instance that needs to be
+ *   destroyed.
+ */
+void grpc_alts_credentials_options_destroy(
+    grpc_alts_credentials_options* options);
+
+/* This method creates a grpc ALTS credentials client options instance. */
+grpc_alts_credentials_options* grpc_alts_credentials_client_options_create();
+
+/* This method creates a grpc ALTS credentials server options instance. */
+grpc_alts_credentials_options* grpc_alts_credentials_server_options_create();
+
+/**
+ * This method adds a target service account to grpc ALTS credentials client
+ * options instance.
+ *
+ * - options: grpc ALTS credentials client options instance.
+ * - service_account: service account of target endpoint.
+ *
+ * It returns true on success and false on failure.
+ */
+bool grpc_alts_credentials_client_options_add_target_service_account(
+    grpc_alts_credentials_client_options* options, const char* service_account);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H \
+        */
diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
new file mode 100644
index 0000000..62aa7a6
--- /dev/null
+++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+static grpc_alts_credentials_options* alts_server_options_copy(
+    const grpc_alts_credentials_options* options);
+
+static void alts_server_options_destroy(
+    grpc_alts_credentials_options* options) {}
+
+static const grpc_alts_credentials_options_vtable vtable = {
+    alts_server_options_copy, alts_server_options_destroy};
+
+grpc_alts_credentials_options* grpc_alts_credentials_server_options_create() {
+  grpc_alts_credentials_server_options* server_options =
+      static_cast<grpc_alts_credentials_server_options*>(
+          gpr_zalloc(sizeof(*server_options)));
+  server_options->base.vtable = &vtable;
+  return &server_options->base;
+}
+
+static grpc_alts_credentials_options* alts_server_options_copy(
+    const grpc_alts_credentials_options* options) {
+  if (options == nullptr) {
+    return nullptr;
+  }
+  grpc_alts_credentials_options* new_options =
+      grpc_alts_credentials_server_options_create();
+  /* Copy rpc protocol versions. */
+  grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions,
+                                      &new_options->rpc_versions);
+  return new_options;
+}
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.cc b/src/core/lib/security/credentials/composite/composite_credentials.cc
index ea2c4e2..b8f4092 100644
--- a/src/core/lib/security/credentials/composite/composite_credentials.cc
+++ b/src/core/lib/security/credentials/composite/composite_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/composite/composite_credentials.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.h b/src/core/lib/security/credentials/composite/composite_credentials.h
index 11990d3..a952ad5 100644
--- a/src/core/lib/security/credentials/composite/composite_credentials.h
+++ b/src/core/lib/security/credentials/composite/composite_credentials.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 typedef struct {
diff --git a/src/core/lib/security/credentials/credentials.cc b/src/core/lib/security/credentials/credentials.cc
index 17f7de5..c43cb44 100644
--- a/src/core/lib/security/credentials/credentials.cc
+++ b/src/core/lib/security/credentials/credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 #include <stdio.h>
diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h
index 4825b65..b1421e8 100644
--- a/src/core/lib/security/credentials/credentials.h
+++ b/src/core/lib/security/credentials/credentials.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
 #include <grpc/support/sync.h>
@@ -27,7 +29,7 @@
 #include "src/core/lib/http/httpcli.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/polling_entity.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 
 struct grpc_http_response;
 
diff --git a/src/core/lib/security/credentials/credentials_metadata.cc b/src/core/lib/security/credentials/credentials_metadata.cc
index 250e384..703de4a 100644
--- a/src/core/lib/security/credentials/credentials_metadata.cc
+++ b/src/core/lib/security/credentials/credentials_metadata.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.cc b/src/core/lib/security/credentials/fake/fake_credentials.cc
index 3b29db2..858ab6b 100644
--- a/src/core/lib/security/credentials/fake/fake_credentials.cc
+++ b/src/core/lib/security/credentials/fake/fake_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
 
 #include <string.h>
@@ -30,9 +32,6 @@
 
 /* -- Fake transport security credentials. -- */
 
-#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \
-  "grpc.fake_security.expected_targets"
-
 static grpc_security_status fake_transport_security_create_security_connector(
     grpc_channel_credentials* c, grpc_call_credentials* call_creds,
     const char* target, const grpc_channel_args* args,
@@ -87,11 +86,7 @@
     const grpc_channel_args* args) {
   const grpc_arg* expected_target_arg =
       grpc_channel_args_find(args, GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS);
-  if (expected_target_arg != nullptr &&
-      expected_target_arg->type == GRPC_ARG_STRING) {
-    return expected_target_arg->value.string;
-  }
-  return nullptr;
+  return grpc_channel_arg_get_string(expected_target_arg);
 }
 
 /* -- Metadata-only test credentials. -- */
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.h b/src/core/lib/security/credentials/fake/fake_credentials.h
index 0e9ff15..e89e6e2 100644
--- a/src/core/lib/security/credentials/fake/fake_credentials.h
+++ b/src/core/lib/security/credentials/fake/fake_credentials.h
@@ -19,8 +19,13 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
+#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \
+  "grpc.fake_security.expected_targets"
+
 /* -- Fake transport security credentials. -- */
 
 /* Creates a fake transport security credentials object for testing. */
diff --git a/src/core/lib/security/credentials/google_default/credentials_generic.cc b/src/core/lib/security/credentials/google_default/credentials_generic.cc
index 15ae9d6..10ff0f6 100644
--- a/src/core/lib/security/credentials/google_default/credentials_generic.cc
+++ b/src/core/lib/security/credentials/google_default/credentials_generic.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc
index 65455f9..70d4c3e 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.cc
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/iam/iam_credentials.cc b/src/core/lib/security/credentials/iam/iam_credentials.cc
index 7f19fbc..5d92fa8 100644
--- a/src/core/lib/security/credentials/iam/iam_credentials.cc
+++ b/src/core/lib/security/credentials/iam/iam_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/iam/iam_credentials.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/iam/iam_credentials.h b/src/core/lib/security/credentials/iam/iam_credentials.h
index 5e3cf65..a45710f 100644
--- a/src/core/lib/security/credentials/iam/iam_credentials.h
+++ b/src/core/lib/security/credentials/iam/iam_credentials.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 typedef struct {
diff --git a/src/core/lib/security/credentials/jwt/json_token.cc b/src/core/lib/security/credentials/jwt/json_token.cc
index f7c9f57..1c4827d 100644
--- a/src/core/lib/security/credentials/jwt/json_token.cc
+++ b/src/core/lib/security/credentials/jwt/json_token.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/jwt/json_token.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/jwt/json_token.h b/src/core/lib/security/credentials/jwt/json_token.h
index 9b77488..d0fb4eb 100644
--- a/src/core/lib/security/credentials/jwt/json_token.h
+++ b/src/core/lib/security/credentials/jwt/json_token.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <openssl/rsa.h>
 
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h
index f58a8b6..5c3d34a 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.h
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/jwt/json_token.h"
 
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.cc b/src/core/lib/security/credentials/jwt/jwt_verifier.cc
index f5c1ada..5c47276 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.cc
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
 
 #include <limits.h>
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.h b/src/core/lib/security/credentials/jwt/jwt_verifier.h
index b3805e7..cdb0987 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.h
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/json/json.h"
 
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
index afae7d8..2129029 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
index e5b8df8..c0dd154 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/json/json.h"
 #include "src/core/lib/security/credentials/credentials.h"
 
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/src/core/lib/security/credentials/plugin/plugin_credentials.cc
index ddb86e1..73946ce 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.cc
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h
index e1467b0..caf990e 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.h
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 extern grpc_core::TraceFlag grpc_plugin_credentials_trace;
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc
index a4fce25..252b25b 100644
--- a/src/core/lib/security/credentials/ssl/ssl_credentials.cc
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.h b/src/core/lib/security/credentials/ssl/ssl_credentials.h
index 0003905..712d34c 100644
--- a/src/core/lib/security/credentials/ssl/ssl_credentials.h
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.h
@@ -18,6 +18,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H
 #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/credentials/credentials.h"
 
 typedef struct {
diff --git a/src/core/lib/security/security_connector/alts_security_connector.cc b/src/core/lib/security/security_connector/alts_security_connector.cc
new file mode 100644
index 0000000..5ff7d79
--- /dev/null
+++ b/src/core/lib/security/security_connector/alts_security_connector.cc
@@ -0,0 +1,287 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/alts_security_connector.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/credentials/alts/alts_credentials.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/transport/transport.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+
+typedef struct {
+  grpc_channel_security_connector base;
+  char* target_name;
+} grpc_alts_channel_security_connector;
+
+typedef struct {
+  grpc_server_security_connector base;
+} grpc_alts_server_security_connector;
+
+static void alts_channel_destroy(grpc_security_connector* sc) {
+  if (sc == nullptr) {
+    return;
+  }
+  auto c = reinterpret_cast<grpc_alts_channel_security_connector*>(sc);
+  grpc_call_credentials_unref(c->base.request_metadata_creds);
+  grpc_channel_credentials_unref(c->base.channel_creds);
+  gpr_free(c->target_name);
+  gpr_free(sc);
+}
+
+static void alts_server_destroy(grpc_security_connector* sc) {
+  if (sc == nullptr) {
+    return;
+  }
+  auto c = reinterpret_cast<grpc_alts_server_security_connector*>(sc);
+  grpc_server_credentials_unref(c->base.server_creds);
+  gpr_free(sc);
+}
+
+static void alts_channel_add_handshakers(
+    grpc_channel_security_connector* sc,
+    grpc_handshake_manager* handshake_manager) {
+  tsi_handshaker* handshaker = nullptr;
+  auto c = reinterpret_cast<grpc_alts_channel_security_connector*>(sc);
+  grpc_alts_credentials* creds =
+      reinterpret_cast<grpc_alts_credentials*>(c->base.channel_creds);
+  GPR_ASSERT(alts_tsi_handshaker_create(creds->options, c->target_name,
+                                        creds->handshaker_service_url, true,
+                                        &handshaker) == TSI_OK);
+  grpc_handshake_manager_add(handshake_manager, grpc_security_handshaker_create(
+                                                    handshaker, &sc->base));
+}
+
+static void alts_server_add_handshakers(
+    grpc_server_security_connector* sc,
+    grpc_handshake_manager* handshake_manager) {
+  tsi_handshaker* handshaker = nullptr;
+  auto c = reinterpret_cast<grpc_alts_server_security_connector*>(sc);
+  grpc_alts_server_credentials* creds =
+      reinterpret_cast<grpc_alts_server_credentials*>(c->base.server_creds);
+  GPR_ASSERT(alts_tsi_handshaker_create(creds->options, nullptr,
+                                        creds->handshaker_service_url, false,
+                                        &handshaker) == TSI_OK);
+  grpc_handshake_manager_add(handshake_manager, grpc_security_handshaker_create(
+                                                    handshaker, &sc->base));
+}
+
+static void alts_set_rpc_protocol_versions(
+    grpc_gcp_rpc_protocol_versions* rpc_versions) {
+  grpc_gcp_rpc_protocol_versions_set_max(rpc_versions,
+                                         GRPC_PROTOCOL_VERSION_MAX_MAJOR,
+                                         GRPC_PROTOCOL_VERSION_MAX_MINOR);
+  grpc_gcp_rpc_protocol_versions_set_min(rpc_versions,
+                                         GRPC_PROTOCOL_VERSION_MIN_MAJOR,
+                                         GRPC_PROTOCOL_VERSION_MIN_MINOR);
+}
+
+namespace grpc_core {
+namespace internal {
+
+grpc_security_status grpc_alts_auth_context_from_tsi_peer(
+    const tsi_peer* peer, grpc_auth_context** ctx) {
+  if (peer == nullptr || ctx == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to grpc_alts_auth_context_from_tsi_peer()");
+    return GRPC_SECURITY_ERROR;
+  }
+  *ctx = nullptr;
+  /* Validate certificate type. */
+  const tsi_peer_property* cert_type_prop =
+      tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY);
+  if (cert_type_prop == nullptr ||
+      strncmp(cert_type_prop->value.data, TSI_ALTS_CERTIFICATE_TYPE,
+              cert_type_prop->value.length) != 0) {
+    gpr_log(GPR_ERROR, "Invalid or missing certificate type property.");
+    return GRPC_SECURITY_ERROR;
+  }
+  /* Validate RPC protocol versions. */
+  const tsi_peer_property* rpc_versions_prop =
+      tsi_peer_get_property_by_name(peer, TSI_ALTS_RPC_VERSIONS);
+  if (rpc_versions_prop == nullptr) {
+    gpr_log(GPR_ERROR, "Missing rpc protocol versions property.");
+    return GRPC_SECURITY_ERROR;
+  }
+  grpc_gcp_rpc_protocol_versions local_versions, peer_versions;
+  alts_set_rpc_protocol_versions(&local_versions);
+  grpc_slice slice = grpc_slice_from_copied_buffer(
+      rpc_versions_prop->value.data, rpc_versions_prop->value.length);
+  bool decode_result =
+      grpc_gcp_rpc_protocol_versions_decode(slice, &peer_versions);
+  grpc_slice_unref(slice);
+  if (!decode_result) {
+    gpr_log(GPR_ERROR, "Invalid peer rpc protocol versions.");
+    return GRPC_SECURITY_ERROR;
+  }
+  /* TODO: Pass highest common rpc protocol version to grpc caller. */
+  bool check_result = grpc_gcp_rpc_protocol_versions_check(
+      &local_versions, &peer_versions, nullptr);
+  if (!check_result) {
+    gpr_log(GPR_ERROR, "Mismatch of local and peer rpc protocol versions.");
+    return GRPC_SECURITY_ERROR;
+  }
+  /* Create auth context. */
+  *ctx = grpc_auth_context_create(nullptr);
+  grpc_auth_context_add_cstring_property(
+      *ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+      GRPC_ALTS_TRANSPORT_SECURITY_TYPE);
+  size_t i = 0;
+  for (i = 0; i < peer->property_count; i++) {
+    const tsi_peer_property* tsi_prop = &peer->properties[i];
+    /* Add service account to auth context. */
+    if (strcmp(tsi_prop->name, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 0) {
+      grpc_auth_context_add_property(
+          *ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, tsi_prop->value.data,
+          tsi_prop->value.length);
+      GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
+                     *ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 1);
+    }
+  }
+  if (!grpc_auth_context_peer_is_authenticated(*ctx)) {
+    gpr_log(GPR_ERROR, "Invalid unauthenticated peer.");
+    GRPC_AUTH_CONTEXT_UNREF(*ctx, "test");
+    *ctx = nullptr;
+    return GRPC_SECURITY_ERROR;
+  }
+  return GRPC_SECURITY_OK;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+static void alts_check_peer(grpc_security_connector* sc, tsi_peer peer,
+                            grpc_auth_context** auth_context,
+                            grpc_closure* on_peer_checked) {
+  grpc_security_status status;
+  status = grpc_core::internal::grpc_alts_auth_context_from_tsi_peer(
+      &peer, auth_context);
+  tsi_peer_destruct(&peer);
+  grpc_error* error =
+      status == GRPC_SECURITY_OK
+          ? GRPC_ERROR_NONE
+          : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "Could not get ALTS auth context from TSI peer");
+  GRPC_CLOSURE_SCHED(on_peer_checked, error);
+}
+
+static int alts_channel_cmp(grpc_security_connector* sc1,
+                            grpc_security_connector* sc2) {
+  grpc_alts_channel_security_connector* c1 =
+      reinterpret_cast<grpc_alts_channel_security_connector*>(sc1);
+  grpc_alts_channel_security_connector* c2 =
+      reinterpret_cast<grpc_alts_channel_security_connector*>(sc2);
+  int c = grpc_channel_security_connector_cmp(&c1->base, &c2->base);
+  if (c != 0) return c;
+  return strcmp(c1->target_name, c2->target_name);
+}
+
+static int alts_server_cmp(grpc_security_connector* sc1,
+                           grpc_security_connector* sc2) {
+  grpc_alts_server_security_connector* c1 =
+      reinterpret_cast<grpc_alts_server_security_connector*>(sc1);
+  grpc_alts_server_security_connector* c2 =
+      reinterpret_cast<grpc_alts_server_security_connector*>(sc2);
+  return grpc_server_security_connector_cmp(&c1->base, &c2->base);
+}
+
+static grpc_security_connector_vtable alts_channel_vtable = {
+    alts_channel_destroy, alts_check_peer, alts_channel_cmp};
+
+static grpc_security_connector_vtable alts_server_vtable = {
+    alts_server_destroy, alts_check_peer, alts_server_cmp};
+
+static bool alts_check_call_host(grpc_channel_security_connector* sc,
+                                 const char* host,
+                                 grpc_auth_context* auth_context,
+                                 grpc_closure* on_call_host_checked,
+                                 grpc_error** error) {
+  grpc_alts_channel_security_connector* alts_sc =
+      reinterpret_cast<grpc_alts_channel_security_connector*>(sc);
+  if (host == nullptr || alts_sc == nullptr ||
+      strcmp(host, alts_sc->target_name) != 0) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "ALTS call host does not match target name");
+  }
+  return true;
+}
+
+static void alts_cancel_check_call_host(grpc_channel_security_connector* sc,
+                                        grpc_closure* on_call_host_checked,
+                                        grpc_error* error) {
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_security_status grpc_alts_channel_security_connector_create(
+    grpc_channel_credentials* channel_creds,
+    grpc_call_credentials* request_metadata_creds, const char* target_name,
+    grpc_channel_security_connector** sc) {
+  if (channel_creds == nullptr || sc == nullptr || target_name == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to grpc_alts_channel_security_connector_create()");
+    return GRPC_SECURITY_ERROR;
+  }
+  auto c = static_cast<grpc_alts_channel_security_connector*>(
+      gpr_zalloc(sizeof(grpc_alts_channel_security_connector)));
+  gpr_ref_init(&c->base.base.refcount, 1);
+  c->base.base.vtable = &alts_channel_vtable;
+  c->base.add_handshakers = alts_channel_add_handshakers;
+  c->base.channel_creds = grpc_channel_credentials_ref(channel_creds);
+  c->base.request_metadata_creds =
+      grpc_call_credentials_ref(request_metadata_creds);
+  c->base.check_call_host = alts_check_call_host;
+  c->base.cancel_check_call_host = alts_cancel_check_call_host;
+  grpc_alts_credentials* creds =
+      reinterpret_cast<grpc_alts_credentials*>(c->base.channel_creds);
+  alts_set_rpc_protocol_versions(&creds->options->rpc_versions);
+  c->target_name = gpr_strdup(target_name);
+  *sc = &c->base;
+  return GRPC_SECURITY_OK;
+}
+
+grpc_security_status grpc_alts_server_security_connector_create(
+    grpc_server_credentials* server_creds,
+    grpc_server_security_connector** sc) {
+  if (server_creds == nullptr || sc == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to grpc_alts_server_security_connector_create()");
+    return GRPC_SECURITY_ERROR;
+  }
+  auto c = static_cast<grpc_alts_server_security_connector*>(
+      gpr_zalloc(sizeof(grpc_alts_server_security_connector)));
+  gpr_ref_init(&c->base.base.refcount, 1);
+  c->base.base.vtable = &alts_server_vtable;
+  c->base.server_creds = grpc_server_credentials_ref(server_creds);
+  c->base.add_handshakers = alts_server_add_handshakers;
+  grpc_alts_server_credentials* creds =
+      reinterpret_cast<grpc_alts_server_credentials*>(c->base.server_creds);
+  alts_set_rpc_protocol_versions(&creds->options->rpc_versions);
+  *sc = &c->base;
+  return GRPC_SECURITY_OK;
+}
diff --git a/src/core/lib/security/security_connector/alts_security_connector.h b/src/core/lib/security/security_connector/alts_security_connector.h
new file mode 100644
index 0000000..e7e4cff
--- /dev/null
+++ b/src/core/lib/security/security_connector/alts_security_connector.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+
+#define GRPC_ALTS_TRANSPORT_SECURITY_TYPE "alts"
+
+/**
+ * This method creates an ALTS channel security connector.
+ *
+ * - channel_creds: channel credential instance.
+ * - request_metadata_creds: credential object which will be sent with each
+ *   request. This parameter can be nullptr.
+ * - target_name: the name of the endpoint that the channel is connecting to.
+ * - sc: address of ALTS channel security connector instance to be returned from
+ *   the method.
+ *
+ * It returns GRPC_SECURITY_OK on success, and an error stauts code on failure.
+ */
+grpc_security_status grpc_alts_channel_security_connector_create(
+    grpc_channel_credentials* channel_creds,
+    grpc_call_credentials* request_metadata_creds, const char* target_name,
+    grpc_channel_security_connector** sc);
+
+/**
+ * This method creates an ALTS server security connector.
+ *
+ * - server_creds: server credential instance.
+ * - sc: address of ALTS server security connector instance to be returned from
+ *   the method.
+ *
+ * It returns GRPC_SECURITY_OK on success, and an error status code on failure.
+ */
+grpc_security_status grpc_alts_server_security_connector_create(
+    grpc_server_credentials* server_creds, grpc_server_security_connector** sc);
+
+namespace grpc_core {
+namespace internal {
+
+/* Exposed only for testing. */
+grpc_security_status grpc_alts_auth_context_from_tsi_peer(
+    const tsi_peer* peer, grpc_auth_context** ctx);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H \
+        */
diff --git a/src/core/lib/security/transport/security_connector.cc b/src/core/lib/security/security_connector/security_connector.cc
similarity index 98%
rename from src/core/lib/security/transport/security_connector.cc
rename to src/core/lib/security/security_connector/security_connector.cc
index bd5da1b..3cc151b 100644
--- a/src/core/lib/security/transport/security_connector.cc
+++ b/src/core/lib/security/security_connector/security_connector.cc
@@ -16,7 +16,9 @@
  *
  */
 
-#include "src/core/lib/security/transport/security_connector.h"
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/security_connector.h"
 
 #include <stdbool.h>
 #include <string.h>
@@ -37,9 +39,9 @@
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
 #include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
-#include "src/core/lib/security/transport/lb_targets_info.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
 #include "src/core/tsi/fake_transport_security.h"
 #include "src/core/tsi/ssl_transport_security.h"
 #include "src/core/tsi/transport_security_adapter.h"
@@ -461,6 +463,15 @@
                                          grpc_auth_context* auth_context,
                                          grpc_closure* on_call_host_checked,
                                          grpc_error** error) {
+  grpc_fake_channel_security_connector* c =
+      reinterpret_cast<grpc_fake_channel_security_connector*>(sc);
+  if (c->is_lb_channel) {
+    // TODO(dgq): verify that the host (ie, authority header) matches that of
+    // the LB, as opposed to that of the backends.
+  } else {
+    // TODO(dgq): verify that the host (ie, authority header) matches that of
+    // the backend, not the LB's.
+  }
   return true;
 }
 
@@ -512,7 +523,7 @@
   c->target = gpr_strdup(target);
   const char* expected_targets = grpc_fake_transport_get_expected_targets(args);
   c->expected_targets = gpr_strdup(expected_targets);
-  c->is_lb_channel = (grpc_lb_targets_info_find_in_args(args) != nullptr);
+  c->is_lb_channel = grpc_core::FindTargetAuthorityTableInArgs(args) != nullptr;
   return &c->base;
 }
 
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/security_connector/security_connector.h
similarity index 97%
rename from src/core/lib/security/transport/security_connector.h
rename to src/core/lib/security/security_connector/security_connector.h
index 495821d..130c8ec 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/security_connector/security_connector.h
@@ -16,8 +16,10 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H
-#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
 
 #include <stdbool.h>
 
@@ -246,4 +248,4 @@
     const grpc_auth_context* auth_context);
 void tsi_shallow_peer_destruct(tsi_peer* peer);
 
-#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H */
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H */
diff --git a/src/core/lib/security/transport/auth_filters.h b/src/core/lib/security/transport/auth_filters.h
index e999a02..af2104c 100644
--- a/src/core/lib/security/transport/auth_filters.h
+++ b/src/core/lib/security/transport/auth_filters.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H
 #define GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc_security.h>
 #include "src/core/lib/channel/channel_stack.h"
 
diff --git a/src/core/lib/security/transport/client_auth_filter.cc b/src/core/lib/security/transport/client_auth_filter.cc
index d231380..d6ca8ee 100644
--- a/src/core/lib/security/transport/client_auth_filter.cc
+++ b/src/core/lib/security/transport/client_auth_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/transport/auth_filters.h"
 
 #include <string.h>
@@ -29,7 +31,7 @@
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/surface/call.h"
diff --git a/src/core/lib/security/transport/lb_targets_info.cc b/src/core/lib/security/transport/lb_targets_info.cc
deleted file mode 100644
index 67a3c74..0000000
--- a/src/core/lib/security/transport/lb_targets_info.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *
- * Copyright 2017 gRPC authors.
- *
- * 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 <grpc/support/log.h>
-
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/security/transport/lb_targets_info.h"
-
-/* Channel arg key for the mapping of LB server addresses to their names for
- * secure naming purposes. */
-#define GRPC_ARG_LB_SECURE_NAMING_MAP "grpc.lb_secure_naming_map"
-
-static void* targets_info_copy(void* p) {
-  return grpc_slice_hash_table_ref(static_cast<grpc_slice_hash_table*>(p));
-}
-static void targets_info_destroy(void* p) {
-  grpc_slice_hash_table_unref(static_cast<grpc_slice_hash_table*>(p));
-}
-static int targets_info_cmp(void* a, void* b) {
-  return grpc_slice_hash_table_cmp(
-      static_cast<const grpc_slice_hash_table*>(a),
-      static_cast<const grpc_slice_hash_table*>(b));
-}
-static const grpc_arg_pointer_vtable server_to_balancer_names_vtable = {
-    targets_info_copy, targets_info_destroy, targets_info_cmp};
-
-grpc_arg grpc_lb_targets_info_create_channel_arg(
-    grpc_slice_hash_table* targets_info) {
-  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_LB_SECURE_NAMING_MAP,
-                                         targets_info,
-                                         &server_to_balancer_names_vtable);
-}
-
-grpc_slice_hash_table* grpc_lb_targets_info_find_in_args(
-    const grpc_channel_args* args) {
-  const grpc_arg* targets_info_arg =
-      grpc_channel_args_find(args, GRPC_ARG_LB_SECURE_NAMING_MAP);
-  if (targets_info_arg != nullptr) {
-    GPR_ASSERT(targets_info_arg->type == GRPC_ARG_POINTER);
-    return static_cast<grpc_slice_hash_table*>(
-        targets_info_arg->value.pointer.p);
-  }
-  return nullptr;
-}
diff --git a/src/core/lib/security/transport/lb_targets_info.h b/src/core/lib/security/transport/lb_targets_info.h
deleted file mode 100644
index 7543d3c..0000000
--- a/src/core/lib/security/transport/lb_targets_info.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- *
- * Copyright 2017 gRPC authors.
- *
- * 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 GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H
-#define GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H
-
-#include "src/core/lib/slice/slice_hash_table.h"
-
-/** Return a channel argument containing \a targets_info. */
-grpc_arg grpc_lb_targets_info_create_channel_arg(
-    grpc_slice_hash_table* targets_info);
-
-/** Return the instance of targets info in \a args or NULL */
-grpc_slice_hash_table* grpc_lb_targets_info_find_in_args(
-    const grpc_channel_args* args);
-
-#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H */
diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc
index f72f8b6..31b779e 100644
--- a/src/core/lib/security/transport/secure_endpoint.cc
+++ b/src/core/lib/security/transport/secure_endpoint.cc
@@ -20,6 +20,8 @@
    using that endpoint. Because of various transitive includes in uv.h,
    including windows.h on Windows, uv.h must be included before other system
    headers. Therefore, sockaddr.h must always be included first */
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/sockaddr.h"
 
 #include <grpc/slice.h>
diff --git a/src/core/lib/security/transport/secure_endpoint.h b/src/core/lib/security/transport/secure_endpoint.h
index b2556a0..e7e3351 100644
--- a/src/core/lib/security/transport/secure_endpoint.h
+++ b/src/core/lib/security/transport/secure_endpoint.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H
 #define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include "src/core/lib/iomgr/endpoint.h"
 
diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc
index b37392a..0c97dfa 100644
--- a/src/core/lib/security/transport/security_handshaker.cc
+++ b/src/core/lib/security/transport/security_handshaker.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/transport/security_handshaker.h"
 
 #include <stdbool.h>
diff --git a/src/core/lib/security/transport/security_handshaker.h b/src/core/lib/security/transport/security_handshaker.h
index 6cd6446..ecf59ec 100644
--- a/src/core/lib/security/transport/security_handshaker.h
+++ b/src/core/lib/security/transport/security_handshaker.h
@@ -19,9 +19,11 @@
 #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H
 #define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/handshaker.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 
 /// Creates a security handshaker using \a handshaker.
 grpc_handshaker* grpc_security_handshaker_create(
diff --git a/src/core/lib/security/transport/server_auth_filter.cc b/src/core/lib/security/transport/server_auth_filter.cc
index 409aded..a560a4a 100644
--- a/src/core/lib/security/transport/server_auth_filter.cc
+++ b/src/core/lib/security/transport/server_auth_filter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/security/transport/target_authority_table.cc b/src/core/lib/security/transport/target_authority_table.cc
new file mode 100644
index 0000000..1eeb557
--- /dev/null
+++ b/src/core/lib/security/transport/target_authority_table.cc
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
+
+// Channel arg key for the mapping of target addresses to their authorities.
+#define GRPC_ARG_TARGET_AUTHORITY_TABLE "grpc.target_authority_table"
+
+namespace grpc_core {
+namespace {
+
+void* target_authority_table_copy(void* p) {
+  TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p);
+  // TODO(roth): When channel_args are converted to C++, pass the
+  // RefCountedPtr<> directly instead of managing the ref manually.
+  table->Ref().release();
+  return p;
+}
+void target_authority_table_destroy(void* p) {
+  TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p);
+  table->Unref();
+}
+int target_authority_table_cmp(void* a, void* b) {
+  return TargetAuthorityTable::Cmp(
+      *static_cast<const TargetAuthorityTable*>(a),
+      *static_cast<const TargetAuthorityTable*>(b));
+}
+const grpc_arg_pointer_vtable target_authority_table_arg_vtable = {
+    target_authority_table_copy, target_authority_table_destroy,
+    target_authority_table_cmp};
+
+}  // namespace
+
+grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_TARGET_AUTHORITY_TABLE,
+                                         table,
+                                         &target_authority_table_arg_vtable);
+}
+
+TargetAuthorityTable* FindTargetAuthorityTableInArgs(
+    const grpc_channel_args* args) {
+  const grpc_arg* arg =
+      grpc_channel_args_find(args, GRPC_ARG_TARGET_AUTHORITY_TABLE);
+  if (arg != nullptr) {
+    if (arg->type == GRPC_ARG_POINTER) {
+      return static_cast<TargetAuthorityTable*>(arg->value.pointer.p);
+    } else {
+      gpr_log(GPR_ERROR, "value of " GRPC_ARG_TARGET_AUTHORITY_TABLE
+                         " channel arg was not pointer type; ignoring");
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace grpc_core
diff --git a/src/core/lib/security/transport/target_authority_table.h b/src/core/lib/security/transport/target_authority_table.h
new file mode 100644
index 0000000..a2e7dc6
--- /dev/null
+++ b/src/core/lib/security/transport/target_authority_table.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/slice_hash_table.h"
+
+namespace grpc_core {
+
+/// A hash table mapping target addresses to authorities.
+typedef SliceHashTable<UniquePtr<char>> TargetAuthorityTable;
+
+/// Returns a channel argument containing \a table.
+grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table);
+
+/// Returns the target authority table from \a args or nullptr.
+TargetAuthorityTable* FindTargetAuthorityTableInArgs(
+    const grpc_channel_args* args);
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H */
diff --git a/src/core/lib/security/transport/tsi_error.cc b/src/core/lib/security/transport/tsi_error.cc
index f71696d..f78bb8d 100644
--- a/src/core/lib/security/transport/tsi_error.cc
+++ b/src/core/lib/security/transport/tsi_error.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/transport/tsi_error.h"
 
 grpc_error* grpc_set_tsi_error_result(grpc_error* error, tsi_result result) {
diff --git a/src/core/lib/security/transport/tsi_error.h b/src/core/lib/security/transport/tsi_error.h
index 8fa6c48..16e04f7 100644
--- a/src/core/lib/security/transport/tsi_error.h
+++ b/src/core/lib/security/transport/tsi_error.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H
 #define GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/tsi/transport_security_interface.h"
 
diff --git a/src/core/lib/security/util/json_util.cc b/src/core/lib/security/util/json_util.cc
index fef1a1f..75512a1 100644
--- a/src/core/lib/security/util/json_util.cc
+++ b/src/core/lib/security/util/json_util.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/security/util/json_util.h"
 
 #include <string.h>
diff --git a/src/core/lib/security/util/json_util.h b/src/core/lib/security/util/json_util.h
index b7e46d4..89deffc 100644
--- a/src/core/lib/security/util/json_util.h
+++ b/src/core/lib/security/util/json_util.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
 #define GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include "src/core/lib/json/json.h"
diff --git a/src/core/lib/slice/b64.cc b/src/core/lib/slice/b64.cc
index 3e19b71..27f2724 100644
--- a/src/core/lib/slice/b64.cc
+++ b/src/core/lib/slice/b64.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/slice/b64.h"
 
 #include <stdint.h>
diff --git a/src/core/lib/slice/b64.h b/src/core/lib/slice/b64.h
index 17e7306..4475568 100644
--- a/src/core/lib/slice/b64.h
+++ b/src/core/lib/slice/b64.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SLICE_B64_H
 #define GRPC_CORE_LIB_SLICE_B64_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 
 /* Encodes data using base64. It is the caller's responsability to free
diff --git a/src/core/lib/slice/percent_encoding.cc b/src/core/lib/slice/percent_encoding.cc
index 84fb554..45cd2cc 100644
--- a/src/core/lib/slice/percent_encoding.cc
+++ b/src/core/lib/slice/percent_encoding.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/slice/percent_encoding.h"
 
 #include <grpc/support/log.h>
diff --git a/src/core/lib/slice/percent_encoding.h b/src/core/lib/slice/percent_encoding.h
index a1009ff..6b13ffc 100644
--- a/src/core/lib/slice/percent_encoding.h
+++ b/src/core/lib/slice/percent_encoding.h
@@ -26,6 +26,8 @@
      and another which applies percent encoding only to non-http2 header
      bytes (the 'compatible' variant) */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/slice.h>
diff --git a/src/core/lib/slice/slice.cc b/src/core/lib/slice/slice.cc
index 476d941..585b41c 100644
--- a/src/core/lib/slice/slice.cc
+++ b/src/core/lib/slice/slice.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/slice/slice_internal.h"
 
 #include <grpc/slice.h>
diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc
index 0416c9d..e418ab1 100644
--- a/src/core/lib/slice/slice_buffer.cc
+++ b/src/core/lib/slice/slice_buffer.cc
@@ -16,9 +16,10 @@
  *
  */
 
-#include <grpc/slice_buffer.h>
 #include <grpc/support/port_platform.h>
 
+#include <grpc/slice_buffer.h>
+
 #include <string.h>
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/slice/slice_hash_table.cc b/src/core/lib/slice/slice_hash_table.cc
deleted file mode 100644
index 2342e90..0000000
--- a/src/core/lib/slice/slice_hash_table.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-//
-// Copyright 2016 gRPC authors.
-//
-// 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 "src/core/lib/slice/slice_hash_table.h"
-
-#include <stdbool.h>
-#include <string.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-
-#include "src/core/lib/slice/slice_internal.h"
-#include "src/core/lib/transport/metadata.h"
-
-struct grpc_slice_hash_table {
-  gpr_refcount refs;
-  void (*destroy_value)(void* value);
-  int (*value_cmp)(void* a, void* b);
-  size_t size;
-  size_t max_num_probes;
-  grpc_slice_hash_table_entry* entries;
-};
-
-static bool is_empty(grpc_slice_hash_table_entry* entry) {
-  return entry->value == nullptr;
-}
-
-static void grpc_slice_hash_table_add(grpc_slice_hash_table* table,
-                                      grpc_slice key, void* value) {
-  GPR_ASSERT(value != nullptr);
-  const size_t hash = grpc_slice_hash(key);
-  for (size_t offset = 0; offset < table->size; ++offset) {
-    const size_t idx = (hash + offset) % table->size;
-    if (is_empty(&table->entries[idx])) {
-      table->entries[idx].key = key;
-      table->entries[idx].value = value;
-      // Keep track of the maximum number of probes needed, since this
-      // provides an upper bound for lookups.
-      if (offset > table->max_num_probes) table->max_num_probes = offset;
-      return;
-    }
-  }
-  GPR_ASSERT(false);  // Table should never be full.
-}
-
-grpc_slice_hash_table* grpc_slice_hash_table_create(
-    size_t num_entries, grpc_slice_hash_table_entry* entries,
-    void (*destroy_value)(void* value), int (*value_cmp)(void* a, void* b)) {
-  grpc_slice_hash_table* table =
-      static_cast<grpc_slice_hash_table*>(gpr_zalloc(sizeof(*table)));
-  gpr_ref_init(&table->refs, 1);
-  table->destroy_value = destroy_value;
-  table->value_cmp = value_cmp;
-  // Keep load factor low to improve performance of lookups.
-  table->size = num_entries * 2;
-  const size_t entry_size = sizeof(grpc_slice_hash_table_entry) * table->size;
-  table->entries =
-      static_cast<grpc_slice_hash_table_entry*>(gpr_zalloc(entry_size));
-  for (size_t i = 0; i < num_entries; ++i) {
-    grpc_slice_hash_table_entry* entry = &entries[i];
-    grpc_slice_hash_table_add(table, entry->key, entry->value);
-  }
-  return table;
-}
-
-grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table) {
-  if (table != nullptr) gpr_ref(&table->refs);
-  return table;
-}
-
-void grpc_slice_hash_table_unref(grpc_slice_hash_table* table) {
-  if (table != nullptr && gpr_unref(&table->refs)) {
-    for (size_t i = 0; i < table->size; ++i) {
-      grpc_slice_hash_table_entry* entry = &table->entries[i];
-      if (!is_empty(entry)) {
-        grpc_slice_unref_internal(entry->key);
-        table->destroy_value(entry->value);
-      }
-    }
-    gpr_free(table->entries);
-    gpr_free(table);
-  }
-}
-
-void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table,
-                                const grpc_slice key) {
-  const size_t hash = grpc_slice_hash(key);
-  // We cap the number of probes at the max number recorded when
-  // populating the table.
-  for (size_t offset = 0; offset <= table->max_num_probes; ++offset) {
-    const size_t idx = (hash + offset) % table->size;
-    if (is_empty(&table->entries[idx])) break;
-    if (grpc_slice_eq(table->entries[idx].key, key)) {
-      return table->entries[idx].value;
-    }
-  }
-  return nullptr;  // Not found.
-}
-
-static int pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
-int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a,
-                              const grpc_slice_hash_table* b) {
-  int (*const value_cmp_fn_a)(void* a, void* b) =
-      a->value_cmp != nullptr ? a->value_cmp : pointer_cmp;
-  int (*const value_cmp_fn_b)(void* a, void* b) =
-      b->value_cmp != nullptr ? b->value_cmp : pointer_cmp;
-  // Compare value_fns
-  const int value_fns_cmp =
-      GPR_ICMP((void*)value_cmp_fn_a, (void*)value_cmp_fn_b);
-  if (value_fns_cmp != 0) return value_fns_cmp;
-  // Compare sizes
-  if (a->size < b->size) return -1;
-  if (a->size > b->size) return 1;
-  // Compare rows.
-  for (size_t i = 0; i < a->size; ++i) {
-    if (is_empty(&a->entries[i])) {
-      if (!is_empty(&b->entries[i])) {
-        return -1;  // a empty but b non-empty
-      }
-      continue;  // both empty, no need to check key or value
-    } else if (is_empty(&b->entries[i])) {
-      return 1;  // a non-empty but b empty
-    }
-    // neither entry is empty
-    const int key_cmp = grpc_slice_cmp(a->entries[i].key, b->entries[i].key);
-    if (key_cmp != 0) return key_cmp;
-    const int value_cmp =
-        value_cmp_fn_a(a->entries[i].value, b->entries[i].value);
-    if (value_cmp != 0) return value_cmp;
-  }
-  return 0;
-}
diff --git a/src/core/lib/slice/slice_hash_table.h b/src/core/lib/slice/slice_hash_table.h
index db69da6..fbe9cc5 100644
--- a/src/core/lib/slice/slice_hash_table.h
+++ b/src/core/lib/slice/slice_hash_table.h
@@ -17,52 +17,185 @@
 #ifndef GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H
 #define GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H
 
-#include "src/core/lib/transport/metadata.h"
+#include <grpc/support/port_platform.h>
 
-/** Hash table implementation.
- *
- * This implementation uses open addressing
- * (https://en.wikipedia.org/wiki/Open_addressing) with linear
- * probing (https://en.wikipedia.org/wiki/Linear_probing).
- *
- * The keys are \a grpc_slice objects.  The values are arbitrary pointers
- * with a common destroy function.
- *
- * Hash tables are intentionally immutable, to avoid the need for locking.
- */
+#include <string.h>
 
-typedef struct grpc_slice_hash_table grpc_slice_hash_table;
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 
-typedef struct grpc_slice_hash_table_entry {
-  grpc_slice key;
-  void* value; /* Must not be NULL. */
-} grpc_slice_hash_table_entry;
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/slice/slice_internal.h"
 
-/** Creates a new hash table of containing \a entries, which is an array
-    of length \a num_entries.  Takes ownership of all keys and values in \a
-    entries.  Values will be cleaned up via \a destroy_value(). If not NULL, \a
-    value_cmp will be used to compare values in the context of \a
-    grpc_slice_hash_table_cmp. If NULL, raw pointer (\a GPR_ICMP) comparison
-    will be used. */
-grpc_slice_hash_table* grpc_slice_hash_table_create(
-    size_t num_entries, grpc_slice_hash_table_entry* entries,
-    void (*destroy_value)(void* value), int (*value_cmp)(void* a, void* b));
+/// Hash table implementation.
+///
+/// This implementation uses open addressing
+/// (https://en.wikipedia.org/wiki/Open_addressing) with linear
+/// probing (https://en.wikipedia.org/wiki/Linear_probing).
+///
+/// The keys are \a grpc_slice objects.  The values can be any type.
+///
+/// Hash tables are intentionally immutable, to avoid the need for locking.
 
-grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table);
-void grpc_slice_hash_table_unref(grpc_slice_hash_table* table);
+namespace grpc_core {
 
-/** Returns the value from \a table associated with \a key.
-    Returns NULL if \a key is not found. */
-void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table,
-                                const grpc_slice key);
+template <typename T>
+class SliceHashTable : public RefCounted<SliceHashTable<T>> {
+ public:
+  struct Entry {
+    grpc_slice key;
+    T value;
+    bool is_set;
+  };
 
-/** Compares \a a vs. \a b.
- * A table is considered "smaller" (resp. "greater") if:
- *  - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1),
- *  - else, it contains fewer (resp. more) entries,
- *  - else, if strcmp(a_key, b_key) < 1 (resp. > 1),
- *  - else, if value_cmp(a_value, b_value) < 1 (resp. > 1). */
-int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a,
-                              const grpc_slice_hash_table* b);
+  // Function for comparing values.
+  // TODO(roth): Eliminate this and the Cmp() method from this API once
+  // grpc_channel_args is redesigned to require that keys are unique.
+  typedef int (*ValueCmp)(const T&, const T&);
+
+  /// Creates a new hash table containing \a entries, which is an array
+  /// of length \a num_entries.  Takes ownership of all keys and values in \a
+  /// entries.  If not null, \a value_cmp will be used to compare values in
+  /// the context of \a Cmp(). If null, raw pointer (\a GPR_ICMP) comparison
+  /// will be used.
+  static RefCountedPtr<SliceHashTable> Create(size_t num_entries,
+                                              Entry* entries,
+                                              ValueCmp value_cmp);
+
+  /// Returns the value from the table associated with \a key.
+  /// Returns null if \a key is not found.
+  const T* Get(const grpc_slice& key) const;
+
+  /// Compares \a a vs. \a b.
+  /// A table is considered "smaller" (resp. "greater") if:
+  ///  - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1),
+  ///  - else, it contains fewer (resp. more) entries,
+  ///  - else, if strcmp(a_key, b_key) < 1 (resp. > 1),
+  ///  - else, if value_cmp(a_value, b_value) < 1 (resp. > 1).
+  static int Cmp(const SliceHashTable& a, const SliceHashTable& b);
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T2, typename... Args>
+  friend T2* New(Args&&... args);
+
+  SliceHashTable(size_t num_entries, Entry* entries, ValueCmp value_cmp);
+  virtual ~SliceHashTable();
+
+  void Add(grpc_slice key, T& value);
+
+  // Default value comparison function, if none specified by caller.
+  static int DefaultValueCmp(const T& a, const T& b) { return GPR_ICMP(a, b); }
+
+  const ValueCmp value_cmp_;
+  const size_t size_;
+  size_t max_num_probes_;
+  Entry* entries_;
+};
+
+//
+// implementation -- no user-serviceable parts below
+//
+
+template <typename T>
+RefCountedPtr<SliceHashTable<T>> SliceHashTable<T>::Create(size_t num_entries,
+                                                           Entry* entries,
+                                                           ValueCmp value_cmp) {
+  return MakeRefCounted<SliceHashTable<T>>(num_entries, entries, value_cmp);
+}
+
+template <typename T>
+SliceHashTable<T>::SliceHashTable(size_t num_entries, Entry* entries,
+                                  ValueCmp value_cmp)
+    : value_cmp_(value_cmp),
+      // Keep load factor low to improve performance of lookups.
+      size_(num_entries * 2),
+      max_num_probes_(0) {
+  entries_ = static_cast<Entry*>(gpr_zalloc(sizeof(Entry) * size_));
+  for (size_t i = 0; i < num_entries; ++i) {
+    Entry* entry = &entries[i];
+    Add(entry->key, entry->value);
+  }
+}
+
+template <typename T>
+SliceHashTable<T>::~SliceHashTable() {
+  for (size_t i = 0; i < size_; ++i) {
+    Entry& entry = entries_[i];
+    if (entry.is_set) {
+      grpc_slice_unref_internal(entry.key);
+      entry.value.~T();
+    }
+  }
+  gpr_free(entries_);
+}
+
+template <typename T>
+void SliceHashTable<T>::Add(grpc_slice key, T& value) {
+  const size_t hash = grpc_slice_hash(key);
+  for (size_t offset = 0; offset < size_; ++offset) {
+    const size_t idx = (hash + offset) % size_;
+    if (!entries_[idx].is_set) {
+      entries_[idx].is_set = true;
+      entries_[idx].key = key;
+      entries_[idx].value = std::move(value);
+      // Keep track of the maximum number of probes needed, since this
+      // provides an upper bound for lookups.
+      if (offset > max_num_probes_) max_num_probes_ = offset;
+      return;
+    }
+  }
+  GPR_ASSERT(false);  // Table should never be full.
+}
+
+template <typename T>
+const T* SliceHashTable<T>::Get(const grpc_slice& key) const {
+  const size_t hash = grpc_slice_hash(key);
+  // We cap the number of probes at the max number recorded when
+  // populating the table.
+  for (size_t offset = 0; offset <= max_num_probes_; ++offset) {
+    const size_t idx = (hash + offset) % size_;
+    if (!entries_[idx].is_set) break;
+    if (grpc_slice_eq(entries_[idx].key, key)) {
+      return &entries_[idx].value;
+    }
+  }
+  return nullptr;  // Not found.
+}
+
+template <typename T>
+int SliceHashTable<T>::Cmp(const SliceHashTable& a, const SliceHashTable& b) {
+  ValueCmp value_cmp_a =
+      a.value_cmp_ != nullptr ? a.value_cmp_ : DefaultValueCmp;
+  ValueCmp value_cmp_b =
+      b.value_cmp_ != nullptr ? b.value_cmp_ : DefaultValueCmp;
+  // Compare value_fns
+  const int value_fns_cmp = GPR_ICMP((void*)value_cmp_a, (void*)value_cmp_b);
+  if (value_fns_cmp != 0) return value_fns_cmp;
+  // Compare sizes
+  if (a.size_ < b.size_) return -1;
+  if (a.size_ > b.size_) return 1;
+  // Compare rows.
+  for (size_t i = 0; i < a.size_; ++i) {
+    if (!a.entries_[i].is_set) {
+      if (b.entries_[i].is_set) {
+        return -1;  // a empty but b non-empty
+      }
+      continue;  // both empty, no need to check key or value
+    } else if (!b.entries_[i].is_set) {
+      return 1;  // a non-empty but b empty
+    }
+    // neither entry is empty
+    const int key_cmp = grpc_slice_cmp(a.entries_[i].key, b.entries_[i].key);
+    if (key_cmp != 0) return key_cmp;
+    const int value_cmp = value_cmp_a(a.entries_[i].value, b.entries_[i].value);
+    if (value_cmp != 0) return value_cmp;
+  }
+  return 0;
+}
+
+}  // namespace grpc_core
 
 #endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */
diff --git a/src/core/lib/slice/slice_intern.cc b/src/core/lib/slice/slice_intern.cc
index 2d633c4..e53c040 100644
--- a/src/core/lib/slice/slice_intern.cc
+++ b/src/core/lib/slice/slice_intern.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/slice/slice_internal.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h
index 4e9ab80..065c25c 100644
--- a/src/core/lib/slice/slice_internal.h
+++ b/src/core/lib/slice/slice_internal.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H
 #define GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
 
diff --git a/src/core/lib/slice/slice_string_helpers.cc b/src/core/lib/slice/slice_string_helpers.cc
index f91ece3..6af9c33 100644
--- a/src/core/lib/slice/slice_string_helpers.cc
+++ b/src/core/lib/slice/slice_string_helpers.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/slice/slice_string_helpers.h"
 
 #include <string.h>
diff --git a/src/core/lib/slice/slice_string_helpers.h b/src/core/lib/slice/slice_string_helpers.h
index 429f9ff..976f724 100644
--- a/src/core/lib/slice/slice_string_helpers.h
+++ b/src/core/lib/slice/slice_string_helpers.h
@@ -19,12 +19,13 @@
 #ifndef GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H
 #define GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 #include <stddef.h>
 
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
-#include <grpc/support/port_platform.h>
 
 #include "src/core/lib/gpr/string.h"
 
diff --git a/src/core/lib/slice/slice_traits.h b/src/core/lib/slice/slice_traits.h
index 4b898bd..ee01916 100644
--- a/src/core/lib/slice/slice_traits.h
+++ b/src/core/lib/slice/slice_traits.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H
 #define GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <stdbool.h>
 
diff --git a/src/core/lib/slice/slice_weak_hash_table.h b/src/core/lib/slice/slice_weak_hash_table.h
new file mode 100644
index 0000000..9d0ddfc
--- /dev/null
+++ b/src/core/lib/slice/slice_weak_hash_table.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H
+#define GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+/// Weak hash table implementation.
+///
+/// This entries in this table are weak: an entry may be removed at any time due
+/// to a number of reasons: memory pressure, hash collisions, etc.
+///
+/// The keys are \a grpc_slice objects. The values are of arbitrary type.
+///
+/// This class is thread unsafe. It's the caller's responsibility to ensure
+/// proper locking when accessing its methods.
+
+namespace grpc_core {
+
+template <typename T, size_t Size>
+class SliceWeakHashTable : public RefCounted<SliceWeakHashTable<T, Size>> {
+ public:
+  /// Creates a new table of at most \a size entries.
+  static RefCountedPtr<SliceWeakHashTable> Create() {
+    return MakeRefCounted<SliceWeakHashTable<T, Size>>();
+  }
+
+  /// Add a mapping from \a key to \a value, taking ownership of \a key. This
+  /// operation will always succeed. It may discard older entries.
+  void Add(grpc_slice key, T value) {
+    const size_t idx = grpc_slice_hash(key) % Size;
+    entries_[idx].Set(key, std::move(value));
+    return;
+  }
+
+  /// Returns the value from the table associated with / \a key or null if not
+  /// found.
+  const T* Get(const grpc_slice key) const {
+    const size_t idx = grpc_slice_hash(key) % Size;
+    const auto& entry = entries_[idx];
+    return grpc_slice_eq(entry.key(), key) ? entry.value() : nullptr;
+  }
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T2, typename... Args>
+  friend T2* New(Args&&... args);
+
+  SliceWeakHashTable() = default;
+  ~SliceWeakHashTable() = default;
+
+  /// The type of the table "rows".
+  class Entry {
+   public:
+    Entry() = default;
+    ~Entry() {
+      if (is_set_) grpc_slice_unref_internal(key_);
+    }
+    grpc_slice key() const { return key_; }
+
+    /// Return the entry's value, or null if unset.
+    const T* value() const {
+      if (!is_set_) return nullptr;
+      return &value_;
+    }
+
+    /// Set the \a key and \a value (which is moved) for the entry.
+    void Set(grpc_slice key, T&& value) {
+      if (is_set_) grpc_slice_unref_internal(key_);
+      key_ = key;
+      value_ = std::move(value);
+      is_set_ = true;
+    }
+
+   private:
+    grpc_slice key_;
+    T value_;
+    bool is_set_ = false;
+  };
+
+  Entry entries_[Size];
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H */
diff --git a/src/core/lib/surface/api_trace.cc b/src/core/lib/surface/api_trace.cc
index 7ab836a..bab5a79 100644
--- a/src/core/lib/surface/api_trace.cc
+++ b/src/core/lib/surface/api_trace.cc
@@ -16,7 +16,9 @@
  *
  */
 
-#include "src/core/lib/surface/api_trace.h"
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/surface/api_trace.h"
 
 grpc_core::TraceFlag grpc_api_trace(false, "api");
diff --git a/src/core/lib/surface/api_trace.h b/src/core/lib/surface/api_trace.h
index a4e11ce..72ed830 100644
--- a/src/core/lib/surface/api_trace.h
+++ b/src/core/lib/surface/api_trace.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_API_TRACE_H
 #define GRPC_CORE_LIB_SURFACE_API_TRACE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 
diff --git a/src/core/lib/surface/byte_buffer.cc b/src/core/lib/surface/byte_buffer.cc
index 01cbf73..fce87dc 100644
--- a/src/core/lib/surface/byte_buffer.cc
+++ b/src/core/lib/surface/byte_buffer.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/byte_buffer.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
diff --git a/src/core/lib/surface/byte_buffer_reader.cc b/src/core/lib/surface/byte_buffer_reader.cc
index f7ea516..a10f1a3 100644
--- a/src/core/lib/surface/byte_buffer_reader.cc
+++ b/src/core/lib/surface/byte_buffer_reader.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/byte_buffer_reader.h>
 #include <string.h>
 
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index 2d50775..c4844da 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <assert.h>
 #include <limits.h>
 #include <stdio.h>
@@ -48,6 +50,7 @@
 #include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_metadata.h"
 #include "src/core/lib/transport/transport.h"
 
 /** The maximum number of concurrent batches possible.
@@ -974,32 +977,6 @@
   return 1;
 }
 
-/* we offset status by a small amount when storing it into transport metadata
-   as metadata cannot store a 0 value (which is used as OK for grpc_status_codes
-   */
-#define STATUS_OFFSET 1
-static void destroy_status(void* ignored) {}
-
-static uint32_t decode_status(grpc_mdelem md) {
-  uint32_t status;
-  void* user_data;
-  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) return 0;
-  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) return 1;
-  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) return 2;
-  user_data = grpc_mdelem_get_user_data(md, destroy_status);
-  if (user_data != nullptr) {
-    status = (static_cast<uint32_t>((intptr_t)user_data)) - STATUS_OFFSET;
-  } else {
-    if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) {
-      status = GRPC_STATUS_UNKNOWN; /* could not parse status code */
-    }
-    grpc_mdelem_set_user_data(
-        md, destroy_status,
-        (void*)static_cast<intptr_t>(status + STATUS_OFFSET));
-  }
-  return status;
-}
-
 static grpc_message_compression_algorithm decode_message_compression(
     grpc_mdelem md) {
   grpc_message_compression_algorithm algorithm =
@@ -1091,7 +1068,8 @@
 static void recv_trailing_filter(void* args, grpc_metadata_batch* b) {
   grpc_call* call = static_cast<grpc_call*>(args);
   if (b->idx.named.grpc_status != nullptr) {
-    uint32_t status_code = decode_status(b->idx.named.grpc_status->md);
+    grpc_status_code status_code =
+        grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md);
     grpc_error* error =
         status_code == GRPC_STATUS_OK
             ? GRPC_ERROR_NONE
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index 189329c..793cce4 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_CALL_H
 #define GRPC_CORE_LIB_SURFACE_CALL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/context.h"
 #include "src/core/lib/surface/api_trace.h"
diff --git a/src/core/lib/surface/call_details.cc b/src/core/lib/surface/call_details.cc
index cd0b145..7f20b1d 100644
--- a/src/core/lib/surface/call_details.cc
+++ b/src/core/lib/surface/call_details.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 
diff --git a/src/core/lib/surface/call_log_batch.cc b/src/core/lib/surface/call_log_batch.cc
index d56ea2a..f0c82c0 100644
--- a/src/core/lib/surface/call_log_batch.cc
+++ b/src/core/lib/surface/call_log_batch.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/call.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/surface/call_test_only.h b/src/core/lib/surface/call_test_only.h
index 9eb32f0..dbd1a86 100644
--- a/src/core/lib/surface/call_test_only.h
+++ b/src/core/lib/surface/call_test_only.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H
 #define GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 /** Return the message compression algorithm from \a call.
diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc
index d536027..03353d6 100644
--- a/src/core/lib/surface/channel.cc
+++ b/src/core/lib/surface/channel.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/channel.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 26d8fce..2883139 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_H
 #define GRPC_CORE_LIB_SURFACE_CHANNEL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/channel_stack_builder.h"
 #include "src/core/lib/surface/channel_stack_type.h"
diff --git a/src/core/lib/surface/channel_init.cc b/src/core/lib/surface/channel_init.cc
index b1e1dce..62eb1c3 100644
--- a/src/core/lib/surface/channel_init.cc
+++ b/src/core/lib/surface/channel_init.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/channel_init.h"
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/lib/surface/channel_init.h b/src/core/lib/surface/channel_init.h
index d702f0f..f018524 100644
--- a/src/core/lib/surface/channel_init.h
+++ b/src/core/lib/surface/channel_init.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H
 #define GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack_builder.h"
 #include "src/core/lib/surface/channel_stack_type.h"
 #include "src/core/lib/transport/transport.h"
diff --git a/src/core/lib/surface/channel_ping.cc b/src/core/lib/surface/channel_ping.cc
index 513519d..bae9459 100644
--- a/src/core/lib/surface/channel_ping.cc
+++ b/src/core/lib/surface/channel_ping.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/channel.h"
 
 #include <string.h>
diff --git a/src/core/lib/surface/channel_stack_type.cc b/src/core/lib/surface/channel_stack_type.cc
index 366c452..fcf96dd 100644
--- a/src/core/lib/surface/channel_stack_type.cc
+++ b/src/core/lib/surface/channel_stack_type.cc
@@ -16,10 +16,11 @@
  *
  */
 
-#include "src/core/lib/surface/channel_stack_type.h"
-#include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 
+#include <grpc/support/log.h>
+#include "src/core/lib/surface/channel_stack_type.h"
+
 bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type) {
   switch (type) {
     case GRPC_CLIENT_CHANNEL:
diff --git a/src/core/lib/surface/channel_stack_type.h b/src/core/lib/surface/channel_stack_type.h
index 52f85a6..8a3c08e 100644
--- a/src/core/lib/surface/channel_stack_type.h
+++ b/src/core/lib/surface/channel_stack_type.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H
 #define GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 typedef enum {
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index aea47af..c9dc2d9 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -21,6 +21,8 @@
 
 /* Internal API for completion queues */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/pollset.h"
diff --git a/src/core/lib/surface/completion_queue_factory.cc b/src/core/lib/surface/completion_queue_factory.cc
index d0bb065..51c1183 100644
--- a/src/core/lib/surface/completion_queue_factory.cc
+++ b/src/core/lib/surface/completion_queue_factory.cc
@@ -16,8 +16,10 @@
  *
  */
 
-#include "src/core/lib/surface/completion_queue_factory.h"
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/completion_queue_factory.h"
 
 #include <grpc/support/log.h>
 
diff --git a/src/core/lib/surface/completion_queue_factory.h b/src/core/lib/surface/completion_queue_factory.h
index 89be8f8..d2b30a9 100644
--- a/src/core/lib/surface/completion_queue_factory.h
+++ b/src/core/lib/surface/completion_queue_factory.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H
 #define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include "src/core/lib/surface/completion_queue.h"
 
diff --git a/src/core/lib/surface/event_string.cc b/src/core/lib/surface/event_string.cc
index 7f40bb2..d639bae 100644
--- a/src/core/lib/surface/event_string.cc
+++ b/src/core/lib/surface/event_string.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/event_string.h"
 
 #include <stdio.h>
diff --git a/src/core/lib/surface/event_string.h b/src/core/lib/surface/event_string.h
index cbf96da..e609570 100644
--- a/src/core/lib/surface/event_string.h
+++ b/src/core/lib/surface/event_string.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_EVENT_STRING_H
 #define GRPC_CORE_LIB_SURFACE_EVENT_STRING_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 /* Returns a string describing an event. Must be later freed with gpr_free() */
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index 7bc24a5..ac9f9e6 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -32,7 +32,7 @@
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gpr/fork.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/call_combiner.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -123,7 +123,7 @@
   gpr_mu_lock(&g_init_mu);
   if (++g_initializations == 1) {
     gpr_time_init();
-    gpr_thd_init();
+    grpc_core::Thread::Init();
     grpc_stats_init();
     grpc_slice_intern_init();
     grpc_mdctx_global_init();
diff --git a/src/core/lib/surface/init_secure.cc b/src/core/lib/surface/init_secure.cc
index 75ed9fa..78e983e 100644
--- a/src/core/lib/surface/init_secure.cc
+++ b/src/core/lib/surface/init_secure.cc
@@ -27,9 +27,9 @@
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/lib/security/transport/auth_filters.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
-#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/security/transport/security_handshaker.h"
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/tsi/transport_security_interface.h"
diff --git a/src/core/lib/surface/init_unsecure.cc b/src/core/lib/surface/init_unsecure.cc
index b852cab..2b3bc64 100644
--- a/src/core/lib/surface/init_unsecure.cc
+++ b/src/core/lib/surface/init_unsecure.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/init.h"
 
 void grpc_security_pre_init(void) {}
diff --git a/src/core/lib/surface/lame_client.cc b/src/core/lib/surface/lame_client.cc
index a1f1cf1..5a84428 100644
--- a/src/core/lib/surface/lame_client.cc
+++ b/src/core/lib/surface/lame_client.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 #include <string.h>
@@ -50,14 +52,14 @@
 };
 
 static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) {
-  CallData* calld = reinterpret_cast<CallData*>(elem->call_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
   bool expected = false;
   if (!calld->filled_metadata.compare_exchange_strong(
           expected, true, grpc_core::memory_order_relaxed,
           grpc_core::memory_order_relaxed)) {
     return;
   }
-  ChannelData* chand = reinterpret_cast<ChannelData*>(elem->channel_data);
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   gpr_ltoa(chand->error_code, tmp);
   calld->status.md = grpc_mdelem_from_slices(
@@ -76,7 +78,7 @@
 
 static void lame_start_transport_stream_op_batch(
     grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
-  CallData* calld = reinterpret_cast<CallData*>(elem->call_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
   if (op->recv_initial_metadata) {
     fill_metadata(elem,
                   op->payload->recv_initial_metadata.recv_initial_metadata);
@@ -117,7 +119,7 @@
 
 static grpc_error* init_call_elem(grpc_call_element* elem,
                                   const grpc_call_element_args* args) {
-  CallData* calld = reinterpret_cast<CallData*>(elem->call_data);
+  CallData* calld = static_cast<CallData*>(elem->call_data);
   calld->call_combiner = args->call_combiner;
   return GRPC_ERROR_NONE;
 }
@@ -170,7 +172,7 @@
       "error_message=%s)",
       3, (target, (int)error_code, error_message));
   GPR_ASSERT(elem->filter == &grpc_lame_filter);
-  auto chand = reinterpret_cast<grpc_core::ChannelData*>(elem->channel_data);
+  auto chand = static_cast<grpc_core::ChannelData*>(elem->channel_data);
   chand->error_code = error_code;
   chand->error_message = error_message;
 
diff --git a/src/core/lib/surface/lame_client.h b/src/core/lib/surface/lame_client.h
index 3ce353f..aefa67c 100644
--- a/src/core/lib/surface/lame_client.h
+++ b/src/core/lib/surface/lame_client.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H
 #define GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_lame_filter;
diff --git a/src/core/lib/surface/metadata_array.cc b/src/core/lib/surface/metadata_array.cc
index 0afb8b4..f794a2b 100644
--- a/src/core/lib/surface/metadata_array.cc
+++ b/src/core/lib/surface/metadata_array.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index d71a23a..f7505c8 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/surface/server.h"
 
 #include <limits.h>
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index 63b6dff..c617cc2 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_SERVER_H
 #define GRPC_CORE_LIB_SURFACE_SERVER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/debug/trace.h"
diff --git a/src/core/lib/surface/validate_metadata.cc b/src/core/lib/surface/validate_metadata.cc
index fc94ea7..2dd18f3 100644
--- a/src/core/lib/surface/validate_metadata.cc
+++ b/src/core/lib/surface/validate_metadata.cc
@@ -16,12 +16,13 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <stdlib.h>
 #include <string.h>
 
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
-#include <grpc/support/port_platform.h>
 
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/slice/slice_internal.h"
diff --git a/src/core/lib/surface/validate_metadata.h b/src/core/lib/surface/validate_metadata.h
index ff074b0..e87fb7b 100644
--- a/src/core/lib/surface/validate_metadata.h
+++ b/src/core/lib/surface/validate_metadata.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H
 #define GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include "src/core/lib/iomgr/error.h"
 
diff --git a/src/core/lib/surface/version.cc b/src/core/lib/surface/version.cc
index 51daad0..be196a7 100644
--- a/src/core/lib/surface/version.cc
+++ b/src/core/lib/surface/version.cc
@@ -19,8 +19,10 @@
 /* This file is autogenerated from:
    templates/src/core/surface/version.c.template */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 const char* grpc_version_string(void) { return "6.0.0-dev"; }
 
-const char* grpc_g_stands_for(void) { return "glamorous"; }
+const char* grpc_g_stands_for(void) { return "gorgeous"; }
diff --git a/src/core/lib/transport/bdp_estimator.cc b/src/core/lib/transport/bdp_estimator.cc
index 70b3082..8130535 100644
--- a/src/core/lib/transport/bdp_estimator.cc
+++ b/src/core/lib/transport/bdp_estimator.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/bdp_estimator.h"
 
 #include <inttypes.h>
diff --git a/src/core/lib/transport/byte_stream.cc b/src/core/lib/transport/byte_stream.cc
index afb55b2..e1751f8 100644
--- a/src/core/lib/transport/byte_stream.cc
+++ b/src/core/lib/transport/byte_stream.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/byte_stream.h"
 
 #include <stdlib.h>
@@ -51,7 +53,7 @@
                                      grpc_closure* on_complete) {
   grpc_slice_buffer_stream* stream =
       reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream);
-  GPR_ASSERT(stream->cursor < stream->backing_buffer->count);
+  GPR_ASSERT(stream->cursor < stream->backing_buffer.count);
   return true;
 }
 
@@ -62,9 +64,9 @@
   if (stream->shutdown_error != GRPC_ERROR_NONE) {
     return GRPC_ERROR_REF(stream->shutdown_error);
   }
-  GPR_ASSERT(stream->cursor < stream->backing_buffer->count);
+  GPR_ASSERT(stream->cursor < stream->backing_buffer.count);
   *slice =
-      grpc_slice_ref_internal(stream->backing_buffer->slices[stream->cursor]);
+      grpc_slice_ref_internal(stream->backing_buffer.slices[stream->cursor]);
   stream->cursor++;
   return GRPC_ERROR_NONE;
 }
@@ -80,7 +82,7 @@
 static void slice_buffer_stream_destroy(grpc_byte_stream* byte_stream) {
   grpc_slice_buffer_stream* stream =
       reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream);
-  grpc_slice_buffer_reset_and_unref_internal(stream->backing_buffer);
+  grpc_slice_buffer_destroy(&stream->backing_buffer);
   GRPC_ERROR_UNREF(stream->shutdown_error);
 }
 
@@ -95,7 +97,8 @@
   stream->base.length = static_cast<uint32_t>(slice_buffer->length);
   stream->base.flags = flags;
   stream->base.vtable = &slice_buffer_stream_vtable;
-  stream->backing_buffer = slice_buffer;
+  grpc_slice_buffer_init(&stream->backing_buffer);
+  grpc_slice_buffer_swap(slice_buffer, &stream->backing_buffer);
   stream->cursor = 0;
   stream->shutdown_error = GRPC_ERROR_NONE;
 }
diff --git a/src/core/lib/transport/byte_stream.h b/src/core/lib/transport/byte_stream.h
index 52c7a07..4d3c3c1 100644
--- a/src/core/lib/transport/byte_stream.h
+++ b/src/core/lib/transport/byte_stream.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H
 #define GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice_buffer.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 
@@ -81,7 +83,7 @@
 
 typedef struct grpc_slice_buffer_stream {
   grpc_byte_stream base;
-  grpc_slice_buffer* backing_buffer;
+  grpc_slice_buffer backing_buffer;
   size_t cursor;
   grpc_error* shutdown_error;
 } grpc_slice_buffer_stream;
diff --git a/src/core/lib/transport/connectivity_state.cc b/src/core/lib/transport/connectivity_state.cc
index 17f3529..0122e77 100644
--- a/src/core/lib/transport/connectivity_state.cc
+++ b/src/core/lib/transport/connectivity_state.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/connectivity_state.h"
 
 #include <string.h>
diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h
index c3a50f3..421db5a 100644
--- a/src/core/lib/transport/connectivity_state.h
+++ b/src/core/lib/transport/connectivity_state.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H
 #define GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc
index 79d9043..2eff8b2 100644
--- a/src/core/lib/transport/error_utils.cc
+++ b/src/core/lib/transport/error_utils.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/error_utils.h"
 
 #include <grpc/support/string_util.h>
diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h
index 4100f65..9a46267 100644
--- a/src/core/lib/transport/error_utils.h
+++ b/src/core/lib/transport/error_utils.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
 #define GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/transport/http2_errors.h"
diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc
index e06e0b5..d10194a 100644
--- a/src/core/lib/transport/metadata.cc
+++ b/src/core/lib/transport/metadata.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/metadata.h"
 
 #include <assert.h>
diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
index 82142eb..5e0ecbd 100644
--- a/src/core/lib/transport/metadata.h
+++ b/src/core/lib/transport/metadata.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_H
 #define GRPC_CORE_LIB_TRANSPORT_METADATA_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/slice.h>
 
diff --git a/src/core/lib/transport/metadata_batch.cc b/src/core/lib/transport/metadata_batch.cc
index 9c95339..49740fc 100644
--- a/src/core/lib/transport/metadata_batch.cc
+++ b/src/core/lib/transport/metadata_batch.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/metadata_batch.h"
 
 #include <stdbool.h>
@@ -301,3 +303,27 @@
   }
   return error;
 }
+
+void grpc_metadata_batch_copy(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst,
+                              grpc_linked_mdelem* storage) {
+  grpc_metadata_batch_init(dst);
+  dst->deadline = src->deadline;
+  size_t i = 0;
+  for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr;
+       elem = elem->next) {
+    grpc_error* error = grpc_metadata_batch_add_tail(dst, &storage[i++],
+                                                     GRPC_MDELEM_REF(elem->md));
+    // The only way that grpc_metadata_batch_add_tail() can fail is if
+    // there's a duplicate entry for a callout.  However, that can't be
+    // the case here, because we would not have been allowed to create
+    // a source batch that had that kind of conflict.
+    GPR_ASSERT(error == GRPC_ERROR_NONE);
+  }
+}
+
+void grpc_metadata_batch_move(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst) {
+  *dst = *src;
+  grpc_metadata_batch_init(src);
+}
diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index 8353a42..3876063 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -19,11 +19,12 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
 #define GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include <grpc/grpc.h>
 #include <grpc/slice.h>
-#include <grpc/support/port_platform.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/static_metadata.h"
@@ -136,4 +137,13 @@
   } while (0)
 #endif
 
+/// Copies \a src to \a dst.  \a storage must point to an array of
+/// \a grpc_linked_mdelem structs of at least the same size as \a src.
+void grpc_metadata_batch_copy(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst,
+                              grpc_linked_mdelem* storage);
+
+void grpc_metadata_batch_move(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst);
+
 #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */
diff --git a/src/core/lib/transport/pid_controller.cc b/src/core/lib/transport/pid_controller.cc
index b33ea63..dbc98f4 100644
--- a/src/core/lib/transport/pid_controller.cc
+++ b/src/core/lib/transport/pid_controller.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/pid_controller.h"
 
 #include "src/core/lib/gpr/useful.h"
diff --git a/src/core/lib/transport/pid_controller.h b/src/core/lib/transport/pid_controller.h
index 87e59a1..e26205b 100644
--- a/src/core/lib/transport/pid_controller.h
+++ b/src/core/lib/transport/pid_controller.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H
 #define GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H
 
+#include <grpc/support/port_platform.h>
+
 #include <limits>
 
 /* \file Simple PID controller.
diff --git a/src/core/lib/transport/service_config.cc b/src/core/lib/transport/service_config.cc
index 75196c5..e1a55d9 100644
--- a/src/core/lib/transport/service_config.cc
+++ b/src/core/lib/transport/service_config.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/service_config.h"
 
 #include <string.h>
@@ -29,74 +31,30 @@
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 
-// The main purpose of the code here is to parse the service config in
-// JSON form, which will look like this:
-//
-// {
-//   "loadBalancingPolicy": "string",  // optional
-//   "methodConfig": [  // array of one or more method_config objects
-//     {
-//       "name": [  // array of one or more name objects
-//         {
-//           "service": "string",  // required
-//           "method": "string",  // optional
-//         }
-//       ],
-//       // remaining fields are optional.
-//       // see https://developers.google.com/protocol-buffers/docs/proto3#json
-//       // for format details.
-//       "waitForReady": bool,
-//       "timeout": "duration_string",
-//       "maxRequestMessageBytes": "int64_string",
-//       "maxResponseMessageBytes": "int64_string",
-//     }
-//   ]
-// }
+namespace grpc_core {
 
-struct grpc_service_config {
-  char* json_string;  // Underlying storage for json_tree.
-  grpc_json* json_tree;
-};
-
-grpc_service_config* grpc_service_config_create(const char* json_string) {
-  grpc_service_config* service_config =
-      static_cast<grpc_service_config*>(gpr_malloc(sizeof(*service_config)));
-  service_config->json_string = gpr_strdup(json_string);
-  service_config->json_tree =
-      grpc_json_parse_string(service_config->json_string);
-  if (service_config->json_tree == nullptr) {
+UniquePtr<ServiceConfig> ServiceConfig::Create(const char* json) {
+  UniquePtr<char> json_string(gpr_strdup(json));
+  grpc_json* json_tree = grpc_json_parse_string(json_string.get());
+  if (json_tree == nullptr) {
     gpr_log(GPR_INFO, "failed to parse JSON for service config");
-    gpr_free(service_config->json_string);
-    gpr_free(service_config);
     return nullptr;
   }
-  return service_config;
+  return MakeUnique<ServiceConfig>(std::move(json_string), json_tree);
 }
 
-void grpc_service_config_destroy(grpc_service_config* service_config) {
-  grpc_json_destroy(service_config->json_tree);
-  gpr_free(service_config->json_string);
-  gpr_free(service_config);
-}
+ServiceConfig::ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree)
+    : json_string_(std::move(json_string)), json_tree_(json_tree) {}
 
-void grpc_service_config_parse_global_params(
-    const grpc_service_config* service_config,
-    void (*process_json)(const grpc_json* json, void* arg), void* arg) {
-  const grpc_json* json = service_config->json_tree;
-  if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return;
-  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
-    if (field->key == nullptr) return;
-    if (strcmp(field->key, "methodConfig") == 0) continue;
-    process_json(field, arg);
+ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); }
+
+const char* ServiceConfig::GetLoadBalancingPolicyName() const {
+  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
+    return nullptr;
   }
-}
-
-const char* grpc_service_config_get_lb_policy_name(
-    const grpc_service_config* service_config) {
-  const grpc_json* json = service_config->json_tree;
-  if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return nullptr;
   const char* lb_policy_name = nullptr;
-  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+  for (grpc_json* field = json_tree_->child; field != nullptr;
+       field = field->next) {
     if (field->key == nullptr) return nullptr;
     if (strcmp(field->key, "loadBalancingPolicy") == 0) {
       if (lb_policy_name != nullptr) return nullptr;  // Duplicate.
@@ -107,8 +65,7 @@
   return lb_policy_name;
 }
 
-// Returns the number of names specified in the method config \a json.
-static size_t count_names_in_method_config_json(grpc_json* json) {
+size_t ServiceConfig::CountNamesInMethodConfig(grpc_json* json) {
   size_t num_names = 0;
   for (grpc_json* field = json->child; field != nullptr; field = field->next) {
     if (field->key != nullptr && strcmp(field->key, "name") == 0) {
@@ -122,9 +79,7 @@
   return num_names;
 }
 
-// Returns a path string for the JSON name object specified by \a json.
-// Returns NULL on error.  Caller takes ownership of result.
-static char* parse_json_method_name(grpc_json* json) {
+UniquePtr<char> ServiceConfig::ParseJsonMethodName(grpc_json* json) {
   if (json->type != GRPC_JSON_OBJECT) return nullptr;
   const char* service_name = nullptr;
   const char* method_name = nullptr;
@@ -145,116 +100,7 @@
   char* path;
   gpr_asprintf(&path, "/%s/%s", service_name,
                method_name == nullptr ? "*" : method_name);
-  return path;
+  return UniquePtr<char>(path);
 }
 
-// Parses the method config from \a json.  Adds an entry to \a entries for
-// each name found, incrementing \a idx for each entry added.
-// Returns false on error.
-static bool parse_json_method_config(
-    grpc_json* json, void* (*create_value)(const grpc_json* method_config_json),
-    void* (*ref_value)(void* value), void (*unref_value)(void* value),
-    grpc_slice_hash_table_entry* entries, size_t* idx) {
-  // Construct value.
-  void* method_config = create_value(json);
-  if (method_config == nullptr) return false;
-  // Construct list of paths.
-  bool success = false;
-  gpr_strvec paths;
-  gpr_strvec_init(&paths);
-  for (grpc_json* child = json->child; child != nullptr; child = child->next) {
-    if (child->key == nullptr) continue;
-    if (strcmp(child->key, "name") == 0) {
-      if (child->type != GRPC_JSON_ARRAY) goto done;
-      for (grpc_json* name = child->child; name != nullptr; name = name->next) {
-        char* path = parse_json_method_name(name);
-        if (path == nullptr) goto done;
-        gpr_strvec_add(&paths, path);
-      }
-    }
-  }
-  if (paths.count == 0) goto done;  // No names specified.
-  // Add entry for each path.
-  for (size_t i = 0; i < paths.count; ++i) {
-    entries[*idx].key = grpc_slice_from_copied_string(paths.strs[i]);
-    entries[*idx].value = ref_value(method_config);
-    ++*idx;
-  }
-  success = true;
-done:
-  unref_value(method_config);
-  gpr_strvec_destroy(&paths);
-  return success;
-}
-
-grpc_slice_hash_table* grpc_service_config_create_method_config_table(
-    const grpc_service_config* service_config,
-    void* (*create_value)(const grpc_json* method_config_json),
-    void* (*ref_value)(void* value), void (*unref_value)(void* value)) {
-  const grpc_json* json = service_config->json_tree;
-  // Traverse parsed JSON tree.
-  if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return nullptr;
-  size_t num_entries = 0;
-  grpc_slice_hash_table_entry* entries = nullptr;
-  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
-    if (field->key == nullptr) return nullptr;
-    if (strcmp(field->key, "methodConfig") == 0) {
-      if (entries != nullptr) return nullptr;  // Duplicate.
-      if (field->type != GRPC_JSON_ARRAY) return nullptr;
-      // Find number of entries.
-      for (grpc_json* method = field->child; method != nullptr;
-           method = method->next) {
-        size_t count = count_names_in_method_config_json(method);
-        if (count <= 0) return nullptr;
-        num_entries += count;
-      }
-      // Populate method config table entries.
-      entries = static_cast<grpc_slice_hash_table_entry*>(
-          gpr_malloc(num_entries * sizeof(grpc_slice_hash_table_entry)));
-      size_t idx = 0;
-      for (grpc_json* method = field->child; method != nullptr;
-           method = method->next) {
-        if (!parse_json_method_config(method, create_value, ref_value,
-                                      unref_value, entries, &idx)) {
-          for (size_t i = 0; i < idx; ++i) {
-            grpc_slice_unref_internal(entries[i].key);
-            unref_value(entries[i].value);
-          }
-          gpr_free(entries);
-          return nullptr;
-        }
-      }
-      GPR_ASSERT(idx == num_entries);
-    }
-  }
-  // Instantiate method config table.
-  grpc_slice_hash_table* method_config_table = nullptr;
-  if (entries != nullptr) {
-    method_config_table = grpc_slice_hash_table_create(num_entries, entries,
-                                                       unref_value, nullptr);
-    gpr_free(entries);
-  }
-  return method_config_table;
-}
-
-void* grpc_method_config_table_get(const grpc_slice_hash_table* table,
-                                   grpc_slice path) {
-  void* value = grpc_slice_hash_table_get(table, path);
-  // If we didn't find a match for the path, try looking for a wildcard
-  // entry (i.e., change "/service/method" to "/service/*").
-  if (value == nullptr) {
-    char* path_str = grpc_slice_to_c_string(path);
-    const char* sep = strrchr(path_str, '/') + 1;
-    const size_t len = static_cast<size_t>(sep - path_str);
-    char* buf = static_cast<char*>(gpr_malloc(len + 2));  // '*' and NUL
-    memcpy(buf, path_str, len);
-    buf[len] = '*';
-    buf[len + 1] = '\0';
-    grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
-    gpr_free(buf);
-    value = grpc_slice_hash_table_get(table, wildcard_path);
-    grpc_slice_unref_internal(wildcard_path);
-    gpr_free(path_str);
-  }
-  return value;
-}
+}  // namespace grpc_core
diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h
index 98554b9..a65b267 100644
--- a/src/core/lib/transport/service_config.h
+++ b/src/core/lib/transport/service_config.h
@@ -17,45 +17,233 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
 #define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
 
-#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/port_platform.h>
 
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/json/json.h"
 #include "src/core/lib/slice/slice_hash_table.h"
 
-typedef struct grpc_service_config grpc_service_config;
+// The main purpose of the code here is to parse the service config in
+// JSON form, which will look like this:
+//
+// {
+//   "loadBalancingPolicy": "string",  // optional
+//   "methodConfig": [  // array of one or more method_config objects
+//     {
+//       "name": [  // array of one or more name objects
+//         {
+//           "service": "string",  // required
+//           "method": "string",  // optional
+//         }
+//       ],
+//       // remaining fields are optional.
+//       // see
+//       https://developers.google.com/protocol-buffers/docs/proto3#json
+//       // for format details.
+//       "waitForReady": bool,
+//       "timeout": "duration_string",
+//       "maxRequestMessageBytes": "int64_string",
+//       "maxResponseMessageBytes": "int64_string",
+//     }
+//   ]
+// }
 
-grpc_service_config* grpc_service_config_create(const char* json_string);
-void grpc_service_config_destroy(grpc_service_config* service_config);
+namespace grpc_core {
 
-/// Invokes \a process_json() for each global parameter in the service
-/// config.  \a arg is passed as the second argument to \a process_json().
-void grpc_service_config_parse_global_params(
-    const grpc_service_config* service_config,
-    void (*process_json)(const grpc_json* json, void* arg), void* arg);
+class ServiceConfig {
+ public:
+  /// Creates a new service config from parsing \a json_string.
+  /// Returns null on parse error.
+  static UniquePtr<ServiceConfig> Create(const char* json);
 
-/// Gets the LB policy name from \a service_config.
-/// Returns NULL if no LB policy name was specified.
-/// Caller does NOT take ownership.
-const char* grpc_service_config_get_lb_policy_name(
-    const grpc_service_config* service_config);
+  ~ServiceConfig();
 
-/// Creates a method config table based on the data in \a json.
-/// The table's keys are request paths.  The table's value type is
-/// returned by \a create_value(), based on data parsed from the JSON tree.
-/// \a ref_value() and \a unref_value() are used to ref and unref values.
-/// Returns NULL on error.
-grpc_slice_hash_table* grpc_service_config_create_method_config_table(
-    const grpc_service_config* service_config,
-    void* (*create_value)(const grpc_json* method_config_json),
-    void* (*ref_value)(void* value), void (*unref_value)(void* value));
+  /// Invokes \a process_json() for each global parameter in the service
+  /// config.  \a arg is passed as the second argument to \a process_json().
+  template <typename T>
+  using ProcessJson = void (*)(const grpc_json*, T*);
+  template <typename T>
+  void ParseGlobalParams(ProcessJson<T> process_json, T* arg) const;
 
-/// A helper function for looking up values in the table returned by
-/// \a grpc_service_config_create_method_config_table().
-/// Gets the method config for the specified \a path, which should be of
-/// the form "/service/method".
-/// Returns NULL if the method has no config.
-/// Caller does NOT own a reference to the result.
-void* grpc_method_config_table_get(const grpc_slice_hash_table* table,
-                                   grpc_slice path);
+  /// Gets the LB policy name from \a service_config.
+  /// Returns NULL if no LB policy name was specified.
+  /// Caller does NOT take ownership.
+  const char* GetLoadBalancingPolicyName() const;
+
+  /// Creates a method config table based on the data in \a json.
+  /// The table's keys are request paths.  The table's value type is
+  /// returned by \a create_value(), based on data parsed from the JSON tree.
+  /// Returns null on error.
+  template <typename T>
+  using CreateValue = RefCountedPtr<T> (*)(const grpc_json* method_config_json);
+  template <typename T>
+  RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> CreateMethodConfigTable(
+      CreateValue<T> create_value);
+
+  /// A helper function for looking up values in the table returned by
+  /// \a CreateMethodConfigTable().
+  /// Gets the method config for the specified \a path, which should be of
+  /// the form "/service/method".
+  /// Returns null if the method has no config.
+  /// Caller does NOT own a reference to the result.
+  template <typename T>
+  static RefCountedPtr<T> MethodConfigTableLookup(
+      const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path);
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* New(Args&&... args);
+
+  // Takes ownership of \a json_tree.
+  ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree);
+
+  // Returns the number of names specified in the method config \a json.
+  static size_t CountNamesInMethodConfig(grpc_json* json);
+
+  // Returns a path string for the JSON name object specified by \a json.
+  // Returns null on error.
+  static UniquePtr<char> ParseJsonMethodName(grpc_json* json);
+
+  // Parses the method config from \a json.  Adds an entry to \a entries for
+  // each name found, incrementing \a idx for each entry added.
+  // Returns false on error.
+  template <typename T>
+  static bool ParseJsonMethodConfig(
+      grpc_json* json, CreateValue<T> create_value,
+      typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx);
+
+  UniquePtr<char> json_string_;  // Underlying storage for json_tree.
+  grpc_json* json_tree_;
+};
+
+//
+// implementation -- no user-serviceable parts below
+//
+
+template <typename T>
+void ServiceConfig::ParseGlobalParams(ProcessJson<T> process_json,
+                                      T* arg) const {
+  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
+    return;
+  }
+  for (grpc_json* field = json_tree_->child; field != nullptr;
+       field = field->next) {
+    if (field->key == nullptr) return;
+    if (strcmp(field->key, "methodConfig") == 0) continue;
+    process_json(field, arg);
+  }
+}
+
+template <typename T>
+bool ServiceConfig::ParseJsonMethodConfig(
+    grpc_json* json, CreateValue<T> create_value,
+    typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx) {
+  // Construct value.
+  RefCountedPtr<T> method_config = create_value(json);
+  if (method_config == nullptr) return false;
+  // Construct list of paths.
+  InlinedVector<UniquePtr<char>, 10> paths;
+  for (grpc_json* child = json->child; child != nullptr; child = child->next) {
+    if (child->key == nullptr) continue;
+    if (strcmp(child->key, "name") == 0) {
+      if (child->type != GRPC_JSON_ARRAY) return false;
+      for (grpc_json* name = child->child; name != nullptr; name = name->next) {
+        UniquePtr<char> path = ParseJsonMethodName(name);
+        if (path == nullptr) return false;
+        paths.push_back(std::move(path));
+      }
+    }
+  }
+  if (paths.size() == 0) return false;  // No names specified.
+  // Add entry for each path.
+  for (size_t i = 0; i < paths.size(); ++i) {
+    entries[*idx].key = grpc_slice_from_copied_string(paths[i].get());
+    entries[*idx].value = method_config;  // Takes a new ref.
+    ++*idx;
+  }
+  // Success.
+  return true;
+}
+
+template <typename T>
+RefCountedPtr<SliceHashTable<RefCountedPtr<T>>>
+ServiceConfig::CreateMethodConfigTable(CreateValue<T> create_value) {
+  // Traverse parsed JSON tree.
+  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
+    return nullptr;
+  }
+  size_t num_entries = 0;
+  typename SliceHashTable<RefCountedPtr<T>>::Entry* entries = nullptr;
+  for (grpc_json* field = json_tree_->child; field != nullptr;
+       field = field->next) {
+    if (field->key == nullptr) return nullptr;
+    if (strcmp(field->key, "methodConfig") == 0) {
+      if (entries != nullptr) return nullptr;  // Duplicate.
+      if (field->type != GRPC_JSON_ARRAY) return nullptr;
+      // Find number of entries.
+      for (grpc_json* method = field->child; method != nullptr;
+           method = method->next) {
+        size_t count = CountNamesInMethodConfig(method);
+        if (count <= 0) return nullptr;
+        num_entries += count;
+      }
+      // Populate method config table entries.
+      entries = static_cast<typename SliceHashTable<RefCountedPtr<T>>::Entry*>(
+          gpr_zalloc(num_entries *
+                     sizeof(typename SliceHashTable<RefCountedPtr<T>>::Entry)));
+      size_t idx = 0;
+      for (grpc_json* method = field->child; method != nullptr;
+           method = method->next) {
+        if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) {
+          for (size_t i = 0; i < idx; ++i) {
+            grpc_slice_unref_internal(entries[i].key);
+            entries[i].value.reset();
+          }
+          gpr_free(entries);
+          return nullptr;
+        }
+      }
+      GPR_ASSERT(idx == num_entries);
+    }
+  }
+  // Instantiate method config table.
+  RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> method_config_table;
+  if (entries != nullptr) {
+    method_config_table =
+        SliceHashTable<RefCountedPtr<T>>::Create(num_entries, entries, nullptr);
+    gpr_free(entries);
+  }
+  return method_config_table;
+}
+
+template <typename T>
+RefCountedPtr<T> ServiceConfig::MethodConfigTableLookup(
+    const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path) {
+  const RefCountedPtr<T>* value = table.Get(path);
+  // If we didn't find a match for the path, try looking for a wildcard
+  // entry (i.e., change "/service/method" to "/service/*").
+  if (value == nullptr) {
+    char* path_str = grpc_slice_to_c_string(path);
+    const char* sep = strrchr(path_str, '/') + 1;
+    const size_t len = (size_t)(sep - path_str);
+    char* buf = (char*)gpr_malloc(len + 2);  // '*' and NUL
+    memcpy(buf, path_str, len);
+    buf[len] = '*';
+    buf[len + 1] = '\0';
+    grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
+    gpr_free(buf);
+    value = table.Get(wildcard_path);
+    grpc_slice_unref_internal(wildcard_path);
+    gpr_free(path_str);
+  }
+  return RefCountedPtr<T>(*value);
+}
+
+}  // namespace grpc_core
 
 #endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */
diff --git a/src/core/lib/transport/static_metadata.cc b/src/core/lib/transport/static_metadata.cc
index 5994cbc..6a5144f 100644
--- a/src/core/lib/transport/static_metadata.cc
+++ b/src/core/lib/transport/static_metadata.cc
@@ -24,6 +24,8 @@
  * an explanation of what's going on.
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/static_metadata.h"
 
 #include "src/core/lib/slice/slice_internal.h"
@@ -48,61 +50,64 @@
     114, 110, 97,  108, 45,  115, 116, 114, 101, 97,  109, 45,  101, 110, 99,
     111, 100, 105, 110, 103, 45,  114, 101, 113, 117, 101, 115, 116, 117, 115,
     101, 114, 45,  97,  103, 101, 110, 116, 104, 111, 115, 116, 108, 98,  45,
-    116, 111, 107, 101, 110, 103, 114, 112, 99,  45,  116, 105, 109, 101, 111,
-    117, 116, 103, 114, 112, 99,  46,  119, 97,  105, 116, 95,  102, 111, 114,
-    95,  114, 101, 97,  100, 121, 103, 114, 112, 99,  46,  116, 105, 109, 101,
-    111, 117, 116, 103, 114, 112, 99,  46,  109, 97,  120, 95,  114, 101, 113,
-    117, 101, 115, 116, 95,  109, 101, 115, 115, 97,  103, 101, 95,  98,  121,
-    116, 101, 115, 103, 114, 112, 99,  46,  109, 97,  120, 95,  114, 101, 115,
-    112, 111, 110, 115, 101, 95,  109, 101, 115, 115, 97,  103, 101, 95,  98,
-    121, 116, 101, 115, 47,  103, 114, 112, 99,  46,  108, 98,  46,  118, 49,
-    46,  76,  111, 97,  100, 66,  97,  108, 97,  110, 99,  101, 114, 47,  66,
-    97,  108, 97,  110, 99,  101, 76,  111, 97,  100, 100, 101, 102, 108, 97,
-    116, 101, 103, 122, 105, 112, 115, 116, 114, 101, 97,  109, 47,  103, 122,
-    105, 112, 48,  49,  50,  105, 100, 101, 110, 116, 105, 116, 121, 116, 114,
-    97,  105, 108, 101, 114, 115, 97,  112, 112, 108, 105, 99,  97,  116, 105,
-    111, 110, 47,  103, 114, 112, 99,  80,  79,  83,  84,  50,  48,  48,  52,
-    48,  52,  104, 116, 116, 112, 104, 116, 116, 112, 115, 103, 114, 112, 99,
-    71,  69,  84,  80,  85,  84,  47,  47,  105, 110, 100, 101, 120, 46,  104,
-    116, 109, 108, 50,  48,  52,  50,  48,  54,  51,  48,  52,  52,  48,  48,
-    53,  48,  48,  97,  99,  99,  101, 112, 116, 45,  99,  104, 97,  114, 115,
-    101, 116, 103, 122, 105, 112, 44,  32,  100, 101, 102, 108, 97,  116, 101,
-    97,  99,  99,  101, 112, 116, 45,  108, 97,  110, 103, 117, 97,  103, 101,
-    97,  99,  99,  101, 112, 116, 45,  114, 97,  110, 103, 101, 115, 97,  99,
-    99,  101, 112, 116, 97,  99,  99,  101, 115, 115, 45,  99,  111, 110, 116,
-    114, 111, 108, 45,  97,  108, 108, 111, 119, 45,  111, 114, 105, 103, 105,
-    110, 97,  103, 101, 97,  108, 108, 111, 119, 97,  117, 116, 104, 111, 114,
-    105, 122, 97,  116, 105, 111, 110, 99,  97,  99,  104, 101, 45,  99,  111,
-    110, 116, 114, 111, 108, 99,  111, 110, 116, 101, 110, 116, 45,  100, 105,
-    115, 112, 111, 115, 105, 116, 105, 111, 110, 99,  111, 110, 116, 101, 110,
-    116, 45,  108, 97,  110, 103, 117, 97,  103, 101, 99,  111, 110, 116, 101,
-    110, 116, 45,  108, 101, 110, 103, 116, 104, 99,  111, 110, 116, 101, 110,
-    116, 45,  108, 111, 99,  97,  116, 105, 111, 110, 99,  111, 110, 116, 101,
-    110, 116, 45,  114, 97,  110, 103, 101, 99,  111, 111, 107, 105, 101, 100,
-    97,  116, 101, 101, 116, 97,  103, 101, 120, 112, 101, 99,  116, 101, 120,
-    112, 105, 114, 101, 115, 102, 114, 111, 109, 105, 102, 45,  109, 97,  116,
-    99,  104, 105, 102, 45,  109, 111, 100, 105, 102, 105, 101, 100, 45,  115,
-    105, 110, 99,  101, 105, 102, 45,  110, 111, 110, 101, 45,  109, 97,  116,
-    99,  104, 105, 102, 45,  114, 97,  110, 103, 101, 105, 102, 45,  117, 110,
-    109, 111, 100, 105, 102, 105, 101, 100, 45,  115, 105, 110, 99,  101, 108,
-    97,  115, 116, 45,  109, 111, 100, 105, 102, 105, 101, 100, 108, 98,  45,
-    99,  111, 115, 116, 45,  98,  105, 110, 108, 105, 110, 107, 108, 111, 99,
-    97,  116, 105, 111, 110, 109, 97,  120, 45,  102, 111, 114, 119, 97,  114,
-    100, 115, 112, 114, 111, 120, 121, 45,  97,  117, 116, 104, 101, 110, 116,
-    105, 99,  97,  116, 101, 112, 114, 111, 120, 121, 45,  97,  117, 116, 104,
-    111, 114, 105, 122, 97,  116, 105, 111, 110, 114, 97,  110, 103, 101, 114,
-    101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, 115, 104, 114, 101,
-    116, 114, 121, 45,  97,  102, 116, 101, 114, 115, 101, 114, 118, 101, 114,
-    115, 101, 116, 45,  99,  111, 111, 107, 105, 101, 115, 116, 114, 105, 99,
-    116, 45,  116, 114, 97,  110, 115, 112, 111, 114, 116, 45,  115, 101, 99,
-    117, 114, 105, 116, 121, 116, 114, 97,  110, 115, 102, 101, 114, 45,  101,
-    110, 99,  111, 100, 105, 110, 103, 118, 97,  114, 121, 118, 105, 97,  119,
-    119, 119, 45,  97,  117, 116, 104, 101, 110, 116, 105, 99,  97,  116, 101,
-    105, 100, 101, 110, 116, 105, 116, 121, 44,  100, 101, 102, 108, 97,  116,
-    101, 105, 100, 101, 110, 116, 105, 116, 121, 44,  103, 122, 105, 112, 100,
-    101, 102, 108, 97,  116, 101, 44,  103, 122, 105, 112, 105, 100, 101, 110,
-    116, 105, 116, 121, 44,  100, 101, 102, 108, 97,  116, 101, 44,  103, 122,
-    105, 112};
+    116, 111, 107, 101, 110, 103, 114, 112, 99,  45,  112, 114, 101, 118, 105,
+    111, 117, 115, 45,  114, 112, 99,  45,  97,  116, 116, 101, 109, 112, 116,
+    115, 103, 114, 112, 99,  45,  114, 101, 116, 114, 121, 45,  112, 117, 115,
+    104, 98,  97,  99,  107, 45,  109, 115, 103, 114, 112, 99,  45,  116, 105,
+    109, 101, 111, 117, 116, 49,  50,  51,  52,  103, 114, 112, 99,  46,  119,
+    97,  105, 116, 95,  102, 111, 114, 95,  114, 101, 97,  100, 121, 103, 114,
+    112, 99,  46,  116, 105, 109, 101, 111, 117, 116, 103, 114, 112, 99,  46,
+    109, 97,  120, 95,  114, 101, 113, 117, 101, 115, 116, 95,  109, 101, 115,
+    115, 97,  103, 101, 95,  98,  121, 116, 101, 115, 103, 114, 112, 99,  46,
+    109, 97,  120, 95,  114, 101, 115, 112, 111, 110, 115, 101, 95,  109, 101,
+    115, 115, 97,  103, 101, 95,  98,  121, 116, 101, 115, 47,  103, 114, 112,
+    99,  46,  108, 98,  46,  118, 49,  46,  76,  111, 97,  100, 66,  97,  108,
+    97,  110, 99,  101, 114, 47,  66,  97,  108, 97,  110, 99,  101, 76,  111,
+    97,  100, 100, 101, 102, 108, 97,  116, 101, 103, 122, 105, 112, 115, 116,
+    114, 101, 97,  109, 47,  103, 122, 105, 112, 48,  105, 100, 101, 110, 116,
+    105, 116, 121, 116, 114, 97,  105, 108, 101, 114, 115, 97,  112, 112, 108,
+    105, 99,  97,  116, 105, 111, 110, 47,  103, 114, 112, 99,  80,  79,  83,
+    84,  50,  48,  48,  52,  48,  52,  104, 116, 116, 112, 104, 116, 116, 112,
+    115, 103, 114, 112, 99,  71,  69,  84,  80,  85,  84,  47,  47,  105, 110,
+    100, 101, 120, 46,  104, 116, 109, 108, 50,  48,  52,  50,  48,  54,  51,
+    48,  52,  52,  48,  48,  53,  48,  48,  97,  99,  99,  101, 112, 116, 45,
+    99,  104, 97,  114, 115, 101, 116, 103, 122, 105, 112, 44,  32,  100, 101,
+    102, 108, 97,  116, 101, 97,  99,  99,  101, 112, 116, 45,  108, 97,  110,
+    103, 117, 97,  103, 101, 97,  99,  99,  101, 112, 116, 45,  114, 97,  110,
+    103, 101, 115, 97,  99,  99,  101, 112, 116, 97,  99,  99,  101, 115, 115,
+    45,  99,  111, 110, 116, 114, 111, 108, 45,  97,  108, 108, 111, 119, 45,
+    111, 114, 105, 103, 105, 110, 97,  103, 101, 97,  108, 108, 111, 119, 97,
+    117, 116, 104, 111, 114, 105, 122, 97,  116, 105, 111, 110, 99,  97,  99,
+    104, 101, 45,  99,  111, 110, 116, 114, 111, 108, 99,  111, 110, 116, 101,
+    110, 116, 45,  100, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 99,
+    111, 110, 116, 101, 110, 116, 45,  108, 97,  110, 103, 117, 97,  103, 101,
+    99,  111, 110, 116, 101, 110, 116, 45,  108, 101, 110, 103, 116, 104, 99,
+    111, 110, 116, 101, 110, 116, 45,  108, 111, 99,  97,  116, 105, 111, 110,
+    99,  111, 110, 116, 101, 110, 116, 45,  114, 97,  110, 103, 101, 99,  111,
+    111, 107, 105, 101, 100, 97,  116, 101, 101, 116, 97,  103, 101, 120, 112,
+    101, 99,  116, 101, 120, 112, 105, 114, 101, 115, 102, 114, 111, 109, 105,
+    102, 45,  109, 97,  116, 99,  104, 105, 102, 45,  109, 111, 100, 105, 102,
+    105, 101, 100, 45,  115, 105, 110, 99,  101, 105, 102, 45,  110, 111, 110,
+    101, 45,  109, 97,  116, 99,  104, 105, 102, 45,  114, 97,  110, 103, 101,
+    105, 102, 45,  117, 110, 109, 111, 100, 105, 102, 105, 101, 100, 45,  115,
+    105, 110, 99,  101, 108, 97,  115, 116, 45,  109, 111, 100, 105, 102, 105,
+    101, 100, 108, 98,  45,  99,  111, 115, 116, 45,  98,  105, 110, 108, 105,
+    110, 107, 108, 111, 99,  97,  116, 105, 111, 110, 109, 97,  120, 45,  102,
+    111, 114, 119, 97,  114, 100, 115, 112, 114, 111, 120, 121, 45,  97,  117,
+    116, 104, 101, 110, 116, 105, 99,  97,  116, 101, 112, 114, 111, 120, 121,
+    45,  97,  117, 116, 104, 111, 114, 105, 122, 97,  116, 105, 111, 110, 114,
+    97,  110, 103, 101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114,
+    101, 115, 104, 114, 101, 116, 114, 121, 45,  97,  102, 116, 101, 114, 115,
+    101, 114, 118, 101, 114, 115, 101, 116, 45,  99,  111, 111, 107, 105, 101,
+    115, 116, 114, 105, 99,  116, 45,  116, 114, 97,  110, 115, 112, 111, 114,
+    116, 45,  115, 101, 99,  117, 114, 105, 116, 121, 116, 114, 97,  110, 115,
+    102, 101, 114, 45,  101, 110, 99,  111, 100, 105, 110, 103, 118, 97,  114,
+    121, 118, 105, 97,  119, 119, 119, 45,  97,  117, 116, 104, 101, 110, 116,
+    105, 99,  97,  116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44,  100,
+    101, 102, 108, 97,  116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44,
+    103, 122, 105, 112, 100, 101, 102, 108, 97,  116, 101, 44,  103, 122, 105,
+    112, 105, 100, 101, 110, 116, 105, 116, 121, 44,  100, 101, 102, 108, 97,
+    116, 101, 44,  103, 122, 105, 112};
 
 static void static_ref(void* unused) {}
 static void static_unref(void* unused) {}
@@ -215,6 +220,10 @@
     {&grpc_static_metadata_vtable, &static_sub_refcnt},
     {&grpc_static_metadata_vtable, &static_sub_refcnt},
     {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
 };
 
 const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = {
@@ -240,85 +249,89 @@
     {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}},
     {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}},
     {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}},
-    {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 12}}},
-    {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}},
-    {&grpc_static_metadata_refcounts[24], {{g_bytes + 302, 19}}},
-    {&grpc_static_metadata_refcounts[25], {{g_bytes + 321, 12}}},
-    {&grpc_static_metadata_refcounts[26], {{g_bytes + 333, 30}}},
-    {&grpc_static_metadata_refcounts[27], {{g_bytes + 363, 31}}},
-    {&grpc_static_metadata_refcounts[28], {{g_bytes + 394, 36}}},
-    {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}},
-    {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}},
-    {&grpc_static_metadata_refcounts[31], {{g_bytes + 441, 11}}},
-    {&grpc_static_metadata_refcounts[32], {{g_bytes + 452, 1}}},
-    {&grpc_static_metadata_refcounts[33], {{g_bytes + 453, 1}}},
-    {&grpc_static_metadata_refcounts[34], {{g_bytes + 454, 1}}},
-    {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}},
-    {&grpc_static_metadata_refcounts[36], {{g_bytes + 463, 8}}},
-    {&grpc_static_metadata_refcounts[37], {{g_bytes + 471, 16}}},
-    {&grpc_static_metadata_refcounts[38], {{g_bytes + 487, 4}}},
-    {&grpc_static_metadata_refcounts[39], {{g_bytes + 491, 3}}},
-    {&grpc_static_metadata_refcounts[40], {{g_bytes + 494, 3}}},
-    {&grpc_static_metadata_refcounts[41], {{g_bytes + 497, 4}}},
-    {&grpc_static_metadata_refcounts[42], {{g_bytes + 501, 5}}},
-    {&grpc_static_metadata_refcounts[43], {{g_bytes + 506, 4}}},
-    {&grpc_static_metadata_refcounts[44], {{g_bytes + 510, 3}}},
-    {&grpc_static_metadata_refcounts[45], {{g_bytes + 513, 3}}},
-    {&grpc_static_metadata_refcounts[46], {{g_bytes + 516, 1}}},
-    {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 11}}},
-    {&grpc_static_metadata_refcounts[48], {{g_bytes + 528, 3}}},
-    {&grpc_static_metadata_refcounts[49], {{g_bytes + 531, 3}}},
-    {&grpc_static_metadata_refcounts[50], {{g_bytes + 534, 3}}},
-    {&grpc_static_metadata_refcounts[51], {{g_bytes + 537, 3}}},
-    {&grpc_static_metadata_refcounts[52], {{g_bytes + 540, 3}}},
-    {&grpc_static_metadata_refcounts[53], {{g_bytes + 543, 14}}},
-    {&grpc_static_metadata_refcounts[54], {{g_bytes + 557, 13}}},
-    {&grpc_static_metadata_refcounts[55], {{g_bytes + 570, 15}}},
-    {&grpc_static_metadata_refcounts[56], {{g_bytes + 585, 13}}},
-    {&grpc_static_metadata_refcounts[57], {{g_bytes + 598, 6}}},
-    {&grpc_static_metadata_refcounts[58], {{g_bytes + 604, 27}}},
-    {&grpc_static_metadata_refcounts[59], {{g_bytes + 631, 3}}},
-    {&grpc_static_metadata_refcounts[60], {{g_bytes + 634, 5}}},
-    {&grpc_static_metadata_refcounts[61], {{g_bytes + 639, 13}}},
-    {&grpc_static_metadata_refcounts[62], {{g_bytes + 652, 13}}},
-    {&grpc_static_metadata_refcounts[63], {{g_bytes + 665, 19}}},
-    {&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 16}}},
-    {&grpc_static_metadata_refcounts[65], {{g_bytes + 700, 14}}},
-    {&grpc_static_metadata_refcounts[66], {{g_bytes + 714, 16}}},
-    {&grpc_static_metadata_refcounts[67], {{g_bytes + 730, 13}}},
-    {&grpc_static_metadata_refcounts[68], {{g_bytes + 743, 6}}},
-    {&grpc_static_metadata_refcounts[69], {{g_bytes + 749, 4}}},
-    {&grpc_static_metadata_refcounts[70], {{g_bytes + 753, 4}}},
-    {&grpc_static_metadata_refcounts[71], {{g_bytes + 757, 6}}},
-    {&grpc_static_metadata_refcounts[72], {{g_bytes + 763, 7}}},
-    {&grpc_static_metadata_refcounts[73], {{g_bytes + 770, 4}}},
-    {&grpc_static_metadata_refcounts[74], {{g_bytes + 774, 8}}},
-    {&grpc_static_metadata_refcounts[75], {{g_bytes + 782, 17}}},
-    {&grpc_static_metadata_refcounts[76], {{g_bytes + 799, 13}}},
-    {&grpc_static_metadata_refcounts[77], {{g_bytes + 812, 8}}},
-    {&grpc_static_metadata_refcounts[78], {{g_bytes + 820, 19}}},
-    {&grpc_static_metadata_refcounts[79], {{g_bytes + 839, 13}}},
-    {&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 11}}},
-    {&grpc_static_metadata_refcounts[81], {{g_bytes + 863, 4}}},
-    {&grpc_static_metadata_refcounts[82], {{g_bytes + 867, 8}}},
-    {&grpc_static_metadata_refcounts[83], {{g_bytes + 875, 12}}},
-    {&grpc_static_metadata_refcounts[84], {{g_bytes + 887, 18}}},
-    {&grpc_static_metadata_refcounts[85], {{g_bytes + 905, 19}}},
-    {&grpc_static_metadata_refcounts[86], {{g_bytes + 924, 5}}},
-    {&grpc_static_metadata_refcounts[87], {{g_bytes + 929, 7}}},
-    {&grpc_static_metadata_refcounts[88], {{g_bytes + 936, 7}}},
-    {&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 11}}},
-    {&grpc_static_metadata_refcounts[90], {{g_bytes + 954, 6}}},
-    {&grpc_static_metadata_refcounts[91], {{g_bytes + 960, 10}}},
-    {&grpc_static_metadata_refcounts[92], {{g_bytes + 970, 25}}},
-    {&grpc_static_metadata_refcounts[93], {{g_bytes + 995, 17}}},
-    {&grpc_static_metadata_refcounts[94], {{g_bytes + 1012, 4}}},
-    {&grpc_static_metadata_refcounts[95], {{g_bytes + 1016, 3}}},
-    {&grpc_static_metadata_refcounts[96], {{g_bytes + 1019, 16}}},
-    {&grpc_static_metadata_refcounts[97], {{g_bytes + 1035, 16}}},
-    {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}},
-    {&grpc_static_metadata_refcounts[99], {{g_bytes + 1064, 12}}},
-    {&grpc_static_metadata_refcounts[100], {{g_bytes + 1076, 21}}},
+    {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 26}}},
+    {&grpc_static_metadata_refcounts[23], {{g_bytes + 316, 22}}},
+    {&grpc_static_metadata_refcounts[24], {{g_bytes + 338, 12}}},
+    {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}},
+    {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}},
+    {&grpc_static_metadata_refcounts[27], {{g_bytes + 352, 1}}},
+    {&grpc_static_metadata_refcounts[28], {{g_bytes + 353, 1}}},
+    {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}},
+    {&grpc_static_metadata_refcounts[30], {{g_bytes + 354, 19}}},
+    {&grpc_static_metadata_refcounts[31], {{g_bytes + 373, 12}}},
+    {&grpc_static_metadata_refcounts[32], {{g_bytes + 385, 30}}},
+    {&grpc_static_metadata_refcounts[33], {{g_bytes + 415, 31}}},
+    {&grpc_static_metadata_refcounts[34], {{g_bytes + 446, 36}}},
+    {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}},
+    {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}},
+    {&grpc_static_metadata_refcounts[37], {{g_bytes + 493, 11}}},
+    {&grpc_static_metadata_refcounts[38], {{g_bytes + 504, 1}}},
+    {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}},
+    {&grpc_static_metadata_refcounts[40], {{g_bytes + 513, 8}}},
+    {&grpc_static_metadata_refcounts[41], {{g_bytes + 521, 16}}},
+    {&grpc_static_metadata_refcounts[42], {{g_bytes + 537, 4}}},
+    {&grpc_static_metadata_refcounts[43], {{g_bytes + 541, 3}}},
+    {&grpc_static_metadata_refcounts[44], {{g_bytes + 544, 3}}},
+    {&grpc_static_metadata_refcounts[45], {{g_bytes + 547, 4}}},
+    {&grpc_static_metadata_refcounts[46], {{g_bytes + 551, 5}}},
+    {&grpc_static_metadata_refcounts[47], {{g_bytes + 556, 4}}},
+    {&grpc_static_metadata_refcounts[48], {{g_bytes + 560, 3}}},
+    {&grpc_static_metadata_refcounts[49], {{g_bytes + 563, 3}}},
+    {&grpc_static_metadata_refcounts[50], {{g_bytes + 566, 1}}},
+    {&grpc_static_metadata_refcounts[51], {{g_bytes + 567, 11}}},
+    {&grpc_static_metadata_refcounts[52], {{g_bytes + 578, 3}}},
+    {&grpc_static_metadata_refcounts[53], {{g_bytes + 581, 3}}},
+    {&grpc_static_metadata_refcounts[54], {{g_bytes + 584, 3}}},
+    {&grpc_static_metadata_refcounts[55], {{g_bytes + 587, 3}}},
+    {&grpc_static_metadata_refcounts[56], {{g_bytes + 590, 3}}},
+    {&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 14}}},
+    {&grpc_static_metadata_refcounts[58], {{g_bytes + 607, 13}}},
+    {&grpc_static_metadata_refcounts[59], {{g_bytes + 620, 15}}},
+    {&grpc_static_metadata_refcounts[60], {{g_bytes + 635, 13}}},
+    {&grpc_static_metadata_refcounts[61], {{g_bytes + 648, 6}}},
+    {&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 27}}},
+    {&grpc_static_metadata_refcounts[63], {{g_bytes + 681, 3}}},
+    {&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 5}}},
+    {&grpc_static_metadata_refcounts[65], {{g_bytes + 689, 13}}},
+    {&grpc_static_metadata_refcounts[66], {{g_bytes + 702, 13}}},
+    {&grpc_static_metadata_refcounts[67], {{g_bytes + 715, 19}}},
+    {&grpc_static_metadata_refcounts[68], {{g_bytes + 734, 16}}},
+    {&grpc_static_metadata_refcounts[69], {{g_bytes + 750, 14}}},
+    {&grpc_static_metadata_refcounts[70], {{g_bytes + 764, 16}}},
+    {&grpc_static_metadata_refcounts[71], {{g_bytes + 780, 13}}},
+    {&grpc_static_metadata_refcounts[72], {{g_bytes + 793, 6}}},
+    {&grpc_static_metadata_refcounts[73], {{g_bytes + 799, 4}}},
+    {&grpc_static_metadata_refcounts[74], {{g_bytes + 803, 4}}},
+    {&grpc_static_metadata_refcounts[75], {{g_bytes + 807, 6}}},
+    {&grpc_static_metadata_refcounts[76], {{g_bytes + 813, 7}}},
+    {&grpc_static_metadata_refcounts[77], {{g_bytes + 820, 4}}},
+    {&grpc_static_metadata_refcounts[78], {{g_bytes + 824, 8}}},
+    {&grpc_static_metadata_refcounts[79], {{g_bytes + 832, 17}}},
+    {&grpc_static_metadata_refcounts[80], {{g_bytes + 849, 13}}},
+    {&grpc_static_metadata_refcounts[81], {{g_bytes + 862, 8}}},
+    {&grpc_static_metadata_refcounts[82], {{g_bytes + 870, 19}}},
+    {&grpc_static_metadata_refcounts[83], {{g_bytes + 889, 13}}},
+    {&grpc_static_metadata_refcounts[84], {{g_bytes + 902, 11}}},
+    {&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 4}}},
+    {&grpc_static_metadata_refcounts[86], {{g_bytes + 917, 8}}},
+    {&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 12}}},
+    {&grpc_static_metadata_refcounts[88], {{g_bytes + 937, 18}}},
+    {&grpc_static_metadata_refcounts[89], {{g_bytes + 955, 19}}},
+    {&grpc_static_metadata_refcounts[90], {{g_bytes + 974, 5}}},
+    {&grpc_static_metadata_refcounts[91], {{g_bytes + 979, 7}}},
+    {&grpc_static_metadata_refcounts[92], {{g_bytes + 986, 7}}},
+    {&grpc_static_metadata_refcounts[93], {{g_bytes + 993, 11}}},
+    {&grpc_static_metadata_refcounts[94], {{g_bytes + 1004, 6}}},
+    {&grpc_static_metadata_refcounts[95], {{g_bytes + 1010, 10}}},
+    {&grpc_static_metadata_refcounts[96], {{g_bytes + 1020, 25}}},
+    {&grpc_static_metadata_refcounts[97], {{g_bytes + 1045, 17}}},
+    {&grpc_static_metadata_refcounts[98], {{g_bytes + 1062, 4}}},
+    {&grpc_static_metadata_refcounts[99], {{g_bytes + 1066, 3}}},
+    {&grpc_static_metadata_refcounts[100], {{g_bytes + 1069, 16}}},
+    {&grpc_static_metadata_refcounts[101], {{g_bytes + 1085, 16}}},
+    {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}},
+    {&grpc_static_metadata_refcounts[103], {{g_bytes + 1114, 12}}},
+    {&grpc_static_metadata_refcounts[104], {{g_bytes + 1126, 21}}},
 };
 
 uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {
@@ -328,50 +341,51 @@
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4};
 
 static const int8_t elems_r[] = {
-    13, 2,  1,   0,  15,  4,   0,   21,  0,   23,  -3, 0,  0,  0,  10, 19, -4,
-    0,  0,  1,   10, 9,   0,   0,   0,   0,   0,   0,  0,  0,  0,  0,  0,  0,
-    0,  0,  0,   0,  0,   0,   0,   0,   0,   0,   0,  0,  0,  0,  0,  0,  0,
-    0,  0,  -52, 0,  -55, -36, -57, -58, -58, -58, 0,  40, 39, 38, 37, 36, 35,
-    34, 33, 32,  31, 30,  29,  28,  28,  27,  26,  25, 24, 23, 22, 21, 20, 19,
-    18, 17, 16,  15, 18,  17,  16,  15,  14,  13,  12, 11, 11, 0};
+    16, 11, -1, 0,   15, 2,   -78, 24,  0,  18, -5, 0,  0,  0,  17, 14, -8, 0,
+    0,  27, 8,  7,   0,  0,   0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,   0,  0,   0,   0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  -64, 0,  -44, -43, -70, 0,  34, 33, 33, 32, 31, 30, 29, 28, 27,
+    27, 26, 25, 24,  23, 22,  21,  20,  20, 19, 19, 18, 17, 16, 15, 14, 13, 12,
+    11, 14, 13, 12,  11, 10,  9,   9,   8,  7,  6,  5,  0};
 static uint32_t elems_phash(uint32_t i) {
-  i -= 46;
-  uint32_t x = i % 99;
-  uint32_t y = i / 99;
+  i -= 50;
+  uint32_t x = i % 103;
+  uint32_t y = i / 103;
   uint32_t h = x;
   if (y < GPR_ARRAY_SIZE(elems_r)) {
-    uint32_t delta = static_cast<uint32_t>(elems_r[y]);
+    uint32_t delta = (uint32_t)elems_r[y];
     h += delta;
   }
   return h;
 }
 
 static const uint16_t elem_keys[] = {
-    1039, 1040, 145,  146,  541,  1639, 1045, 250,  251,  252,  253,  254,
-    1646, 46,   47,   1437, 1942, 1651, 445,  446,  447,  739,  740,  741,
-    938,  939,  1538, 2043, 2144, 1451, 944,  5376, 5578, 1545, 5780, 5881,
-    1670, 5982, 1550, 6083, 6184, 6285, 6386, 6487, 6588, 6689, 6790, 6891,
-    6992, 7093, 7194, 7295, 7396, 5679, 7497, 7598, 7699, 7800, 7901, 8002,
-    8103, 8204, 8305, 8406, 8507, 8608, 8709, 8810, 1107, 1108, 1109, 1110,
-    8911, 9012, 9113, 9214, 9315, 9416, 9517, 9618, 1714, 9719, 0,    326,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    241,  242,  0,    0,    0,    0,    0,    0,    139,  0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0};
+    1085,  1086,  565,   1709,  1089,  262,  263,  264,  265,  266,   1716,
+    153,   154,   1719,  760,   761,   50,   51,   465,  466,  467,   980,
+    981,   1604,  1499,  984,   773,   2129, 2234, 6014, 1611, 6434,  1738,
+    1614,  6539,  6644,  1511,  6749,  6854, 6959, 7064, 7169, 7274,  7379,
+    2024,  7484,  7589,  7694,  7799,  7904, 8009, 8114, 8219, 6224,  8324,
+    8429,  6329,  8534,  8639,  8744,  8849, 8954, 9059, 9164, 9269,  9374,
+    1151,  1152,  1153,  1154,  9479,  9584, 9689, 9794, 9899, 10004, 1782,
+    10109, 10214, 10319, 10424, 10529, 0,    0,    0,    0,    0,     344,
+    0,     0,     0,     0,     0,     0,    0,    0,    0,    0,     0,
+    0,     0,     0,     0,     0,     0,    0,    0,    0,    0,     0,
+    0,     253,   254,   147,   0,     0,    0,    0,    0,    0,     0,
+    0,     0,     0,     0,     0,     0,    0,    0,    0,    0,     0,
+    0,     0,     0,     0};
 static const uint8_t elem_idxs[] = {
-    77,  79,  15,  16,  6,   25,  76,  19,  20,  21,  22,  23,  84,  17,
-    18,  43,  72,  83,  11,  12,  13,  0,   1,   2,   5,   4,   38,  50,
-    57,  7,   3,   24,  27,  37,  29,  30,  26,  31,  36,  32,  33,  34,
-    35,  39,  40,  41,  42,  44,  45,  46,  47,  48,  49,  28,  51,  52,
-    53,  54,  55,  56,  58,  59,  60,  61,  62,  63,  64,  65,  78,  80,
-    81,  82,  66,  67,  68,  69,  70,  71,  73,  74,  85,  75,  255, 14,
-    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-    9,   10,  255, 255, 255, 255, 255, 255, 8};
+    77,  79,  6,   25,  76,  19,  20,  21,  22,  23,  84,  15,  16,  83,  1,
+    2,   17,  18,  11,  12,  13,  5,   4,   38,  43,  3,   0,   50,  57,  24,
+    37,  29,  26,  36,  30,  31,  7,   32,  33,  34,  35,  39,  40,  41,  72,
+    42,  44,  45,  46,  47,  48,  49,  51,  27,  52,  53,  28,  54,  55,  56,
+    58,  59,  60,  61,  62,  63,  78,  80,  81,  82,  64,  65,  66,  67,  68,
+    69,  85,  70,  71,  73,  74,  75,  255, 255, 255, 255, 255, 14,  255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 9,   10,  8};
 
 grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) {
   if (a == -1 || b == -1) return GRPC_MDNULL;
-  uint32_t k = static_cast<uint32_t>(a * 101 + b);
+  uint32_t k = (uint32_t)(a * 105 + b);
   uint32_t h = elems_phash(k);
   return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k &&
                  elem_idxs[h] != 255
@@ -382,177 +396,177 @@
 
 grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = {
     {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
-     {&grpc_static_metadata_refcounts[32], {{g_bytes + 452, 1}}}},
+     {&grpc_static_metadata_refcounts[38], {{g_bytes + 504, 1}}}},
     {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
-     {&grpc_static_metadata_refcounts[33], {{g_bytes + 453, 1}}}},
+     {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}},
     {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
-     {&grpc_static_metadata_refcounts[34], {{g_bytes + 454, 1}}}},
+     {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}},
     {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
-     {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}},
+     {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}},
     {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
-     {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}},
+     {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}},
     {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
-     {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}},
+     {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}},
     {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}},
-     {&grpc_static_metadata_refcounts[36], {{g_bytes + 463, 8}}}},
+     {&grpc_static_metadata_refcounts[40], {{g_bytes + 513, 8}}}},
     {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}},
-     {&grpc_static_metadata_refcounts[37], {{g_bytes + 471, 16}}}},
+     {&grpc_static_metadata_refcounts[41], {{g_bytes + 521, 16}}}},
     {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
-     {&grpc_static_metadata_refcounts[38], {{g_bytes + 487, 4}}}},
+     {&grpc_static_metadata_refcounts[42], {{g_bytes + 537, 4}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[39], {{g_bytes + 491, 3}}}},
+     {&grpc_static_metadata_refcounts[43], {{g_bytes + 541, 3}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[40], {{g_bytes + 494, 3}}}},
+     {&grpc_static_metadata_refcounts[44], {{g_bytes + 544, 3}}}},
     {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
-     {&grpc_static_metadata_refcounts[41], {{g_bytes + 497, 4}}}},
+     {&grpc_static_metadata_refcounts[45], {{g_bytes + 547, 4}}}},
     {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
-     {&grpc_static_metadata_refcounts[42], {{g_bytes + 501, 5}}}},
+     {&grpc_static_metadata_refcounts[46], {{g_bytes + 551, 5}}}},
     {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
-     {&grpc_static_metadata_refcounts[43], {{g_bytes + 506, 4}}}},
+     {&grpc_static_metadata_refcounts[47], {{g_bytes + 556, 4}}}},
     {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
-     {&grpc_static_metadata_refcounts[44], {{g_bytes + 510, 3}}}},
+     {&grpc_static_metadata_refcounts[48], {{g_bytes + 560, 3}}}},
     {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
-     {&grpc_static_metadata_refcounts[45], {{g_bytes + 513, 3}}}},
+     {&grpc_static_metadata_refcounts[49], {{g_bytes + 563, 3}}}},
     {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}},
-     {&grpc_static_metadata_refcounts[46], {{g_bytes + 516, 1}}}},
+     {&grpc_static_metadata_refcounts[50], {{g_bytes + 566, 1}}}},
     {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}},
-     {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 11}}}},
+     {&grpc_static_metadata_refcounts[51], {{g_bytes + 567, 11}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[48], {{g_bytes + 528, 3}}}},
+     {&grpc_static_metadata_refcounts[52], {{g_bytes + 578, 3}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[49], {{g_bytes + 531, 3}}}},
+     {&grpc_static_metadata_refcounts[53], {{g_bytes + 581, 3}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[50], {{g_bytes + 534, 3}}}},
+     {&grpc_static_metadata_refcounts[54], {{g_bytes + 584, 3}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[51], {{g_bytes + 537, 3}}}},
+     {&grpc_static_metadata_refcounts[55], {{g_bytes + 587, 3}}}},
     {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
-     {&grpc_static_metadata_refcounts[52], {{g_bytes + 540, 3}}}},
-    {{&grpc_static_metadata_refcounts[53], {{g_bytes + 543, 14}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[56], {{g_bytes + 590, 3}}}},
+    {{&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 14}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
-     {&grpc_static_metadata_refcounts[54], {{g_bytes + 557, 13}}}},
-    {{&grpc_static_metadata_refcounts[55], {{g_bytes + 570, 15}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[56], {{g_bytes + 585, 13}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[57], {{g_bytes + 598, 6}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[58], {{g_bytes + 604, 27}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[59], {{g_bytes + 631, 3}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[60], {{g_bytes + 634, 5}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[61], {{g_bytes + 639, 13}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[62], {{g_bytes + 652, 13}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[63], {{g_bytes + 665, 19}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[58], {{g_bytes + 607, 13}}}},
+    {{&grpc_static_metadata_refcounts[59], {{g_bytes + 620, 15}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[60], {{g_bytes + 635, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[61], {{g_bytes + 648, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 27}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[63], {{g_bytes + 681, 3}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 5}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[65], {{g_bytes + 689, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[66], {{g_bytes + 702, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[67], {{g_bytes + 715, 19}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
-     {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}},
+     {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}},
     {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
-     {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}},
+     {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}},
     {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 16}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[65], {{g_bytes + 700, 14}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[66], {{g_bytes + 714, 16}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[67], {{g_bytes + 730, 13}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[68], {{g_bytes + 734, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[69], {{g_bytes + 750, 14}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[70], {{g_bytes + 764, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[71], {{g_bytes + 780, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[68], {{g_bytes + 743, 6}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[69], {{g_bytes + 749, 4}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[70], {{g_bytes + 753, 4}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[71], {{g_bytes + 757, 6}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[72], {{g_bytes + 763, 7}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[73], {{g_bytes + 770, 4}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[72], {{g_bytes + 793, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[73], {{g_bytes + 799, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[74], {{g_bytes + 803, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[75], {{g_bytes + 807, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[76], {{g_bytes + 813, 7}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[77], {{g_bytes + 820, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[74], {{g_bytes + 774, 8}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[75], {{g_bytes + 782, 17}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[76], {{g_bytes + 799, 13}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[77], {{g_bytes + 812, 8}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[78], {{g_bytes + 820, 19}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[79], {{g_bytes + 839, 13}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[78], {{g_bytes + 824, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[79], {{g_bytes + 832, 17}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[80], {{g_bytes + 849, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[81], {{g_bytes + 862, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[82], {{g_bytes + 870, 19}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[83], {{g_bytes + 889, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 11}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[81], {{g_bytes + 863, 4}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[82], {{g_bytes + 867, 8}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[83], {{g_bytes + 875, 12}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[84], {{g_bytes + 887, 18}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[85], {{g_bytes + 905, 19}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[86], {{g_bytes + 924, 5}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[87], {{g_bytes + 929, 7}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[88], {{g_bytes + 936, 7}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 11}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[90], {{g_bytes + 954, 6}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[91], {{g_bytes + 960, 10}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[92], {{g_bytes + 970, 25}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[93], {{g_bytes + 995, 17}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[84], {{g_bytes + 902, 11}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[86], {{g_bytes + 917, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 12}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[88], {{g_bytes + 937, 18}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[89], {{g_bytes + 955, 19}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[90], {{g_bytes + 974, 5}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[91], {{g_bytes + 979, 7}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[92], {{g_bytes + 986, 7}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[93], {{g_bytes + 993, 11}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1004, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1010, 10}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[96], {{g_bytes + 1020, 25}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[97], {{g_bytes + 1045, 17}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1012, 4}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1016, 3}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
-    {{&grpc_static_metadata_refcounts[96], {{g_bytes + 1019, 16}}},
-     {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[98], {{g_bytes + 1062, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[99], {{g_bytes + 1066, 3}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[100], {{g_bytes + 1069, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}},
+     {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}},
+     {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[97], {{g_bytes + 1035, 16}}}},
+     {&grpc_static_metadata_refcounts[101], {{g_bytes + 1085, 16}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}},
+     {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}},
+     {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[99], {{g_bytes + 1064, 12}}}},
+     {&grpc_static_metadata_refcounts[103], {{g_bytes + 1114, 12}}}},
     {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
-     {&grpc_static_metadata_refcounts[100], {{g_bytes + 1076, 21}}}},
+     {&grpc_static_metadata_refcounts[104], {{g_bytes + 1126, 21}}}},
     {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
-     {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}},
+     {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}},
     {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
-     {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}},
+     {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}},
     {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
-     {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}},
+     {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}},
 };
 bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = {
     true,  // :path
@@ -577,6 +591,8 @@
     true,  // user-agent
     true,  // host
     true,  // lb-token
+    true,  // grpc-previous-rpc-attempts
+    true,  // grpc-retry-pushback-ms
 };
 
 const uint8_t grpc_static_accept_encoding_metadata[8] = {0,  76, 77, 78,
diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
index 8ce9b21..b3a10f5 100644
--- a/src/core/lib/transport/static_metadata.h
+++ b/src/core/lib/transport/static_metadata.h
@@ -27,9 +27,11 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H
 #define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/metadata.h"
 
-#define GRPC_STATIC_MDSTR_COUNT 101
+#define GRPC_STATIC_MDSTR_COUNT 105
 extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT];
 /* ":path" */
 #define GRPC_MDSTR_PATH (grpc_static_slice_table[0])
@@ -76,168 +78,176 @@
 #define GRPC_MDSTR_HOST (grpc_static_slice_table[20])
 /* "lb-token" */
 #define GRPC_MDSTR_LB_TOKEN (grpc_static_slice_table[21])
+/* "grpc-previous-rpc-attempts" */
+#define GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS (grpc_static_slice_table[22])
+/* "grpc-retry-pushback-ms" */
+#define GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS (grpc_static_slice_table[23])
 /* "grpc-timeout" */
-#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[22])
+#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[24])
+/* "1" */
+#define GRPC_MDSTR_1 (grpc_static_slice_table[25])
+/* "2" */
+#define GRPC_MDSTR_2 (grpc_static_slice_table[26])
+/* "3" */
+#define GRPC_MDSTR_3 (grpc_static_slice_table[27])
+/* "4" */
+#define GRPC_MDSTR_4 (grpc_static_slice_table[28])
 /* "" */
-#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[23])
+#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[29])
 /* "grpc.wait_for_ready" */
-#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[24])
+#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[30])
 /* "grpc.timeout" */
-#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[25])
+#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[31])
 /* "grpc.max_request_message_bytes" */
 #define GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES \
-  (grpc_static_slice_table[26])
+  (grpc_static_slice_table[32])
 /* "grpc.max_response_message_bytes" */
 #define GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES \
-  (grpc_static_slice_table[27])
+  (grpc_static_slice_table[33])
 /* "/grpc.lb.v1.LoadBalancer/BalanceLoad" */
 #define GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD \
-  (grpc_static_slice_table[28])
+  (grpc_static_slice_table[34])
 /* "deflate" */
-#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[29])
+#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[35])
 /* "gzip" */
-#define GRPC_MDSTR_GZIP (grpc_static_slice_table[30])
+#define GRPC_MDSTR_GZIP (grpc_static_slice_table[36])
 /* "stream/gzip" */
-#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[31])
+#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[37])
 /* "0" */
-#define GRPC_MDSTR_0 (grpc_static_slice_table[32])
-/* "1" */
-#define GRPC_MDSTR_1 (grpc_static_slice_table[33])
-/* "2" */
-#define GRPC_MDSTR_2 (grpc_static_slice_table[34])
+#define GRPC_MDSTR_0 (grpc_static_slice_table[38])
 /* "identity" */
-#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[35])
+#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[39])
 /* "trailers" */
-#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[36])
+#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[40])
 /* "application/grpc" */
-#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[37])
+#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[41])
 /* "POST" */
-#define GRPC_MDSTR_POST (grpc_static_slice_table[38])
+#define GRPC_MDSTR_POST (grpc_static_slice_table[42])
 /* "200" */
-#define GRPC_MDSTR_200 (grpc_static_slice_table[39])
+#define GRPC_MDSTR_200 (grpc_static_slice_table[43])
 /* "404" */
-#define GRPC_MDSTR_404 (grpc_static_slice_table[40])
+#define GRPC_MDSTR_404 (grpc_static_slice_table[44])
 /* "http" */
-#define GRPC_MDSTR_HTTP (grpc_static_slice_table[41])
+#define GRPC_MDSTR_HTTP (grpc_static_slice_table[45])
 /* "https" */
-#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[42])
+#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[46])
 /* "grpc" */
-#define GRPC_MDSTR_GRPC (grpc_static_slice_table[43])
+#define GRPC_MDSTR_GRPC (grpc_static_slice_table[47])
 /* "GET" */
-#define GRPC_MDSTR_GET (grpc_static_slice_table[44])
+#define GRPC_MDSTR_GET (grpc_static_slice_table[48])
 /* "PUT" */
-#define GRPC_MDSTR_PUT (grpc_static_slice_table[45])
+#define GRPC_MDSTR_PUT (grpc_static_slice_table[49])
 /* "/" */
-#define GRPC_MDSTR_SLASH (grpc_static_slice_table[46])
+#define GRPC_MDSTR_SLASH (grpc_static_slice_table[50])
 /* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[47])
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[51])
 /* "204" */
-#define GRPC_MDSTR_204 (grpc_static_slice_table[48])
+#define GRPC_MDSTR_204 (grpc_static_slice_table[52])
 /* "206" */
-#define GRPC_MDSTR_206 (grpc_static_slice_table[49])
+#define GRPC_MDSTR_206 (grpc_static_slice_table[53])
 /* "304" */
-#define GRPC_MDSTR_304 (grpc_static_slice_table[50])
+#define GRPC_MDSTR_304 (grpc_static_slice_table[54])
 /* "400" */
-#define GRPC_MDSTR_400 (grpc_static_slice_table[51])
+#define GRPC_MDSTR_400 (grpc_static_slice_table[55])
 /* "500" */
-#define GRPC_MDSTR_500 (grpc_static_slice_table[52])
+#define GRPC_MDSTR_500 (grpc_static_slice_table[56])
 /* "accept-charset" */
-#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[53])
+#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[57])
 /* "gzip, deflate" */
-#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[54])
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[58])
 /* "accept-language" */
-#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[55])
+#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[59])
 /* "accept-ranges" */
-#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[56])
+#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[60])
 /* "accept" */
-#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[57])
+#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[61])
 /* "access-control-allow-origin" */
-#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[58])
+#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[62])
 /* "age" */
-#define GRPC_MDSTR_AGE (grpc_static_slice_table[59])
+#define GRPC_MDSTR_AGE (grpc_static_slice_table[63])
 /* "allow" */
-#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[60])
+#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[64])
 /* "authorization" */
-#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[61])
+#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[65])
 /* "cache-control" */
-#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[62])
+#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[66])
 /* "content-disposition" */
-#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[63])
+#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[67])
 /* "content-language" */
-#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[64])
+#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[68])
 /* "content-length" */
-#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[65])
+#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[69])
 /* "content-location" */
-#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[66])
+#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[70])
 /* "content-range" */
-#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[67])
+#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[71])
 /* "cookie" */
-#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[68])
+#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[72])
 /* "date" */
-#define GRPC_MDSTR_DATE (grpc_static_slice_table[69])
+#define GRPC_MDSTR_DATE (grpc_static_slice_table[73])
 /* "etag" */
-#define GRPC_MDSTR_ETAG (grpc_static_slice_table[70])
+#define GRPC_MDSTR_ETAG (grpc_static_slice_table[74])
 /* "expect" */
-#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[71])
+#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[75])
 /* "expires" */
-#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[72])
+#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[76])
 /* "from" */
-#define GRPC_MDSTR_FROM (grpc_static_slice_table[73])
+#define GRPC_MDSTR_FROM (grpc_static_slice_table[77])
 /* "if-match" */
-#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[74])
+#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[78])
 /* "if-modified-since" */
-#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[75])
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[79])
 /* "if-none-match" */
-#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[76])
+#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[80])
 /* "if-range" */
-#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[77])
+#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[81])
 /* "if-unmodified-since" */
-#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[78])
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[82])
 /* "last-modified" */
-#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[79])
+#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[83])
 /* "lb-cost-bin" */
-#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[80])
+#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[84])
 /* "link" */
-#define GRPC_MDSTR_LINK (grpc_static_slice_table[81])
+#define GRPC_MDSTR_LINK (grpc_static_slice_table[85])
 /* "location" */
-#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[82])
+#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[86])
 /* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[83])
+#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[87])
 /* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[84])
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[88])
 /* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[85])
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[89])
 /* "range" */
-#define GRPC_MDSTR_RANGE (grpc_static_slice_table[86])
+#define GRPC_MDSTR_RANGE (grpc_static_slice_table[90])
 /* "referer" */
-#define GRPC_MDSTR_REFERER (grpc_static_slice_table[87])
+#define GRPC_MDSTR_REFERER (grpc_static_slice_table[91])
 /* "refresh" */
-#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[88])
+#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[92])
 /* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[89])
+#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[93])
 /* "server" */
-#define GRPC_MDSTR_SERVER (grpc_static_slice_table[90])
+#define GRPC_MDSTR_SERVER (grpc_static_slice_table[94])
 /* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[91])
+#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[95])
 /* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[92])
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[96])
 /* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[93])
+#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[97])
 /* "vary" */
-#define GRPC_MDSTR_VARY (grpc_static_slice_table[94])
+#define GRPC_MDSTR_VARY (grpc_static_slice_table[98])
 /* "via" */
-#define GRPC_MDSTR_VIA (grpc_static_slice_table[95])
+#define GRPC_MDSTR_VIA (grpc_static_slice_table[99])
 /* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[96])
+#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[100])
 /* "identity,deflate" */
-#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[97])
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[101])
 /* "identity,gzip" */
-#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[98])
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[102])
 /* "deflate,gzip" */
-#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[99])
+#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[103])
 /* "identity,deflate,gzip" */
 #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
-  (grpc_static_slice_table[100])
+  (grpc_static_slice_table[104])
 
 extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable;
 extern grpc_slice_refcount
@@ -535,6 +545,8 @@
   GRPC_BATCH_USER_AGENT,
   GRPC_BATCH_HOST,
   GRPC_BATCH_LB_TOKEN,
+  GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS,
+  GRPC_BATCH_GRPC_RETRY_PUSHBACK_MS,
   GRPC_BATCH_CALLOUTS_COUNT
 } grpc_metadata_batch_callouts_index;
 
@@ -563,6 +575,8 @@
     struct grpc_linked_mdelem* user_agent;
     struct grpc_linked_mdelem* host;
     struct grpc_linked_mdelem* lb_token;
+    struct grpc_linked_mdelem* grpc_previous_rpc_attempts;
+    struct grpc_linked_mdelem* grpc_retry_pushback_ms;
   } named;
 } grpc_metadata_batch_callouts;
 
diff --git a/src/core/lib/transport/status_conversion.cc b/src/core/lib/transport/status_conversion.cc
index 46cba42..e58bef5 100644
--- a/src/core/lib/transport/status_conversion.cc
+++ b/src/core/lib/transport/status_conversion.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/status_conversion.h"
 
 grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status) {
diff --git a/src/core/lib/transport/status_conversion.h b/src/core/lib/transport/status_conversion.h
index 107eb92..9f14e9b 100644
--- a/src/core/lib/transport/status_conversion.h
+++ b/src/core/lib/transport/status_conversion.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
 #define GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/transport/http2_errors.h"
diff --git a/src/core/lib/transport/status_metadata.cc b/src/core/lib/transport/status_metadata.cc
new file mode 100644
index 0000000..f896053
--- /dev/null
+++ b/src/core/lib/transport/status_metadata.cc
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/status_metadata.h"
+
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+/* we offset status by a small amount when storing it into transport metadata
+   as metadata cannot store a 0 value (which is used as OK for grpc_status_codes
+   */
+#define STATUS_OFFSET 1
+
+static void destroy_status(void* ignored) {}
+
+grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md) {
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
+    return GRPC_STATUS_OK;
+  }
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) {
+    return GRPC_STATUS_CANCELLED;
+  }
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) {
+    return GRPC_STATUS_UNKNOWN;
+  }
+  void* user_data = grpc_mdelem_get_user_data(md, destroy_status);
+  if (user_data != nullptr) {
+    return static_cast<grpc_status_code>((intptr_t)user_data - STATUS_OFFSET);
+  }
+  uint32_t status;
+  if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) {
+    status = GRPC_STATUS_UNKNOWN; /* could not parse status code */
+  }
+  grpc_mdelem_set_user_data(
+      md, destroy_status, (void*)static_cast<intptr_t>(status + STATUS_OFFSET));
+  return static_cast<grpc_status_code>(status);
+}
diff --git a/src/core/lib/transport/status_metadata.h b/src/core/lib/transport/status_metadata.h
new file mode 100644
index 0000000..aed9c7a
--- /dev/null
+++ b/src/core/lib/transport/status_metadata.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/status.h>
+
+#include "src/core/lib/transport/metadata.h"
+
+grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H */
diff --git a/src/core/lib/transport/timeout_encoding.cc b/src/core/lib/transport/timeout_encoding.cc
index 6800255..c372499 100644
--- a/src/core/lib/transport/timeout_encoding.cc
+++ b/src/core/lib/transport/timeout_encoding.cc
@@ -16,12 +16,13 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/timeout_encoding.h"
 
 #include <stdio.h>
 #include <string.h>
 
-#include <grpc/support/port_platform.h>
 #include "src/core/lib/gpr/string.h"
 
 static int64_t round_up(int64_t x, int64_t divisor) {
diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h
index 4e92682..8505e32 100644
--- a/src/core/lib/transport/timeout_encoding.h
+++ b/src/core/lib/transport/timeout_encoding.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
 #define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice.h>
 #include <grpc/support/time.h>
 
diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc
index d71d4fd..c90d16f 100644
--- a/src/core/lib/transport/transport.cc
+++ b/src/core/lib/transport/transport.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/transport.h"
 
 #include <string.h>
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index b392c69..37e5034 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H
 #define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stddef.h>
 
 #include "src/core/lib/channel/context.h"
@@ -96,6 +98,19 @@
 void grpc_transport_move_stats(grpc_transport_stream_stats* from,
                                grpc_transport_stream_stats* to);
 
+// This struct (which is present in both grpc_transport_stream_op_batch
+// and grpc_transport_op_batch) is a convenience to allow filters or
+// transports to schedule a closure related to a particular batch without
+// having to allocate memory.  The general pattern is to initialize the
+// closure with the callback arg set to the batch and extra_arg set to
+// whatever state is associated with the handler (e.g., the call element
+// or the transport stream object).
+//
+// Note that this can only be used by the current handler of a given
+// batch on the way down the stack (i.e., whichever filter or transport is
+// currently handling the batch).  Once a filter or transport passes control
+// of the batch to the next handler, it cannot depend on the contents of
+// this struct anymore, because the next handler may reuse it.
 typedef struct {
   void* extra_arg;
   grpc_closure closure;
@@ -155,6 +170,11 @@
     uint32_t send_initial_metadata_flags;
     // If non-NULL, will be set by the transport to the peer string
     // (a char*, which the caller takes ownership of).
+    // Note: This pointer may be used by the transport after the
+    // send_initial_metadata op is completed.  It must remain valid
+    // until the call is destroyed.
+    // Note: When a transport sets this, it must free the previous
+    // value, if any.
     gpr_atm* peer_string;
   } send_initial_metadata;
 
@@ -173,6 +193,9 @@
 
   struct {
     grpc_metadata_batch* recv_initial_metadata;
+    // Flags are used only on the server side.  If non-null, will be set to
+    // a bitfield of the GRPC_INITIAL_METADATA_xxx macros (e.g., to
+    // indicate if the call is idempotent).
     uint32_t* recv_flags;
     /** Should be enqueued when initial metadata is ready to be processed. */
     grpc_closure* recv_initial_metadata_ready;
@@ -182,6 +205,11 @@
     bool* trailing_metadata_available;
     // If non-NULL, will be set by the transport to the peer string
     // (a char*, which the caller takes ownership of).
+    // Note: This pointer may be used by the transport after the
+    // recv_initial_metadata op is completed.  It must remain valid
+    // until the call is destroyed.
+    // Note: When a transport sets this, it must free the previous
+    // value, if any.
     gpr_atm* peer_string;
   } recv_initial_metadata;
 
@@ -190,6 +218,7 @@
     // containing a received message.
     // The caller is responsible for calling grpc_byte_stream_destroy()
     // on this byte stream.
+    // Will be NULL if trailing metadata is received instead of a message.
     grpc_byte_stream** recv_message;
     /** Should be enqueued when one message is ready to be processed. */
     grpc_closure* recv_message_ready;
diff --git a/src/core/lib/transport/transport_impl.h b/src/core/lib/transport/transport_impl.h
index 50b8a5f..ba5e05d 100644
--- a/src/core/lib/transport/transport_impl.h
+++ b/src/core/lib/transport/transport_impl.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H
 #define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/lib/transport/transport.h"
 
 typedef struct grpc_transport_vtable {
diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
index fe5eb28..49b9c7d 100644
--- a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
+++ b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 void grpc_http_filters_init(void);
diff --git a/src/core/plugin_registry/grpc_plugin_registry.cc b/src/core/plugin_registry/grpc_plugin_registry.cc
index fdf9acc..6f11e6b 100644
--- a/src/core/plugin_registry/grpc_plugin_registry.cc
+++ b/src/core/plugin_registry/grpc_plugin_registry.cc
@@ -16,18 +16,20 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 void grpc_http_filters_init(void);
 void grpc_http_filters_shutdown(void);
 void grpc_chttp2_plugin_init(void);
 void grpc_chttp2_plugin_shutdown(void);
-void grpc_tsi_alts_init(void);
-void grpc_tsi_alts_shutdown(void);
 void grpc_deadline_filter_init(void);
 void grpc_deadline_filter_shutdown(void);
 void grpc_client_channel_init(void);
 void grpc_client_channel_shutdown(void);
+void grpc_tsi_alts_init(void);
+void grpc_tsi_alts_shutdown(void);
 void grpc_inproc_plugin_init(void);
 void grpc_inproc_plugin_shutdown(void);
 void grpc_resolver_fake_init(void);
@@ -58,12 +60,12 @@
                        grpc_http_filters_shutdown);
   grpc_register_plugin(grpc_chttp2_plugin_init,
                        grpc_chttp2_plugin_shutdown);
-  grpc_register_plugin(grpc_tsi_alts_init,
-                       grpc_tsi_alts_shutdown);
   grpc_register_plugin(grpc_deadline_filter_init,
                        grpc_deadline_filter_shutdown);
   grpc_register_plugin(grpc_client_channel_init,
                        grpc_client_channel_shutdown);
+  grpc_register_plugin(grpc_tsi_alts_init,
+                       grpc_tsi_alts_shutdown);
   grpc_register_plugin(grpc_inproc_plugin_init,
                        grpc_inproc_plugin_shutdown);
   grpc_register_plugin(grpc_resolver_fake_init,
diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
index d73f946..b08c5ce 100644
--- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
+++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 
 void grpc_http_filters_init(void);
diff --git a/src/core/tsi/alts/crypt/aes_gcm.cc b/src/core/tsi/alts/crypt/aes_gcm.cc
new file mode 100644
index 0000000..02b1ac4
--- /dev/null
+++ b/src/core/tsi/alts/crypt/aes_gcm.cc
@@ -0,0 +1,687 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+constexpr size_t kKdfKeyLen = 32;
+constexpr size_t kKdfCounterLen = 6;
+constexpr size_t kKdfCounterOffset = 2;
+constexpr size_t kRekeyAeadKeyLen = kAes128GcmKeyLength;
+
+/* Struct for additional data required if rekeying is enabled. */
+struct gsec_aes_gcm_aead_rekey_data {
+  uint8_t kdf_counter[kKdfCounterLen];
+  uint8_t nonce_mask[kAesGcmNonceLength];
+};
+
+/* Main struct for AES_GCM crypter interface. */
+struct gsec_aes_gcm_aead_crypter {
+  gsec_aead_crypter crypter;
+  size_t key_length;
+  size_t nonce_length;
+  size_t tag_length;
+  uint8_t* key;
+  gsec_aes_gcm_aead_rekey_data* rekey_data;
+  EVP_CIPHER_CTX* ctx;
+};
+
+static char* aes_gcm_get_openssl_errors() {
+  BIO* bio = BIO_new(BIO_s_mem());
+  ERR_print_errors(bio);
+  BUF_MEM* mem = nullptr;
+  char* error_msg = nullptr;
+  BIO_get_mem_ptr(bio, &mem);
+  if (mem != nullptr) {
+    error_msg = static_cast<char*>(gpr_malloc(mem->length + 1));
+    memcpy(error_msg, mem->data, mem->length);
+    error_msg[mem->length] = '\0';
+  }
+  BIO_free_all(bio);
+  return error_msg;
+}
+
+static void aes_gcm_format_errors(const char* error_msg, char** error_details) {
+  if (error_details == nullptr) {
+    return;
+  }
+  unsigned long error = ERR_get_error();
+  if (error == 0 && error_msg != nullptr) {
+    *error_details = static_cast<char*>(gpr_malloc(strlen(error_msg) + 1));
+    memcpy(*error_details, error_msg, strlen(error_msg) + 1);
+    return;
+  }
+  char* openssl_errors = aes_gcm_get_openssl_errors();
+  if (openssl_errors != nullptr && error_msg != nullptr) {
+    size_t len = strlen(error_msg) + strlen(openssl_errors) + 2; /* ", " */
+    *error_details = static_cast<char*>(gpr_malloc(len + 1));
+    snprintf(*error_details, len + 1, "%s, %s", error_msg, openssl_errors);
+    gpr_free(openssl_errors);
+  }
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length(
+    const gsec_aead_crypter* crypter, size_t plaintext_length,
+    size_t* max_ciphertext_and_tag_length, char** error_details) {
+  if (max_ciphertext_and_tag_length == nullptr) {
+    aes_gcm_format_errors("max_ciphertext_and_tag_length is nullptr.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *max_ciphertext_and_tag_length =
+      plaintext_length + aes_gcm_crypter->tag_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_max_plaintext_length(
+    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+    size_t* max_plaintext_length, char** error_details) {
+  if (max_plaintext_length == nullptr) {
+    aes_gcm_format_errors("max_plaintext_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  if (ciphertext_and_tag_length < aes_gcm_crypter->tag_length) {
+    *max_plaintext_length = 0;
+    aes_gcm_format_errors(
+        "ciphertext_and_tag_length is smaller than tag_length.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *max_plaintext_length =
+      ciphertext_and_tag_length - aes_gcm_crypter->tag_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_nonce_length(
+    const gsec_aead_crypter* crypter, size_t* nonce_length,
+    char** error_details) {
+  if (nonce_length == nullptr) {
+    aes_gcm_format_errors("nonce_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *nonce_length = aes_gcm_crypter->nonce_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_key_length(
+    const gsec_aead_crypter* crypter, size_t* key_length,
+    char** error_details) {
+  if (key_length == nullptr) {
+    aes_gcm_format_errors("key_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *key_length = aes_gcm_crypter->key_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_tag_length(
+    const gsec_aead_crypter* crypter, size_t* tag_length,
+    char** error_details) {
+  if (tag_length == nullptr) {
+    aes_gcm_format_errors("tag_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *tag_length = aes_gcm_crypter->tag_length;
+  return GRPC_STATUS_OK;
+}
+
+static void aes_gcm_mask_nonce(uint8_t* dst, const uint8_t* nonce,
+                               const uint8_t* mask) {
+  uint64_t mask1;
+  uint32_t mask2;
+  memcpy(&mask1, mask, sizeof(mask1));
+  memcpy(&mask2, mask + sizeof(mask1), sizeof(mask2));
+  uint64_t nonce1;
+  uint32_t nonce2;
+  memcpy(&nonce1, nonce, sizeof(nonce1));
+  memcpy(&nonce2, nonce + sizeof(nonce1), sizeof(nonce2));
+  nonce1 ^= mask1;
+  nonce2 ^= mask2;
+  memcpy(dst, &nonce1, sizeof(nonce1));
+  memcpy(dst + sizeof(nonce1), &nonce2, sizeof(nonce2));
+}
+
+static grpc_status_code aes_gcm_derive_aead_key(uint8_t* dst,
+                                                const uint8_t* kdf_key,
+                                                const uint8_t* kdf_counter) {
+  unsigned char buf[EVP_MAX_MD_SIZE];
+  unsigned char ctr = 1;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+  HMAC_CTX hmac;
+  HMAC_CTX_init(&hmac);
+  if (!HMAC_Init_ex(&hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) ||
+      !HMAC_Update(&hmac, kdf_counter, kKdfCounterLen) ||
+      !HMAC_Update(&hmac, &ctr, 1) || !HMAC_Final(&hmac, buf, nullptr)) {
+    HMAC_CTX_cleanup(&hmac);
+    return GRPC_STATUS_INTERNAL;
+  }
+  HMAC_CTX_cleanup(&hmac);
+#else
+  HMAC_CTX* hmac = HMAC_CTX_new();
+  if (hmac == nullptr) {
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (!HMAC_Init_ex(hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) ||
+      !HMAC_Update(hmac, kdf_counter, kKdfCounterLen) ||
+      !HMAC_Update(hmac, &ctr, 1) || !HMAC_Final(hmac, buf, nullptr)) {
+    HMAC_CTX_free(hmac);
+    return GRPC_STATUS_INTERNAL;
+  }
+  HMAC_CTX_free(hmac);
+#endif
+  memcpy(dst, buf, kRekeyAeadKeyLen);
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code aes_gcm_rekey_if_required(
+    gsec_aes_gcm_aead_crypter* aes_gcm_crypter, const uint8_t* nonce,
+    char** error_details) {
+  // If rekey_data is nullptr, then rekeying is not supported and not required.
+  // If bytes 2-7 of kdf_counter differ from the (per message) nonce, then the
+  // encryption key is recomputed from a new kdf_counter to ensure that we don't
+  // encrypt more than 2^16 messages per encryption key (in each direction).
+  if (aes_gcm_crypter->rekey_data == nullptr ||
+      memcmp(aes_gcm_crypter->rekey_data->kdf_counter,
+             nonce + kKdfCounterOffset, kKdfCounterLen) == 0) {
+    return GRPC_STATUS_OK;
+  }
+  memcpy(aes_gcm_crypter->rekey_data->kdf_counter, nonce + kKdfCounterOffset,
+         kKdfCounterLen);
+  uint8_t aead_key[kRekeyAeadKeyLen];
+  if (aes_gcm_derive_aead_key(aead_key, aes_gcm_crypter->key,
+                              aes_gcm_crypter->rekey_data->kdf_counter) !=
+      GRPC_STATUS_OK) {
+    aes_gcm_format_errors("Rekeying failed in key derivation.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, aead_key,
+                          nullptr)) {
+    aes_gcm_format_errors("Rekeying failed in context update.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_encrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+    struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+    char** error_details) {
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(crypter);
+  // Input checks
+  if (nonce == nullptr) {
+    aes_gcm_format_errors("Nonce buffer is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (kAesGcmNonceLength != nonce_length) {
+    aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (aad_vec_length > 0 && aad_vec == nullptr) {
+    aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (plaintext_vec_length > 0 && plaintext_vec == nullptr) {
+    aes_gcm_format_errors(
+        "Non-zero plaintext_vec_length but plaintext_vec is nullptr.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (ciphertext_bytes_written == nullptr) {
+    aes_gcm_format_errors("bytes_written is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *ciphertext_bytes_written = 0;
+  // rekey if required
+  if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) !=
+      GRPC_STATUS_OK) {
+    return GRPC_STATUS_INTERNAL;
+  }
+  // mask nonce if required
+  const uint8_t* nonce_aead = nonce;
+  uint8_t nonce_masked[kAesGcmNonceLength];
+  if (aes_gcm_crypter->rekey_data != nullptr) {
+    aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask,
+                       nonce);
+    nonce_aead = nonce_masked;
+  }
+  // init openssl context
+  if (!EVP_EncryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr,
+                          nonce_aead)) {
+    aes_gcm_format_errors("Initializing nonce failed", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  // process aad
+  size_t i;
+  for (i = 0; i < aad_vec_length; i++) {
+    const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base);
+    size_t aad_length = aad_vec[i].iov_len;
+    if (aad_length == 0) {
+      continue;
+    }
+    size_t aad_bytes_read = 0;
+    if (aad == nullptr) {
+      aes_gcm_format_errors("aad is nullptr.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, nullptr,
+                           reinterpret_cast<int*>(&aad_bytes_read), aad,
+                           static_cast<int>(aad_length)) ||
+        aad_bytes_read != aad_length) {
+      aes_gcm_format_errors("Setting authenticated associated data failed",
+                            error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+  }
+  uint8_t* ciphertext = static_cast<uint8_t*>(ciphertext_vec.iov_base);
+  size_t ciphertext_length = ciphertext_vec.iov_len;
+  if (ciphertext == nullptr) {
+    aes_gcm_format_errors("ciphertext is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  // process plaintext
+  for (i = 0; i < plaintext_vec_length; i++) {
+    const uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec[i].iov_base);
+    size_t plaintext_length = plaintext_vec[i].iov_len;
+    if (plaintext == nullptr) {
+      if (plaintext_length == 0) {
+        continue;
+      }
+      aes_gcm_format_errors("plaintext is nullptr.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (ciphertext_length < plaintext_length) {
+      aes_gcm_format_errors(
+          "ciphertext is not large enough to hold the result.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    int bytes_written = 0;
+    int bytes_to_write = static_cast<int>(plaintext_length);
+    if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, ciphertext, &bytes_written,
+                           plaintext, bytes_to_write)) {
+      aes_gcm_format_errors("Encrypting plaintext failed.", error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+    if (bytes_written > bytes_to_write) {
+      aes_gcm_format_errors("More bytes written than expected.", error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+    ciphertext += bytes_written;
+    ciphertext_length -= bytes_written;
+  }
+  int bytes_written_temp = 0;
+  if (!EVP_EncryptFinal_ex(aes_gcm_crypter->ctx, nullptr,
+                           &bytes_written_temp)) {
+    aes_gcm_format_errors("Finalizing encryption failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (bytes_written_temp != 0) {
+    aes_gcm_format_errors("Openssl wrote some unexpected bytes.",
+                          error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (ciphertext_length < kAesGcmTagLength) {
+    aes_gcm_format_errors("ciphertext is too small to hold a tag.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+
+  if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_GET_TAG,
+                           kAesGcmTagLength, ciphertext)) {
+    aes_gcm_format_errors("Writing tag failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  ciphertext += kAesGcmTagLength;
+  ciphertext_length -= kAesGcmTagLength;
+  *ciphertext_bytes_written = ciphertext_vec.iov_len - ciphertext_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_decrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+    struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+    char** error_details) {
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  if (nonce == nullptr) {
+    aes_gcm_format_errors("Nonce buffer is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (kAesGcmNonceLength != nonce_length) {
+    aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (aad_vec_length > 0 && aad_vec == nullptr) {
+    aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (ciphertext_vec_length > 0 && ciphertext_vec == nullptr) {
+    aes_gcm_format_errors(
+        "Non-zero plaintext_vec_length but plaintext_vec is nullptr.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  // Compute the total length so we can ensure we don't pass the tag into
+  // EVP_decrypt.
+  size_t total_ciphertext_length = 0;
+  size_t i;
+  for (i = 0; i < ciphertext_vec_length; i++) {
+    total_ciphertext_length += ciphertext_vec[i].iov_len;
+  }
+  if (total_ciphertext_length < kAesGcmTagLength) {
+    aes_gcm_format_errors("ciphertext is too small to hold a tag.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (plaintext_bytes_written == nullptr) {
+    aes_gcm_format_errors("bytes_written is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *plaintext_bytes_written = 0;
+  // rekey if required
+  if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) !=
+      GRPC_STATUS_OK) {
+    aes_gcm_format_errors("Rekeying failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  // mask nonce if required
+  const uint8_t* nonce_aead = nonce;
+  uint8_t nonce_masked[kAesGcmNonceLength];
+  if (aes_gcm_crypter->rekey_data != nullptr) {
+    aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask,
+                       nonce);
+    nonce_aead = nonce_masked;
+  }
+  // init openssl context
+  if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr,
+                          nonce_aead)) {
+    aes_gcm_format_errors("Initializing nonce failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  // process aad
+  for (i = 0; i < aad_vec_length; i++) {
+    const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base);
+    size_t aad_length = aad_vec[i].iov_len;
+    if (aad_length == 0) {
+      continue;
+    }
+    size_t aad_bytes_read = 0;
+    if (aad == nullptr) {
+      aes_gcm_format_errors("aad is nullptr.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, nullptr,
+                           reinterpret_cast<int*>(&aad_bytes_read), aad,
+                           static_cast<int>(aad_length)) ||
+        aad_bytes_read != aad_length) {
+      aes_gcm_format_errors("Setting authenticated associated data failed.",
+                            error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+  }
+  // process ciphertext
+  uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec.iov_base);
+  size_t plaintext_length = plaintext_vec.iov_len;
+  if (plaintext_length > 0 && plaintext == nullptr) {
+    aes_gcm_format_errors(
+        "plaintext is nullptr, but plaintext_length is positive.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  const uint8_t* ciphertext = nullptr;
+  size_t ciphertext_length = 0;
+  for (i = 0;
+       i < ciphertext_vec_length && total_ciphertext_length > kAesGcmTagLength;
+       i++) {
+    ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base);
+    ciphertext_length = ciphertext_vec[i].iov_len;
+    if (ciphertext == nullptr) {
+      if (ciphertext_length == 0) {
+        continue;
+      }
+      aes_gcm_format_errors("ciphertext is nullptr.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    size_t bytes_written = 0;
+    size_t bytes_to_write = ciphertext_length;
+    // Don't include the tag
+    if (bytes_to_write > total_ciphertext_length - kAesGcmTagLength) {
+      bytes_to_write = total_ciphertext_length - kAesGcmTagLength;
+    }
+    if (plaintext_length < bytes_to_write) {
+      aes_gcm_format_errors(
+          "Not enough plaintext buffer to hold encrypted ciphertext.",
+          error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, plaintext,
+                           reinterpret_cast<int*>(&bytes_written), ciphertext,
+                           static_cast<int>(bytes_to_write))) {
+      aes_gcm_format_errors("Decrypting ciphertext failed.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INTERNAL;
+    }
+    if (bytes_written > ciphertext_length) {
+      aes_gcm_format_errors("More bytes written than expected.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INTERNAL;
+    }
+    ciphertext += bytes_written;
+    ciphertext_length -= bytes_written;
+    total_ciphertext_length -= bytes_written;
+    plaintext += bytes_written;
+    plaintext_length -= bytes_written;
+  }
+  if (total_ciphertext_length > kAesGcmTagLength) {
+    aes_gcm_format_errors(
+        "Not enough plaintext buffer to hold encrypted ciphertext.",
+        error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  uint8_t tag[kAesGcmTagLength];
+  uint8_t* tag_tmp = tag;
+  if (ciphertext_length > 0) {
+    memcpy(tag_tmp, ciphertext, ciphertext_length);
+    tag_tmp += ciphertext_length;
+    total_ciphertext_length -= ciphertext_length;
+  }
+  for (; i < ciphertext_vec_length; i++) {
+    ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base);
+    ciphertext_length = ciphertext_vec[i].iov_len;
+    if (ciphertext == nullptr) {
+      if (ciphertext_length == 0) {
+        continue;
+      }
+      aes_gcm_format_errors("ciphertext is nullptr.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    memcpy(tag_tmp, ciphertext, ciphertext_length);
+    tag_tmp += ciphertext_length;
+    total_ciphertext_length -= ciphertext_length;
+  }
+  if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_TAG,
+                           kAesGcmTagLength, reinterpret_cast<void*>(tag))) {
+    aes_gcm_format_errors("Setting tag failed.", error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_INTERNAL;
+  }
+  int bytes_written_temp = 0;
+  if (!EVP_DecryptFinal_ex(aes_gcm_crypter->ctx, nullptr,
+                           &bytes_written_temp)) {
+    aes_gcm_format_errors("Checking tag failed.", error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (bytes_written_temp != 0) {
+    aes_gcm_format_errors("Openssl wrote some unexpected bytes.",
+                          error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_INTERNAL;
+  }
+  *plaintext_bytes_written = plaintext_vec.iov_len - plaintext_length;
+  return GRPC_STATUS_OK;
+}
+
+static void gsec_aes_gcm_aead_crypter_destroy(gsec_aead_crypter* crypter) {
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  gpr_free(aes_gcm_crypter->key);
+  gpr_free(aes_gcm_crypter->rekey_data);
+  EVP_CIPHER_CTX_free(aes_gcm_crypter->ctx);
+}
+
+static const gsec_aead_crypter_vtable vtable = {
+    gsec_aes_gcm_aead_crypter_encrypt_iovec,
+    gsec_aes_gcm_aead_crypter_decrypt_iovec,
+    gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length,
+    gsec_aes_gcm_aead_crypter_max_plaintext_length,
+    gsec_aes_gcm_aead_crypter_nonce_length,
+    gsec_aes_gcm_aead_crypter_key_length,
+    gsec_aes_gcm_aead_crypter_tag_length,
+    gsec_aes_gcm_aead_crypter_destroy};
+
+static grpc_status_code aes_gcm_new_evp_cipher_ctx(
+    gsec_aes_gcm_aead_crypter* aes_gcm_crypter, char** error_details) {
+  const EVP_CIPHER* cipher = nullptr;
+  bool is_rekey = aes_gcm_crypter->rekey_data != nullptr;
+  switch (is_rekey ? kRekeyAeadKeyLen : aes_gcm_crypter->key_length) {
+    case kAes128GcmKeyLength:
+      cipher = EVP_aes_128_gcm();
+      break;
+    case kAes256GcmKeyLength:
+      cipher = EVP_aes_256_gcm();
+      break;
+  }
+  const uint8_t* aead_key = aes_gcm_crypter->key;
+  uint8_t aead_key_rekey[kRekeyAeadKeyLen];
+  if (is_rekey) {
+    if (aes_gcm_derive_aead_key(aead_key_rekey, aes_gcm_crypter->key,
+                                aes_gcm_crypter->rekey_data->kdf_counter) !=
+        GRPC_STATUS_OK) {
+      aes_gcm_format_errors("Deriving key failed.", error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+    aead_key = aead_key_rekey;
+  }
+  if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, cipher, nullptr, aead_key,
+                          nullptr)) {
+    aes_gcm_format_errors("Setting key failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_IVLEN,
+                           static_cast<int>(aes_gcm_crypter->nonce_length),
+                           nullptr)) {
+    aes_gcm_format_errors("Setting nonce length failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key,
+                                                  size_t key_length,
+                                                  size_t nonce_length,
+                                                  size_t tag_length, bool rekey,
+                                                  gsec_aead_crypter** crypter,
+                                                  char** error_details) {
+  if (key == nullptr) {
+    aes_gcm_format_errors("key is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (crypter == nullptr) {
+    aes_gcm_format_errors("crypter is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  *crypter = nullptr;
+  if ((rekey && key_length != kAes128GcmRekeyKeyLength) ||
+      (!rekey && key_length != kAes128GcmKeyLength &&
+       key_length != kAes256GcmKeyLength) ||
+      (tag_length != kAesGcmTagLength) ||
+      (nonce_length != kAesGcmNonceLength)) {
+    aes_gcm_format_errors(
+        "Invalid key and/or nonce and/or tag length are provided at AEAD "
+        "crypter instance construction time.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      static_cast<gsec_aes_gcm_aead_crypter*>(
+          gpr_malloc(sizeof(gsec_aes_gcm_aead_crypter)));
+  aes_gcm_crypter->crypter.vtable = &vtable;
+  aes_gcm_crypter->nonce_length = nonce_length;
+  aes_gcm_crypter->tag_length = tag_length;
+  if (rekey) {
+    aes_gcm_crypter->key_length = kKdfKeyLen;
+    aes_gcm_crypter->rekey_data = static_cast<gsec_aes_gcm_aead_rekey_data*>(
+        gpr_malloc(sizeof(gsec_aes_gcm_aead_rekey_data)));
+    memcpy(aes_gcm_crypter->rekey_data->nonce_mask, key + kKdfKeyLen,
+           kAesGcmNonceLength);
+    // Set kdf_counter to all-zero for initial key derivation.
+    memset(aes_gcm_crypter->rekey_data->kdf_counter, 0, kKdfCounterLen);
+  } else {
+    aes_gcm_crypter->key_length = key_length;
+    aes_gcm_crypter->rekey_data = nullptr;
+  }
+  aes_gcm_crypter->key =
+      static_cast<uint8_t*>(gpr_malloc(aes_gcm_crypter->key_length));
+  memcpy(aes_gcm_crypter->key, key, aes_gcm_crypter->key_length);
+  aes_gcm_crypter->ctx = EVP_CIPHER_CTX_new();
+  grpc_status_code status =
+      aes_gcm_new_evp_cipher_ctx(aes_gcm_crypter, error_details);
+  if (status != GRPC_STATUS_OK) {
+    gsec_aes_gcm_aead_crypter_destroy(&aes_gcm_crypter->crypter);
+    gpr_free(aes_gcm_crypter);
+    return status;
+  }
+  *crypter = &aes_gcm_crypter->crypter;
+  return GRPC_STATUS_OK;
+}
diff --git a/src/core/tsi/alts/crypt/gsec.cc b/src/core/tsi/alts/crypt/gsec.cc
new file mode 100644
index 0000000..6236591
--- /dev/null
+++ b/src/core/tsi/alts/crypt/gsec.cc
@@ -0,0 +1,189 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+static const char vtable_error_msg[] =
+    "crypter or crypter->vtable has not been initialized properly";
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code gsec_aead_crypter_encrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* plaintext,
+    size_t plaintext_length, uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, size_t* bytes_written,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->encrypt_iovec != nullptr) {
+    struct iovec aad_vec = {(void*)aad, aad_length};
+    struct iovec plaintext_vec = {(void*)plaintext, plaintext_length};
+    struct iovec ciphertext_vec = {ciphertext_and_tag,
+                                   ciphertext_and_tag_length};
+    return crypter->vtable->encrypt_iovec(
+        crypter, nonce, nonce_length, &aad_vec, 1, &plaintext_vec, 1,
+        ciphertext_vec, bytes_written, error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_encrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+    struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->encrypt_iovec != nullptr) {
+    return crypter->vtable->encrypt_iovec(
+        crypter, nonce, nonce_length, aad_vec, aad_vec_length, plaintext_vec,
+        plaintext_vec_length, ciphertext_vec, ciphertext_bytes_written,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_decrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, uint8_t* plaintext,
+    size_t plaintext_length, size_t* bytes_written, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->decrypt_iovec != nullptr) {
+    struct iovec aad_vec = {(void*)aad, aad_length};
+    struct iovec ciphertext_vec = {(void*)ciphertext_and_tag,
+                                   ciphertext_and_tag_length};
+    struct iovec plaintext_vec = {plaintext, plaintext_length};
+    return crypter->vtable->decrypt_iovec(
+        crypter, nonce, nonce_length, &aad_vec, 1, &ciphertext_vec, 1,
+        plaintext_vec, bytes_written, error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_decrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+    struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->encrypt_iovec != nullptr) {
+    return crypter->vtable->decrypt_iovec(
+        crypter, nonce, nonce_length, aad_vec, aad_vec_length, ciphertext_vec,
+        ciphertext_vec_length, plaintext_vec, plaintext_bytes_written,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length(
+    const gsec_aead_crypter* crypter, size_t plaintext_length,
+    size_t* max_ciphertext_and_tag_length_to_return, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->max_ciphertext_and_tag_length != nullptr) {
+    return crypter->vtable->max_ciphertext_and_tag_length(
+        crypter, plaintext_length, max_ciphertext_and_tag_length_to_return,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_max_plaintext_length(
+    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+    size_t* max_plaintext_length_to_return, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->max_plaintext_length != nullptr) {
+    return crypter->vtable->max_plaintext_length(
+        crypter, ciphertext_and_tag_length, max_plaintext_length_to_return,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_nonce_length(
+    const gsec_aead_crypter* crypter, size_t* nonce_length_to_return,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->nonce_length != nullptr) {
+    return crypter->vtable->nonce_length(crypter, nonce_length_to_return,
+                                         error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter,
+                                              size_t* key_length_to_return,
+                                              char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->key_length != nullptr) {
+    return crypter->vtable->key_length(crypter, key_length_to_return,
+                                       error_details);
+  }
+  /* An error occurred */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter,
+                                              size_t* tag_length_to_return,
+                                              char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->tag_length != nullptr) {
+    return crypter->vtable->tag_length(crypter, tag_length_to_return,
+                                       error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter) {
+  if (crypter != nullptr) {
+    if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) {
+      crypter->vtable->destruct(crypter);
+    }
+    gpr_free(crypter);
+  }
+}
diff --git a/src/core/tsi/alts/crypt/gsec.h b/src/core/tsi/alts/crypt/gsec.h
new file mode 100644
index 0000000..4d65caa
--- /dev/null
+++ b/src/core/tsi/alts/crypt/gsec.h
@@ -0,0 +1,454 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H
+#define GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <grpc/grpc.h>
+
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+
+/**
+ * A gsec interface for AEAD encryption schemes. The API is thread-compatible.
+ * Each implementation of this interface should specify supported values for
+ * key, nonce, and tag lengths.
+ */
+
+/* Key, nonce, and tag length in bytes */
+const size_t kAesGcmNonceLength = 12;
+const size_t kAesGcmTagLength = 16;
+const size_t kAes128GcmKeyLength = 16;
+const size_t kAes256GcmKeyLength = 32;
+
+// The first 32 bytes are used as a KDF key and the remaining 12 bytes are used
+// to mask the nonce.
+const size_t kAes128GcmRekeyKeyLength = 44;
+
+typedef struct gsec_aead_crypter gsec_aead_crypter;
+
+/**
+ * The gsec_aead_crypter is an API for different AEAD implementations such as
+ * AES_GCM. It encapsulates all AEAD-related operations in the format of
+ * V-table that stores pointers to functions implementing those operations.
+ * It also provides helper functions to wrap each of those function pointers.
+ *
+ * A typical usage of this object would be:
+ *
+ *------------------------------------------------------------------------------
+ * // Declare a gsec_aead_crypter object, and create and assign an instance
+ * // of specific AEAD implementation e.g., AES_GCM to it. We assume both
+ * // key and nonce contain cryptographically secure random bytes, and the key
+ * // can be derived from an upper-layer application.
+ * gsec_aead_crypter* crypter;
+ * char* error_in_creation;
+ * // User can populate the message with any 100 bytes data.
+ * uint8_t* message = gpr_malloc(100);
+ * grpc_status_code creation_status = gsec_aes_gcm_aead_crypter_create(key,
+ *                                                      kAes128GcmKeyLength,
+ *                                                      kAesGcmNonceLength,
+ *                                                      kAesGcmTagLength,
+ *                                                      &crypter,
+ *                                                      false,
+ *                                                      0
+ *                                                      &error_in_creation);
+ *
+ * if (creation_status == GRPC_STATUS_OK) {
+ *    // Allocate a correct amount of memory to hold a ciphertext.
+ *    size_t clength = 0;
+ *    gsec_aead_crypter_max_ciphertext_and_tag_length(crypter, 100, &clength,
+ *                                                    nullptr);
+ *    uint8_t* ciphertext = gpr_malloc(clength);
+ *
+ *    // Perform encryption
+ *    size_t num_encrypted_bytes = 0;
+ *    char* error_in_encryption = nullptr;
+ *    grpc_status_code status = gsec_aead_crypter_encrypt(crypter, nonce,
+ *                                                        kAesGcmNonceLength,
+ *                                                        nullptr, 0, message,
+ *                                                        100, ciphertext,
+ *                                                        clength,
+ *                                                        &num_encrypted_bytes,
+ *                                                        &error_in_encryption);
+ * if (status == GRPC_STATUS_OK) {
+ *       // Allocate a correct amount of memory to hold a plaintext.
+ *       size_t plength = 0;
+ *       gsec_aead_crypter_max_plaintext_length(crypter, num_encrypted_bytes,
+ *                                              &plength, nullptr);
+ *       uint8_t* plaintext = gpr_malloc(plength);
+ *
+ *       // Perform decryption.
+ *       size_t num_decrypted_bytes = 0;
+ *       char* error_in_decryption = nullptr;
+ *       status = gsec_aead_crypter_decrypt(crypter, nonce,
+ *                                          kAesGcmNonceLength, nullptr, 0,
+ *                                          ciphertext, num_encrypted_bytes,
+ *                                          plaintext, plength,
+ *                                          &num_decrypted_bytes,
+ *                                          &error_in_decryption);
+ *       if (status != GRPC_STATUS_OK) {
+ *         fprintf(stderr, "AEAD decrypt operation failed with error code:"
+ *                         "%d, message: %s\n", status, error_in_decryption);
+ *       }
+ *       ...
+ *       gpr_free(plaintext);
+ *       gpr_free(error_in_decryption);
+ *    } else {
+ *        fprintf(stderr, "AEAD encrypt operation failed with error code:"
+ *                        "%d, message: %s\n", status, error_in_encryption);
+ *    }
+ *    ...
+ *    gpr_free(ciphertext);
+ *    gpr_free(error_in_encryption);
+ * } else {
+ *   fprintf(stderr, "Creation of AEAD crypter instance failed with error code:"
+ *                   "%d, message: %s\n", creation_status, error_in_creation);
+ * }
+ *
+ * // Destruct AEAD crypter instance.
+ * if (creation_status == GRPC_STATUS_OK) {
+ *   gsec_aead_crypter_destroy(crypter);
+ * }
+ * gpr_free(error_in_creation);
+ * gpr_free(message);
+ * -----------------------------------------------------------------------------
+ */
+
+/* V-table for gsec AEAD operations */
+typedef struct gsec_aead_crypter_vtable {
+  grpc_status_code (*encrypt_iovec)(
+      gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+      const struct iovec* aad_vec, size_t aad_vec_length,
+      const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+      struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+      char** error_details);
+  grpc_status_code (*decrypt_iovec)(
+      gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+      const struct iovec* aad_vec, size_t aad_vec_length,
+      const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+      struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+      char** error_details);
+  grpc_status_code (*max_ciphertext_and_tag_length)(
+      const gsec_aead_crypter* crypter, size_t plaintext_length,
+      size_t* max_ciphertext_and_tag_length_to_return, char** error_details);
+  grpc_status_code (*max_plaintext_length)(
+      const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+      size_t* max_plaintext_length_to_return, char** error_details);
+  grpc_status_code (*nonce_length)(const gsec_aead_crypter* crypter,
+                                   size_t* nonce_length_to_return,
+                                   char** error_details);
+  grpc_status_code (*key_length)(const gsec_aead_crypter* crypter,
+                                 size_t* key_length_to_return,
+                                 char** error_details);
+  grpc_status_code (*tag_length)(const gsec_aead_crypter* crypter,
+                                 size_t* tag_length_to_return,
+                                 char** error_details);
+  void (*destruct)(gsec_aead_crypter* crypter);
+} gsec_aead_crypter_vtable;
+
+/* Main struct for gsec interface */
+struct gsec_aead_crypter {
+  const struct gsec_aead_crypter_vtable* vtable;
+};
+
+/**
+ * This method performs an AEAD encrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad: buffer containing data that needs to be authenticated but not
+ *   encrypted with its size equal to aad_length.
+ * - aad_length: size of aad buffer, which should be zero if the buffer is
+ *   nullptr.
+ * - plaintext: buffer containing data that needs to be both encrypted and
+ *   authenticated with its size equal to plaintext_length.
+ * - plaintext_length: size of plaintext buffer, which should be zero if
+ *   plaintext is nullptr.
+ * - ciphertext_and_tag: buffer that will contain ciphertext and tags the method
+ *   produced. The buffer should not overlap the plaintext buffer, and pointers
+ *   to those buffers should not be equal. Also if the ciphertext+tag buffer is
+ *   nullptr, the plaintext_length should be zero.
+ * - ciphertext_and_tag_length: size of ciphertext+tag buffer, which should be
+ *   at least as long as the one returned from method
+ *   gsec_aead_crypter_max_ciphertext_and_tag_length.
+ * - bytes_written: the actual number of bytes written to the ciphertext+tag
+ *   buffer. If bytes_written is nullptr, the plaintext_length should be zero.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ *
+ */
+grpc_status_code gsec_aead_crypter_encrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* plaintext,
+    size_t plaintext_length, uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, size_t* bytes_written,
+    char** error_details);
+
+/**
+ * This method performs an AEAD encrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad_vec: an iovec array containing data that needs to be authenticated but
+ *   not encrypted.
+ * - aad_vec_length: the array length of aad_vec.
+ * - plaintext_vec: an iovec array containing data that needs to be both
+ *   encrypted and authenticated.
+ * - plaintext_vec_length: the array length of plaintext_vec.
+ * - ciphertext_vec: an iovec containing a ciphertext buffer. The buffer should
+ *   not overlap the plaintext buffer.
+ * - ciphertext_bytes_written: the actual number of bytes written to
+ *   ciphertext_vec.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ *
+ */
+grpc_status_code gsec_aead_crypter_encrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+    struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+    char** error_details);
+
+/**
+ * This method performs an AEAD decrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad: buffer containing data that needs to be authenticated only.
+ * - aad_length: size of aad buffer, which should be zero if the buffer is
+ *   nullptr.
+ * - ciphertext_and_tag: buffer containing ciphertext and tag.
+ * - ciphertext_and_tag_length: length of ciphertext and tag. It should be zero
+ *   if any of plaintext, ciphertext_and_tag, or bytes_written is nullptr. Also,
+ *   ciphertext_and_tag_length should be at least as large as the tag length set
+ *   at AEAD crypter instance construction time.
+ * - plaintext: buffer containing decrypted and authenticated data the method
+ *   produced. The buffer should not overlap with the ciphertext+tag buffer, and
+ *   pointers to those buffers should not be equal.
+ * - plaintext_length: size of plaintext buffer, which should be at least as
+ *   long as the one returned from gsec_aead_crypter_max_plaintext_length
+ *   method.
+ * - bytes_written: the actual number of bytes written to the plaintext
+ *   buffer.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_decrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, uint8_t* plaintext,
+    size_t plaintext_length, size_t* bytes_written, char** error_details);
+
+/**
+ * This method performs an AEAD decrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad_vec: an iovec array containing data that needs to be authenticated but
+ *   not encrypted.
+ * - aad_vec_length: the array length of aad_vec.
+ * - ciphertext_vec: an iovec array containing the ciphertext and tag.
+ * - ciphertext_vec_length: the array length of ciphertext_vec.
+ * - plaintext_vec: an iovec containing a plaintext buffer. The buffer should
+ *   not overlap the ciphertext buffer.
+ * - plaintext_bytes_written: the actual number of bytes written to
+ *   plaintext_vec.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_decrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+    struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+    char** error_details);
+
+/**
+ * This method computes the size of ciphertext+tag buffer that must be passed to
+ * gsec_aead_crypter_encrypt function to ensure correct encryption of a
+ * plaintext. The actual size of ciphertext+tag written to the buffer could be
+ * smaller.
+ *
+ * - crypter: AEAD crypter instance.
+ * - plaintext_length: length of plaintext.
+ * - max_ciphertext_and_tag_length_to_return: the size of ciphertext+tag buffer
+ *   the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length(
+    const gsec_aead_crypter* crypter, size_t plaintext_length,
+    size_t* max_ciphertext_and_tag_length_to_return, char** error_details);
+
+/**
+ * This method computes the size of plaintext buffer that must be passed to
+ * gsec_aead_crypter_decrypt function to ensure correct decryption of a
+ * ciphertext. The actual size of plaintext written to the buffer could be
+ * smaller.
+ *
+ * - crypter: AEAD crypter instance.
+ * - ciphertext_and_tag_length: length of ciphertext and tag.
+ * - max_plaintext_length_to_return: the size of plaintext buffer the method
+ *   returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_max_plaintext_length(
+    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+    size_t* max_plaintext_length_to_return, char** error_details);
+
+/**
+ * This method returns a valid size of nonce array used at the construction of
+ * AEAD crypter instance. It is also the size that should be passed to encrypt
+ * and decrypt methods executed on the instance.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce_length_to_return: the length of nonce array the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_nonce_length(
+    const gsec_aead_crypter* crypter, size_t* nonce_length_to_return,
+    char** error_details);
+
+/**
+ * This method returns a valid size of key array used at the construction of
+ * AEAD crypter instance. It is also the size that should be passed to encrypt
+ * and decrypt methods executed on the instance.
+ *
+ * - crypter: AEAD crypter instance.
+ * - key_length_to_return: the length of key array the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter,
+                                              size_t* key_length_to_return,
+                                              char** error_details);
+/**
+ * This method returns a valid size of tag array used at the construction of
+ * AEAD crypter instance. It is also the size that should be passed to encrypt
+ * and decrypt methods executed on the instance.
+ *
+ * - crypter: AEAD crypter instance.
+ * - tag_length_to_return: the length of tag array the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter,
+                                              size_t* tag_length_to_return,
+                                              char** error_details);
+
+/**
+ * This method destroys an AEAD crypter instance by de-allocating all of its
+ * occupied memory.
+ *
+ * - crypter: AEAD crypter instance that needs to be destroyed.
+ */
+void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter);
+
+/**
+ * This method creates an AEAD crypter instance of AES-GCM encryption scheme
+ * which supports 16 and 32 bytes long keys, 12 and 16 bytes long nonces, and
+ * 16 bytes long tags. It should be noted that once the lengths of key, nonce,
+ * and tag are determined at construction time, they cannot be modified later.
+ *
+ * - key: buffer containing a key which is binded with AEAD crypter instance.
+ * - key_length: length of a key in bytes, which should be 44 if rekeying is
+ *   enabled and 16 or 32 otherwise.
+ * - nonce_length: length of a nonce in bytes, which should be either 12 or 16.
+ * - tag_length: length of a tag in bytes, which should be always 16.
+ * - rekey: enable nonce-based rekeying and nonce-masking.
+ * - crypter: address of AES_GCM crypter instance returned from the method.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of instance creation, it stores the address of instance at
+ * crypter. Otherwise, it returns an error status code together with its details
+ * specified in error_details.
+ */
+grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key,
+                                                  size_t key_length,
+                                                  size_t nonce_length,
+                                                  size_t tag_length, bool rekey,
+                                                  gsec_aead_crypter** crypter,
+                                                  char** error_details);
+
+#endif /* GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H */
diff --git a/src/core/tsi/alts/frame_protector/alts_counter.cc b/src/core/tsi/alts/frame_protector/alts_counter.cc
new file mode 100644
index 0000000..de163e3
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_counter.cc
@@ -0,0 +1,118 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code alts_counter_create(bool is_client, size_t counter_size,
+                                     size_t overflow_size,
+                                     alts_counter** crypter_counter,
+                                     char** error_details) {
+  /* Perform input sanity check. */
+  if (counter_size == 0) {
+    const char error_msg[] = "counter_size is invalid.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (overflow_size == 0 || overflow_size >= counter_size) {
+    const char error_msg[] = "overflow_size is invalid.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (crypter_counter == nullptr) {
+    const char error_msg[] = "crypter_counter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *crypter_counter =
+      static_cast<alts_counter*>(gpr_malloc(sizeof(**crypter_counter)));
+  (*crypter_counter)->size = counter_size;
+  (*crypter_counter)->overflow_size = overflow_size;
+  (*crypter_counter)->counter =
+      static_cast<unsigned char*>(gpr_zalloc(counter_size));
+  if (is_client) {
+    ((*crypter_counter)->counter)[counter_size - 1] = 0x80;
+  }
+  return GRPC_STATUS_OK;
+}
+
+grpc_status_code alts_counter_increment(alts_counter* crypter_counter,
+                                        bool* is_overflow,
+                                        char** error_details) {
+  /* Perform input sanity check. */
+  if (crypter_counter == nullptr) {
+    const char error_msg[] = "crypter_counter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (is_overflow == nullptr) {
+    const char error_msg[] = "is_overflow is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Increment the internal counter. */
+  size_t i = 0;
+  for (; i < crypter_counter->overflow_size; i++) {
+    (crypter_counter->counter)[i]++;
+    if ((crypter_counter->counter)[i] != 0x00) {
+      break;
+    }
+  }
+  /**
+   * If the lower overflow_size bytes are all zero, the counter has overflowed.
+   */
+  if (i == crypter_counter->overflow_size) {
+    *is_overflow = true;
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  *is_overflow = false;
+  return GRPC_STATUS_OK;
+}
+
+size_t alts_counter_get_size(alts_counter* crypter_counter) {
+  if (crypter_counter == nullptr) {
+    return 0;
+  }
+  return crypter_counter->size;
+}
+
+unsigned char* alts_counter_get_counter(alts_counter* crypter_counter) {
+  if (crypter_counter == nullptr) {
+    return nullptr;
+  }
+  return crypter_counter->counter;
+}
+
+void alts_counter_destroy(alts_counter* crypter_counter) {
+  if (crypter_counter != nullptr) {
+    gpr_free(crypter_counter->counter);
+    gpr_free(crypter_counter);
+  }
+}
diff --git a/src/core/tsi/alts/frame_protector/alts_counter.h b/src/core/tsi/alts/frame_protector/alts_counter.h
new file mode 100644
index 0000000..d705638
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_counter.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <grpc/grpc.h>
+
+/* Main struct for a crypter counter managed within seal/unseal operations. */
+typedef struct alts_counter {
+  size_t size;
+  size_t overflow_size;
+  unsigned char* counter;
+} alts_counter;
+
+/**
+ * This method creates and initializes an alts_counter instance.
+ *
+ * - is_client: a flag indicating if the alts_counter instance will be used
+ *   at client (is_client = true) or server (is_client = false) side.
+ * - counter_size: size of buffer holding the counter value.
+ * - overflow_size: overflow size in bytes. The counter instance can be used
+ *   to produce at most 2^(overflow_size*8) frames.
+ * - crypter_counter: an alts_counter instance to be returned from the method.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_counter_create(bool is_client, size_t counter_size,
+                                     size_t overflow_size,
+                                     alts_counter** crypter_counter,
+                                     char** error_details);
+
+/**
+ * This method increments the internal counter.
+ *
+ * - crypter_counter: an alts_counter instance.
+ * - is_overflow: after incrementing the internal counter, if an overflow
+ *   occurs, is_overflow is set to true, and no further calls to
+ *   alts_counter_increment() should be made. Otherwise, is_overflow is set to
+ *   false.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_counter_increment(alts_counter* crypter_counter,
+                                        bool* is_overflow,
+                                        char** error_details);
+
+/**
+ * This method returns the size of counter buffer.
+ *
+ * - crypter_counter: an alts_counter instance.
+ */
+size_t alts_counter_get_size(alts_counter* crypter_counter);
+
+/**
+ * This method returns the counter buffer.
+ *
+ * - crypter_counter: an alts_counter instance.
+ */
+unsigned char* alts_counter_get_counter(alts_counter* crypter_counter);
+
+/**
+ * This method de-allocates all memory allocated to an alts_coutner instance.
+ * - crypter_counter: an alts_counter instance.
+ */
+void alts_counter_destroy(alts_counter* crypter_counter);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H */
diff --git a/src/core/tsi/alts/frame_protector/alts_crypter.cc b/src/core/tsi/alts/frame_protector/alts_crypter.cc
new file mode 100644
index 0000000..56f0512
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_crypter.cc
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code alts_crypter_process_in_place(
+    alts_crypter* crypter, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->process_in_place != nullptr) {
+    return crypter->vtable->process_in_place(crypter, data, data_allocated_size,
+                                             data_size, output_size,
+                                             error_details);
+  }
+  /* An error occurred. */
+  const char error_msg[] =
+      "crypter or crypter->vtable has not been initialized properly.";
+  maybe_copy_error_msg(error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->num_overhead_bytes != nullptr) {
+    return crypter->vtable->num_overhead_bytes(crypter);
+  }
+  /* An error occurred. */
+  return 0;
+}
+
+void alts_crypter_destroy(alts_crypter* crypter) {
+  if (crypter != nullptr) {
+    if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) {
+      crypter->vtable->destruct(crypter);
+    }
+    gpr_free(crypter);
+  }
+}
diff --git a/src/core/tsi/alts/frame_protector/alts_crypter.h b/src/core/tsi/alts/frame_protector/alts_crypter.h
new file mode 100644
index 0000000..3140778
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_crypter.h
@@ -0,0 +1,255 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+/**
+ * An alts_crypter interface for an ALTS record protocol providing
+ * seal/unseal functionality. The interface is thread-compatible.
+ */
+
+typedef struct alts_crypter alts_crypter;
+
+/**
+ * A typical usage of the interface would be
+ *------------------------------------------------------------------------------
+ * // Perform a seal operation. We assume the gsec_aead_crypter instance -
+ * // client_aead_crypter is created beforehand with a 16-byte key and 12-byte
+ * // nonce length.
+ *
+ * alts_crypter* client = nullptr;
+ * char* client_error_in_creation = nullptr;
+ * unsigned char* data = nullptr;
+ * grpc_status_code client_status =
+ *                 alts_seal_crypter_create(client_aead_crypter, 1, 5, &client,
+ *                                          &client_error_in_creation);
+ * if (client_status == GRPC_STATUS_OK) {
+ *   size_t data_size = 100;
+ *   size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(client);
+ *   size_t data_allocated_size = data_size + num_overhead_bytes;
+ *   data = gpr_malloc(data_allocated_size);
+ *   char* client_error_in_seal = nullptr;
+ *   // Client performs a seal operation.
+ *   client_status = alts_crypter_process_in_place(client, data,
+ *                                                 data_allocated_size,
+ *                                                 &data_size,
+ *                                                 &client_error_in_seal);
+ *   if (client_status != GRPC_STATUS_OK) {
+ *     fprintf(stderr, "seal operation failed with error code:"
+ *                     "%d, message: %s\n", client_status,
+ *                      client_error_in_seal);
+ *    }
+ *    gpr_free(client_error_in_seal);
+ * } else {
+ *     fprintf(stderr, "alts_crypter instance creation failed with error"
+ *                     "code: %d, message: %s\n", client_status,
+ *                      client_error_in_creation);
+ * }
+ *
+ * ...
+ *
+ * gpr_free(client_error_in_creation);
+ * alts_crypter_destroy(client);
+ *
+ * ...
+ *
+ * // Perform an unseal operation. We assume the gsec_aead_crypter instance -
+ * // server_aead_crypter is created beforehand with a 16-byte key and 12-byte
+ * // nonce length. The key used in the creation of gsec_aead_crypter instances
+ * // at server and client sides should be identical.
+ *
+ * alts_crypter* server = nullptr;
+ * char* server_error_in_creation = nullptr;
+ * grpc_status_code server_status =
+ *               alts_unseal_crypter_create(server_aead_crypter, 0, 5, &server,
+ *                                          &server_error_in_creation);
+ * if (server_status == GRPC_STATUS_OK) {
+ *   size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server);
+ *   size_t data_size = 100 + num_overhead_bytes;
+ *   size_t data_allocated_size = data_size;
+ *   char* server_error_in_unseal = nullptr;
+ *   // Server performs an unseal operation.
+ *   server_status = alts_crypter_process_in_place(server, data,
+ *                                                 data_allocated_size,
+ *                                                 &data_size,
+ *                                                 &server_error_in_unseal);
+ *   if (server_status != GRPC_STATUS_OK) {
+ *     fprintf(stderr, "unseal operation failed with error code:"
+ *                     "%d, message: %s\n", server_status,
+ *                      server_error_in_unseal);
+ *   }
+ *   gpr_free(server_error_in_unseal);
+ * } else {
+ *     fprintf(stderr, "alts_crypter instance creation failed with error"
+ *                     "code: %d, message: %s\n", server_status,
+ *                      server_error_in_creation);
+ * }
+ *
+ * ...
+ *
+ * gpr_free(data);
+ * gpr_free(server_error_in_creation);
+ * alts_crypter_destroy(server);
+ *
+ * ...
+ *------------------------------------------------------------------------------
+ */
+
+/* V-table for alts_crypter operations */
+typedef struct alts_crypter_vtable {
+  size_t (*num_overhead_bytes)(const alts_crypter* crypter);
+  grpc_status_code (*process_in_place)(alts_crypter* crypter,
+                                       unsigned char* data,
+                                       size_t data_allocated_size,
+                                       size_t data_size, size_t* output_size,
+                                       char** error_details);
+  void (*destruct)(alts_crypter* crypter);
+} alts_crypter_vtable;
+
+/* Main struct for alts_crypter interface */
+struct alts_crypter {
+  const alts_crypter_vtable* vtable;
+};
+
+/**
+ * This method gets the number of overhead bytes needed for sealing data that
+ * is the difference in size between the protected and raw data. The counter
+ * value used in a seal or unseal operation is locally maintained (not sent or
+ * received from the other peer) and therefore, will not be counted as part of
+ * overhead bytes.
+ *
+ * - crypter: an alts_crypter instance.
+ *
+ * On success, the method returns the number of overhead bytes. Otherwise, it
+ * returns zero.
+ *
+ */
+size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter);
+
+/**
+ * This method performs either a seal or an unseal operation depending on the
+ * alts_crypter instance - crypter passed to the method. If the crypter is
+ * an instance implementing a seal operation, the method will perform a seal
+ * operation. That is, it seals raw data and stores the result in-place, and the
+ * memory allocated for data must be at least data_length +
+ * alts_crypter_num_overhead_bytes(). If the crypter is an instance
+ * implementing an unseal operation, the method will perform an unseal
+ * operation. That is, it unseals protected data and stores the result in-place.
+ * The size of unsealed data will be data_length -
+ * alts_crypter_num_overhead_bytes(). Integrity tag will be verified during
+ * the unseal operation, and if verification fails, the data will be wiped.
+ * The counters used in both seal and unseal operations are managed internally.
+ *
+ * - crypter: an alts_crypter instance.
+ * - data: if the method performs a seal operation, the data represents raw data
+ *   that needs to be sealed. It also plays the role of buffer to hold the
+ *   protected data as a result of seal. If the method performs an unseal
+ *   operation, the data represents protected data that needs to be unsealed. It
+ *   also plays the role of buffer to hold raw data as a result of unseal.
+ * - data_allocated_size: the size of data buffer. The parameter is used to
+ *   check whether the result of either seal or unseal can be safely written to
+ *   the data buffer.
+ * - data_size: if the method performs a seal operation, data_size
+ *   represents the size of raw data that needs to be sealed, and if the method
+ *   performs an unseal operation, data_size represents the size of protected
+ *   data that needs to be unsealed.
+ * - output_size: size of data written to the data buffer after a seal or an
+ *   unseal operation.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_crypter_process_in_place(
+    alts_crypter* crypter, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details);
+
+/**
+ * This method creates an alts_crypter instance to be used to perform a seal
+ * operation, given a gsec_aead_crypter instance and a flag indicating if the
+ * created instance will be used at the client or server side. It takes
+ * ownership of gsec_aead_crypter instance.
+ *
+ * - gc: a gsec_aead_crypter instance used to perform AEAD encryption.
+ * - is_client: a flag indicating if the alts_crypter instance will be
+ *   used at the client (is_client = true) or server (is_client =
+ *   false) side.
+ * - overflow_size: overflow size of counter in bytes.
+ * - crypter: an alts_crypter instance to be returned from the method.
+ * - error_details: a buffer containing an error message if the method does
+ *   not function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of creation, the method returns GRPC_STATUS_OK.
+ * Otherwise, it returns an error status code along with its details specified
+ * in error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client,
+                                          size_t overflow_size,
+                                          alts_crypter** crypter,
+                                          char** error_details);
+
+/**
+ * This method creates an alts_crypter instance used to perform an unseal
+ * operation, given a gsec_aead_crypter instance and a flag indicating if the
+ * created instance will be used at the client or server side. It takes
+ * ownership of gsec_aead_crypter instance.
+ *
+ * - gc: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - is_client: a flag indicating if the alts_crypter instance will be
+ *   used at the client (is_client = true) or server (is_client =
+ *   false) side.
+ * - overflow_size: overflow size of counter in bytes.
+ * - crypter: an alts_crypter instance to be returned from the method.
+ * - error_details: a buffer containing an error message if the method does
+ *   not function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of creation, the method returns GRPC_STATUS_OK.
+ * Otherwise, it returns an error status code along with its details specified
+ * in error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc,
+                                            bool is_client,
+                                            size_t overflow_size,
+                                            alts_crypter** crypter,
+                                            char** error_details);
+
+/**
+ * This method destroys an alts_crypter instance by de-allocating all of its
+ * occupied memory. A gsec_aead_crypter instance passed in at alts_crypter
+ * instance creation time will be destroyed in this method.
+ *
+ * - crypter: an alts_crypter instance.
+ */
+void alts_crypter_destroy(alts_crypter* crypter);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H */
diff --git a/src/core/tsi/alts/frame_protector/alts_frame_protector.cc b/src/core/tsi/alts/frame_protector/alts_frame_protector.cc
new file mode 100644
index 0000000..bfa0b7a
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_frame_protector.cc
@@ -0,0 +1,407 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "src/core/tsi/alts/frame_protector/frame_handler.h"
+#include "src/core/tsi/transport_security.h"
+
+constexpr size_t kMinFrameLength = 1024;
+constexpr size_t kDefaultFrameLength = 16 * 1024;
+constexpr size_t kMaxFrameLength = 1024 * 1024;
+
+// Limit k on number of frames such that at most 2^(8 * k) frames can be sent.
+constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8;
+constexpr size_t kAltsRecordProtocolFrameLimit = 5;
+
+/* Main struct for alts_frame_protector. */
+struct alts_frame_protector {
+  tsi_frame_protector base;
+  alts_crypter* seal_crypter;
+  alts_crypter* unseal_crypter;
+  alts_frame_writer* writer;
+  alts_frame_reader* reader;
+  unsigned char* in_place_protect_buffer;
+  unsigned char* in_place_unprotect_buffer;
+  size_t in_place_protect_bytes_buffered;
+  size_t in_place_unprotect_bytes_processed;
+  size_t max_protected_frame_size;
+  size_t max_unprotected_frame_size;
+  size_t overhead_length;
+  size_t counter_overflow;
+};
+
+static tsi_result seal(alts_frame_protector* impl) {
+  char* error_details = nullptr;
+  size_t output_size = 0;
+  grpc_status_code status = alts_crypter_process_in_place(
+      impl->seal_crypter, impl->in_place_protect_buffer,
+      impl->max_protected_frame_size, impl->in_place_protect_bytes_buffered,
+      &output_size, &error_details);
+  impl->in_place_protect_bytes_buffered = output_size;
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "%s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  return TSI_OK;
+}
+
+static size_t max_encrypted_payload_bytes(alts_frame_protector* impl) {
+  return impl->max_protected_frame_size - kFrameHeaderSize;
+}
+
+static tsi_result alts_protect_flush(tsi_frame_protector* self,
+                                     unsigned char* protected_output_frames,
+                                     size_t* protected_output_frames_size,
+                                     size_t* still_pending_size) {
+  if (self == nullptr || protected_output_frames == nullptr ||
+      protected_output_frames_size == nullptr ||
+      still_pending_size == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect_flush().");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+  /**
+   * If there's nothing to flush (i.e., in_place_protect_buffer is empty),
+   * we're done.
+   */
+  if (impl->in_place_protect_bytes_buffered == 0) {
+    *protected_output_frames_size = 0;
+    *still_pending_size = 0;
+    return TSI_OK;
+  }
+  /**
+   * If a new frame can start being processed, we encrypt the payload and reset
+   * the frame writer to point to in_place_protect_buffer that holds the newly
+   * sealed frame.
+   */
+  if (alts_is_frame_writer_done(impl->writer)) {
+    tsi_result result = seal(impl);
+    if (result != TSI_OK) {
+      return result;
+    }
+    if (!alts_reset_frame_writer(impl->writer, impl->in_place_protect_buffer,
+                                 impl->in_place_protect_bytes_buffered)) {
+      gpr_log(GPR_ERROR, "Couldn't reset frame writer.");
+      return TSI_INTERNAL_ERROR;
+    }
+  }
+  /**
+   * Write the sealed frame as much as possible to protected_output_frames. It's
+   * possible a frame will not be written out completely by a single flush
+   * (i.e., still_pending_size != 0), in which case the flush should be called
+   * iteratively until a complete frame has been written out.
+   */
+  size_t written_frame_bytes = *protected_output_frames_size;
+  if (!alts_write_frame_bytes(impl->writer, protected_output_frames,
+                              &written_frame_bytes)) {
+    gpr_log(GPR_ERROR, "Couldn't write frame bytes.");
+    return TSI_INTERNAL_ERROR;
+  }
+  *protected_output_frames_size = written_frame_bytes;
+  *still_pending_size = alts_get_num_writer_bytes_remaining(impl->writer);
+  /**
+   * If the current frame has been finished processing (i.e., sealed and written
+   * out completely), we empty in_place_protect_buffer.
+   */
+  if (alts_is_frame_writer_done(impl->writer)) {
+    impl->in_place_protect_bytes_buffered = 0;
+  }
+  return TSI_OK;
+}
+
+static tsi_result alts_protect(tsi_frame_protector* self,
+                               const unsigned char* unprotected_bytes,
+                               size_t* unprotected_bytes_size,
+                               unsigned char* protected_output_frames,
+                               size_t* protected_output_frames_size) {
+  if (self == nullptr || unprotected_bytes == nullptr ||
+      unprotected_bytes_size == nullptr || protected_output_frames == nullptr ||
+      protected_output_frames_size == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect().");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+
+  /**
+   * If more payload can be buffered, we buffer it as much as possible to
+   * in_place_protect_buffer.
+   */
+  if (impl->in_place_protect_bytes_buffered + impl->overhead_length <
+      max_encrypted_payload_bytes(impl)) {
+    size_t bytes_to_buffer = GPR_MIN(*unprotected_bytes_size,
+                                     max_encrypted_payload_bytes(impl) -
+                                         impl->in_place_protect_bytes_buffered -
+                                         impl->overhead_length);
+    *unprotected_bytes_size = bytes_to_buffer;
+    if (bytes_to_buffer > 0) {
+      memcpy(
+          impl->in_place_protect_buffer + impl->in_place_protect_bytes_buffered,
+          unprotected_bytes, bytes_to_buffer);
+      impl->in_place_protect_bytes_buffered += bytes_to_buffer;
+    }
+  } else {
+    *unprotected_bytes_size = 0;
+  }
+  /**
+   * If a full frame has been buffered, we output it. If the first condition
+   * holds, then there exists an unencrypted full frame. If the second
+   * condition holds, then there exists a full frame that has already been
+   * encrypted.
+   */
+  if (max_encrypted_payload_bytes(impl) ==
+          impl->in_place_protect_bytes_buffered + impl->overhead_length ||
+      max_encrypted_payload_bytes(impl) ==
+          impl->in_place_protect_bytes_buffered) {
+    size_t still_pending_size = 0;
+    return alts_protect_flush(self, protected_output_frames,
+                              protected_output_frames_size,
+                              &still_pending_size);
+  } else {
+    *protected_output_frames_size = 0;
+    return TSI_OK;
+  }
+}
+
+static tsi_result unseal(alts_frame_protector* impl) {
+  char* error_details = nullptr;
+  size_t output_size = 0;
+  grpc_status_code status = alts_crypter_process_in_place(
+      impl->unseal_crypter, impl->in_place_unprotect_buffer,
+      impl->max_unprotected_frame_size,
+      alts_get_output_bytes_read(impl->reader), &output_size, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "%s", error_details);
+    gpr_free(error_details);
+    return TSI_DATA_CORRUPTED;
+  }
+  return TSI_OK;
+}
+
+static void ensure_buffer_size(alts_frame_protector* impl) {
+  if (!alts_has_read_frame_length(impl->reader)) {
+    return;
+  }
+  size_t buffer_space_remaining = impl->max_unprotected_frame_size -
+                                  alts_get_output_bytes_read(impl->reader);
+  /**
+   * Check if we need to resize in_place_unprotect_buffer in order to hold
+   * remaining bytes of a full frame.
+   */
+  if (buffer_space_remaining < alts_get_reader_bytes_remaining(impl->reader)) {
+    size_t buffer_len = alts_get_output_bytes_read(impl->reader) +
+                        alts_get_reader_bytes_remaining(impl->reader);
+    unsigned char* buffer = static_cast<unsigned char*>(gpr_malloc(buffer_len));
+    memcpy(buffer, impl->in_place_unprotect_buffer,
+           alts_get_output_bytes_read(impl->reader));
+    impl->max_unprotected_frame_size = buffer_len;
+    gpr_free(impl->in_place_unprotect_buffer);
+    impl->in_place_unprotect_buffer = buffer;
+    alts_reset_reader_output_buffer(
+        impl->reader, buffer + alts_get_output_bytes_read(impl->reader));
+  }
+}
+
+static tsi_result alts_unprotect(tsi_frame_protector* self,
+                                 const unsigned char* protected_frames_bytes,
+                                 size_t* protected_frames_bytes_size,
+                                 unsigned char* unprotected_bytes,
+                                 size_t* unprotected_bytes_size) {
+  if (self == nullptr || protected_frames_bytes == nullptr ||
+      protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr ||
+      unprotected_bytes_size == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_unprotect().");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+  /**
+   * If a new frame can start being processed, we reset the frame reader to
+   * point to in_place_unprotect_buffer that will be used to hold deframed
+   * result.
+   */
+  if (alts_is_frame_reader_done(impl->reader) &&
+      ((alts_get_output_buffer(impl->reader) == nullptr) ||
+       (alts_get_output_bytes_read(impl->reader) ==
+        impl->in_place_unprotect_bytes_processed + impl->overhead_length))) {
+    if (!alts_reset_frame_reader(impl->reader,
+                                 impl->in_place_unprotect_buffer)) {
+      gpr_log(GPR_ERROR, "Couldn't reset frame reader.");
+      return TSI_INTERNAL_ERROR;
+    }
+    impl->in_place_unprotect_bytes_processed = 0;
+  }
+  /**
+   * If a full frame has not yet been read, we read more bytes from
+   * protected_frames_bytes until a full frame has been read. We also need to
+   * make sure in_place_unprotect_buffer is large enough to hold a complete
+   * frame.
+   */
+  if (!alts_is_frame_reader_done(impl->reader)) {
+    ensure_buffer_size(impl);
+    *protected_frames_bytes_size =
+        GPR_MIN(impl->max_unprotected_frame_size -
+                    alts_get_output_bytes_read(impl->reader),
+                *protected_frames_bytes_size);
+    size_t read_frames_bytes_size = *protected_frames_bytes_size;
+    if (!alts_read_frame_bytes(impl->reader, protected_frames_bytes,
+                               &read_frames_bytes_size)) {
+      gpr_log(GPR_ERROR, "Failed to process frame.");
+      return TSI_INTERNAL_ERROR;
+    }
+    *protected_frames_bytes_size = read_frames_bytes_size;
+  } else {
+    *protected_frames_bytes_size = 0;
+  }
+  /**
+   * If a full frame has been read, we unseal it, and write out the
+   * deframed result to unprotected_bytes.
+   */
+  if (alts_is_frame_reader_done(impl->reader)) {
+    if (impl->in_place_unprotect_bytes_processed == 0) {
+      tsi_result result = unseal(impl);
+      if (result != TSI_OK) {
+        return result;
+      }
+    }
+    size_t bytes_to_write = GPR_MIN(
+        *unprotected_bytes_size, alts_get_output_bytes_read(impl->reader) -
+                                     impl->in_place_unprotect_bytes_processed -
+                                     impl->overhead_length);
+    if (bytes_to_write > 0) {
+      memcpy(unprotected_bytes,
+             impl->in_place_unprotect_buffer +
+                 impl->in_place_unprotect_bytes_processed,
+             bytes_to_write);
+    }
+    *unprotected_bytes_size = bytes_to_write;
+    impl->in_place_unprotect_bytes_processed += bytes_to_write;
+    return TSI_OK;
+  } else {
+    *unprotected_bytes_size = 0;
+    return TSI_OK;
+  }
+}
+
+static void alts_destroy(tsi_frame_protector* self) {
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+  if (impl != nullptr) {
+    alts_crypter_destroy(impl->seal_crypter);
+    alts_crypter_destroy(impl->unseal_crypter);
+    gpr_free(impl->in_place_protect_buffer);
+    gpr_free(impl->in_place_unprotect_buffer);
+    alts_destroy_frame_writer(impl->writer);
+    alts_destroy_frame_reader(impl->reader);
+    gpr_free(impl);
+  }
+}
+
+static const tsi_frame_protector_vtable alts_frame_protector_vtable = {
+    alts_protect, alts_protect_flush, alts_unprotect, alts_destroy};
+
+static grpc_status_code create_alts_crypters(const uint8_t* key,
+                                             size_t key_size, bool is_client,
+                                             bool is_rekey,
+                                             alts_frame_protector* impl,
+                                             char** error_details) {
+  grpc_status_code status;
+  gsec_aead_crypter* aead_crypter_seal = nullptr;
+  gsec_aead_crypter* aead_crypter_unseal = nullptr;
+  status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
+                                            kAesGcmTagLength, is_rekey,
+                                            &aead_crypter_seal, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  status = gsec_aes_gcm_aead_crypter_create(
+      key, key_size, kAesGcmNonceLength, kAesGcmTagLength, is_rekey,
+      &aead_crypter_unseal, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  size_t overflow_size = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
+                                  : kAltsRecordProtocolFrameLimit;
+  status = alts_seal_crypter_create(aead_crypter_seal, is_client, overflow_size,
+                                    &impl->seal_crypter, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  status =
+      alts_unseal_crypter_create(aead_crypter_unseal, is_client, overflow_size,
+                                 &impl->unseal_crypter, error_details);
+  return status;
+}
+
+tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size,
+                                       bool is_client, bool is_rekey,
+                                       size_t* max_protected_frame_size,
+                                       tsi_frame_protector** self) {
+  if (key == nullptr || self == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_create_frame_protector().");
+    return TSI_INTERNAL_ERROR;
+  }
+  char* error_details = nullptr;
+  alts_frame_protector* impl =
+      static_cast<alts_frame_protector*>(gpr_zalloc(sizeof(*impl)));
+  grpc_status_code status = create_alts_crypters(
+      key, key_size, is_client, is_rekey, impl, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to create ALTS crypters, %s.", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  /**
+   * Set maximum frame size to be used by a frame protector. If it is nullptr, a
+   * default frame size will be used. Otherwise, the provided frame size will be
+   * adjusted (if not falling into a valid frame range) and used.
+   */
+  size_t max_protected_frame_size_to_set = kDefaultFrameLength;
+  if (max_protected_frame_size != nullptr) {
+    *max_protected_frame_size =
+        GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
+    *max_protected_frame_size =
+        GPR_MAX(*max_protected_frame_size, kMinFrameLength);
+    max_protected_frame_size_to_set = *max_protected_frame_size;
+  }
+  impl->max_protected_frame_size = max_protected_frame_size_to_set;
+  impl->max_unprotected_frame_size = max_protected_frame_size_to_set;
+  impl->in_place_protect_bytes_buffered = 0;
+  impl->in_place_unprotect_bytes_processed = 0;
+  impl->in_place_protect_buffer = static_cast<unsigned char*>(
+      gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set));
+  impl->in_place_unprotect_buffer = static_cast<unsigned char*>(
+      gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set));
+  impl->overhead_length = alts_crypter_num_overhead_bytes(impl->seal_crypter);
+  impl->writer = alts_create_frame_writer();
+  impl->reader = alts_create_frame_reader();
+  impl->base.vtable = &alts_frame_protector_vtable;
+  *self = &impl->base;
+  return TSI_OK;
+}
diff --git a/src/core/tsi/alts/frame_protector/alts_frame_protector.h b/src/core/tsi/alts/frame_protector/alts_frame_protector.h
new file mode 100644
index 0000000..321bffa
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_frame_protector.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+typedef struct alts_frame_protector alts_frame_protector;
+
+/**
+ * TODO: Add a parameter to the interface to support the use of
+ * different record protocols within a frame protector.
+ *
+ * This method creates a frame protector.
+ *
+ * - key: a symmetric key used to seal/unseal frames.
+ * - key_size: the size of symmetric key.
+ * - is_client: a flag indicating if the frame protector will be used at client
+ *   (is_client = true) or server (is_client = false) side.
+ * - is_rekey: a flag indicating if the frame protector will use an AEAD with
+ *   rekeying.
+ * - max_protected_frame_size: an in/out parameter indicating max frame size
+ *   to be used by the frame protector. If it is nullptr, the default frame
+ *   size will be used. Otherwise, the provided frame size will be adjusted (if
+ *   not falling into a valid frame range) and used.
+ * - self: a pointer to the frame protector returned from the method.
+ *
+ * This method returns TSI_OK on success and TSI_INTERNAL_ERROR otherwise.
+ */
+tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size,
+                                       bool is_client, bool is_rekey,
+                                       size_t* max_protected_frame_size,
+                                       tsi_frame_protector** self);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H */
diff --git a/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
new file mode 100644
index 0000000..0574ed5
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h"
+
+#include <grpc/support/alloc.h>
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code input_sanity_check(
+    const alts_record_protocol_crypter* rp_crypter, const unsigned char* data,
+    size_t* output_size, char** error_details) {
+  if (rp_crypter == nullptr) {
+    maybe_copy_error_msg("alts_crypter instance is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  } else if (data == nullptr) {
+    maybe_copy_error_msg("data is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  } else if (output_size == nullptr) {
+    maybe_copy_error_msg("output_size is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter,
+                                   char** error_details) {
+  bool is_overflow = false;
+  grpc_status_code status =
+      alts_counter_increment(rp_crypter->ctr, &is_overflow, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (is_overflow) {
+    const char error_msg[] =
+        "crypter counter is wrapped. The connection"
+        "should be closed and the key should be deleted.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c) {
+  if (c != nullptr) {
+    size_t num_overhead_bytes = 0;
+    char* error_details = nullptr;
+    const alts_record_protocol_crypter* rp_crypter =
+        reinterpret_cast<const alts_record_protocol_crypter*>(c);
+    grpc_status_code status = gsec_aead_crypter_tag_length(
+        rp_crypter->crypter, &num_overhead_bytes, &error_details);
+    if (status == GRPC_STATUS_OK) {
+      return num_overhead_bytes;
+    }
+  }
+  return 0;
+}
+
+void alts_record_protocol_crypter_destruct(alts_crypter* c) {
+  if (c != nullptr) {
+    alts_record_protocol_crypter* rp_crypter =
+        reinterpret_cast<alts_record_protocol_crypter*>(c);
+    alts_counter_destroy(rp_crypter->ctr);
+    gsec_aead_crypter_destroy(rp_crypter->crypter);
+  }
+}
+
+alts_record_protocol_crypter* alts_crypter_create_common(
+    gsec_aead_crypter* crypter, bool is_client, size_t overflow_size,
+    char** error_details) {
+  if (crypter != nullptr) {
+    auto* rp_crypter = static_cast<alts_record_protocol_crypter*>(
+        gpr_malloc(sizeof(alts_record_protocol_crypter)));
+    size_t counter_size = 0;
+    grpc_status_code status =
+        gsec_aead_crypter_nonce_length(crypter, &counter_size, error_details);
+    if (status != GRPC_STATUS_OK) {
+      return nullptr;
+    }
+    /* Create a counter. */
+    status = alts_counter_create(is_client, counter_size, overflow_size,
+                                 &rp_crypter->ctr, error_details);
+    if (status != GRPC_STATUS_OK) {
+      return nullptr;
+    }
+    rp_crypter->crypter = crypter;
+    return rp_crypter;
+  }
+  const char error_msg[] = "crypter is nullptr.";
+  maybe_copy_error_msg(error_msg, error_details);
+  return nullptr;
+}
diff --git a/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h
new file mode 100644
index 0000000..682a8f7
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+
+/**
+ * This file contains common implementation that will be used in both seal and
+ * unseal operations.
+ */
+
+/**
+ * Main struct for alts_record_protocol_crypter that will be used in both
+ * seal and unseal operations.
+ */
+typedef struct alts_record_protocol_crypter {
+  alts_crypter base;
+  gsec_aead_crypter* crypter;
+  alts_counter* ctr;
+} alts_record_protocol_crypter;
+
+/**
+ * This method performs input sanity checks on a subset of inputs to
+ * alts_crypter_process_in_place() for both seal and unseal operations.
+ *
+ * - rp_crypter: an alts_record_protocol_crypter instance.
+ * - data: it represents raw data that needs to be sealed in a seal operation or
+ *   protected data that needs to be unsealed in an unseal operation.
+ * - output_size: size of data written to the data buffer after a seal or
+ *   unseal operation.
+ * - error_details: a buffer containing an error message if any of checked
+ *   inputs is nullptr. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code input_sanity_check(
+    const alts_record_protocol_crypter* rp_crypter, const unsigned char* data,
+    size_t* output_size, char** error_details);
+
+/**
+ * This method increments the counter within an alts_record_protocol_crypter
+ * instance.
+ *
+ * - rp_crypter: an alts_record_protocol_crypter instance.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly or the counter is wrapped. It is legal to pass nullptr
+ *   into error_details and otherwise, the parameter should be freed with
+ *   gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter,
+                                   char** error_details);
+
+/**
+ * This method creates an alts_crypter instance, and populates the fields
+ * that are common to both seal and unseal operations.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. The
+ *   function does not take ownership of crypter.
+ * - is_client: a flag indicating if the alts_crypter instance will be
+ *   used at the client (is_client = true) or server (is_client =
+ *   false) side.
+ * - overflow_size: overflow size of counter in bytes.
+ * - error_details: a buffer containing an error message if the method does
+ *   not function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of creation, the method returns alts_record_protocol_crypter
+ * instance. Otherwise, it returns nullptr with its details specified in
+ * error_details (if error_details is not nullptr).
+ *
+ */
+alts_record_protocol_crypter* alts_crypter_create_common(
+    gsec_aead_crypter* crypter, bool is_client, size_t overflow_size,
+    char** error_details);
+
+/**
+ * For the following two methods, please refer to the corresponding API in
+ * alts_crypter.h for detailed specifications.
+ */
+size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c);
+
+void alts_record_protocol_crypter_destruct(alts_crypter* c);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H \
+        */
diff --git a/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc b/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
new file mode 100644
index 0000000..f407831
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h"
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+/* Perform input santity check for a seal operation. */
+static grpc_status_code seal_check(alts_crypter* c, const unsigned char* data,
+                                   size_t data_allocated_size, size_t data_size,
+                                   size_t* output_size, char** error_details) {
+  /* Do common input sanity check. */
+  grpc_status_code status = input_sanity_check(
+      reinterpret_cast<const alts_record_protocol_crypter*>(c), data,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) return status;
+  /* Do seal-specific check. */
+  size_t num_overhead_bytes =
+      alts_crypter_num_overhead_bytes(reinterpret_cast<const alts_crypter*>(c));
+  if (data_size == 0) {
+    const char error_msg[] = "data_size is zero.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (data_size + num_overhead_bytes > data_allocated_size) {
+    const char error_msg[] =
+        "data_allocated_size is smaller than sum of data_size and "
+        "num_overhead_bytes.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code alts_seal_crypter_process_in_place(
+    alts_crypter* c, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details) {
+  grpc_status_code status = seal_check(c, data, data_allocated_size, data_size,
+                                       output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Do AEAD encryption. */
+  alts_record_protocol_crypter* rp_crypter =
+      reinterpret_cast<alts_record_protocol_crypter*>(c);
+  status = gsec_aead_crypter_encrypt(
+      rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr),
+      alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */,
+      0 /* aad_length */, data, data_size, data, data_allocated_size,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Increment the crypter counter. */
+  return increment_counter(rp_crypter, error_details);
+}
+
+static const alts_crypter_vtable vtable = {
+    alts_record_protocol_crypter_num_overhead_bytes,
+    alts_seal_crypter_process_in_place, alts_record_protocol_crypter_destruct};
+
+grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client,
+                                          size_t overflow_size,
+                                          alts_crypter** crypter,
+                                          char** error_details) {
+  if (crypter == nullptr) {
+    const char error_msg[] = "crypter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  alts_record_protocol_crypter* rp_crypter =
+      alts_crypter_create_common(gc, !is_client, overflow_size, error_details);
+  if (rp_crypter == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  rp_crypter->base.vtable = &vtable;
+  *crypter = &rp_crypter->base;
+  return GRPC_STATUS_OK;
+}
diff --git a/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc b/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
new file mode 100644
index 0000000..51bea24
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h"
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+/* Perform input santity check. */
+static grpc_status_code unseal_check(alts_crypter* c, const unsigned char* data,
+                                     size_t data_allocated_size,
+                                     size_t data_size, size_t* output_size,
+                                     char** error_details) {
+  /* Do common input sanity check. */
+  grpc_status_code status = input_sanity_check(
+      reinterpret_cast<const alts_record_protocol_crypter*>(c), data,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Do unseal-specific input check. */
+  size_t num_overhead_bytes =
+      alts_crypter_num_overhead_bytes(reinterpret_cast<const alts_crypter*>(c));
+  if (num_overhead_bytes > data_size) {
+    const char error_msg[] = "data_size is smaller than num_overhead_bytes.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code alts_unseal_crypter_process_in_place(
+    alts_crypter* c, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details) {
+  grpc_status_code status = unseal_check(c, data, data_allocated_size,
+                                         data_size, output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Do AEAD decryption. */
+  alts_record_protocol_crypter* rp_crypter =
+      reinterpret_cast<alts_record_protocol_crypter*>(c);
+  status = gsec_aead_crypter_decrypt(
+      rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr),
+      alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */,
+      0 /* aad_length */, data, data_size, data, data_allocated_size,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Increment the crypter counter. */
+  return increment_counter(rp_crypter, error_details);
+}
+
+static const alts_crypter_vtable vtable = {
+    alts_record_protocol_crypter_num_overhead_bytes,
+    alts_unseal_crypter_process_in_place,
+    alts_record_protocol_crypter_destruct};
+
+grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc,
+                                            bool is_client,
+                                            size_t overflow_size,
+                                            alts_crypter** crypter,
+                                            char** error_details) {
+  if (crypter == nullptr) {
+    const char error_msg[] = "crypter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  alts_record_protocol_crypter* rp_crypter =
+      alts_crypter_create_common(gc, is_client, overflow_size, error_details);
+  if (rp_crypter == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  rp_crypter->base.vtable = &vtable;
+  *crypter = &rp_crypter->base;
+  return GRPC_STATUS_OK;
+}
diff --git a/src/core/tsi/alts/frame_protector/frame_handler.cc b/src/core/tsi/alts/frame_protector/frame_handler.cc
new file mode 100644
index 0000000..d3fda63
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/frame_handler.cc
@@ -0,0 +1,218 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/frame_handler.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+/* Use little endian to interpret a string of bytes as uint32_t. */
+static uint32_t load_32_le(const unsigned char* buffer) {
+  return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) |
+         (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]);
+}
+
+/* Store uint32_t as a string of little endian bytes. */
+static void store_32_le(uint32_t value, unsigned char* buffer) {
+  buffer[3] = (unsigned char)(value >> 24) & 0xFF;
+  buffer[2] = (unsigned char)(value >> 16) & 0xFF;
+  buffer[1] = (unsigned char)(value >> 8) & 0xFF;
+  buffer[0] = (unsigned char)(value)&0xFF;
+}
+
+/* Frame writer implementation. */
+alts_frame_writer* alts_create_frame_writer() {
+  alts_frame_writer* writer =
+      static_cast<alts_frame_writer*>(gpr_zalloc(sizeof(*writer)));
+  return writer;
+}
+
+bool alts_reset_frame_writer(alts_frame_writer* writer,
+                             const unsigned char* buffer, size_t length) {
+  if (buffer == nullptr) return false;
+  size_t max_input_size = SIZE_MAX - kFrameLengthFieldSize;
+  if (length > max_input_size) {
+    gpr_log(GPR_ERROR, "length must be at most %zu", max_input_size);
+    return false;
+  }
+  writer->input_buffer = buffer;
+  writer->input_size = length;
+  writer->input_bytes_written = 0;
+  writer->header_bytes_written = 0;
+  store_32_le(
+      static_cast<uint32_t>(writer->input_size + kFrameMessageTypeFieldSize),
+      writer->header_buffer);
+  store_32_le(kFrameMessageType, writer->header_buffer + kFrameLengthFieldSize);
+  return true;
+}
+
+bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output,
+                            size_t* bytes_size) {
+  if (bytes_size == nullptr || output == nullptr) return false;
+  if (alts_is_frame_writer_done(writer)) {
+    *bytes_size = 0;
+    return true;
+  }
+  size_t bytes_written = 0;
+  /* Write some header bytes, if needed. */
+  if (writer->header_bytes_written != sizeof(writer->header_buffer)) {
+    size_t bytes_to_write =
+        GPR_MIN(*bytes_size,
+                sizeof(writer->header_buffer) - writer->header_bytes_written);
+    memcpy(output, writer->header_buffer + writer->header_bytes_written,
+           bytes_to_write);
+    bytes_written += bytes_to_write;
+    *bytes_size -= bytes_to_write;
+    writer->header_bytes_written += bytes_to_write;
+    output += bytes_to_write;
+    if (writer->header_bytes_written != sizeof(writer->header_buffer)) {
+      *bytes_size = bytes_written;
+      return true;
+    }
+  }
+  /* Write some non-header bytes. */
+  size_t bytes_to_write =
+      GPR_MIN(writer->input_size - writer->input_bytes_written, *bytes_size);
+  memcpy(output, writer->input_buffer, bytes_to_write);
+  writer->input_buffer += bytes_to_write;
+  bytes_written += bytes_to_write;
+  writer->input_bytes_written += bytes_to_write;
+  *bytes_size = bytes_written;
+  return true;
+}
+
+bool alts_is_frame_writer_done(alts_frame_writer* writer) {
+  return writer->input_buffer == nullptr ||
+         writer->input_size == writer->input_bytes_written;
+}
+
+size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer) {
+  return (sizeof(writer->header_buffer) - writer->header_bytes_written) +
+         (writer->input_size - writer->input_bytes_written);
+}
+
+void alts_destroy_frame_writer(alts_frame_writer* writer) { gpr_free(writer); }
+
+/* Frame reader implementation. */
+alts_frame_reader* alts_create_frame_reader() {
+  alts_frame_reader* reader =
+      static_cast<alts_frame_reader*>(gpr_zalloc(sizeof(*reader)));
+  return reader;
+}
+
+bool alts_is_frame_reader_done(alts_frame_reader* reader) {
+  return reader->output_buffer == nullptr ||
+         (reader->header_bytes_read == sizeof(reader->header_buffer) &&
+          reader->bytes_remaining == 0);
+}
+
+bool alts_has_read_frame_length(alts_frame_reader* reader) {
+  return sizeof(reader->header_buffer) == reader->header_bytes_read;
+}
+
+size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader) {
+  return alts_has_read_frame_length(reader) ? reader->bytes_remaining : 0;
+}
+
+void alts_reset_reader_output_buffer(alts_frame_reader* reader,
+                                     unsigned char* buffer) {
+  reader->output_buffer = buffer;
+}
+
+bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer) {
+  if (buffer == nullptr) return false;
+  reader->output_buffer = buffer;
+  reader->bytes_remaining = 0;
+  reader->header_bytes_read = 0;
+  reader->output_bytes_read = 0;
+  return true;
+}
+
+bool alts_read_frame_bytes(alts_frame_reader* reader,
+                           const unsigned char* bytes, size_t* bytes_size) {
+  if (bytes_size == nullptr) return false;
+  if (bytes == nullptr) {
+    *bytes_size = 0;
+    return false;
+  }
+  if (alts_is_frame_reader_done(reader)) {
+    *bytes_size = 0;
+    return true;
+  }
+  size_t bytes_processed = 0;
+  /* Process the header, if needed. */
+  if (reader->header_bytes_read != sizeof(reader->header_buffer)) {
+    size_t bytes_to_write = GPR_MIN(
+        *bytes_size, sizeof(reader->header_buffer) - reader->header_bytes_read);
+    memcpy(reader->header_buffer + reader->header_bytes_read, bytes,
+           bytes_to_write);
+    reader->header_bytes_read += bytes_to_write;
+    bytes_processed += bytes_to_write;
+    bytes += bytes_to_write;
+    *bytes_size -= bytes_to_write;
+    if (reader->header_bytes_read != sizeof(reader->header_buffer)) {
+      *bytes_size = bytes_processed;
+      return true;
+    }
+    size_t frame_length = load_32_le(reader->header_buffer);
+    if (frame_length < kFrameMessageTypeFieldSize ||
+        frame_length > kFrameMaxSize) {
+      gpr_log(GPR_ERROR,
+              "Bad frame length (should be at least %zu, and at most %zu)",
+              kFrameMessageTypeFieldSize, kFrameMaxSize);
+      *bytes_size = 0;
+      return false;
+    }
+    size_t message_type =
+        load_32_le(reader->header_buffer + kFrameLengthFieldSize);
+    if (message_type != kFrameMessageType) {
+      gpr_log(GPR_ERROR, "Unsupported message type %zu (should be %zu)",
+              message_type, kFrameMessageType);
+      *bytes_size = 0;
+      return false;
+    }
+    reader->bytes_remaining = frame_length - kFrameMessageTypeFieldSize;
+  }
+  /* Process the non-header bytes. */
+  size_t bytes_to_write = GPR_MIN(*bytes_size, reader->bytes_remaining);
+  memcpy(reader->output_buffer, bytes, bytes_to_write);
+  reader->output_buffer += bytes_to_write;
+  bytes_processed += bytes_to_write;
+  reader->bytes_remaining -= bytes_to_write;
+  reader->output_bytes_read += bytes_to_write;
+  *bytes_size = bytes_processed;
+  return true;
+}
+
+size_t alts_get_output_bytes_read(alts_frame_reader* reader) {
+  return reader->output_bytes_read;
+}
+
+unsigned char* alts_get_output_buffer(alts_frame_reader* reader) {
+  return reader->output_buffer;
+}
+
+void alts_destroy_frame_reader(alts_frame_reader* reader) { gpr_free(reader); }
diff --git a/src/core/tsi/alts/frame_protector/frame_handler.h b/src/core/tsi/alts/frame_protector/frame_handler.h
new file mode 100644
index 0000000..a703ff4
--- /dev/null
+++ b/src/core/tsi/alts/frame_protector/frame_handler.h
@@ -0,0 +1,236 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+const size_t kFrameMessageType = 0x06;
+const size_t kFrameLengthFieldSize = 4;
+const size_t kFrameMessageTypeFieldSize = 4;
+const size_t kFrameMaxSize = 1024 * 1024;
+const size_t kFrameHeaderSize =
+    kFrameLengthFieldSize + kFrameMessageTypeFieldSize;
+
+/**
+ * Implementation of frame reader and frame writer. All APIs in the
+ * header are thread-compatible.
+ */
+
+/**
+ * Main struct for a frame writer. It reads frames from an input buffer, and
+ * writes the contents as raw bytes. It does not own the input buffer.
+ */
+typedef struct alts_frame_writer {
+  const unsigned char* input_buffer;
+  unsigned char header_buffer[kFrameHeaderSize];
+  size_t input_bytes_written;
+  size_t header_bytes_written;
+  size_t input_size;
+} alts_frame_writer;
+
+/**
+ * Main struct for a frame reader. It reads raw bytes and puts the framed
+ * result into an output buffer. It does not own the output buffer.
+ */
+typedef struct alts_frame_reader {
+  unsigned char* output_buffer;
+  unsigned char header_buffer[kFrameHeaderSize];
+  size_t header_bytes_read;
+  size_t output_bytes_read;
+  size_t bytes_remaining;
+} alts_frame_reader;
+
+/**
+ * This method creates a frame writer instance and initializes its internal
+ * states.
+ */
+alts_frame_writer* alts_create_frame_writer();
+
+/**
+ * This method resets internal states of a frame writer and prepares to write
+ * a single frame. It does not take ownership of payload_buffer.
+ * The payload_buffer must outlive the writer.
+ *
+ * - writer: a frame writer instance.
+ * - buffer: a buffer storing full payload data to be framed.
+ * - length: size of payload data.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_reset_frame_writer(alts_frame_writer* writer,
+                             const unsigned char* buffer, size_t length);
+
+/**
+ * This method writes up to bytes_size bytes of a frame to output.
+ *
+ * - writer: a frame writer instance.
+ * - output: an output buffer used to store the frame.
+ * - bytes_size: an in/out parameter that stores the size of output buffer
+ *   before the call, and gets written the number of frame bytes written to the
+ *   buffer.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output,
+                            size_t* bytes_size);
+
+/**
+ * This method checks if a reset can be called to write a new frame. It returns
+ * true if it's the first time to frame a payload, or the current frame has
+ * been finished processing. It returns false if it's not ready yet to start a
+ * new frame (e.g., more payload data needs to be accumulated to process the
+ * current frame).
+ *
+ * if (alts_is_frame_writer_done(writer)) {
+ *   // a new frame can be written, call reset.
+ *   alts_reset_frame_writer(writer, payload_buffer, payload_size);
+ * } else {
+ *   // accumulate more payload data until a full frame can be written.
+ * }
+ *
+ * - writer: a frame writer instance.
+ */
+bool alts_is_frame_writer_done(alts_frame_writer* writer);
+
+/**
+ * This method returns the number of bytes left to write before a complete frame
+ * is formed.
+ *
+ * - writer: a frame writer instance.
+ */
+size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer);
+
+/**
+ * This method destroys a frame writer instance.
+ *
+ * - writer: a frame writer instance.
+ */
+void alts_destroy_frame_writer(alts_frame_writer* writer);
+
+/**
+ * This method creates a frame reader instance and initializes its internal
+ * states.
+ */
+alts_frame_reader* alts_create_frame_reader();
+
+/**
+ * This method resets internal states of a frame reader (including setting its
+ * output_buffer with buffer), and prepares to write processed bytes to
+ * an output_buffer. It does not take ownership of buffer. The buffer must
+ * outlive reader.
+ *
+ * - reader: a frame reader instance.
+ * - buffer: an output buffer used to store deframed results.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer);
+
+/**
+ * This method processes up to the number of bytes given in bytes_size. It may
+ * choose not to process all the bytes, if, for instance, more bytes are
+ * given to the method than required to complete the current frame.
+ *
+ * - reader: a frame reader instance.
+ * - bytes: a buffer that stores data to be processed.
+ * - bytes_size: an in/out parameter that stores the size of bytes before the
+ *   call and gets written the number of bytes processed.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_read_frame_bytes(alts_frame_reader* reader,
+                           const unsigned char* bytes, size_t* bytes_size);
+
+/**
+ * This method checks if a frame length has been read.
+ *
+ * - reader: a frame reader instance.
+ *
+ * The method returns true if a frame length has been read and false otherwise.
+ */
+bool alts_has_read_frame_length(alts_frame_reader* reader);
+
+/**
+ * This method returns the number of bytes the frame reader intends to write.
+ * It may only be called if alts_has_read_frame_length() returns true.
+ *
+ * - reader: a frame reader instance.
+ */
+size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader);
+
+/**
+ * This method resets output_buffer but does not otherwise modify other internal
+ * states of a frame reader instance. After being set, the new output_buffer
+ * will hold the deframed payload held by the original output_buffer. It does
+ * not take ownership of buffer. The buffer must outlive the reader.
+ * To distinguish between two reset methods on a frame reader,
+ *
+ * if (alts_fh_is_frame_reader_done(reader)) {
+ *   // if buffer contains a full payload to be deframed, call reset.
+ *   alts_reset_frame_reader(reader, buffer);
+ * }
+ *
+ * // if remaining buffer space is not enough to hold a full payload
+ * if (buffer_space_remaining < alts_get_reader_bytes_remaining(reader)) {
+ *   // allocate enough space for a new buffer, copy back data processed so far,
+ *   // and call reset.
+ *   alts_reset_reader_output_buffer(reader, new_buffer).
+ * }
+ *
+ * - reader: a frame reader instance.
+ * - buffer: a buffer used to set reader's output_buffer.
+ */
+void alts_reset_reader_output_buffer(alts_frame_reader* reader,
+                                     unsigned char* buffer);
+
+/**
+ * This method checks if reset can be called to start processing a new frame.
+ * If true and reset was previously called, a full frame has been processed and
+ * the content of the frame is available in output_buffer.
+
+ * - reader: a frame reader instance.
+ */
+bool alts_is_frame_reader_done(alts_frame_reader* reader);
+
+/**
+ * This method returns output_bytes_read of a frame reader instance.
+ *
+ * - reader: a frame reader instance.
+ */
+size_t alts_get_output_bytes_read(alts_frame_reader* reader);
+
+/**
+ * This method returns output_buffer of a frame reader instance.
+ *
+ * - reader: a frame reader instance.
+ */
+unsigned char* alts_get_output_buffer(alts_frame_reader* reader);
+
+/**
+ * This method destroys a frame reader instance.
+ *
+ * - reader: a frame reader instance.
+ */
+void alts_destroy_frame_reader(alts_frame_reader* reader);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H */
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_client.cc b/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
new file mode 100644
index 0000000..40f30e4
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
@@ -0,0 +1,316 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+
+const int kHandshakerClientOpNum = 4;
+
+typedef struct alts_grpc_handshaker_client {
+  alts_handshaker_client base;
+  grpc_call* call;
+  alts_grpc_caller grpc_caller;
+} alts_grpc_handshaker_client;
+
+static grpc_call_error grpc_start_batch(grpc_call* call, const grpc_op* ops,
+                                        size_t nops, void* tag) {
+  return grpc_call_start_batch(call, ops, nops, tag, nullptr);
+}
+
+/**
+ * Populate grpc operation data with the fields of ALTS TSI event and make a
+ * grpc call.
+ */
+static tsi_result make_grpc_call(alts_handshaker_client* client,
+                                 alts_tsi_event* event, bool is_start) {
+  GPR_ASSERT(client != nullptr && event != nullptr);
+  alts_grpc_handshaker_client* grpc_client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(client);
+  grpc_op ops[kHandshakerClientOpNum];
+  memset(ops, 0, sizeof(ops));
+  grpc_op* op = ops;
+  if (is_start) {
+    op->op = GRPC_OP_SEND_INITIAL_METADATA;
+    op->data.send_initial_metadata.count = 0;
+    op++;
+    GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+    op->op = GRPC_OP_RECV_INITIAL_METADATA;
+    op->data.recv_initial_metadata.recv_initial_metadata =
+        &event->initial_metadata;
+    op++;
+    GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+  }
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = event->send_buffer;
+  op++;
+  GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &event->recv_buffer;
+  op++;
+  GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+  GPR_ASSERT(grpc_client->grpc_caller != nullptr);
+  if (grpc_client->grpc_caller(grpc_client->call, ops,
+                               static_cast<size_t>(op - ops),
+                               (void*)event) != GRPC_CALL_OK) {
+    gpr_log(GPR_ERROR, "Start batch operation failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  return TSI_OK;
+}
+
+/* Create and populate a client_start handshaker request, then serialize it. */
+static grpc_byte_buffer* get_serialized_start_client(alts_tsi_event* event) {
+  bool ok = true;
+  grpc_gcp_handshaker_req* req =
+      grpc_gcp_handshaker_req_create(CLIENT_START_REQ);
+  ok &= grpc_gcp_handshaker_req_set_handshake_protocol(
+      req, grpc_gcp_HandshakeProtocol_ALTS);
+  ok &= grpc_gcp_handshaker_req_add_application_protocol(
+      req, ALTS_APPLICATION_PROTOCOL);
+  ok &= grpc_gcp_handshaker_req_add_record_protocol(req, ALTS_RECORD_PROTOCOL);
+  grpc_gcp_rpc_protocol_versions* versions = &event->options->rpc_versions;
+  ok &= grpc_gcp_handshaker_req_set_rpc_versions(
+      req, versions->max_rpc_version.major, versions->max_rpc_version.minor,
+      versions->min_rpc_version.major, versions->min_rpc_version.minor);
+  char* target_name = grpc_slice_to_c_string(event->target_name);
+  ok &= grpc_gcp_handshaker_req_set_target_name(req, target_name);
+  target_service_account* ptr =
+      (reinterpret_cast<grpc_alts_credentials_client_options*>(event->options))
+          ->target_account_list_head;
+  while (ptr != nullptr) {
+    grpc_gcp_handshaker_req_add_target_identity_service_account(req, ptr->data);
+    ptr = ptr->next;
+  }
+  grpc_slice slice;
+  ok &= grpc_gcp_handshaker_req_encode(req, &slice);
+  grpc_byte_buffer* buffer = nullptr;
+  if (ok) {
+    buffer = grpc_raw_byte_buffer_create(&slice, 1 /* number of slices */);
+  }
+  grpc_slice_unref(slice);
+  gpr_free(target_name);
+  grpc_gcp_handshaker_req_destroy(req);
+  return buffer;
+}
+
+static tsi_result handshaker_client_start_client(alts_handshaker_client* client,
+                                                 alts_tsi_event* event) {
+  if (client == nullptr || event == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to alts_grpc_handshaker_client_start_client()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_byte_buffer* buffer = get_serialized_start_client(event);
+  if (buffer == nullptr) {
+    gpr_log(GPR_ERROR, "get_serialized_start_client() failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  event->send_buffer = buffer;
+  tsi_result result = make_grpc_call(client, event, true /* is_start */);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "make_grpc_call() failed");
+  }
+  return result;
+}
+
+/* Create and populate a start_server handshaker request, then serialize it. */
+static grpc_byte_buffer* get_serialized_start_server(
+    alts_tsi_event* event, grpc_slice* bytes_received) {
+  GPR_ASSERT(bytes_received != nullptr);
+  grpc_gcp_handshaker_req* req =
+      grpc_gcp_handshaker_req_create(SERVER_START_REQ);
+  bool ok = grpc_gcp_handshaker_req_add_application_protocol(
+      req, ALTS_APPLICATION_PROTOCOL);
+  ok &= grpc_gcp_handshaker_req_param_add_record_protocol(
+      req, grpc_gcp_HandshakeProtocol_ALTS, ALTS_RECORD_PROTOCOL);
+  ok &= grpc_gcp_handshaker_req_set_in_bytes(
+      req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received),
+      GRPC_SLICE_LENGTH(*bytes_received));
+  grpc_gcp_rpc_protocol_versions* versions = &event->options->rpc_versions;
+  ok &= grpc_gcp_handshaker_req_set_rpc_versions(
+      req, versions->max_rpc_version.major, versions->max_rpc_version.minor,
+      versions->min_rpc_version.major, versions->min_rpc_version.minor);
+  grpc_slice req_slice;
+  ok &= grpc_gcp_handshaker_req_encode(req, &req_slice);
+  grpc_byte_buffer* buffer = nullptr;
+  if (ok) {
+    buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */);
+  }
+  grpc_slice_unref(req_slice);
+  grpc_gcp_handshaker_req_destroy(req);
+  return buffer;
+}
+
+static tsi_result handshaker_client_start_server(alts_handshaker_client* client,
+                                                 alts_tsi_event* event,
+                                                 grpc_slice* bytes_received) {
+  if (client == nullptr || event == nullptr || bytes_received == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to alts_grpc_handshaker_client_start_server()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_byte_buffer* buffer = get_serialized_start_server(event, bytes_received);
+  if (buffer == nullptr) {
+    gpr_log(GPR_ERROR, "get_serialized_start_server() failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  event->send_buffer = buffer;
+  tsi_result result = make_grpc_call(client, event, true /* is_start */);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "make_grpc_call() failed");
+  }
+  return result;
+}
+
+/* Create and populate a next handshaker request, then serialize it. */
+static grpc_byte_buffer* get_serialized_next(grpc_slice* bytes_received) {
+  GPR_ASSERT(bytes_received != nullptr);
+  grpc_gcp_handshaker_req* req = grpc_gcp_handshaker_req_create(NEXT_REQ);
+  bool ok = grpc_gcp_handshaker_req_set_in_bytes(
+      req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received),
+      GRPC_SLICE_LENGTH(*bytes_received));
+  grpc_slice req_slice;
+  ok &= grpc_gcp_handshaker_req_encode(req, &req_slice);
+  grpc_byte_buffer* buffer = nullptr;
+  if (ok) {
+    buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */);
+  }
+  grpc_slice_unref(req_slice);
+  grpc_gcp_handshaker_req_destroy(req);
+  return buffer;
+}
+
+static tsi_result handshaker_client_next(alts_handshaker_client* client,
+                                         alts_tsi_event* event,
+                                         grpc_slice* bytes_received) {
+  if (client == nullptr || event == nullptr || bytes_received == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to alts_grpc_handshaker_client_next()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_byte_buffer* buffer = get_serialized_next(bytes_received);
+  if (buffer == nullptr) {
+    gpr_log(GPR_ERROR, "get_serialized_next() failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  event->send_buffer = buffer;
+  tsi_result result = make_grpc_call(client, event, false /* is_start */);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "make_grpc_call() failed");
+  }
+  return result;
+}
+
+static void handshaker_client_destruct(alts_handshaker_client* client) {
+  if (client == nullptr) {
+    return;
+  }
+  alts_grpc_handshaker_client* grpc_client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(client);
+  grpc_call_unref(grpc_client->call);
+}
+
+static const alts_handshaker_client_vtable vtable = {
+    handshaker_client_start_client, handshaker_client_start_server,
+    handshaker_client_next, handshaker_client_destruct};
+
+alts_handshaker_client* alts_grpc_handshaker_client_create(
+    grpc_channel* channel, grpc_completion_queue* queue,
+    const char* handshaker_service_url) {
+  if (channel == nullptr || queue == nullptr ||
+      handshaker_service_url == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to alts_handshaker_client_create()");
+    return nullptr;
+  }
+  alts_grpc_handshaker_client* client =
+      static_cast<alts_grpc_handshaker_client*>(gpr_zalloc(sizeof(*client)));
+  client->grpc_caller = grpc_start_batch;
+  grpc_slice slice = grpc_slice_from_copied_string(handshaker_service_url);
+  client->call = grpc_channel_create_call(
+      channel, nullptr, GRPC_PROPAGATE_DEFAULTS, queue,
+      grpc_slice_from_static_string(ALTS_SERVICE_METHOD), &slice,
+      gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
+  client->base.vtable = &vtable;
+  grpc_slice_unref(slice);
+  return &client->base;
+}
+
+namespace grpc_core {
+namespace internal {
+
+void alts_handshaker_client_set_grpc_caller_for_testing(
+    alts_handshaker_client* client, alts_grpc_caller caller) {
+  GPR_ASSERT(client != nullptr && caller != nullptr);
+  alts_grpc_handshaker_client* grpc_client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(client);
+  grpc_client->grpc_caller = caller;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client,
+                                               alts_tsi_event* event) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->client_start != nullptr) {
+    return client->vtable->client_start(client, event);
+  }
+  gpr_log(GPR_ERROR,
+          "client or client->vtable has not been initialized properly");
+  return TSI_INVALID_ARGUMENT;
+}
+
+tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
+                                               alts_tsi_event* event,
+                                               grpc_slice* bytes_received) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->server_start != nullptr) {
+    return client->vtable->server_start(client, event, bytes_received);
+  }
+  gpr_log(GPR_ERROR,
+          "client or client->vtable has not been initialized properly");
+  return TSI_INVALID_ARGUMENT;
+}
+
+tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
+                                       alts_tsi_event* event,
+                                       grpc_slice* bytes_received) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->next != nullptr) {
+    return client->vtable->next(client, event, bytes_received);
+  }
+  gpr_log(GPR_ERROR,
+          "client or client->vtable has not been initialized properly");
+  return TSI_INVALID_ARGUMENT;
+}
+
+void alts_handshaker_client_destroy(alts_handshaker_client* client) {
+  if (client != nullptr) {
+    if (client->vtable != nullptr && client->vtable->destruct != nullptr) {
+      client->vtable->destruct(client);
+    }
+    gpr_free(client);
+  }
+}
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_client.h b/src/core/tsi/alts/handshaker/alts_handshaker_client.h
new file mode 100644
index 0000000..fb2d2cf
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_client.h
@@ -0,0 +1,137 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+
+#define ALTS_SERVICE_METHOD "/grpc.gcp.HandshakerService/DoHandshake"
+#define ALTS_APPLICATION_PROTOCOL "grpc"
+#define ALTS_RECORD_PROTOCOL "ALTSRP_GCM_AES128_REKEY"
+
+const size_t kAltsAes128GcmRekeyKeyLength = 44;
+
+/**
+ * A ALTS handshaker client interface. It is used to communicate with
+ * ALTS handshaker service by scheduling a handshaker request that could be one
+ * of client_start, server_start, and next handshaker requests. All APIs in the
+ * header are thread-compatible.
+ */
+typedef struct alts_handshaker_client alts_handshaker_client;
+
+/* A function that makes the grpc call to the handshaker service. */
+typedef grpc_call_error (*alts_grpc_caller)(grpc_call* call, const grpc_op* ops,
+                                            size_t nops, void* tag);
+
+/* V-table for ALTS handshaker client operations. */
+typedef struct alts_handshaker_client_vtable {
+  tsi_result (*client_start)(alts_handshaker_client* client,
+                             alts_tsi_event* event);
+  tsi_result (*server_start)(alts_handshaker_client* client,
+                             alts_tsi_event* event, grpc_slice* bytes_received);
+  tsi_result (*next)(alts_handshaker_client* client, alts_tsi_event* event,
+                     grpc_slice* bytes_received);
+  void (*destruct)(alts_handshaker_client* client);
+} alts_handshaker_client_vtable;
+
+struct alts_handshaker_client {
+  const alts_handshaker_client_vtable* vtable;
+};
+
+/**
+ * This method schedules a client_start handshaker request to ALTS handshaker
+ * service.
+ *
+ * - client: ALTS handshaker client instance.
+ * - event: ALTS TSI event instance.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client,
+                                               alts_tsi_event* event);
+
+/**
+ * This method schedules a server_start handshaker request to ALTS handshaker
+ * service.
+ *
+ * - client: ALTS handshaker client instance.
+ * - event: ALTS TSI event instance.
+ * - bytes_received: bytes in out_frames returned from the peer's handshaker
+ *   response.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
+                                               alts_tsi_event* event,
+                                               grpc_slice* bytes_received);
+
+/**
+ * This method schedules a next handshaker request to ALTS handshaker service.
+ *
+ * - client: ALTS handshaker client instance.
+ * - event: ALTS TSI event instance.
+ * - bytes_received: bytes in out_frames returned from the peer's handshaker
+ *   response.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
+                                       alts_tsi_event* event,
+                                       grpc_slice* bytes_received);
+
+/**
+ * This method destroys a ALTS handshaker client.
+ *
+ * - client: a ALTS handshaker client instance.
+ */
+void alts_handshaker_client_destroy(alts_handshaker_client* client);
+
+/**
+ * This method creates a ALTS handshaker client.
+ *
+ * - channel: grpc channel to ALTS handshaker service.
+ * - queue: grpc completion queue.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port".
+ *
+ * It returns the created ALTS handshaker client on success, and NULL on
+ * failure.
+ */
+alts_handshaker_client* alts_grpc_handshaker_client_create(
+    grpc_channel* channel, grpc_completion_queue* queue,
+    const char* handshaker_service_url);
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * Unsafe, use for testing only. It allows the caller to change the way that
+ * GRPC calls are made to the handshaker service.
+ */
+void alts_handshaker_client_set_grpc_caller_for_testing(
+    alts_handshaker_client* client, alts_grpc_caller caller);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H */
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
new file mode 100644
index 0000000..256e414
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
@@ -0,0 +1,520 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+/* HandshakerReq */
+grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create(
+    grpc_gcp_handshaker_req_type type) {
+  grpc_gcp_handshaker_req* req =
+      static_cast<grpc_gcp_handshaker_req*>(gpr_zalloc(sizeof(*req)));
+  switch (type) {
+    case CLIENT_START_REQ:
+      req->has_client_start = true;
+      break;
+    case SERVER_START_REQ:
+      req->has_server_start = true;
+      break;
+    case NEXT_REQ:
+      req->has_next = true;
+      break;
+  }
+  return req;
+}
+
+void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req) {
+  if (req == nullptr) {
+    return;
+  }
+  if (req->has_client_start) {
+    /* Destroy client_start request. */
+    destroy_repeated_field_list_identity(
+        static_cast<repeated_field*>(req->client_start.target_identities.arg));
+    destroy_repeated_field_list_string(static_cast<repeated_field*>(
+        req->client_start.application_protocols.arg));
+    destroy_repeated_field_list_string(
+        static_cast<repeated_field*>(req->client_start.record_protocols.arg));
+    if (req->client_start.has_local_identity) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.local_identity.hostname.arg));
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.local_identity.service_account.arg));
+    }
+    if (req->client_start.has_local_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.local_endpoint.ip_address.arg));
+    }
+    if (req->client_start.has_remote_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.remote_endpoint.ip_address.arg));
+    }
+    destroy_slice(static_cast<grpc_slice*>(req->client_start.target_name.arg));
+  } else if (req->has_server_start) {
+    /* Destroy server_start request. */
+    size_t i = 0;
+    for (i = 0; i < req->server_start.handshake_parameters_count; i++) {
+      destroy_repeated_field_list_identity(
+          static_cast<repeated_field*>(req->server_start.handshake_parameters[i]
+                                           .value.local_identities.arg));
+      destroy_repeated_field_list_string(
+          static_cast<repeated_field*>(req->server_start.handshake_parameters[i]
+                                           .value.record_protocols.arg));
+    }
+    destroy_repeated_field_list_string(static_cast<repeated_field*>(
+        req->server_start.application_protocols.arg));
+    if (req->server_start.has_local_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->server_start.local_endpoint.ip_address.arg));
+    }
+    if (req->server_start.has_remote_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->server_start.remote_endpoint.ip_address.arg));
+    }
+    destroy_slice(static_cast<grpc_slice*>(req->server_start.in_bytes.arg));
+  } else {
+    /* Destroy next request. */
+    destroy_slice(static_cast<grpc_slice*>(req->next.in_bytes.arg));
+  }
+  gpr_free(req);
+}
+
+bool grpc_gcp_handshaker_req_set_handshake_protocol(
+    grpc_gcp_handshaker_req* req,
+    grpc_gcp_handshake_protocol handshake_protocol) {
+  if (req == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_handshake_protocol().");
+    return false;
+  }
+  req->client_start.has_handshake_security_protocol = true;
+  req->client_start.handshake_security_protocol = handshake_protocol;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req,
+                                             const char* target_name) {
+  if (req == nullptr || target_name == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_target_name().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(target_name, strlen(target_name));
+  req->client_start.target_name.arg = slice;
+  req->client_start.target_name.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_add_application_protocol(
+    grpc_gcp_handshaker_req* req, const char* application_protocol) {
+  if (req == nullptr || application_protocol == nullptr || req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_add_application_protocol().");
+    return false;
+  }
+  grpc_slice* slice =
+      create_slice(application_protocol, strlen(application_protocol));
+  if (req->has_client_start) {
+    add_repeated_field(reinterpret_cast<repeated_field**>(
+                           &req->client_start.application_protocols.arg),
+                       slice);
+    req->client_start.application_protocols.funcs.encode =
+        encode_repeated_string_cb;
+  } else {
+    add_repeated_field(reinterpret_cast<repeated_field**>(
+                           &req->server_start.application_protocols.arg),
+                       slice);
+    req->server_start.application_protocols.funcs.encode =
+        encode_repeated_string_cb;
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req,
+                                                 const char* record_protocol) {
+  if (req == nullptr || record_protocol == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_add_record_protocol().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol));
+  add_repeated_field(reinterpret_cast<repeated_field**>(
+                         &req->client_start.record_protocols.arg),
+                     slice);
+  req->client_start.record_protocols.funcs.encode = encode_repeated_string_cb;
+  return true;
+}
+
+static void set_identity_hostname(grpc_gcp_identity* identity,
+                                  const char* hostname) {
+  grpc_slice* slice = create_slice(hostname, strlen(hostname));
+  identity->hostname.arg = slice;
+  identity->hostname.funcs.encode = encode_string_or_bytes_cb;
+}
+
+static void set_identity_service_account(grpc_gcp_identity* identity,
+                                         const char* service_account) {
+  grpc_slice* slice = create_slice(service_account, strlen(service_account));
+  identity->service_account.arg = slice;
+  identity->service_account.funcs.encode = encode_string_or_bytes_cb;
+}
+
+bool grpc_gcp_handshaker_req_add_target_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname) {
+  if (req == nullptr || hostname == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_add_target_identity_hostname().");
+    return false;
+  }
+  grpc_gcp_identity* target_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*target_identity)));
+  set_identity_hostname(target_identity, hostname);
+  req->client_start.target_identities.funcs.encode =
+      encode_repeated_identity_cb;
+  add_repeated_field(reinterpret_cast<repeated_field**>(
+                         &req->client_start.target_identities.arg),
+                     target_identity);
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_add_target_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account) {
+  if (req == nullptr || service_account == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_add_target_identity_service_account().");
+    return false;
+  }
+  grpc_gcp_identity* target_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*target_identity)));
+  set_identity_service_account(target_identity, service_account);
+  req->client_start.target_identities.funcs.encode =
+      encode_repeated_identity_cb;
+  add_repeated_field(reinterpret_cast<repeated_field**>(
+                         &req->client_start.target_identities.arg),
+                     target_identity);
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname) {
+  if (req == nullptr || hostname == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_set_local_identity_hostname().");
+    return false;
+  }
+  req->client_start.has_local_identity = true;
+  set_identity_hostname(&req->client_start.local_identity, hostname);
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account) {
+  if (req == nullptr || service_account == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_set_local_identity_service_account().");
+    return false;
+  }
+  req->client_start.has_local_identity = true;
+  set_identity_service_account(&req->client_start.local_identity,
+                               service_account);
+  return true;
+}
+
+static void set_endpoint(grpc_gcp_endpoint* endpoint, const char* ip_address,
+                         size_t port, grpc_gcp_network_protocol protocol) {
+  grpc_slice* slice = create_slice(ip_address, strlen(ip_address));
+  endpoint->ip_address.arg = slice;
+  endpoint->ip_address.funcs.encode = encode_string_or_bytes_cb;
+  endpoint->has_port = true;
+  endpoint->port = static_cast<int32_t>(port);
+  endpoint->has_protocol = true;
+  endpoint->protocol = protocol;
+}
+
+bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req,
+                                              uint32_t max_major,
+                                              uint32_t max_minor,
+                                              uint32_t min_major,
+                                              uint32_t min_minor) {
+  if (req == nullptr || req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_rpc_versions().");
+    return false;
+  }
+  if (req->has_client_start) {
+    req->client_start.has_rpc_versions = true;
+    grpc_gcp_rpc_protocol_versions_set_max(&req->client_start.rpc_versions,
+                                           max_major, max_minor);
+    grpc_gcp_rpc_protocol_versions_set_min(&req->client_start.rpc_versions,
+                                           min_major, min_minor);
+  } else {
+    req->server_start.has_rpc_versions = true;
+    grpc_gcp_rpc_protocol_versions_set_max(&req->server_start.rpc_versions,
+                                           max_major, max_minor);
+    grpc_gcp_rpc_protocol_versions_set_min(&req->server_start.rpc_versions,
+                                           min_major, min_minor);
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_local_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol) {
+  if (req == nullptr || ip_address == nullptr || port > 65535 ||
+      req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_local_endpoint().");
+    return false;
+  }
+  if (req->has_client_start) {
+    req->client_start.has_local_endpoint = true;
+    set_endpoint(&req->client_start.local_endpoint, ip_address, port, protocol);
+  } else {
+    req->server_start.has_local_endpoint = true;
+    set_endpoint(&req->server_start.local_endpoint, ip_address, port, protocol);
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_remote_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol) {
+  if (req == nullptr || ip_address == nullptr || port > 65535 ||
+      req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_remote_endpoint().");
+    return false;
+  }
+  if (req->has_client_start) {
+    req->client_start.has_remote_endpoint = true;
+    set_endpoint(&req->client_start.remote_endpoint, ip_address, port,
+                 protocol);
+  } else {
+    req->server_start.has_remote_endpoint = true;
+    set_endpoint(&req->server_start.remote_endpoint, ip_address, port,
+                 protocol);
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req,
+                                          const char* in_bytes, size_t size) {
+  if (req == nullptr || in_bytes == nullptr || req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_in_bytes().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(in_bytes, size);
+  if (req->has_next) {
+    req->next.in_bytes.arg = slice;
+    req->next.in_bytes.funcs.encode = &encode_string_or_bytes_cb;
+  } else {
+    req->server_start.in_bytes.arg = slice;
+    req->server_start.in_bytes.funcs.encode = &encode_string_or_bytes_cb;
+  }
+  return true;
+}
+
+static grpc_gcp_server_handshake_parameters* server_start_find_param(
+    grpc_gcp_handshaker_req* req, int32_t key) {
+  size_t i = 0;
+  for (i = 0; i < req->server_start.handshake_parameters_count; i++) {
+    if (req->server_start.handshake_parameters[i].key == key) {
+      return &req->server_start.handshake_parameters[i].value;
+    }
+  }
+  req->server_start
+      .handshake_parameters[req->server_start.handshake_parameters_count]
+      .has_key = true;
+  req->server_start
+      .handshake_parameters[req->server_start.handshake_parameters_count]
+      .has_value = true;
+  req->server_start
+      .handshake_parameters[req->server_start.handshake_parameters_count++]
+      .key = key;
+  return &req->server_start
+              .handshake_parameters
+                  [req->server_start.handshake_parameters_count - 1]
+              .value;
+}
+
+bool grpc_gcp_handshaker_req_param_add_record_protocol(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* record_protocol) {
+  if (req == nullptr || record_protocol == nullptr || !req->has_server_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_param_add_record_protocol().");
+    return false;
+  }
+  grpc_gcp_server_handshake_parameters* param =
+      server_start_find_param(req, key);
+  grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol));
+  add_repeated_field(
+      reinterpret_cast<repeated_field**>(&param->record_protocols.arg), slice);
+  param->record_protocols.funcs.encode = &encode_repeated_string_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_param_add_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* hostname) {
+  if (req == nullptr || hostname == nullptr || !req->has_server_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_param_add_local_identity_hostname().");
+    return false;
+  }
+  grpc_gcp_server_handshake_parameters* param =
+      server_start_find_param(req, key);
+  grpc_gcp_identity* local_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*local_identity)));
+  set_identity_hostname(local_identity, hostname);
+  add_repeated_field(
+      reinterpret_cast<repeated_field**>(&param->local_identities.arg),
+      local_identity);
+  param->local_identities.funcs.encode = &encode_repeated_identity_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_param_add_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* service_account) {
+  if (req == nullptr || service_account == nullptr || !req->has_server_start) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to "
+        "grpc_gcp_handshaker_req_param_add_local_identity_service_account().");
+    return false;
+  }
+  grpc_gcp_server_handshake_parameters* param =
+      server_start_find_param(req, key);
+  grpc_gcp_identity* local_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*local_identity)));
+  set_identity_service_account(local_identity, service_account);
+  add_repeated_field(
+      reinterpret_cast<repeated_field**>(&param->local_identities.arg),
+      local_identity);
+  param->local_identities.funcs.encode = &encode_repeated_identity_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req,
+                                    grpc_slice* slice) {
+  if (req == nullptr || slice == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to grpc_gcp_handshaker_req_encode().");
+    return false;
+  }
+  pb_ostream_t size_stream;
+  memset(&size_stream, 0, sizeof(pb_ostream_t));
+  if (!pb_encode(&size_stream, grpc_gcp_HandshakerReq_fields, req)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream));
+    return false;
+  }
+  size_t encoded_length = size_stream.bytes_written;
+  *slice = grpc_slice_malloc(encoded_length);
+  pb_ostream_t output_stream =
+      pb_ostream_from_buffer(GRPC_SLICE_START_PTR(*slice), encoded_length);
+  if (!pb_encode(&output_stream, grpc_gcp_HandshakerReq_fields, req) != 0) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream));
+    return false;
+  }
+  return true;
+}
+
+/* HandshakerResp. */
+grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void) {
+  grpc_gcp_handshaker_resp* resp =
+      static_cast<grpc_gcp_handshaker_resp*>(gpr_zalloc(sizeof(*resp)));
+  return resp;
+}
+
+void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp) {
+  if (resp != nullptr) {
+    destroy_slice(static_cast<grpc_slice*>(resp->out_frames.arg));
+    if (resp->has_status) {
+      destroy_slice(static_cast<grpc_slice*>(resp->status.details.arg));
+    }
+    if (resp->has_result) {
+      destroy_slice(
+          static_cast<grpc_slice*>(resp->result.application_protocol.arg));
+      destroy_slice(static_cast<grpc_slice*>(resp->result.record_protocol.arg));
+      destroy_slice(static_cast<grpc_slice*>(resp->result.key_data.arg));
+      if (resp->result.has_local_identity) {
+        destroy_slice(
+            static_cast<grpc_slice*>(resp->result.local_identity.hostname.arg));
+        destroy_slice(static_cast<grpc_slice*>(
+            resp->result.local_identity.service_account.arg));
+      }
+      if (resp->result.has_peer_identity) {
+        destroy_slice(
+            static_cast<grpc_slice*>(resp->result.peer_identity.hostname.arg));
+        destroy_slice(static_cast<grpc_slice*>(
+            resp->result.peer_identity.service_account.arg));
+      }
+    }
+    gpr_free(resp);
+  }
+}
+
+bool grpc_gcp_handshaker_resp_decode(grpc_slice encoded_handshaker_resp,
+                                     grpc_gcp_handshaker_resp* resp) {
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to grpc_gcp_handshaker_resp_decode().");
+    return false;
+  }
+  pb_istream_t stream =
+      pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_handshaker_resp),
+                             GRPC_SLICE_LENGTH(encoded_handshaker_resp));
+  resp->out_frames.funcs.decode = decode_string_or_bytes_cb;
+  resp->status.details.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.application_protocol.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.record_protocol.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.key_data.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.peer_identity.hostname.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.peer_identity.service_account.funcs.decode =
+      decode_string_or_bytes_cb;
+  resp->result.local_identity.hostname.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.local_identity.service_account.funcs.decode =
+      decode_string_or_bytes_cb;
+  if (!pb_decode(&stream, grpc_gcp_HandshakerResp_fields, resp)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return false;
+  }
+  return true;
+}
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h
new file mode 100644
index 0000000..5df56a8
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h
@@ -0,0 +1,323 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
+
+/**
+ * An implementation of nanopb thin wrapper used to set/get and
+ * serialize/de-serialize of ALTS handshake requests and responses.
+ *
+ * All APIs in the header are thread-compatible. A typical usage of this API at
+ * the client side is as follows:
+ *
+ * -----------------------------------------------------------------------------
+ * // Create, populate, and serialize an ALTS client_start handshake request to
+ * // send to the server.
+ * grpc_gcp_handshaker_req* req =
+ *     grpc_gcp_handshaker_req_create(CLIENT_START_REQ);
+ * grpc_gcp_handshaker_req_set_handshake_protocol(
+       req, grpc_gcp_HandshakeProtocol_ALTS);
+ * grpc_gcp_handshaker_req_add_application_protocol(req, "grpc");
+ * grpc_gcp_handshaker_req_add_record_protocol(req, "ALTSRP_GCM_AES128");
+ * grpc_slice client_slice;
+ * if (!grpc_gcp_handshaker_req_encode(req, &client_slice)) {
+ *   fprintf(stderr, "ALTS handshake request encoding failed.";
+ * }
+ *
+ * // De-serialize a data stream received from the server, and store the result
+ * // at ALTS handshake response.
+ * grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+ * if (!grpc_gcp_handshaker_resp_decode(server_slice, resp)) {
+ *    fprintf(stderr, "ALTS handshake response decoding failed.");
+ * }
+ * // To access a variable-length datatype field (i.e., pb_callback_t),
+ * // access its "arg" subfield (if it has been set).
+ * if (resp->out_frames.arg != nullptr) {
+ *   grpc_slice* slice = resp->out_frames.arg;
+ * }
+ * // To access a fixed-length datatype field (i.e., not pb_calback_t),
+ * // access the field directly (if it has been set).
+ * if (resp->has_status && resp->status->has_code) {
+ *   uint32_t code = resp->status->code;
+ * }
+ *------------------------------------------------------------------------------
+ */
+
+/**
+ * This method creates an ALTS handshake request.
+ *
+ * - type: an enum type value that can be either CLIENT_START_REQ,
+ *   SERVER_START_REQ, or NEXT_REQ to indicate the created instance will be
+ *   client_start, server_start, and next handshake request message
+ * respectively.
+ *
+ * The method returns a pointer to the created instance.
+ */
+grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create(
+    grpc_gcp_handshaker_req_type type);
+
+/**
+ * This method sets the value for handshake_security_protocol field of ALTS
+ * client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - handshake_protocol: a enum type value representing the handshake security
+ *   protocol.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_handshake_protocol(
+    grpc_gcp_handshaker_req* req,
+    grpc_gcp_handshake_protocol handshake_protocol);
+
+/**
+ * This method sets the value for target_name field of ALTS client_start
+ * handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - target_name: a target name to be set.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req,
+                                             const char* target_name);
+
+/**
+ * This method adds an application protocol supported by the server (or
+ * client) to ALTS server_start (or client_start) handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - application_protocol: an application protocol (e.g., grpc) to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_application_protocol(
+    grpc_gcp_handshaker_req* req, const char* application_protocol);
+
+/**
+ * This method adds a record protocol supported by the client to ALTS
+ * client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - record_protocol: a record protocol (e.g., ALTSRP_GCM_AES128) to be
+ *   added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req,
+                                                 const char* record_protocol);
+
+/**
+ * This method adds a target server identity represented as hostname and
+ * acceptable by a client to ALTS client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - hostname: a string representation of hostname at the connection
+ *   endpoint to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_target_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname);
+
+/**
+ * This method adds a target server identity represented as service account and
+ * acceptable by a client to ALTS client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - service_account: a string representation of service account at the
+ *   connection endpoint to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_target_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account);
+
+/**
+ * This method sets the hostname for local_identity field of ALTS client_start
+ * handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - hostname: a string representation of hostname.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname);
+
+/**
+ * This method sets the service account for local_identity field of ALTS
+ * client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - service_account: a string representation of service account.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account);
+
+/**
+ * This method sets the value for local_endpoint field of either ALTS
+ * client_start or server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - ip_address: a string representation of ip address associated with the
+ *   local endpoint, that could be either IPv4 or IPv6.
+ * - port: a port number associated with the local endpoint.
+ * - protocol: a network protocol (e.g., TCP or UDP) associated with the
+ *   local endpoint.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_local_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol);
+
+/**
+ * This method sets the value for remote_endpoint field of either ALTS
+ * client_start or server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - ip_address: a string representation of ip address associated with the
+ *   remote endpoint, that could be either IPv4 or IPv6.
+ * - port: a port number associated with the remote endpoint.
+ * - protocol: a network protocol (e.g., TCP or UDP) associated with the
+ *   remote endpoint.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_remote_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol);
+
+/**
+ * This method sets the value for in_bytes field of either ALTS server_start or
+ * next handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - in_bytes: a buffer containing bytes taken from out_frames of the peer's
+ *   ALTS handshake response. It is possible that the peer's out_frames are
+ * split into multiple handshake request messages.
+ * - size: size of in_bytes buffer.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req,
+                                          const char* in_bytes, size_t size);
+
+/**
+ * This method adds a record protocol to handshake parameters mapped by the
+ * handshake protocol for ALTS server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - key: an enum type value representing a handshake security protocol.
+ * - record_protocol: a record protocol to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_param_add_record_protocol(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* record_protocol);
+
+/**
+ * This method adds a local identity represented as hostname to handshake
+ * parameters mapped by the handshake protocol for ALTS server_start handshake
+ * request.
+ *
+ * - req: an ALTS handshake request.
+ * - key: an enum type value representing a handshake security protocol.
+ * - hostname: a string representation of hostname to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_param_add_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* hostname);
+
+/**
+ * This method adds a local identity represented as service account to handshake
+ * parameters mapped by the handshake protocol for ALTS server_start handshake
+ * request.
+ *
+ * - req: an ALTS handshake request.
+ * - key: an enum type value representing a handshake security protocol.
+ * - service_account: a string representation of service account to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_param_add_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* service_account);
+
+/**
+ * This method sets the value for rpc_versions field of either ALTS
+ * client_start or server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - max_major: a major version of maximum supported RPC version.
+ * - max_minor: a minor version of maximum supported RPC version.
+ * - min_major: a major version of minimum supported RPC version.
+ * - min_minor: a minor version of minimum supported RPC version.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req,
+                                              uint32_t max_major,
+                                              uint32_t max_minor,
+                                              uint32_t min_major,
+                                              uint32_t min_minor);
+
+/**
+ * This method serializes an ALTS handshake request and returns a data stream.
+ *
+ * - req: an ALTS handshake request.
+ * - slice: a data stream where the serialized result will be written.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req,
+                                    grpc_slice* slice);
+
+/* This method destroys an ALTS handshake request. */
+void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req);
+
+/* This method creates an ALTS handshake response. */
+grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void);
+
+/**
+ * This method de-serializes a data stream and stores the result
+ * in an ALTS handshake response.
+ *
+ * - slice: a data stream containing a serialized ALTS handshake response.
+ * - resp: an ALTS handshake response used to hold de-serialized result.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_resp_decode(grpc_slice slice,
+                                     grpc_gcp_handshaker_resp* resp);
+
+/* This method destroys an ALTS handshake response. */
+void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H */
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
new file mode 100644
index 0000000..e0e4184
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
+
+void add_repeated_field(repeated_field** head, const void* data) {
+  repeated_field* field =
+      static_cast<repeated_field*>(gpr_zalloc(sizeof(*field)));
+  field->data = data;
+  if (*head == nullptr) {
+    *head = field;
+    (*head)->next = nullptr;
+  } else {
+    field->next = *head;
+    *head = field;
+  }
+}
+
+void destroy_repeated_field_list_identity(repeated_field* head) {
+  repeated_field* field = head;
+  while (field != nullptr) {
+    repeated_field* next_field = field->next;
+    const grpc_gcp_identity* identity =
+        static_cast<const grpc_gcp_identity*>(field->data);
+    destroy_slice(static_cast<grpc_slice*>(identity->hostname.arg));
+    destroy_slice(static_cast<grpc_slice*>(identity->service_account.arg));
+    gpr_free((void*)identity);
+    gpr_free(field);
+    field = next_field;
+  }
+}
+
+void destroy_repeated_field_list_string(repeated_field* head) {
+  repeated_field* field = head;
+  while (field != nullptr) {
+    repeated_field* next_field = field->next;
+    destroy_slice((grpc_slice*)field->data);
+    gpr_free(field);
+    field = next_field;
+  }
+}
+
+grpc_slice* create_slice(const char* data, size_t size) {
+  grpc_slice slice = grpc_slice_from_copied_buffer(data, size);
+  grpc_slice* cb_slice =
+      static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice)));
+  memcpy(cb_slice, &slice, sizeof(*cb_slice));
+  return cb_slice;
+}
+
+void destroy_slice(grpc_slice* slice) {
+  if (slice != nullptr) {
+    grpc_slice_unref(*slice);
+    gpr_free(slice);
+  }
+}
+
+bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg) {
+  grpc_slice* slice = static_cast<grpc_slice*>(*arg);
+  if (!pb_encode_tag_for_field(stream, field)) return false;
+  return pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice),
+                          GRPC_SLICE_LENGTH(*slice));
+}
+
+bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field,
+                                 void* const* arg) {
+  repeated_field* var = static_cast<repeated_field*>(*arg);
+  while (var != nullptr) {
+    if (!pb_encode_tag_for_field(stream, field)) return false;
+    if (!pb_encode_submessage(stream, grpc_gcp_Identity_fields,
+                              (grpc_gcp_identity*)var->data))
+      return false;
+    var = var->next;
+  }
+  return true;
+}
+
+bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg) {
+  repeated_field* var = static_cast<repeated_field*>(*arg);
+  while (var != nullptr) {
+    if (!pb_encode_tag_for_field(stream, field)) return false;
+    const grpc_slice* slice = static_cast<const grpc_slice*>(var->data);
+    if (!pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice),
+                          GRPC_SLICE_LENGTH(*slice)))
+      return false;
+    var = var->next;
+  }
+  return true;
+}
+
+bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg) {
+  grpc_slice slice = grpc_slice_malloc(stream->bytes_left);
+  grpc_slice* cb_slice =
+      static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice)));
+  memcpy(cb_slice, &slice, sizeof(*cb_slice));
+  if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left))
+    return false;
+  *arg = cb_slice;
+  return true;
+}
+
+bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field,
+                                 void** arg) {
+  grpc_gcp_identity* identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*identity)));
+  identity->hostname.funcs.decode = decode_string_or_bytes_cb;
+  identity->service_account.funcs.decode = decode_string_or_bytes_cb;
+  add_repeated_field(reinterpret_cast<repeated_field**>(arg), identity);
+  if (!pb_decode(stream, grpc_gcp_Identity_fields, identity)) return false;
+  return true;
+}
+
+bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg) {
+  grpc_slice slice = grpc_slice_malloc(stream->bytes_left);
+  grpc_slice* cb_slice =
+      static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice)));
+  memcpy(cb_slice, &slice, sizeof(grpc_slice));
+  if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left))
+    return false;
+  add_repeated_field(reinterpret_cast<repeated_field**>(arg), cb_slice);
+  return true;
+}
diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h
new file mode 100644
index 0000000..8fe8f73
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "third_party/nanopb/pb_decode.h"
+#include "third_party/nanopb/pb_encode.h"
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/handshaker.pb.h"
+
+/**
+ * An implementation of utility functions used to serialize/
+ * de-serialize ALTS handshake requests/responses. All APIs in the header
+ * are thread-compatible.
+ */
+
+/* Renaming of message/field structs generated by nanopb compiler. */
+typedef grpc_gcp_HandshakeProtocol grpc_gcp_handshake_protocol;
+typedef grpc_gcp_NetworkProtocol grpc_gcp_network_protocol;
+typedef grpc_gcp_Identity grpc_gcp_identity;
+typedef grpc_gcp_NextHandshakeMessageReq grpc_gcp_next_handshake_message_req;
+typedef grpc_gcp_ServerHandshakeParameters grpc_gcp_server_handshake_parameters;
+typedef grpc_gcp_Endpoint grpc_gcp_endpoint;
+typedef grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry
+    grpc_gcp_handshake_parameters_entry;
+typedef grpc_gcp_StartClientHandshakeReq grpc_gcp_start_client_handshake_req;
+typedef grpc_gcp_StartServerHandshakeReq grpc_gcp_start_server_handshake_req;
+typedef grpc_gcp_HandshakerReq grpc_gcp_handshaker_req;
+typedef grpc_gcp_HandshakerResult grpc_gcp_handshaker_result;
+typedef grpc_gcp_HandshakerStatus grpc_gcp_handshaker_status;
+typedef grpc_gcp_HandshakerResp grpc_gcp_handshaker_resp;
+
+typedef enum {
+  CLIENT_START_REQ = 0, /* StartClientHandshakeReq. */
+  SERVER_START_REQ = 1, /* StartServerHandshakeReq. */
+  NEXT_REQ = 2,         /* NextHandshakeMessageReq. */
+} grpc_gcp_handshaker_req_type;
+
+/**
+ *  A struct representing a repeated field. The struct is used to organize all
+ *  instances of a specific repeated field into a linked list, which then will
+ *  be used at encode/decode phase. For instance at the encode phase, the encode
+ *  function will iterate through the list, encode each field, and then output
+ *  the result to the stream.
+ */
+typedef struct repeated_field_ {
+  struct repeated_field_* next;
+  const void* data;
+} repeated_field;
+
+/**
+ * This method adds a repeated field to the head of repeated field list.
+ *
+ * - head: a head of repeated field list.
+ * - field: a repeated field to be added to the list.
+ */
+void add_repeated_field(repeated_field** head, const void* field);
+
+/**
+ * This method destroys a repeated field list that consists of string type
+ * fields.
+ *
+ * - head: a head of repeated field list.
+ */
+void destroy_repeated_field_list_string(repeated_field* head);
+
+/**
+ * This method destroys a repeated field list that consists of
+ * grpc_gcp_identity type fields.
+ *
+ * - head: a head of repeated field list.
+ */
+void destroy_repeated_field_list_identity(repeated_field* head);
+
+/**
+ * This method creates a grpc_slice instance by copying a data buffer. It is
+ * similar to grpc_slice_from_copied_buffer() except that it returns an instance
+ * allocated from the heap.
+ *
+ * - data: a data buffer to be copied to grpc_slice instance.
+ * - size: size of data buffer.
+ */
+grpc_slice* create_slice(const char* data, size_t size);
+
+/* This method destroys a grpc_slice instance. */
+void destroy_slice(grpc_slice* slice);
+
+/**
+ * The following encode/decode functions will be assigned to encode/decode
+ * function pointers of pb_callback_t struct (defined in
+ * //third_party/nanopb/pb.h), that represent a repeated field with a dynamic
+ * length (e.g., a string type or repeated field).
+ */
+
+/* This method is an encode callback function for a string or byte array. */
+bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg);
+
+/**
+ * This method is an encode callback function for a repeated grpc_gcp_identity
+ * field.
+ */
+bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field,
+                                 void* const* arg);
+
+/* This method is an encode callback function for a repeated string field. */
+bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg);
+
+/**
+ * This method is a decode callback function for a string or byte array field.
+ */
+bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg);
+/**
+ * This method is a decode callback function for a repeated grpc_gcp_identity
+ * field.
+ */
+bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field,
+                                 void** arg);
+
+/* This method is a decode callback function for a repeated string field. */
+bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_event.cc b/src/core/tsi/alts/handshaker/alts_tsi_event.cc
new file mode 100644
index 0000000..ec0bf12
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_event.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+tsi_result alts_tsi_event_create(alts_tsi_handshaker* handshaker,
+                                 tsi_handshaker_on_next_done_cb cb,
+                                 void* user_data,
+                                 grpc_alts_credentials_options* options,
+                                 grpc_slice target_name,
+                                 alts_tsi_event** event) {
+  if (event == nullptr || handshaker == nullptr || cb == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_event_create()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_event* e = static_cast<alts_tsi_event*>(gpr_zalloc(sizeof(*e)));
+  e->handshaker = handshaker;
+  e->cb = cb;
+  e->user_data = user_data;
+  e->options = grpc_alts_credentials_options_copy(options);
+  e->target_name = grpc_slice_copy(target_name);
+  grpc_metadata_array_init(&e->initial_metadata);
+  grpc_metadata_array_init(&e->trailing_metadata);
+  *event = e;
+  return TSI_OK;
+}
+
+void alts_tsi_event_dispatch_to_handshaker(alts_tsi_event* event, bool is_ok) {
+  if (event == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "ALTS TSI event is nullptr in alts_tsi_event_dispatch_to_handshaker()");
+    return;
+  }
+  alts_tsi_handshaker_handle_response(event->handshaker, event->recv_buffer,
+                                      event->status, &event->details, event->cb,
+                                      event->user_data, is_ok);
+}
+
+void alts_tsi_event_destroy(alts_tsi_event* event) {
+  if (event == nullptr) {
+    return;
+  }
+  grpc_byte_buffer_destroy(event->send_buffer);
+  grpc_byte_buffer_destroy(event->recv_buffer);
+  grpc_metadata_array_destroy(&event->initial_metadata);
+  grpc_metadata_array_destroy(&event->trailing_metadata);
+  grpc_slice_unref(event->details);
+  grpc_slice_unref(event->target_name);
+  grpc_alts_credentials_options_destroy(event->options);
+  gpr_free(event);
+}
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_event.h b/src/core/tsi/alts/handshaker/alts_tsi_event.h
new file mode 100644
index 0000000..043e75d
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_event.h
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+/**
+ * A ALTS TSI event interface. In asynchronous implementation of
+ * tsi_handshaker_next(), the function will exit after scheduling a handshaker
+ * request to ALTS handshaker service without waiting for response to return.
+ * The event is used to link the scheduled handshaker request with the
+ * corresponding response so that enough context information can be inferred
+ * from it to handle the response. All APIs in the header are thread-compatible.
+ */
+
+/**
+ * Main struct for ALTS TSI event. It retains ownership on send_buffer and
+ * recv_buffer, but not on handshaker.
+ */
+typedef struct alts_tsi_event {
+  alts_tsi_handshaker* handshaker;
+  grpc_byte_buffer* send_buffer;
+  grpc_byte_buffer* recv_buffer;
+  grpc_status_code status;
+  grpc_slice details;
+  grpc_metadata_array initial_metadata;
+  grpc_metadata_array trailing_metadata;
+  tsi_handshaker_on_next_done_cb cb;
+  void* user_data;
+  grpc_alts_credentials_options* options;
+  grpc_slice target_name;
+} alts_tsi_event;
+
+/**
+ * This method creates a ALTS TSI event.
+ *
+ * - handshaker: ALTS TSI handshaker instance associated with the event to be
+ *   created. The created event does not own the handshaker instance.
+ * - cb: callback function to be called when handling data received from ALTS
+ *   handshaker service.
+ * - user_data: argument to callback function.
+ * - options: ALTS credentials options.
+ * - target_name: name of endpoint used for secure naming check.
+ * - event: address of ALTS TSI event instance to be returned from the method.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_tsi_event_create(alts_tsi_handshaker* handshaker,
+                                 tsi_handshaker_on_next_done_cb cb,
+                                 void* user_data,
+                                 grpc_alts_credentials_options* options,
+                                 grpc_slice target_name,
+                                 alts_tsi_event** event);
+
+/**
+ * This method dispatches a ALTS TSI event received from the handshaker service,
+ * and a boolean flag indicating if the event is valid to read to ALTS TSI
+ * handshaker to process. It is called by TSI thread.
+ *
+ * - event: ALTS TSI event instance.
+ * - is_ok: a boolean value indicating if the event is valid to read.
+ */
+void alts_tsi_event_dispatch_to_handshaker(alts_tsi_event* event, bool is_ok);
+
+/**
+ * This method destroys the ALTS TSI event.
+ */
+void alts_tsi_event_destroy(alts_tsi_event* event);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
new file mode 100644
index 0000000..529f210
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
@@ -0,0 +1,483 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+#include "src/core/tsi/alts_transport_security.h"
+
+#define TSI_ALTS_INITIAL_BUFFER_SIZE 256
+
+static alts_shared_resource* kSharedResource = alts_get_shared_resource();
+
+/* Main struct for ALTS TSI handshaker. */
+typedef struct alts_tsi_handshaker {
+  tsi_handshaker base;
+  alts_handshaker_client* client;
+  grpc_slice recv_bytes;
+  grpc_slice target_name;
+  unsigned char* buffer;
+  size_t buffer_size;
+  bool is_client;
+  bool has_sent_start_message;
+  grpc_alts_credentials_options* options;
+} alts_tsi_handshaker;
+
+/* Main struct for ALTS TSI handshaker result. */
+typedef struct alts_tsi_handshaker_result {
+  tsi_handshaker_result base;
+  char* peer_identity;
+  char* key_data;
+  unsigned char* unused_bytes;
+  size_t unused_bytes_size;
+  grpc_slice rpc_versions;
+  bool is_client;
+} alts_tsi_handshaker_result;
+
+static tsi_result handshaker_result_extract_peer(
+    const tsi_handshaker_result* self, tsi_peer* peer) {
+  if (self == nullptr || peer == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid argument to handshaker_result_extract_peer()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  GPR_ASSERT(kTsiAltsNumOfPeerProperties == 3);
+  tsi_result ok = tsi_construct_peer(kTsiAltsNumOfPeerProperties, peer);
+  int index = 0;
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to construct tsi peer");
+    return ok;
+  }
+  GPR_ASSERT(&peer->properties[index] != nullptr);
+  ok = tsi_construct_string_peer_property_from_cstring(
+      TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
+      &peer->properties[index]);
+  if (ok != TSI_OK) {
+    tsi_peer_destruct(peer);
+    gpr_log(GPR_ERROR, "Failed to set tsi peer property");
+    return ok;
+  }
+  index++;
+  GPR_ASSERT(&peer->properties[index] != nullptr);
+  ok = tsi_construct_string_peer_property_from_cstring(
+      TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, result->peer_identity,
+      &peer->properties[index]);
+  if (ok != TSI_OK) {
+    tsi_peer_destruct(peer);
+    gpr_log(GPR_ERROR, "Failed to set tsi peer property");
+  }
+  index++;
+  GPR_ASSERT(&peer->properties[index] != nullptr);
+  ok = tsi_construct_string_peer_property(
+      TSI_ALTS_RPC_VERSIONS,
+      reinterpret_cast<char*>(GRPC_SLICE_START_PTR(result->rpc_versions)),
+      GRPC_SLICE_LENGTH(result->rpc_versions), &peer->properties[2]);
+  if (ok != TSI_OK) {
+    tsi_peer_destruct(peer);
+    gpr_log(GPR_ERROR, "Failed to set tsi peer property");
+  }
+  GPR_ASSERT(++index == kTsiAltsNumOfPeerProperties);
+  return ok;
+}
+
+static tsi_result handshaker_result_create_zero_copy_grpc_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  if (self == nullptr || protector == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to create_zero_copy_grpc_protector()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  tsi_result ok = alts_zero_copy_grpc_protector_create(
+      reinterpret_cast<const uint8_t*>(result->key_data),
+      kAltsAes128GcmRekeyKeyLength, /*is_rekey=*/true, result->is_client,
+      /*is_integrity_only=*/false, max_output_protected_frame_size, protector);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector");
+  }
+  return ok;
+}
+
+static tsi_result handshaker_result_create_frame_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_frame_protector** protector) {
+  if (self == nullptr || protector == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to handshaker_result_create_frame_protector()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  tsi_result ok = alts_create_frame_protector(
+      reinterpret_cast<const uint8_t*>(result->key_data),
+      kAltsAes128GcmRekeyKeyLength, result->is_client, /*is_rekey=*/true,
+      max_output_protected_frame_size, protector);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to create frame protector");
+  }
+  return ok;
+}
+
+static tsi_result handshaker_result_get_unused_bytes(
+    const tsi_handshaker_result* self, const unsigned char** bytes,
+    size_t* bytes_size) {
+  if (self == nullptr || bytes == nullptr || bytes_size == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to handshaker_result_get_unused_bytes()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  *bytes = result->unused_bytes;
+  *bytes_size = result->unused_bytes_size;
+  return TSI_OK;
+}
+
+static void handshaker_result_destroy(tsi_handshaker_result* self) {
+  if (self == nullptr) {
+    return;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  gpr_free(result->peer_identity);
+  gpr_free(result->key_data);
+  gpr_free(result->unused_bytes);
+  grpc_slice_unref(result->rpc_versions);
+  gpr_free(result);
+}
+
+static const tsi_handshaker_result_vtable result_vtable = {
+    handshaker_result_extract_peer,
+    handshaker_result_create_zero_copy_grpc_protector,
+    handshaker_result_create_frame_protector,
+    handshaker_result_get_unused_bytes, handshaker_result_destroy};
+
+static tsi_result create_handshaker_result(grpc_gcp_handshaker_resp* resp,
+                                           bool is_client,
+                                           tsi_handshaker_result** self) {
+  if (self == nullptr || resp == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_slice* key = static_cast<grpc_slice*>(resp->result.key_data.arg);
+  GPR_ASSERT(key != nullptr);
+  grpc_slice* identity =
+      static_cast<grpc_slice*>(resp->result.peer_identity.service_account.arg);
+  if (identity == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid service account");
+    return TSI_FAILED_PRECONDITION;
+  }
+  if (GRPC_SLICE_LENGTH(*key) < kAltsAes128GcmRekeyKeyLength) {
+    gpr_log(GPR_ERROR, "Bad key length");
+    return TSI_FAILED_PRECONDITION;
+  }
+  alts_tsi_handshaker_result* result =
+      static_cast<alts_tsi_handshaker_result*>(gpr_zalloc(sizeof(*result)));
+  result->key_data =
+      static_cast<char*>(gpr_zalloc(kAltsAes128GcmRekeyKeyLength));
+  memcpy(result->key_data, GRPC_SLICE_START_PTR(*key),
+         kAltsAes128GcmRekeyKeyLength);
+  result->peer_identity = grpc_slice_to_c_string(*identity);
+  if (!resp->result.has_peer_rpc_versions) {
+    gpr_log(GPR_ERROR, "Peer does not set RPC protocol versions.");
+    return TSI_FAILED_PRECONDITION;
+  }
+  if (!grpc_gcp_rpc_protocol_versions_encode(&resp->result.peer_rpc_versions,
+                                             &result->rpc_versions)) {
+    gpr_log(GPR_ERROR, "Failed to serialize peer's RPC protocol versions.");
+    return TSI_FAILED_PRECONDITION;
+  }
+  result->is_client = is_client;
+  result->base.vtable = &result_vtable;
+  *self = &result->base;
+  return TSI_OK;
+}
+
+static tsi_result handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  if (self == nullptr || cb == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker* handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(self);
+  tsi_result ok = TSI_OK;
+  alts_tsi_event* event = nullptr;
+  ok = alts_tsi_event_create(handshaker, cb, user_data, handshaker->options,
+                             handshaker->target_name, &event);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to create ALTS TSI event");
+    return ok;
+  }
+  grpc_slice slice = (received_bytes == nullptr || received_bytes_size == 0)
+                         ? grpc_empty_slice()
+                         : grpc_slice_from_copied_buffer(
+                               reinterpret_cast<const char*>(received_bytes),
+                               received_bytes_size);
+  if (!handshaker->has_sent_start_message) {
+    ok = handshaker->is_client
+             ? alts_handshaker_client_start_client(handshaker->client, event)
+             : alts_handshaker_client_start_server(handshaker->client, event,
+                                                   &slice);
+    handshaker->has_sent_start_message = true;
+  } else {
+    if (!GRPC_SLICE_IS_EMPTY(handshaker->recv_bytes)) {
+      grpc_slice_unref(handshaker->recv_bytes);
+    }
+    handshaker->recv_bytes = grpc_slice_ref(slice);
+    ok = alts_handshaker_client_next(handshaker->client, event, &slice);
+  }
+  grpc_slice_unref(slice);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to schedule ALTS handshaker requests");
+    return ok;
+  }
+  return TSI_ASYNC;
+}
+
+static void handshaker_destroy(tsi_handshaker* self) {
+  if (self == nullptr) {
+    return;
+  }
+  alts_tsi_handshaker* handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(self);
+  alts_handshaker_client_destroy(handshaker->client);
+  grpc_slice_unref(handshaker->recv_bytes);
+  grpc_slice_unref(handshaker->target_name);
+  grpc_alts_credentials_options_destroy(handshaker->options);
+  gpr_free(handshaker->buffer);
+  gpr_free(handshaker);
+}
+
+static const tsi_handshaker_vtable handshaker_vtable = {
+    nullptr,        nullptr, nullptr, nullptr, nullptr, handshaker_destroy,
+    handshaker_next};
+
+static void thread_worker(void* arg) {
+  while (true) {
+    grpc_event event = grpc_completion_queue_next(
+        kSharedResource->cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
+    GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT);
+    if (event.type == GRPC_QUEUE_SHUTDOWN) {
+      /* signal alts_tsi_shutdown() to destroy completion queue. */
+      grpc_tsi_alts_signal_for_cq_destroy();
+      break;
+    }
+    /* event.type == GRPC_OP_COMPLETE. */
+    alts_tsi_event* alts_event = static_cast<alts_tsi_event*>(event.tag);
+    alts_tsi_event_dispatch_to_handshaker(alts_event, event.success);
+    alts_tsi_event_destroy(alts_event);
+  }
+}
+
+static void init_shared_resources(const char* handshaker_service_url) {
+  GPR_ASSERT(handshaker_service_url != nullptr);
+  gpr_mu_lock(&kSharedResource->mu);
+  if (kSharedResource->channel == nullptr) {
+    gpr_cv_init(&kSharedResource->cv);
+    kSharedResource->channel =
+        grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr);
+    kSharedResource->cq = grpc_completion_queue_create_for_next(nullptr);
+    kSharedResource->thread =
+        grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr);
+    kSharedResource->thread.Start();
+  }
+  gpr_mu_unlock(&kSharedResource->mu);
+}
+
+tsi_result alts_tsi_handshaker_create(
+    const grpc_alts_credentials_options* options, const char* target_name,
+    const char* handshaker_service_url, bool is_client, tsi_handshaker** self) {
+  if (handshaker_service_url == nullptr || self == nullptr ||
+      options == nullptr || (is_client && target_name == nullptr)) {
+    gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_handshaker_create()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  init_shared_resources(handshaker_service_url);
+  alts_handshaker_client* client = alts_grpc_handshaker_client_create(
+      kSharedResource->channel, kSharedResource->cq, handshaker_service_url);
+  if (client == nullptr) {
+    gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client");
+    return TSI_FAILED_PRECONDITION;
+  }
+  alts_tsi_handshaker* handshaker =
+      static_cast<alts_tsi_handshaker*>(gpr_zalloc(sizeof(*handshaker)));
+  handshaker->client = client;
+  handshaker->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE;
+  handshaker->buffer =
+      static_cast<unsigned char*>(gpr_zalloc(handshaker->buffer_size));
+  handshaker->is_client = is_client;
+  handshaker->has_sent_start_message = false;
+  handshaker->target_name = target_name == nullptr
+                                ? grpc_empty_slice()
+                                : grpc_slice_from_static_string(target_name);
+  handshaker->options = grpc_alts_credentials_options_copy(options);
+  handshaker->base.vtable = &handshaker_vtable;
+  *self = &handshaker->base;
+  return TSI_OK;
+}
+
+static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) {
+  GPR_ASSERT(resp != nullptr);
+  if (resp->has_result) {
+    return true;
+  }
+  return false;
+}
+
+static void set_unused_bytes(tsi_handshaker_result* self,
+                             grpc_slice* recv_bytes, size_t bytes_consumed) {
+  GPR_ASSERT(recv_bytes != nullptr && self != nullptr);
+  if (GRPC_SLICE_LENGTH(*recv_bytes) == bytes_consumed) {
+    return;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(self);
+  result->unused_bytes_size = GRPC_SLICE_LENGTH(*recv_bytes) - bytes_consumed;
+  result->unused_bytes =
+      static_cast<unsigned char*>(gpr_zalloc(result->unused_bytes_size));
+  memcpy(result->unused_bytes,
+         GRPC_SLICE_START_PTR(*recv_bytes) + bytes_consumed,
+         result->unused_bytes_size);
+}
+
+void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker,
+                                         grpc_byte_buffer* recv_buffer,
+                                         grpc_status_code status,
+                                         grpc_slice* details,
+                                         tsi_handshaker_on_next_done_cb cb,
+                                         void* user_data, bool is_ok) {
+  /* Invalid input check. */
+  if (cb == nullptr) {
+    gpr_log(GPR_ERROR,
+            "cb is nullptr in alts_tsi_handshaker_handle_response()");
+    return;
+  }
+  if (handshaker == nullptr || recv_buffer == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to alts_tsi_handshaker_handle_response()");
+    cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  /* Failed grpc call check. */
+  if (!is_ok || status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "grpc call made to handshaker service failed");
+    if (details != nullptr) {
+      char* error_details = grpc_slice_to_c_string(*details);
+      gpr_log(GPR_ERROR, "error details:%s", error_details);
+      gpr_free(error_details);
+    }
+    cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  grpc_gcp_handshaker_resp* resp =
+      alts_tsi_utils_deserialize_response(recv_buffer);
+  /* Invalid handshaker response check. */
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed");
+    cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  grpc_slice* slice = static_cast<grpc_slice*>(resp->out_frames.arg);
+  unsigned char* bytes_to_send = nullptr;
+  size_t bytes_to_send_size = 0;
+  if (slice != nullptr) {
+    bytes_to_send_size = GRPC_SLICE_LENGTH(*slice);
+    while (bytes_to_send_size > handshaker->buffer_size) {
+      handshaker->buffer_size *= 2;
+      handshaker->buffer = static_cast<unsigned char*>(
+          gpr_realloc(handshaker->buffer, handshaker->buffer_size));
+    }
+    memcpy(handshaker->buffer, GRPC_SLICE_START_PTR(*slice),
+           bytes_to_send_size);
+    bytes_to_send = handshaker->buffer;
+  }
+  tsi_handshaker_result* result = nullptr;
+  if (is_handshake_finished_properly(resp)) {
+    create_handshaker_result(resp, handshaker->is_client, &result);
+    set_unused_bytes(result, &handshaker->recv_bytes, resp->bytes_consumed);
+  }
+  grpc_status_code code = static_cast<grpc_status_code>(resp->status.code);
+  grpc_gcp_handshaker_resp_destroy(resp);
+  cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send,
+     bytes_to_send_size, result);
+}
+
+namespace grpc_core {
+namespace internal {
+
+bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+    alts_tsi_handshaker* handshaker) {
+  GPR_ASSERT(handshaker != nullptr);
+  return handshaker->has_sent_start_message;
+}
+
+bool alts_tsi_handshaker_get_is_client_for_testing(
+    alts_tsi_handshaker* handshaker) {
+  GPR_ASSERT(handshaker != nullptr);
+  return handshaker->is_client;
+}
+
+void alts_tsi_handshaker_set_recv_bytes_for_testing(
+    alts_tsi_handshaker* handshaker, grpc_slice* slice) {
+  GPR_ASSERT(handshaker != nullptr && slice != nullptr);
+  handshaker->recv_bytes = grpc_slice_ref(*slice);
+}
+
+grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing(
+    alts_tsi_handshaker* handshaker) {
+  GPR_ASSERT(handshaker != nullptr);
+  return handshaker->recv_bytes;
+}
+
+void alts_tsi_handshaker_set_client_for_testing(
+    alts_tsi_handshaker* handshaker, alts_handshaker_client* client) {
+  GPR_ASSERT(handshaker != nullptr && client != nullptr);
+  alts_handshaker_client_destroy(handshaker->client);
+  handshaker->client = client;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
new file mode 100644
index 0000000..227b30c
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/tsi/alts_transport_security.h"
+#include "src/core/tsi/transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+#define TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY "service_accont"
+#define TSI_ALTS_CERTIFICATE_TYPE "ALTS"
+#define TSI_ALTS_RPC_VERSIONS "rpc_versions"
+
+const size_t kTsiAltsNumOfPeerProperties = 3;
+
+/**
+ * Main struct for ALTS TSI handshaker. All APIs in the header are
+ * thread-comptabile.
+ */
+typedef struct alts_tsi_handshaker alts_tsi_handshaker;
+
+/**
+ * This method creates a ALTS TSI handshaker instance.
+ *
+ * - options: ALTS credentials options containing information passed from TSI
+ *   caller (e.g., rpc protocol versions).
+ * - target_name: the name of the endpoint that the channel is connecting to,
+ *   and will be used for secure naming check.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port".
+ * - is_client: boolean value indicating if the handshaker is used at the client
+ *   (is_client = true) or server (is_client = false) side.
+ * - self: address of ALTS TSI handshaker instance to be returned from the
+ *   method.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_tsi_handshaker_create(
+    const grpc_alts_credentials_options* options, const char* target_name,
+    const char* handshaker_service_url, bool is_client, tsi_handshaker** self);
+
+/**
+ * This method handles handshaker response returned from ALTS handshaker
+ * service.
+ *
+ * - handshaker: ALTS TSI handshaker instance.
+ * - recv_buffer: buffer holding data received from the handshaker service.
+ * - status: status of the grpc call made to the handshaker service.
+ * - details: error details of the grpc call made to the handshaker service.
+ * - cb: callback function of ALTS TSI event.
+ * - user_data: argument of callback function.
+ * - is_ok: a boolean value indicating if the handshaker response is ok to read.
+ *
+ */
+void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker,
+                                         grpc_byte_buffer* recv_buffer,
+                                         grpc_status_code status,
+                                         grpc_slice* details,
+                                         tsi_handshaker_on_next_done_cb cb,
+                                         void* user_data, bool is_ok);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
new file mode 100644
index 0000000..9b7b9bb
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * Unsafe, use for testing only. It allows the caller to change the way the
+ * ALTS TSI handshaker schedules handshaker requests.
+ */
+void alts_tsi_handshaker_set_client_for_testing(alts_tsi_handshaker* handshaker,
+                                                alts_handshaker_client* client);
+
+/* For testing only. */
+bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+    alts_tsi_handshaker* handshaker);
+
+bool alts_tsi_handshaker_get_is_client_for_testing(
+    alts_tsi_handshaker* handshaker);
+
+void alts_tsi_handshaker_set_recv_bytes_for_testing(
+    alts_tsi_handshaker* handshaker, grpc_slice* slice);
+
+grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing(
+    alts_tsi_handshaker* handshaker);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H */
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_utils.cc b/src/core/tsi/alts/handshaker/alts_tsi_utils.cc
new file mode 100644
index 0000000..d9b5e6c
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_utils.cc
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+
+#include <grpc/byte_buffer_reader.h>
+
+tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code) {
+  switch (code) {
+    case GRPC_STATUS_OK:
+      return TSI_OK;
+    case GRPC_STATUS_UNKNOWN:
+      return TSI_UNKNOWN_ERROR;
+    case GRPC_STATUS_INVALID_ARGUMENT:
+      return TSI_INVALID_ARGUMENT;
+    case GRPC_STATUS_NOT_FOUND:
+      return TSI_NOT_FOUND;
+    case GRPC_STATUS_INTERNAL:
+      return TSI_INTERNAL_ERROR;
+    default:
+      return TSI_UNKNOWN_ERROR;
+  }
+}
+
+grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response(
+    grpc_byte_buffer* resp_buffer) {
+  GPR_ASSERT(resp_buffer != nullptr);
+  grpc_byte_buffer_reader bbr;
+  grpc_byte_buffer_reader_init(&bbr, resp_buffer);
+  grpc_slice slice = grpc_byte_buffer_reader_readall(&bbr);
+  grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+  bool ok = grpc_gcp_handshaker_resp_decode(slice, resp);
+  grpc_slice_unref(slice);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  if (!ok) {
+    grpc_gcp_handshaker_resp_destroy(resp);
+    gpr_log(GPR_ERROR, "grpc_gcp_handshaker_resp_decode() failed");
+    return nullptr;
+  }
+  return resp;
+}
diff --git a/src/core/tsi/alts/handshaker/alts_tsi_utils.h b/src/core/tsi/alts/handshaker/alts_tsi_utils.h
new file mode 100644
index 0000000..9ef649d
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/alts_tsi_utils.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+/**
+ * This method converts grpc_status_code code to the corresponding tsi_result
+ * code.
+ *
+ * - code: grpc_status_code code.
+ *
+ * It returns the converted tsi_result code.
+ */
+tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code);
+
+/**
+ * This method deserializes a handshaker response returned from ALTS handshaker
+ * service.
+ *
+ * - bytes_received: data returned from ALTS handshaker service.
+ *
+ * It returns a deserialized handshaker response on success and nullptr on
+ * failure.
+ */
+grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response(
+    grpc_byte_buffer* resp_buffer);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H */
diff --git a/src/core/tsi/alts/handshaker/altscontext.pb.c b/src/core/tsi/alts/handshaker/altscontext.pb.c
new file mode 100644
index 0000000..81a82f5
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/altscontext.pb.c
@@ -0,0 +1,48 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/tsi/alts/handshaker/altscontext.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_gcp_AltsContext_fields[7] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_AltsContext, application_protocol, application_protocol, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, record_protocol, application_protocol, 0),
+    PB_FIELD(  3, UENUM   , OPTIONAL, STATIC  , OTHER, grpc_gcp_AltsContext, security_level, record_protocol, 0),
+    PB_FIELD(  4, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, peer_service_account, security_level, 0),
+    PB_FIELD(  5, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, local_service_account, peer_service_account, 0),
+    PB_FIELD(  6, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_AltsContext, peer_rpc_versions, local_service_account, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+
+/* Check that field information fits in pb_field_t */
+#if !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_32BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in 8 or 16 bit
+ * field descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(grpc_gcp_AltsContext, peer_rpc_versions) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_AltsContext)
+#endif
+
+#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_16BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in the default
+ * 8 bit descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(grpc_gcp_AltsContext, peer_rpc_versions) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_AltsContext)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/src/core/tsi/alts/handshaker/altscontext.pb.h b/src/core/tsi/alts/handshaker/altscontext.pb.h
new file mode 100644
index 0000000..3e72d7f
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/altscontext.pb.h
@@ -0,0 +1,64 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED
+#define PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED
+#include "third_party/nanopb/pb.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Struct definitions */
+typedef struct _grpc_gcp_AltsContext {
+    pb_callback_t application_protocol;
+    pb_callback_t record_protocol;
+    bool has_security_level;
+    grpc_gcp_SecurityLevel security_level;
+    pb_callback_t peer_service_account;
+    pb_callback_t local_service_account;
+    bool has_peer_rpc_versions;
+    grpc_gcp_RpcProtocolVersions peer_rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_AltsContext) */
+} grpc_gcp_AltsContext;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_gcp_AltsContext_init_default        {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_AltsContext_init_zero           {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_gcp_AltsContext_application_protocol_tag 1
+#define grpc_gcp_AltsContext_record_protocol_tag 2
+#define grpc_gcp_AltsContext_security_level_tag  3
+#define grpc_gcp_AltsContext_peer_service_account_tag 4
+#define grpc_gcp_AltsContext_local_service_account_tag 5
+#define grpc_gcp_AltsContext_peer_rpc_versions_tag 6
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_gcp_AltsContext_fields[7];
+
+/* Maximum encoded size of messages (where known) */
+/* grpc_gcp_AltsContext_size depends on runtime parameters */
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define ALTSCONTEXT_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/src/core/tsi/alts/handshaker/handshaker.pb.c b/src/core/tsi/alts/handshaker/handshaker.pb.c
new file mode 100644
index 0000000..bd992df
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/handshaker.pb.c
@@ -0,0 +1,123 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/tsi/alts/handshaker/handshaker.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_gcp_Endpoint_fields[4] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Endpoint, ip_address, ip_address, 0),
+    PB_FIELD(  2, INT32   , OPTIONAL, STATIC  , OTHER, grpc_gcp_Endpoint, port, ip_address, 0),
+    PB_FIELD(  3, UENUM   , OPTIONAL, STATIC  , OTHER, grpc_gcp_Endpoint, protocol, port, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_Identity_fields[3] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Identity, service_account, service_account, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_Identity, hostname, service_account, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10] = {
+    PB_FIELD(  1, UENUM   , OPTIONAL, STATIC  , FIRST, grpc_gcp_StartClientHandshakeReq, handshake_security_protocol, handshake_security_protocol, 0),
+    PB_FIELD(  2, STRING  , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, application_protocols, handshake_security_protocol, 0),
+    PB_FIELD(  3, STRING  , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, record_protocols, application_protocols, 0),
+    PB_FIELD(  4, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_identities, record_protocols, &grpc_gcp_Identity_fields),
+    PB_FIELD(  5, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, local_identity, target_identities, &grpc_gcp_Identity_fields),
+    PB_FIELD(  6, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, local_endpoint, local_identity, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  7, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  8, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_name, remote_endpoint, 0),
+    PB_FIELD(  9, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, rpc_versions, target_name, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3] = {
+    PB_FIELD(  1, STRING  , REPEATED, CALLBACK, FIRST, grpc_gcp_ServerHandshakeParameters, record_protocols, record_protocols, 0),
+    PB_FIELD(  2, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_ServerHandshakeParameters, local_identities, record_protocols, &grpc_gcp_Identity_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7] = {
+    PB_FIELD(  1, STRING  , REPEATED, CALLBACK, FIRST, grpc_gcp_StartServerHandshakeReq, application_protocols, application_protocols, 0),
+    PB_FIELD(  2, MESSAGE , REPEATED, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, handshake_parameters, application_protocols, &grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields),
+    PB_FIELD(  3, BYTES   , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartServerHandshakeReq, in_bytes, handshake_parameters, 0),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, local_endpoint, in_bytes, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  5, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  6, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, rpc_versions, remote_endpoint, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3] = {
+    PB_FIELD(  1, INT32   , OPTIONAL, STATIC  , FIRST, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, key, key, 0),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value, key, &grpc_gcp_ServerHandshakeParameters_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2] = {
+    PB_FIELD(  1, BYTES   , OPTIONAL, CALLBACK, FIRST, grpc_gcp_NextHandshakeMessageReq, in_bytes, in_bytes, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerReq_fields[4] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_gcp_HandshakerReq, client_start, client_start, &grpc_gcp_StartClientHandshakeReq_fields),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerReq, server_start, client_start, &grpc_gcp_StartServerHandshakeReq_fields),
+    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerReq, next, server_start, &grpc_gcp_NextHandshakeMessageReq_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerResult_fields[8] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResult, application_protocol, application_protocol, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, record_protocol, application_protocol, 0),
+    PB_FIELD(  3, BYTES   , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, key_data, record_protocol, 0),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, peer_identity, key_data, &grpc_gcp_Identity_fields),
+    PB_FIELD(  5, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, local_identity, peer_identity, &grpc_gcp_Identity_fields),
+    PB_FIELD(  6, BOOL    , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, keep_channel_open, local_identity, 0),
+    PB_FIELD(  7, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, peer_rpc_versions, keep_channel_open, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerStatus_fields[3] = {
+    PB_FIELD(  1, UINT32  , OPTIONAL, STATIC  , FIRST, grpc_gcp_HandshakerStatus, code, code, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerStatus, details, code, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerResp_fields[5] = {
+    PB_FIELD(  1, BYTES   , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResp, out_frames, out_frames, 0),
+    PB_FIELD(  2, UINT32  , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResp, bytes_consumed, out_frames, 0),
+    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResp, result, bytes_consumed, &grpc_gcp_HandshakerResult_fields),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResp, status, result, &grpc_gcp_HandshakerStatus_fields),
+    PB_LAST_FIELD
+};
+
+
+/* Check that field information fits in pb_field_t */
+#if !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_32BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in 8 or 16 bit
+ * field descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(grpc_gcp_StartClientHandshakeReq, target_identities) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, next) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, result) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, status) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp)
+#endif
+
+#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_16BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in the default
+ * 8 bit descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(grpc_gcp_StartClientHandshakeReq, target_identities) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 256 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, next) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 256 && pb_membersize(grpc_gcp_HandshakerResp, result) < 256 && pb_membersize(grpc_gcp_HandshakerResp, status) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/src/core/tsi/alts/handshaker/handshaker.pb.h b/src/core/tsi/alts/handshaker/handshaker.pb.h
new file mode 100644
index 0000000..0805a14
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/handshaker.pb.h
@@ -0,0 +1,255 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED
+#define PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED
+#include "third_party/nanopb/pb.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Enum definitions */
+typedef enum _grpc_gcp_HandshakeProtocol {
+    grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED = 0,
+    grpc_gcp_HandshakeProtocol_TLS = 1,
+    grpc_gcp_HandshakeProtocol_ALTS = 2
+} grpc_gcp_HandshakeProtocol;
+#define _grpc_gcp_HandshakeProtocol_MIN grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED
+#define _grpc_gcp_HandshakeProtocol_MAX grpc_gcp_HandshakeProtocol_ALTS
+#define _grpc_gcp_HandshakeProtocol_ARRAYSIZE ((grpc_gcp_HandshakeProtocol)(grpc_gcp_HandshakeProtocol_ALTS+1))
+
+typedef enum _grpc_gcp_NetworkProtocol {
+    grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED = 0,
+    grpc_gcp_NetworkProtocol_TCP = 1,
+    grpc_gcp_NetworkProtocol_UDP = 2
+} grpc_gcp_NetworkProtocol;
+#define _grpc_gcp_NetworkProtocol_MIN grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED
+#define _grpc_gcp_NetworkProtocol_MAX grpc_gcp_NetworkProtocol_UDP
+#define _grpc_gcp_NetworkProtocol_ARRAYSIZE ((grpc_gcp_NetworkProtocol)(grpc_gcp_NetworkProtocol_UDP+1))
+
+/* Struct definitions */
+typedef struct _grpc_gcp_Identity {
+    pb_callback_t service_account;
+    pb_callback_t hostname;
+/* @@protoc_insertion_point(struct:grpc_gcp_Identity) */
+} grpc_gcp_Identity;
+
+typedef struct _grpc_gcp_NextHandshakeMessageReq {
+    pb_callback_t in_bytes;
+/* @@protoc_insertion_point(struct:grpc_gcp_NextHandshakeMessageReq) */
+} grpc_gcp_NextHandshakeMessageReq;
+
+typedef struct _grpc_gcp_ServerHandshakeParameters {
+    pb_callback_t record_protocols;
+    pb_callback_t local_identities;
+/* @@protoc_insertion_point(struct:grpc_gcp_ServerHandshakeParameters) */
+} grpc_gcp_ServerHandshakeParameters;
+
+typedef struct _grpc_gcp_Endpoint {
+    pb_callback_t ip_address;
+    bool has_port;
+    int32_t port;
+    bool has_protocol;
+    grpc_gcp_NetworkProtocol protocol;
+/* @@protoc_insertion_point(struct:grpc_gcp_Endpoint) */
+} grpc_gcp_Endpoint;
+
+typedef struct _grpc_gcp_HandshakerResult {
+    pb_callback_t application_protocol;
+    pb_callback_t record_protocol;
+    pb_callback_t key_data;
+    bool has_peer_identity;
+    grpc_gcp_Identity peer_identity;
+    bool has_local_identity;
+    grpc_gcp_Identity local_identity;
+    bool has_keep_channel_open;
+    bool keep_channel_open;
+    bool has_peer_rpc_versions;
+    grpc_gcp_RpcProtocolVersions peer_rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResult) */
+} grpc_gcp_HandshakerResult;
+
+typedef struct _grpc_gcp_HandshakerStatus {
+    bool has_code;
+    uint32_t code;
+    pb_callback_t details;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerStatus) */
+} grpc_gcp_HandshakerStatus;
+
+typedef struct _grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry {
+    bool has_key;
+    int32_t key;
+    bool has_value;
+    grpc_gcp_ServerHandshakeParameters value;
+/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry) */
+} grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry;
+
+typedef struct _grpc_gcp_HandshakerResp {
+    pb_callback_t out_frames;
+    bool has_bytes_consumed;
+    uint32_t bytes_consumed;
+    bool has_result;
+    grpc_gcp_HandshakerResult result;
+    bool has_status;
+    grpc_gcp_HandshakerStatus status;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResp) */
+} grpc_gcp_HandshakerResp;
+
+typedef struct _grpc_gcp_StartClientHandshakeReq {
+    bool has_handshake_security_protocol;
+    grpc_gcp_HandshakeProtocol handshake_security_protocol;
+    pb_callback_t application_protocols;
+    pb_callback_t record_protocols;
+    pb_callback_t target_identities;
+    bool has_local_identity;
+    grpc_gcp_Identity local_identity;
+    bool has_local_endpoint;
+    grpc_gcp_Endpoint local_endpoint;
+    bool has_remote_endpoint;
+    grpc_gcp_Endpoint remote_endpoint;
+    pb_callback_t target_name;
+    bool has_rpc_versions;
+    grpc_gcp_RpcProtocolVersions rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_StartClientHandshakeReq) */
+} grpc_gcp_StartClientHandshakeReq;
+
+typedef struct _grpc_gcp_StartServerHandshakeReq {
+    pb_callback_t application_protocols;
+    pb_size_t handshake_parameters_count;
+    grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry handshake_parameters[3];
+    pb_callback_t in_bytes;
+    bool has_local_endpoint;
+    grpc_gcp_Endpoint local_endpoint;
+    bool has_remote_endpoint;
+    grpc_gcp_Endpoint remote_endpoint;
+    bool has_rpc_versions;
+    grpc_gcp_RpcProtocolVersions rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq) */
+} grpc_gcp_StartServerHandshakeReq;
+
+typedef struct _grpc_gcp_HandshakerReq {
+    bool has_client_start;
+    grpc_gcp_StartClientHandshakeReq client_start;
+    bool has_server_start;
+    grpc_gcp_StartServerHandshakeReq server_start;
+    bool has_next;
+    grpc_gcp_NextHandshakeMessageReq next;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerReq) */
+} grpc_gcp_HandshakerReq;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_gcp_Endpoint_init_default           {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0}
+#define grpc_gcp_Identity_init_default           {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartClientHandshakeReq_init_default {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_ServerHandshakeParameters_init_default {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartServerHandshakeReq_init_default {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_default}
+#define grpc_gcp_NextHandshakeMessageReq_init_default {{{NULL}, NULL}}
+#define grpc_gcp_HandshakerReq_init_default      {false, grpc_gcp_StartClientHandshakeReq_init_default, false, grpc_gcp_StartServerHandshakeReq_init_default, false, grpc_gcp_NextHandshakeMessageReq_init_default}
+#define grpc_gcp_HandshakerResult_init_default   {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Identity_init_default, false, 0, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_HandshakerStatus_init_default   {false, 0, {{NULL}, NULL}}
+#define grpc_gcp_HandshakerResp_init_default     {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_default, false, grpc_gcp_HandshakerStatus_init_default}
+#define grpc_gcp_Endpoint_init_zero              {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0}
+#define grpc_gcp_Identity_init_zero              {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartClientHandshakeReq_init_zero {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero}
+#define grpc_gcp_ServerHandshakeParameters_init_zero {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartServerHandshakeReq_init_zero {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_RpcProtocolVersions_init_zero}
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_zero}
+#define grpc_gcp_NextHandshakeMessageReq_init_zero {{{NULL}, NULL}}
+#define grpc_gcp_HandshakerReq_init_zero         {false, grpc_gcp_StartClientHandshakeReq_init_zero, false, grpc_gcp_StartServerHandshakeReq_init_zero, false, grpc_gcp_NextHandshakeMessageReq_init_zero}
+#define grpc_gcp_HandshakerResult_init_zero      {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Identity_init_zero, false, 0, false, grpc_gcp_RpcProtocolVersions_init_zero}
+#define grpc_gcp_HandshakerStatus_init_zero      {false, 0, {{NULL}, NULL}}
+#define grpc_gcp_HandshakerResp_init_zero        {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_zero, false, grpc_gcp_HandshakerStatus_init_zero}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_gcp_Identity_service_account_tag    1
+#define grpc_gcp_Identity_hostname_tag           2
+#define grpc_gcp_NextHandshakeMessageReq_in_bytes_tag 1
+#define grpc_gcp_ServerHandshakeParameters_record_protocols_tag 1
+#define grpc_gcp_ServerHandshakeParameters_local_identities_tag 2
+#define grpc_gcp_Endpoint_ip_address_tag         1
+#define grpc_gcp_Endpoint_port_tag               2
+#define grpc_gcp_Endpoint_protocol_tag           3
+#define grpc_gcp_HandshakerResult_application_protocol_tag 1
+#define grpc_gcp_HandshakerResult_record_protocol_tag 2
+#define grpc_gcp_HandshakerResult_key_data_tag   3
+#define grpc_gcp_HandshakerResult_peer_identity_tag 4
+#define grpc_gcp_HandshakerResult_local_identity_tag 5
+#define grpc_gcp_HandshakerResult_keep_channel_open_tag 6
+#define grpc_gcp_HandshakerResult_peer_rpc_versions_tag 7
+#define grpc_gcp_HandshakerStatus_code_tag       1
+#define grpc_gcp_HandshakerStatus_details_tag    2
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_key_tag 1
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_value_tag 2
+#define grpc_gcp_HandshakerResp_out_frames_tag   1
+#define grpc_gcp_HandshakerResp_bytes_consumed_tag 2
+#define grpc_gcp_HandshakerResp_result_tag       3
+#define grpc_gcp_HandshakerResp_status_tag       4
+#define grpc_gcp_StartClientHandshakeReq_handshake_security_protocol_tag 1
+#define grpc_gcp_StartClientHandshakeReq_application_protocols_tag 2
+#define grpc_gcp_StartClientHandshakeReq_record_protocols_tag 3
+#define grpc_gcp_StartClientHandshakeReq_target_identities_tag 4
+#define grpc_gcp_StartClientHandshakeReq_local_identity_tag 5
+#define grpc_gcp_StartClientHandshakeReq_local_endpoint_tag 6
+#define grpc_gcp_StartClientHandshakeReq_remote_endpoint_tag 7
+#define grpc_gcp_StartClientHandshakeReq_target_name_tag 8
+#define grpc_gcp_StartClientHandshakeReq_rpc_versions_tag 9
+#define grpc_gcp_StartServerHandshakeReq_application_protocols_tag 1
+#define grpc_gcp_StartServerHandshakeReq_handshake_parameters_tag 2
+#define grpc_gcp_StartServerHandshakeReq_in_bytes_tag 3
+#define grpc_gcp_StartServerHandshakeReq_local_endpoint_tag 4
+#define grpc_gcp_StartServerHandshakeReq_remote_endpoint_tag 5
+#define grpc_gcp_StartServerHandshakeReq_rpc_versions_tag 6
+#define grpc_gcp_HandshakerReq_client_start_tag  1
+#define grpc_gcp_HandshakerReq_server_start_tag  2
+#define grpc_gcp_HandshakerReq_next_tag          3
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_gcp_Endpoint_fields[4];
+extern const pb_field_t grpc_gcp_Identity_fields[3];
+extern const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10];
+extern const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3];
+extern const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7];
+extern const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3];
+extern const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2];
+extern const pb_field_t grpc_gcp_HandshakerReq_fields[4];
+extern const pb_field_t grpc_gcp_HandshakerResult_fields[8];
+extern const pb_field_t grpc_gcp_HandshakerStatus_fields[3];
+extern const pb_field_t grpc_gcp_HandshakerResp_fields[5];
+
+/* Maximum encoded size of messages (where known) */
+/* grpc_gcp_Endpoint_size depends on runtime parameters */
+/* grpc_gcp_Identity_size depends on runtime parameters */
+/* grpc_gcp_StartClientHandshakeReq_size depends on runtime parameters */
+/* grpc_gcp_ServerHandshakeParameters_size depends on runtime parameters */
+/* grpc_gcp_StartServerHandshakeReq_size depends on runtime parameters */
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_size (17 + grpc_gcp_ServerHandshakeParameters_size)
+/* grpc_gcp_NextHandshakeMessageReq_size depends on runtime parameters */
+#define grpc_gcp_HandshakerReq_size              (18 + grpc_gcp_StartClientHandshakeReq_size + grpc_gcp_StartServerHandshakeReq_size + grpc_gcp_NextHandshakeMessageReq_size)
+/* grpc_gcp_HandshakerResult_size depends on runtime parameters */
+/* grpc_gcp_HandshakerStatus_size depends on runtime parameters */
+/* grpc_gcp_HandshakerResp_size depends on runtime parameters */
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define HANDSHAKER_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/src/core/tsi/alts/handshaker/proto/altscontext.proto b/src/core/tsi/alts/handshaker/proto/altscontext.proto
new file mode 100644
index 0000000..d195b37
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/proto/altscontext.proto
@@ -0,0 +1,41 @@
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+syntax = "proto3";
+
+import "transport_security_common.proto";
+
+package grpc.gcp;
+
+option java_package = "io.grpc.alts";
+
+message AltsContext {
+  // The application protocol negotiated for this connection.
+  string application_protocol = 1;
+
+  // The record protocol negotiated for this connection.
+  string record_protocol = 2;
+
+  // The security level of the created secure channel.
+  SecurityLevel security_level = 3;
+
+  // The peer service account.
+  string peer_service_account = 4;
+
+  // The local service account.
+  string local_service_account = 5;
+
+  // The RPC protocol versions supported by the peer.
+  RpcProtocolVersions peer_rpc_versions = 6;
+}
diff --git a/src/core/tsi/alts/handshaker/proto/handshaker.options b/src/core/tsi/alts/handshaker/proto/handshaker.options
new file mode 100644
index 0000000..702ba38
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/proto/handshaker.options
@@ -0,0 +1,2 @@
+handshaker.proto no_unions:true
+grpc.gcp.StartServerHandshakeReq.handshake_parameters max_count:3
diff --git a/src/core/tsi/alts/handshaker/proto/handshaker.proto b/src/core/tsi/alts/handshaker/proto/handshaker.proto
new file mode 100644
index 0000000..46b8b09
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/proto/handshaker.proto
@@ -0,0 +1,220 @@
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+syntax = "proto3";
+
+import "transport_security_common.proto";
+
+package grpc.gcp;
+
+option java_package = "io.grpc.alts";
+
+enum HandshakeProtocol {
+  // Default value.
+  HANDSHAKE_PROTOCOL_UNSPECIFIED = 0;
+
+  // TLS handshake protocol.
+  TLS = 1;
+
+  // Application Layer Transport Security handshake protocol.
+  ALTS = 2;
+}
+
+enum NetworkProtocol {
+  NETWORK_PROTOCOL_UNSPECIFIED = 0;
+  TCP = 1;
+  UDP = 2;
+}
+
+message Endpoint {
+  // IP address. It should contain an IPv4 or IPv6 string literal, e.g.
+  // "192.168.0.1" or "2001:db8::1".
+  string ip_address = 1;
+
+  // Port number.
+  int32 port = 2;
+
+  // Network protocol (e.g., TCP, UDP) associated with this endpoint.
+  NetworkProtocol protocol = 3;
+}
+
+message Identity {
+  oneof identity_oneof {
+    // Service account of a connection endpoint.
+    string service_account = 1;
+
+    // Hostname of a connection endpoint.
+    string hostname = 2;
+  }
+}
+
+message StartClientHandshakeReq {
+  // Handshake security protocol requested by the client.
+  HandshakeProtocol handshake_security_protocol = 1;
+
+  // The application protocols supported by the client, e.g., "h2" (for http2),
+  // "grpc".
+  repeated string application_protocols = 2;
+
+  // The record protocols supported by the client, e.g.,
+  // "ALTSRP_GCM_AES128".
+  repeated string record_protocols = 3;
+
+  // (Optional) Describes which server identities are acceptable by the client.
+  // If target identities are provided and none of them matches the peer
+  // identity of the server, handshake will fail.
+  repeated Identity target_identities = 4;
+
+  // (Optional) Application may specify a local identity. Otherwise, the
+  // handshaker chooses a default local identity.
+  Identity local_identity = 5;
+
+  // (Optional) Local endpoint information of the connection to the server,
+  // such as local IP address, port number, and network protocol.
+  Endpoint local_endpoint = 6;
+
+  // (Optional) Endpoint information of the remote server, such as IP address,
+  // port number, and network protocool.
+  Endpoint remote_endpoint = 7;
+
+  // (Optional) If target name is provided, a secure naming check is performed
+  // to verify that the peer authenticated identity is indeed authorized to run
+  // the target name.
+  string target_name = 8;
+
+  // (Optional) RPC protocol versions supported by the client.
+  RpcProtocolVersions rpc_versions = 9;
+}
+
+message ServerHandshakeParameters {
+  // The record protocols supported by the server, e.g.,
+  // "ALTSRP_GCM_AES128".
+  repeated string record_protocols = 1;
+
+  // (Optional) A list of local identities supported by the server, if
+  // specified. Otherwise, the handshaker chooses a default local identity.
+  repeated Identity local_identities = 2;
+}
+
+message StartServerHandshakeReq {
+  // The application protocols supported by the server, e.g., "h2" (for http2),
+  // "grpc".
+  repeated string application_protocols = 1;
+
+  // Handshake parameters (record protocols and local identities supported by
+  // the server) mapped by the handshake protocol. Each handshake security
+  // protocol (e.g., TLS or ALTS) has its own set of record protocols and local
+  // identities. Since protobuf does not support enum as key to the map, the key
+  // to handshake_parameters is the integer value of HandshakeProtocol enum.
+  map<int32, ServerHandshakeParameters> handshake_parameters = 2;
+
+  // Bytes in out_frames returned from the peer's HandshakerResp. It is possible
+  // that the peer's out_frames are split into multiple HandshakReq messages.
+  bytes in_bytes = 3;
+
+  // (Optional) Local endpoint information of the connection to the client,
+  // such as local IP address, port number, and network protocol.
+  Endpoint local_endpoint = 4;
+
+  // (Optional) Endpoint information of the remote client, such as IP address,
+  // port number, and network protocool.
+  Endpoint remote_endpoint = 5;
+
+  // (Optional) RPC protocol versions supported by the server.
+  RpcProtocolVersions rpc_versions = 6;
+}
+
+message NextHandshakeMessageReq {
+  // Bytes in out_frames returned from the peer's HandshakerResp. It is possible
+  // that the peer's out_frames are split into multiple NextHandshakerMessageReq
+  // messages.
+  bytes in_bytes = 1;
+}
+
+message HandshakerReq {
+  oneof req_oneof {
+    // The start client handshake request message.
+    StartClientHandshakeReq client_start = 1;
+
+    // The start server handshake request message.
+    StartServerHandshakeReq server_start = 2;
+
+    // The next handshake request message.
+    NextHandshakeMessageReq next = 3;
+  }
+}
+
+message HandshakerResult {
+  // The application protocol negotiated for this connection.
+  string application_protocol = 1;
+
+  // The record protocol negotiated for this connection.
+  string record_protocol = 2;
+
+  // Cryptographic key data. The key data may be more than the key length
+  // required for the record protocol, thus the client of the handshaker
+  // service needs to truncate the key data into the right key length.
+  bytes key_data = 3;
+
+  // The authenticated identity of the peer.
+  Identity peer_identity = 4;
+
+  // The local identity used in the handshake.
+  Identity local_identity = 5;
+
+  // Indicate whether the handshaker service client should keep the channel
+  // between the handshaker service open, e.g., in order to handle
+  // post-handshake messages in the future.
+  bool keep_channel_open = 6;
+
+  // The RPC protocol versions supported by the peer.
+  RpcProtocolVersions peer_rpc_versions = 7;
+}
+
+message HandshakerStatus {
+  // The status code. This could be the gRPC status code.
+  uint32 code = 1;
+
+  // The status details.
+  string details = 2;
+}
+
+message HandshakerResp {
+  // Frames to be given to the peer for the NextHandshakeMessageReq. May be
+  // empty if no out_frames have to be sent to the peer or if in_bytes in the
+  // HandshakerReq are incomplete. All the non-empty out frames must be sent to
+  // the peer even if the handshaker status is not OK as these frames may
+  // contain the alert frames.
+  bytes out_frames = 1;
+
+  // Number of bytes in the in_bytes consumed by the handshaker. It is possible
+  // that part of in_bytes in HandshakerReq was unrelated to the handshake
+  // process.
+  uint32 bytes_consumed = 2;
+
+  // This is set iff the handshake was successful. out_frames may still be set
+  // to frames that needs to be forwarded to the peer.
+  HandshakerResult result = 3;
+
+  // Status of the handshaker.
+  HandshakerStatus status = 4;
+}
+
+service HandshakerService {
+  // Accepts a stream of handshaker request, returning a stream of handshaker
+  // response.
+  rpc DoHandshake(stream HandshakerReq)
+      returns (stream HandshakerResp) {
+  }
+}
diff --git a/src/core/tsi/alts/handshaker/proto/transport_security_common.proto b/src/core/tsi/alts/handshaker/proto/transport_security_common.proto
new file mode 100644
index 0000000..41983ab
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/proto/transport_security_common.proto
@@ -0,0 +1,40 @@
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+syntax = "proto3";
+
+package grpc.gcp;
+
+option java_package = "io.grpc.alts";
+
+// The security level of the created channel. The list is sorted in increasing
+// level of security. This order must always be maintained.
+enum SecurityLevel {
+  SECURITY_NONE = 0;
+  INTEGRITY_ONLY = 1;
+  INTEGRITY_AND_PRIVACY = 2;
+}
+
+// Max and min supported RPC protocol versions.
+message RpcProtocolVersions {
+  // RPC version contains a major version and a minor version.
+  message Version {
+    uint32 major = 1;
+    uint32 minor = 2;
+  }
+  // Maximum supported RPC version.
+  Version max_rpc_version = 1;
+  // Minimum supported RPC version.
+  Version min_rpc_version = 2;
+}
diff --git a/src/core/tsi/alts/handshaker/transport_security_common.pb.c b/src/core/tsi/alts/handshaker/transport_security_common.pb.c
new file mode 100644
index 0000000..6063c76
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/transport_security_common.pb.c
@@ -0,0 +1,50 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_gcp_RpcProtocolVersions, max_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_RpcProtocolVersions, min_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3] = {
+    PB_FIELD(  1, UINT32  , OPTIONAL, STATIC  , FIRST, grpc_gcp_RpcProtocolVersions_Version, major, major, 0),
+    PB_FIELD(  2, UINT32  , OPTIONAL, STATIC  , OTHER, grpc_gcp_RpcProtocolVersions_Version, minor, major, 0),
+    PB_LAST_FIELD
+};
+
+
+/* Check that field information fits in pb_field_t */
+#if !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_32BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in 8 or 16 bit
+ * field descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 65536 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version)
+#endif
+
+#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_16BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in the default
+ * 8 bit descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 256 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/src/core/tsi/alts/handshaker/transport_security_common.pb.h b/src/core/tsi/alts/handshaker/transport_security_common.pb.h
new file mode 100644
index 0000000..49096df
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/transport_security_common.pb.h
@@ -0,0 +1,78 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED
+#define PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED
+#include "third_party/nanopb/pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Enum definitions */
+typedef enum _grpc_gcp_SecurityLevel {
+    grpc_gcp_SecurityLevel_SECURITY_NONE = 0,
+    grpc_gcp_SecurityLevel_INTEGRITY_ONLY = 1,
+    grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY = 2
+} grpc_gcp_SecurityLevel;
+#define _grpc_gcp_SecurityLevel_MIN grpc_gcp_SecurityLevel_SECURITY_NONE
+#define _grpc_gcp_SecurityLevel_MAX grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY
+#define _grpc_gcp_SecurityLevel_ARRAYSIZE ((grpc_gcp_SecurityLevel)(grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY+1))
+
+/* Struct definitions */
+typedef struct _grpc_gcp_RpcProtocolVersions_Version {
+    bool has_major;
+    uint32_t major;
+    bool has_minor;
+    uint32_t minor;
+/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions_Version) */
+} grpc_gcp_RpcProtocolVersions_Version;
+
+typedef struct _grpc_gcp_RpcProtocolVersions {
+    bool has_max_rpc_version;
+    grpc_gcp_RpcProtocolVersions_Version max_rpc_version;
+    bool has_min_rpc_version;
+    grpc_gcp_RpcProtocolVersions_Version min_rpc_version;
+/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions) */
+} grpc_gcp_RpcProtocolVersions;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_gcp_RpcProtocolVersions_init_default {false, grpc_gcp_RpcProtocolVersions_Version_init_default, false, grpc_gcp_RpcProtocolVersions_Version_init_default}
+#define grpc_gcp_RpcProtocolVersions_Version_init_default {false, 0, false, 0}
+#define grpc_gcp_RpcProtocolVersions_init_zero   {false, grpc_gcp_RpcProtocolVersions_Version_init_zero, false, grpc_gcp_RpcProtocolVersions_Version_init_zero}
+#define grpc_gcp_RpcProtocolVersions_Version_init_zero {false, 0, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_gcp_RpcProtocolVersions_Version_major_tag 1
+#define grpc_gcp_RpcProtocolVersions_Version_minor_tag 2
+#define grpc_gcp_RpcProtocolVersions_max_rpc_version_tag 1
+#define grpc_gcp_RpcProtocolVersions_min_rpc_version_tag 2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3];
+extern const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3];
+
+/* Maximum encoded size of messages (where known) */
+#define grpc_gcp_RpcProtocolVersions_size        28
+#define grpc_gcp_RpcProtocolVersions_Version_size 12
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define TRANSPORT_SECURITY_COMMON_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/src/core/tsi/alts/handshaker/transport_security_common_api.cc b/src/core/tsi/alts/handshaker/transport_security_common_api.cc
new file mode 100644
index 0000000..8a7edb5
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/transport_security_common_api.cc
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+bool grpc_gcp_rpc_protocol_versions_set_max(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major,
+    uint32_t max_minor) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "versions is nullptr in "
+            "grpc_gcp_rpc_protocol_versions_set_max().");
+    return false;
+  }
+  versions->has_max_rpc_version = true;
+  versions->max_rpc_version.has_major = true;
+  versions->max_rpc_version.has_minor = true;
+  versions->max_rpc_version.major = max_major;
+  versions->max_rpc_version.minor = max_minor;
+  return true;
+}
+
+bool grpc_gcp_rpc_protocol_versions_set_min(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major,
+    uint32_t min_minor) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "versions is nullptr in "
+            "grpc_gcp_rpc_protocol_versions_set_min().");
+    return false;
+  }
+  versions->has_min_rpc_version = true;
+  versions->min_rpc_version.has_major = true;
+  versions->min_rpc_version.has_minor = true;
+  versions->min_rpc_version.major = min_major;
+  versions->min_rpc_version.minor = min_minor;
+  return true;
+}
+
+size_t grpc_gcp_rpc_protocol_versions_encode_length(
+    const grpc_gcp_rpc_protocol_versions* versions) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_rpc_protocol_versions_encode_length().");
+    return 0;
+  }
+  pb_ostream_t size_stream;
+  memset(&size_stream, 0, sizeof(pb_ostream_t));
+  if (!pb_encode(&size_stream, grpc_gcp_RpcProtocolVersions_fields, versions)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream));
+    return 0;
+  }
+  return size_stream.bytes_written;
+}
+
+bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+    const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes,
+    size_t bytes_length) {
+  if (versions == nullptr || bytes == nullptr || bytes_length == 0) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes().");
+    return false;
+  }
+  pb_ostream_t output_stream = pb_ostream_from_buffer(bytes, bytes_length);
+  if (!pb_encode(&output_stream, grpc_gcp_RpcProtocolVersions_fields,
+                 versions)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream));
+    return false;
+  }
+  return true;
+}
+
+bool grpc_gcp_rpc_protocol_versions_encode(
+    const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice) {
+  if (versions == nullptr || slice == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_rpc_protocol_versions_encode().");
+    return false;
+  }
+  size_t encoded_length =
+      grpc_gcp_rpc_protocol_versions_encode_length(versions);
+  if (encoded_length == 0) return false;
+  *slice = grpc_slice_malloc(encoded_length);
+  return grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+      versions, GRPC_SLICE_START_PTR(*slice), encoded_length);
+}
+
+bool grpc_gcp_rpc_protocol_versions_decode(
+    grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "version is nullptr in "
+            "grpc_gcp_rpc_protocol_versions_decode().");
+    return false;
+  }
+  pb_istream_t stream = pb_istream_from_buffer(GRPC_SLICE_START_PTR(slice),
+                                               GRPC_SLICE_LENGTH(slice));
+  if (!pb_decode(&stream, grpc_gcp_RpcProtocolVersions_fields, versions)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return false;
+  }
+  return true;
+}
+
+bool grpc_gcp_rpc_protocol_versions_copy(
+    const grpc_gcp_rpc_protocol_versions* src,
+    grpc_gcp_rpc_protocol_versions* dst) {
+  if ((src == nullptr && dst != nullptr) ||
+      (src != nullptr && dst == nullptr)) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_rpc_protocol_versions_copy().");
+    return false;
+  }
+  if (src == nullptr) {
+    return true;
+  }
+  grpc_gcp_rpc_protocol_versions_set_max(dst, src->max_rpc_version.major,
+                                         src->max_rpc_version.minor);
+  grpc_gcp_rpc_protocol_versions_set_min(dst, src->min_rpc_version.major,
+                                         src->min_rpc_version.minor);
+  return true;
+}
+
+namespace grpc_core {
+namespace internal {
+
+int grpc_gcp_rpc_protocol_version_compare(
+    const grpc_gcp_rpc_protocol_versions_version* v1,
+    const grpc_gcp_rpc_protocol_versions_version* v2) {
+  if ((v1->major > v2->major) ||
+      (v1->major == v2->major && v1->minor > v2->minor)) {
+    return 1;
+  }
+  if ((v1->major < v2->major) ||
+      (v1->major == v2->major && v1->minor < v2->minor)) {
+    return -1;
+  }
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+bool grpc_gcp_rpc_protocol_versions_check(
+    const grpc_gcp_rpc_protocol_versions* local_versions,
+    const grpc_gcp_rpc_protocol_versions* peer_versions,
+    grpc_gcp_rpc_protocol_versions_version* highest_common_version) {
+  if (local_versions == nullptr || peer_versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_rpc_protocol_versions_check().");
+    return false;
+  }
+  /* max_common_version is MIN(local.max, peer.max) */
+  const grpc_gcp_rpc_protocol_versions_version* max_common_version =
+      grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+          &local_versions->max_rpc_version, &peer_versions->max_rpc_version) > 0
+          ? &peer_versions->max_rpc_version
+          : &local_versions->max_rpc_version;
+  /* min_common_version is MAX(local.min, peer.min) */
+  const grpc_gcp_rpc_protocol_versions_version* min_common_version =
+      grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+          &local_versions->min_rpc_version, &peer_versions->min_rpc_version) > 0
+          ? &local_versions->min_rpc_version
+          : &peer_versions->min_rpc_version;
+  bool result = grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+                    max_common_version, min_common_version) >= 0
+                    ? true
+                    : false;
+  if (result && highest_common_version != nullptr) {
+    memcpy(highest_common_version, max_common_version,
+           sizeof(grpc_gcp_rpc_protocol_versions_version));
+  }
+  return result;
+}
diff --git a/src/core/tsi/alts/handshaker/transport_security_common_api.h b/src/core/tsi/alts/handshaker/transport_security_common_api.h
new file mode 100644
index 0000000..68228cb
--- /dev/null
+++ b/src/core/tsi/alts/handshaker/transport_security_common_api.h
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H
+
+#include <grpc/support/port_platform.h>
+
+#include "third_party/nanopb/pb_decode.h"
+#include "third_party/nanopb/pb_encode.h"
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+
+typedef grpc_gcp_RpcProtocolVersions grpc_gcp_rpc_protocol_versions;
+
+typedef grpc_gcp_RpcProtocolVersions_Version
+    grpc_gcp_rpc_protocol_versions_version;
+
+/**
+ * This method sets the value for max_rpc_versions field of rpc protocol
+ * versions.
+ *
+ * - versions: an rpc protocol version instance.
+ * - max_major: a major version of maximum supported RPC version.
+ * - max_minor: a minor version of maximum supported RPC version.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_set_max(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major,
+    uint32_t max_minor);
+
+/**
+ * This method sets the value for min_rpc_versions field of rpc protocol
+ * versions.
+ *
+ * - versions: an rpc protocol version instance.
+ * - min_major: a major version of minimum supported RPC version.
+ * - min_minor: a minor version of minimum supported RPC version.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_set_min(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major,
+    uint32_t min_minor);
+
+/**
+ * This method computes serialized byte length of rpc protocol versions.
+ *
+ * - versions: an rpc protocol versions instance.
+ *
+ * The method returns serialized byte length. It returns 0 on failure.
+ */
+size_t grpc_gcp_rpc_protocol_versions_encode_length(
+    const grpc_gcp_rpc_protocol_versions* versions);
+
+/**
+ * This method serializes rpc protocol versions and writes the result to
+ * the memory buffer provided by the caller. Caller is responsible for
+ * allocating sufficient memory to store the serialized data.
+ *
+ * - versions: an rpc protocol versions instance.
+ * - bytes: bytes buffer where the result will be written to.
+ * - bytes_length: length of the bytes buffer.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+    const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes,
+    size_t bytes_length);
+
+/**
+ * This method serializes an rpc protocol version and returns serialized rpc
+ * versions in grpc slice.
+ *
+ * - versions: an rpc protocol versions instance.
+ * - slice: grpc slice where the serialized result will be written.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_encode(
+    const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice);
+
+/**
+ * This method de-serializes input in grpc slice form and stores the result
+ * in rpc protocol versions.
+ *
+ * - slice: a data stream containing a serialized rpc protocol version.
+ * - versions: an rpc protocol version instance used to hold de-serialized
+ *   result.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_decode(
+    grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions);
+
+/**
+ * This method performs a deep copy operation on rpc protocol versions
+ * instance.
+ *
+ * - src: rpc protocol versions instance that needs to be copied.
+ * - dst: rpc protocol versions instance that stores the copied result.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_copy(
+    const grpc_gcp_rpc_protocol_versions* src,
+    grpc_gcp_rpc_protocol_versions* dst);
+
+/**
+ * This method performs a version check between local and peer rpc protocol
+ * versions.
+ *
+ * - local_versions: local rpc protocol versions instance.
+ * - peer_versions: peer rpc protocol versions instance.
+ * - highest_common_version: an output parameter that will store the highest
+ *   common rpc protocol version both parties agreed on.
+ *
+ * The method returns true if the check passes which means both parties agreed
+ * on a common rpc protocol to use, and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_check(
+    const grpc_gcp_rpc_protocol_versions* local_versions,
+    const grpc_gcp_rpc_protocol_versions* peer_versions,
+    grpc_gcp_rpc_protocol_versions_version* highest_common_version);
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * Exposed for testing only.
+ * The method returns 0 if v1 = v2,
+ *            returns 1 if v1 > v2,
+ *            returns -1 if v1 < v2.
+ */
+int grpc_gcp_rpc_protocol_version_compare(
+    const grpc_gcp_rpc_protocol_versions_version* v1,
+    const grpc_gcp_rpc_protocol_versions_version* v2);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
new file mode 100644
index 0000000..7ba03eb
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
@@ -0,0 +1,180 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* Main struct for alts_grpc_integrity_only_record_protocol.  */
+typedef struct alts_grpc_integrity_only_record_protocol {
+  alts_grpc_record_protocol base;
+  grpc_slice_buffer data_sb;
+  unsigned char* tag_buf;
+} alts_grpc_integrity_only_record_protocol;
+
+/* --- alts_grpc_record_protocol methods implementation. --- */
+
+static tsi_result alts_grpc_integrity_only_protect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Allocates memory for header and tag slices.  */
+  grpc_slice header_slice = GRPC_SLICE_MALLOC(rp->header_length);
+  grpc_slice tag_slice = GRPC_SLICE_MALLOC(rp->tag_length);
+  /* Calls alts_iovec_record_protocol protect.  */
+  char* error_details = nullptr;
+  iovec_t header_iovec = {GRPC_SLICE_START_PTR(header_slice),
+                          GRPC_SLICE_LENGTH(header_slice)};
+  iovec_t tag_iovec = {GRPC_SLICE_START_PTR(tag_slice),
+                       GRPC_SLICE_LENGTH(tag_slice)};
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
+                                                          unprotected_slices);
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+      rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, header_iovec,
+      tag_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  /* Appends result to protected_slices.  */
+  grpc_slice_buffer_add(protected_slices, header_slice);
+  grpc_slice_buffer_move_into(unprotected_slices, protected_slices);
+  grpc_slice_buffer_add(protected_slices, tag_slice);
+  return TSI_OK;
+}
+
+static tsi_result alts_grpc_integrity_only_unprotect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || protected_slices == nullptr ||
+      unprotected_slices == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (protected_slices->length < rp->header_length + rp->tag_length) {
+    gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* In this method, rp points to alts_grpc_record_protocol struct
+   * and integrity_only_record_protocol points to
+   * alts_grpc_integrity_only_record_protocol struct.  */
+  alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol =
+      reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+  /* Strips frame header from protected slices.  */
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_move_first(protected_slices, rp->header_length,
+                               &rp->header_sb);
+  GPR_ASSERT(rp->header_sb.length == rp->header_length);
+  iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
+  /* Moves protected slices data to data_sb and leaves the remaining tag.  */
+  grpc_slice_buffer_reset_and_unref_internal(
+      &integrity_only_record_protocol->data_sb);
+  grpc_slice_buffer_move_first(protected_slices,
+                               protected_slices->length - rp->tag_length,
+                               &integrity_only_record_protocol->data_sb);
+  GPR_ASSERT(protected_slices->length == rp->tag_length);
+  iovec_t tag_iovec = {nullptr, rp->tag_length};
+  if (protected_slices->count == 1) {
+    tag_iovec.iov_base = GRPC_SLICE_START_PTR(protected_slices->slices[0]);
+  } else {
+    /* Frame tag is in multiple slices, copies the tag bytes from slice
+     * buffer to a single flat buffer.  */
+    alts_grpc_record_protocol_copy_slice_buffer(
+        protected_slices, integrity_only_record_protocol->tag_buf);
+    tag_iovec.iov_base = integrity_only_record_protocol->tag_buf;
+  }
+  /* Calls alts_iovec_record_protocol unprotect.  */
+  char* error_details = nullptr;
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+      rp, &integrity_only_record_protocol->data_sb);
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect(
+      rp->iovec_rp, rp->iovec_buf,
+      integrity_only_record_protocol->data_sb.count, header_iovec, tag_iovec,
+      &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_reset_and_unref_internal(protected_slices);
+  grpc_slice_buffer_move_into(&integrity_only_record_protocol->data_sb,
+                              unprotected_slices);
+  return TSI_OK;
+}
+
+static void alts_grpc_integrity_only_destruct(alts_grpc_record_protocol* rp) {
+  if (rp == nullptr) {
+    return;
+  }
+  alts_grpc_integrity_only_record_protocol* integrity_only_rp =
+      reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+  grpc_slice_buffer_destroy_internal(&integrity_only_rp->data_sb);
+  gpr_free(integrity_only_rp->tag_buf);
+}
+
+static const alts_grpc_record_protocol_vtable
+    alts_grpc_integrity_only_record_protocol_vtable = {
+        alts_grpc_integrity_only_protect, alts_grpc_integrity_only_unprotect,
+        alts_grpc_integrity_only_destruct};
+
+tsi_result alts_grpc_integrity_only_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, alts_grpc_record_protocol** rp) {
+  if (crypter == nullptr || rp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_grpc_integrity_only_record_protocol* impl =
+      static_cast<alts_grpc_integrity_only_record_protocol*>(
+          gpr_zalloc(sizeof(alts_grpc_integrity_only_record_protocol)));
+  /* Calls alts_grpc_record_protocol init.  */
+  tsi_result result = alts_grpc_record_protocol_init(
+      &impl->base, crypter, overflow_size, is_client,
+      /*is_integrity_only=*/true, is_protect);
+  if (result != TSI_OK) {
+    gpr_free(impl);
+    return result;
+  }
+  /* Initializes slice buffer for data_sb.  */
+  grpc_slice_buffer_init(&impl->data_sb);
+  /* Allocates tag buffer.  */
+  impl->tag_buf =
+      static_cast<unsigned char*>(gpr_malloc(impl->base.tag_length));
+  impl->base.vtable = &alts_grpc_integrity_only_record_protocol_vtable;
+  *rp = &impl->base;
+  return TSI_OK;
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
new file mode 100644
index 0000000..8d68b27
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+
+/**
+ * This method creates an integrity-only alts_grpc_record_protocol instance,
+ * given a gsec_aead_crypter instance and a flag indicating if the created
+ * instance will be used at the client or server side. The ownership of
+ * gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - overflow_size: overflow size of counter in bytes.
+ * - is_client: a flag indicating if the alts_grpc_record_protocol instance will
+ *   be used at the client or server side.
+ * - is_protect: a flag indicating if the alts_grpc_record_protocol instance
+ *   will be used for protect or unprotect.
+ * - rp: an alts_grpc_record_protocol instance to be returned from
+ *   the method.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_integrity_only_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, alts_grpc_record_protocol** rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H \
+        */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
new file mode 100644
index 0000000..d4fd88d
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* Privacy-integrity alts_grpc_record_protocol object uses the same struct
+ * defined in alts_grpc_record_protocol_common.h.  */
+
+/* --- alts_grpc_record_protocol methods implementation. --- */
+
+static tsi_result alts_grpc_privacy_integrity_protect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Allocates memory for output frame. In privacy-integrity protect, the
+   * protected frame is stored in a newly allocated buffer.  */
+  size_t protected_frame_size =
+      unprotected_slices->length + rp->header_length +
+      alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
+  grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size);
+  iovec_t protected_iovec = {GRPC_SLICE_START_PTR(protected_slice),
+                             GRPC_SLICE_LENGTH(protected_slice)};
+  /* Calls alts_iovec_record_protocol protect.  */
+  char* error_details = nullptr;
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
+                                                          unprotected_slices);
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_protect(
+          rp->iovec_rp, rp->iovec_buf, unprotected_slices->count,
+          protected_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+    gpr_free(error_details);
+    grpc_slice_unref(protected_slice);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_add(protected_slices, protected_slice);
+  grpc_slice_buffer_reset_and_unref_internal(unprotected_slices);
+  return TSI_OK;
+}
+
+static tsi_result alts_grpc_privacy_integrity_unprotect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || protected_slices == nullptr ||
+      unprotected_slices == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Allocates memory for output frame. In privacy-integrity unprotect, the
+   * unprotected data are stored in a newly allocated buffer.  */
+  if (protected_slices->length < rp->header_length + rp->tag_length) {
+    gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  size_t unprotected_frame_size =
+      protected_slices->length - rp->header_length - rp->tag_length;
+  grpc_slice unprotected_slice = GRPC_SLICE_MALLOC(unprotected_frame_size);
+  iovec_t unprotected_iovec = {GRPC_SLICE_START_PTR(unprotected_slice),
+                               GRPC_SLICE_LENGTH(unprotected_slice)};
+  /* Strips frame header from protected slices.  */
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_move_first(protected_slices, rp->header_length,
+                               &rp->header_sb);
+  iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
+  /* Calls alts_iovec_record_protocol unprotect.  */
+  char* error_details = nullptr;
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, protected_slices);
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_unprotect(
+          rp->iovec_rp, header_iovec, rp->iovec_buf, protected_slices->count,
+          unprotected_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
+    gpr_free(error_details);
+    grpc_slice_unref(unprotected_slice);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_reset_and_unref_internal(protected_slices);
+  grpc_slice_buffer_add(unprotected_slices, unprotected_slice);
+  return TSI_OK;
+}
+
+static const alts_grpc_record_protocol_vtable
+    alts_grpc_privacy_integrity_record_protocol_vtable = {
+        alts_grpc_privacy_integrity_protect,
+        alts_grpc_privacy_integrity_unprotect, nullptr};
+
+tsi_result alts_grpc_privacy_integrity_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, alts_grpc_record_protocol** rp) {
+  if (crypter == nullptr || rp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  auto* impl = static_cast<alts_grpc_record_protocol*>(
+      gpr_zalloc(sizeof(alts_grpc_record_protocol)));
+  /* Calls alts_grpc_record_protocol init.  */
+  tsi_result result =
+      alts_grpc_record_protocol_init(impl, crypter, overflow_size, is_client,
+                                     /*is_integrity_only=*/false, is_protect);
+  if (result != TSI_OK) {
+    gpr_free(impl);
+    return result;
+  }
+  impl->vtable = &alts_grpc_privacy_integrity_record_protocol_vtable;
+  *rp = impl;
+  return TSI_OK;
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
new file mode 100644
index 0000000..1e34aef
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+
+/**
+ * This method creates a privacy-integrity alts_grpc_record_protocol instance,
+ * given a gsec_aead_crypter instance and a flag indicating if the created
+ * instance will be used at the client or server side. The ownership of
+ * gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - is_client: a flag indicating if the alts_grpc_record_protocol instance will
+ *   be used at the client or server side.
+ * - rp: an alts_grpc_record_protocol instance to be returned from
+ *   the method.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_privacy_integrity_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, alts_grpc_record_protocol** rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H \
+        */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
new file mode 100644
index 0000000..d1e433d
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+/**
+ * This alts_grpc_record_protocol object protects and unprotects a single frame
+ * stored in grpc slice buffer with zero or minimized memory copy.
+ * Implementations of this object must be thread compatible.
+ */
+typedef struct alts_grpc_record_protocol alts_grpc_record_protocol;
+
+/**
+ * This methods performs protect operation on unprotected data and appends the
+ * protected frame to protected_slices. The caller needs to ensure the length
+ * of unprotected data plus the frame overhead is less than or equal to the
+ * maximum frame length. The input unprotected data slice buffer will be
+ * cleared, although the actual unprotected data bytes are not modified.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - unprotected_slices: the unprotected data to be protected.
+ * - protected_slices: slice buffer where the protected frame is appended.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_record_protocol_protect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices);
+
+/**
+ * This methods performs unprotect operation on a full frame of protected data
+ * and appends unprotected data to unprotected_slices. It is the caller's
+ * responsibility to prepare a full frame of data before calling this method.
+ * The input protected frame slice buffer will be cleared, although the actual
+ * protected data bytes are not modified.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - protected_slices: a full frame of protected data in grpc slices.
+ * - unprotected_slices: slice buffer where unprotected data is appended.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_record_protocol_unprotect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices);
+
+/**
+ * This method returns maximum allowed unprotected data size, given maximum
+ * protected frame size.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - max_protected_frame_size: maximum protected frame size.
+ *
+ * On success, the method returns the maximum allowed unprotected data size.
+ * Otherwise, it returns zero.
+ */
+size_t alts_grpc_record_protocol_max_unprotected_data_size(
+    const alts_grpc_record_protocol* self, size_t max_protected_frame_size);
+
+/**
+ * This method destroys an alts_grpc_record_protocol instance by de-allocating
+ * all of its occupied memory.
+ */
+void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H \
+        */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
new file mode 100644
index 0000000..ff91aea
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
@@ -0,0 +1,173 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+const size_t kInitialIovecBufferSize = 8;
+
+/* Makes sure iovec_buf in alts_grpc_record_protocol is large enough.  */
+static void ensure_iovec_buf_size(alts_grpc_record_protocol* rp,
+                                  const grpc_slice_buffer* sb) {
+  GPR_ASSERT(rp != nullptr && sb != nullptr);
+  if (sb->count <= rp->iovec_buf_length) {
+    return;
+  }
+  /* At least double the iovec buffer size.  */
+  rp->iovec_buf_length = GPR_MAX(sb->count, 2 * rp->iovec_buf_length);
+  rp->iovec_buf = static_cast<iovec_t*>(
+      gpr_realloc(rp->iovec_buf, rp->iovec_buf_length * sizeof(iovec_t)));
+}
+
+/* --- Implementation of methods defined in tsi_grpc_record_protocol_common.h.
+ * --- */
+
+void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+    alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb) {
+  GPR_ASSERT(rp != nullptr && sb != nullptr);
+  ensure_iovec_buf_size(rp, sb);
+  for (size_t i = 0; i < sb->count; i++) {
+    rp->iovec_buf[i].iov_base = GRPC_SLICE_START_PTR(sb->slices[i]);
+    rp->iovec_buf[i].iov_len = GRPC_SLICE_LENGTH(sb->slices[i]);
+  }
+}
+
+void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
+                                                 unsigned char* dst) {
+  GPR_ASSERT(src != nullptr && dst != nullptr);
+  for (size_t i = 0; i < src->count; i++) {
+    size_t slice_length = GRPC_SLICE_LENGTH(src->slices[i]);
+    memcpy(dst, GRPC_SLICE_START_PTR(src->slices[i]), slice_length);
+    dst += slice_length;
+  }
+}
+
+iovec_t alts_grpc_record_protocol_get_header_iovec(
+    alts_grpc_record_protocol* rp) {
+  iovec_t header_iovec = {nullptr, 0};
+  if (rp == nullptr) {
+    return header_iovec;
+  }
+  header_iovec.iov_len = rp->header_length;
+  if (rp->header_sb.count == 1) {
+    header_iovec.iov_base = GRPC_SLICE_START_PTR(rp->header_sb.slices[0]);
+  } else {
+    /* Frame header is in multiple slices, copies the header bytes from slice
+     * buffer to a single flat buffer.  */
+    alts_grpc_record_protocol_copy_slice_buffer(&rp->header_sb, rp->header_buf);
+    header_iovec.iov_base = rp->header_buf;
+  }
+  return header_iovec;
+}
+
+tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
+                                          gsec_aead_crypter* crypter,
+                                          size_t overflow_size, bool is_client,
+                                          bool is_integrity_only,
+                                          bool is_protect) {
+  if (rp == nullptr || crypter == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol init.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Creates alts_iovec_record_protocol.  */
+  char* error_details = nullptr;
+  grpc_status_code status = alts_iovec_record_protocol_create(
+      crypter, overflow_size, is_client, is_integrity_only, is_protect,
+      &rp->iovec_rp, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to create alts_iovec_record_protocol, %s.",
+            error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  /* Allocates header slice buffer.  */
+  grpc_slice_buffer_init(&rp->header_sb);
+  /* Allocates header buffer.  */
+  rp->header_length = alts_iovec_record_protocol_get_header_length();
+  rp->header_buf = static_cast<unsigned char*>(gpr_malloc(rp->header_length));
+  rp->tag_length = alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
+  /* Allocates iovec buffer.  */
+  rp->iovec_buf_length = kInitialIovecBufferSize;
+  rp->iovec_buf =
+      static_cast<iovec_t*>(gpr_malloc(rp->iovec_buf_length * sizeof(iovec_t)));
+  return TSI_OK;
+}
+
+/* --- Implementation of methods defined in tsi_grpc_record_protocol.h. --- */
+tsi_result alts_grpc_record_protocol_protect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
+      self->vtable == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->protect == nullptr) {
+    return TSI_UNIMPLEMENTED;
+  }
+  return self->vtable->protect(self, unprotected_slices, protected_slices);
+}
+
+tsi_result alts_grpc_record_protocol_unprotect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
+      self->vtable == nullptr || protected_slices == nullptr ||
+      unprotected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->unprotect == nullptr) {
+    return TSI_UNIMPLEMENTED;
+  }
+  return self->vtable->unprotect(self, protected_slices, unprotected_slices);
+}
+
+void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self) {
+  if (self == nullptr) {
+    return;
+  }
+  if (self->vtable->destruct != nullptr) {
+    self->vtable->destruct(self);
+  }
+  alts_iovec_record_protocol_destroy(self->iovec_rp);
+  grpc_slice_buffer_destroy_internal(&self->header_sb);
+  gpr_free(self->header_buf);
+  gpr_free(self->iovec_buf);
+  gpr_free(self);
+}
+
+/* Integrity-only and privacy-integrity share the same implementation. No need
+ * to call vtable.  */
+size_t alts_grpc_record_protocol_max_unprotected_data_size(
+    const alts_grpc_record_protocol* self, size_t max_protected_frame_size) {
+  if (self == nullptr) {
+    return 0;
+  }
+  return alts_iovec_record_protocol_max_unprotected_data_size(
+      self->iovec_rp, max_protected_frame_size);
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
new file mode 100644
index 0000000..43b8a4a
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H
+
+/**
+ * this file contains alts_grpc_record_protocol internals and internal-only
+ * helper functions. The public functions of alts_grpc_record_protocol are
+ * defined in the alts_grpc_record_protocol.h.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* V-table for alts_grpc_record_protocol implementations.  */
+typedef struct {
+  tsi_result (*protect)(alts_grpc_record_protocol* self,
+                        grpc_slice_buffer* unprotected_slices,
+                        grpc_slice_buffer* protected_slices);
+  tsi_result (*unprotect)(alts_grpc_record_protocol* self,
+                          grpc_slice_buffer* protected_slices,
+                          grpc_slice_buffer* unprotected_slices);
+  void (*destruct)(alts_grpc_record_protocol* self);
+} alts_grpc_record_protocol_vtable;
+
+/* Main struct for alts_grpc_record_protocol implementation, shared by both
+ * integrity-only record protocol and privacy-integrity record protocol.
+ * Integrity-only record protocol has additional data elements.
+ * Privacy-integrity record protocol uses this struct directly.  */
+struct alts_grpc_record_protocol {
+  const alts_grpc_record_protocol_vtable* vtable;
+  alts_iovec_record_protocol* iovec_rp;
+  grpc_slice_buffer header_sb;
+  unsigned char* header_buf;
+  size_t header_length;
+  size_t tag_length;
+  iovec_t* iovec_buf;
+  size_t iovec_buf_length;
+};
+
+/**
+ * Converts the slices of input sb into iovec_t's and puts the result into
+ * rp->iovec_buf. Note that the actual data are not copied, only
+ * pointers and lengths are copied.
+ */
+void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+    alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb);
+
+/**
+ * Copies bytes from slice buffer to destination buffer. Caller is responsible
+ * for allocating enough memory of destination buffer. This method is used for
+ * copying frame header and tag in case they are stored in multiple slices.
+ */
+void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
+                                                 unsigned char* dst);
+
+/**
+ * This method returns an iovec object pointing to the frame header stored in
+ * rp->header_sb. If the frame header is stored in multiple slices,
+ * this method will copy the bytes in rp->header_sb to
+ * rp->header_buf, and return an iovec object pointing to
+ * rp->header_buf.
+ */
+iovec_t alts_grpc_record_protocol_get_header_iovec(
+    alts_grpc_record_protocol* rp);
+
+/**
+ * Initializes an alts_grpc_record_protocol object, given a gsec_aead_crypter
+ * instance, the overflow size of the counter in bytes, a flag indicating if the
+ * object is used for client or server side, a flag indicating if it is used for
+ * integrity-only or privacy-integrity mode, and a flag indicating if it is for
+ * protect or unprotect. The ownership of gsec_aead_crypter object is
+ * transferred to the alts_grpc_record_protocol object.
+ */
+tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
+                                          gsec_aead_crypter* crypter,
+                                          size_t overflow_size, bool is_client,
+                                          bool is_integrity_only,
+                                          bool is_protect);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H \
+        */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
new file mode 100644
index 0000000..6a548e5
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
@@ -0,0 +1,476 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+
+struct alts_iovec_record_protocol {
+  alts_counter* ctr;
+  gsec_aead_crypter* crypter;
+  size_t tag_length;
+  bool is_integrity_only;
+  bool is_protect;
+};
+
+/* Copies error message to destination.  */
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+/* Appends error message to destination.  */
+static void maybe_append_error_msg(const char* appendix, char** dst) {
+  if (dst != nullptr && appendix != nullptr) {
+    int dst_len = static_cast<int>(strlen(*dst));
+    *dst = static_cast<char*>(realloc(*dst, dst_len + strlen(appendix) + 1));
+    assert(*dst != nullptr);
+    memcpy(*dst + dst_len, appendix, strlen(appendix) + 1);
+  }
+}
+
+/* Use little endian to interpret a string of bytes as uint32_t.  */
+static uint32_t load_32_le(const unsigned char* buffer) {
+  return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) |
+         (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]);
+}
+
+/* Store uint32_t as a string of little endian bytes.  */
+static void store_32_le(uint32_t value, unsigned char* buffer) {
+  buffer[3] = (unsigned char)(value >> 24) & 0xFF;
+  buffer[2] = (unsigned char)(value >> 16) & 0xFF;
+  buffer[1] = (unsigned char)(value >> 8) & 0xFF;
+  buffer[0] = (unsigned char)(value)&0xFF;
+}
+
+/* Ensures header and tag iovec have sufficient length.  */
+static grpc_status_code ensure_header_and_tag_length(
+    const alts_iovec_record_protocol* rp, iovec_t header, iovec_t tag,
+    char** error_details) {
+  if (rp == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (header.iov_base == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
+    maybe_copy_error_msg("Header length is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (tag.iov_base == nullptr) {
+    maybe_copy_error_msg("Tag is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (tag.iov_len != rp->tag_length) {
+    maybe_copy_error_msg("Tag length is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+/* Increments crypter counter and checks overflow.  */
+static grpc_status_code increment_counter(alts_counter* counter,
+                                          char** error_details) {
+  if (counter == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  bool is_overflow = false;
+  grpc_status_code status =
+      alts_counter_increment(counter, &is_overflow, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (is_overflow) {
+    maybe_copy_error_msg("Crypter counter is overflowed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+/* Given an array of iovec, computes the total length of buffer.  */
+static size_t get_total_length(const iovec_t* vec, size_t vec_length) {
+  size_t total_length = 0;
+  for (size_t i = 0; i < vec_length; ++i) {
+    total_length += vec[i].iov_len;
+  }
+  return total_length;
+}
+
+/* Writes frame header given data and tag length.  */
+static grpc_status_code write_frame_header(size_t data_length,
+                                           unsigned char* header,
+                                           char** error_details) {
+  if (header == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  size_t frame_length = kZeroCopyFrameMessageTypeFieldSize + data_length;
+  store_32_le(static_cast<uint32_t>(frame_length), header);
+  store_32_le(kZeroCopyFrameMessageType,
+              header + kZeroCopyFrameLengthFieldSize);
+  return GRPC_STATUS_OK;
+}
+
+/* Verifies frame header given protected data length.  */
+static grpc_status_code verify_frame_header(size_t data_length,
+                                            unsigned char* header,
+                                            char** error_details) {
+  if (header == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  size_t frame_length = load_32_le(header);
+  if (frame_length != kZeroCopyFrameMessageTypeFieldSize + data_length) {
+    maybe_copy_error_msg("Bad frame length.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  size_t message_type = load_32_le(header + kZeroCopyFrameLengthFieldSize);
+  if (message_type != kZeroCopyFrameMessageType) {
+    maybe_copy_error_msg("Unsupported message type.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+/* --- alts_iovec_record_protocol methods implementation. --- */
+
+size_t alts_iovec_record_protocol_get_header_length() {
+  return kZeroCopyFrameHeaderSize;
+}
+
+size_t alts_iovec_record_protocol_get_tag_length(
+    const alts_iovec_record_protocol* rp) {
+  if (rp != nullptr) {
+    return rp->tag_length;
+  }
+  return 0;
+}
+
+size_t alts_iovec_record_protocol_max_unprotected_data_size(
+    const alts_iovec_record_protocol* rp, size_t max_protected_frame_size) {
+  if (rp == nullptr) {
+    return 0;
+  }
+  size_t overhead_bytes_size =
+      kZeroCopyFrameMessageTypeFieldSize + rp->tag_length;
+  if (max_protected_frame_size <= overhead_bytes_size) return 0;
+  return max_protected_frame_size - overhead_bytes_size;
+}
+
+grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (!rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Integrity-only operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (!rp->is_protect) {
+    maybe_copy_error_msg("Protect operations are not allowed for this object.",
+                         error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  grpc_status_code status =
+      ensure_header_and_tag_length(rp, header, tag, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Unprotected data should not be zero length.  */
+  size_t data_length =
+      get_total_length(unprotected_vec, unprotected_vec_length);
+  /* Sets frame header.  */
+  status = write_frame_header(data_length + rp->tag_length,
+                              static_cast<unsigned char*>(header.iov_base),
+                              error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Computes frame tag by calling AEAD crypter.  */
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_encrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), unprotected_vec, unprotected_vec_length,
+      /* plaintext_vec = */ nullptr, /* plaintext_vec_length = */ 0, tag,
+      &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (bytes_written != rp->tag_length) {
+    maybe_copy_error_msg("Bytes written expects to be the same as tag length.",
+                         error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter.  */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
+    alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
+    size_t protected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (!rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Integrity-only operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (rp->is_protect) {
+    maybe_copy_error_msg(
+        "Unprotect operations are not allowed for this object.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  grpc_status_code status =
+      ensure_header_and_tag_length(rp, header, tag, error_details);
+  if (status != GRPC_STATUS_OK) return status;
+  /* Protected data should not be zero length.  */
+  size_t data_length = get_total_length(protected_vec, protected_vec_length);
+  /* Verifies frame header.  */
+  status = verify_frame_header(data_length + rp->tag_length,
+                               static_cast<unsigned char*>(header.iov_base),
+                               error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Verifies frame tag by calling AEAD crypter.  */
+  iovec_t plaintext = {nullptr, 0};
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_decrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), protected_vec, protected_vec_length, &tag,
+      1, plaintext, &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK || bytes_written != 0) {
+    maybe_append_error_msg(" Frame tag verification failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter.  */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t protected_frame,
+    char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Privacy-integrity operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (!rp->is_protect) {
+    maybe_copy_error_msg("Protect operations are not allowed for this object.",
+                         error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  /* Unprotected data should not be zero length.  */
+  size_t data_length =
+      get_total_length(unprotected_vec, unprotected_vec_length);
+  /* Ensures protected frame iovec has sufficient size.  */
+  if (protected_frame.iov_base == nullptr) {
+    maybe_copy_error_msg("Protected frame is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (protected_frame.iov_len !=
+      alts_iovec_record_protocol_get_header_length() + data_length +
+          rp->tag_length) {
+    maybe_copy_error_msg("Protected frame size is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Writer frame header.  */
+  grpc_status_code status = write_frame_header(
+      data_length + rp->tag_length,
+      static_cast<unsigned char*>(protected_frame.iov_base), error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Encrypt unprotected data by calling AEAD crypter.  */
+  unsigned char* ciphertext_buffer =
+      static_cast<unsigned char*>(protected_frame.iov_base) +
+      alts_iovec_record_protocol_get_header_length();
+  iovec_t ciphertext = {ciphertext_buffer, data_length + rp->tag_length};
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_encrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
+      /* aad_vec_length = */ 0, unprotected_vec, unprotected_vec_length,
+      ciphertext, &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (bytes_written != data_length + rp->tag_length) {
+    maybe_copy_error_msg(
+        "Bytes written expects to be data length plus tag length.",
+        error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter. */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
+    alts_iovec_record_protocol* rp, iovec_t header,
+    const iovec_t* protected_vec, size_t protected_vec_length,
+    iovec_t unprotected_data, char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Privacy-integrity operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (rp->is_protect) {
+    maybe_copy_error_msg(
+        "Unprotect operations are not allowed for this object.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  /* Protected data size should be no less than tag size.  */
+  size_t protected_data_length =
+      get_total_length(protected_vec, protected_vec_length);
+  if (protected_data_length < rp->tag_length) {
+    maybe_copy_error_msg(
+        "Protected data length should be more than the tag length.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Ensures header has sufficient size.  */
+  if (header.iov_base == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
+    maybe_copy_error_msg("Header length is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Ensures unprotected data iovec has sufficient size.  */
+  if (unprotected_data.iov_len != protected_data_length - rp->tag_length) {
+    maybe_copy_error_msg("Unprotected data size is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Verify frame header.  */
+  grpc_status_code status = verify_frame_header(
+      protected_data_length, static_cast<unsigned char*>(header.iov_base),
+      error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Decrypt protected data by calling AEAD crypter.  */
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_decrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
+      /* aad_vec_length = */ 0, protected_vec, protected_vec_length,
+      unprotected_data, &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK) {
+    maybe_append_error_msg(" Frame decryption failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (bytes_written != protected_data_length - rp->tag_length) {
+    maybe_copy_error_msg(
+        "Bytes written expects to be protected data length minus tag length.",
+        error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter. */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
+    char** error_details) {
+  if (crypter == nullptr || rp == nullptr) {
+    maybe_copy_error_msg(
+        "Invalid nullptr arguments to alts_iovec_record_protocol create.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  alts_iovec_record_protocol* impl = static_cast<alts_iovec_record_protocol*>(
+      gpr_zalloc(sizeof(alts_iovec_record_protocol)));
+  /* Gets counter length.  */
+  size_t counter_length = 0;
+  grpc_status_code status =
+      gsec_aead_crypter_nonce_length(crypter, &counter_length, error_details);
+  if (status != GRPC_STATUS_OK) {
+    goto cleanup;
+  }
+  /* Creates counters.  */
+  status =
+      alts_counter_create(is_protect ? !is_client : is_client, counter_length,
+                          overflow_size, &impl->ctr, error_details);
+  if (status != GRPC_STATUS_OK) {
+    goto cleanup;
+  }
+  /* Gets tag length.  */
+  status =
+      gsec_aead_crypter_tag_length(crypter, &impl->tag_length, error_details);
+  if (status != GRPC_STATUS_OK) {
+    goto cleanup;
+  }
+  impl->crypter = crypter;
+  impl->is_integrity_only = is_integrity_only;
+  impl->is_protect = is_protect;
+  *rp = impl;
+  return GRPC_STATUS_OK;
+cleanup:
+  alts_counter_destroy(impl->ctr);
+  gpr_free(impl);
+  return GRPC_STATUS_FAILED_PRECONDITION;
+}
+
+void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp) {
+  if (rp != nullptr) {
+    alts_counter_destroy(rp->ctr);
+    gsec_aead_crypter_destroy(rp->crypter);
+    gpr_free(rp);
+  }
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
new file mode 100644
index 0000000..0b7d1bf
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
@@ -0,0 +1,199 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+constexpr size_t kZeroCopyFrameMessageType = 0x06;
+constexpr size_t kZeroCopyFrameLengthFieldSize = 4;
+constexpr size_t kZeroCopyFrameMessageTypeFieldSize = 4;
+constexpr size_t kZeroCopyFrameHeaderSize =
+    kZeroCopyFrameLengthFieldSize + kZeroCopyFrameMessageTypeFieldSize;
+
+// Limit k on number of frames such that at most 2^(8 * k) frames can be sent.
+constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8;
+constexpr size_t kAltsRecordProtocolFrameLimit = 5;
+
+/* An implementation of alts record protocol. The API is thread-compatible. */
+
+typedef struct iovec iovec_t;
+
+typedef struct alts_iovec_record_protocol alts_iovec_record_protocol;
+
+/**
+ * This method gets the length of record protocol frame header.
+ */
+size_t alts_iovec_record_protocol_get_header_length();
+
+/**
+ * This method gets the length of record protocol frame tag.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ *
+ * On success, the method returns the length of record protocol frame tag.
+ * Otherwise, it returns zero.
+ */
+size_t alts_iovec_record_protocol_get_tag_length(
+    const alts_iovec_record_protocol* rp);
+
+/**
+ * This method returns maximum allowed unprotected data size, given maximum
+ * protected frame size.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - max_protected_frame_size: maximum protected frame size.
+ *
+ * On success, the method returns the maximum allowed unprotected data size.
+ * Otherwise, it returns zero.
+ */
+size_t alts_iovec_record_protocol_max_unprotected_data_size(
+    const alts_iovec_record_protocol* rp, size_t max_protected_frame_size);
+
+/**
+ * This method performs integrity-only protect operation on a
+ * alts_iovec_record_protocol instance, i.e., compute frame header and tag. The
+ * caller needs to allocate the memory for header and tag prior to calling this
+ * method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - unprotected_vec: an iovec array containing unprotected data.
+ * - unprotected_vec_length: the array length of unprotected_vec.
+ * - header: an iovec containing the output frame header.
+ * - tag: an iovec containing the output frame tag.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details);
+
+/**
+ * This method performs integrity-only unprotect operation on a
+ * alts_iovec_record_protocol instance, i.e., verify frame header and tag.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - protected_vec: an iovec array containing protected data.
+ * - protected_vec_length: the array length of protected_vec.
+ * - header: an iovec containing the frame header.
+ * - tag: an iovec containing the frame tag.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
+    alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
+    size_t protected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details);
+
+/**
+ * This method performs privacy-integrity protect operation on a
+ * alts_iovec_record_protocol instance, i.e., compute a protected frame. The
+ * caller needs to allocate the memory for the protected frame prior to calling
+ * this method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - unprotected_vec: an iovec array containing unprotected data.
+ * - unprotected_vec_length: the array length of unprotected_vec.
+ * - protected_frame: an iovec containing the output protected frame.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t protected_frame,
+    char** error_details);
+
+/**
+ * This method performs privacy-integrity unprotect operation on a
+ * alts_iovec_record_protocol instance given a full protected frame, i.e.,
+ * compute the unprotected data. The caller needs to allocated the memory for
+ * the unprotected data prior to calling this method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - header: an iovec containing the frame header.
+ * - protected_vec: an iovec array containing protected data including the tag.
+ * - protected_vec_length: the array length of protected_vec.
+ * - unprotected_data: an iovec containing the output unprotected data.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
+    alts_iovec_record_protocol* rp, iovec_t header,
+    const iovec_t* protected_vec, size_t protected_vec_length,
+    iovec_t unprotected_data, char** error_details);
+
+/**
+ * This method creates an alts_iovec_record_protocol instance, given a
+ * gsec_aead_crypter instance, a flag indicating if the created instance will be
+ * used at the client or server side, and a flag indicating if the created
+ * instance will be used for integrity-only mode or privacy-integrity mode. The
+ * ownership of gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - overflow_size: overflow size of counter in bytes.
+ * - is_client: a flag indicating if the alts_iovec_record_protocol instance
+ *   will be used at the client or server side.
+ * - is_integrity_only: a flag indicating if the alts_iovec_record_protocol
+ *   instance will be used for integrity-only or privacy-integrity mode.
+ * - is_protect: a flag indicating if the alts_grpc_record_protocol instance
+ *   will be used for protect or unprotect.
+ * - rp: an alts_iovec_record_protocol instance to be returned from
+ *   the method.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
+    char** error_details);
+
+/**
+ * This method destroys an alts_iovec_record_protocol instance by de-allocating
+ * all of its occupied memory. A gsec_aead_crypter instance passed in at
+ * gsec_alts_crypter instance creation time will be destroyed in this method.
+ */
+void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H \
+        */
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
new file mode 100644
index 0000000..8c76496
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
@@ -0,0 +1,295 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+constexpr size_t kMinFrameLength = 1024;
+constexpr size_t kDefaultFrameLength = 16 * 1024;
+constexpr size_t kMaxFrameLength = 1024 * 1024;
+
+/**
+ * Main struct for alts_zero_copy_grpc_protector.
+ * We choose to have two alts_grpc_record_protocol objects and two sets of slice
+ * buffers: one for protect and the other for unprotect, so that protect and
+ * unprotect can be executed in parallel. Implementations of this object must be
+ * thread compatible.
+ */
+typedef struct alts_zero_copy_grpc_protector {
+  tsi_zero_copy_grpc_protector base;
+  alts_grpc_record_protocol* record_protocol;
+  alts_grpc_record_protocol* unrecord_protocol;
+  size_t max_protected_frame_size;
+  size_t max_unprotected_data_size;
+  grpc_slice_buffer unprotected_staging_sb;
+  grpc_slice_buffer protected_sb;
+  grpc_slice_buffer protected_staging_sb;
+  uint32_t parsed_frame_size;
+} alts_zero_copy_grpc_protector;
+
+/**
+ * Given a slice buffer, parses the first 4 bytes little-endian unsigned frame
+ * size and returns the total frame size including the frame field. Caller
+ * needs to make sure the input slice buffer has at least 4 bytes. Returns true
+ * on success and false on failure.
+ */
+static bool read_frame_size(const grpc_slice_buffer* sb,
+                            uint32_t* total_frame_size) {
+  if (sb == nullptr || sb->length < kZeroCopyFrameLengthFieldSize) {
+    return false;
+  }
+  uint8_t frame_size_buffer[kZeroCopyFrameLengthFieldSize];
+  uint8_t* buf = frame_size_buffer;
+  /* Copies the first 4 bytes to a temporary buffer.  */
+  size_t remaining = kZeroCopyFrameLengthFieldSize;
+  for (size_t i = 0; i < sb->count; i++) {
+    size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]);
+    if (remaining <= slice_length) {
+      memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining);
+      remaining = 0;
+      break;
+    } else {
+      memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length);
+      buf += slice_length;
+      remaining -= slice_length;
+    }
+  }
+  GPR_ASSERT(remaining == 0);
+  /* Gets little-endian frame size.  */
+  uint32_t frame_size = (((uint32_t)frame_size_buffer[3]) << 24) |
+                        (((uint32_t)frame_size_buffer[2]) << 16) |
+                        (((uint32_t)frame_size_buffer[1]) << 8) |
+                        ((uint32_t)frame_size_buffer[0]);
+  if (frame_size > kMaxFrameLength) {
+    gpr_log(GPR_ERROR, "Frame size is larger than maximum frame size");
+    return false;
+  }
+  /* Returns frame size including frame length field.  */
+  *total_frame_size =
+      static_cast<uint32_t>(frame_size + kZeroCopyFrameLengthFieldSize);
+  return true;
+}
+
+/**
+ * Creates an alts_grpc_record_protocol object, given key, key size, and flags
+ * to indicate whether the record_protocol object uses the rekeying AEAD,
+ * whether the object is for client or server, whether the object is for
+ * integrity-only or privacy-integrity mode, and whether the object is is used
+ * for protect or unprotect.
+ */
+static tsi_result create_alts_grpc_record_protocol(
+    const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+    bool is_integrity_only, bool is_protect,
+    alts_grpc_record_protocol** record_protocol) {
+  if (key == nullptr || record_protocol == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_status_code status;
+  gsec_aead_crypter* crypter = nullptr;
+  char* error_details = nullptr;
+  status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
+                                            kAesGcmTagLength, is_rekey,
+                                            &crypter, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to create AEAD crypter, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  size_t overflow_limit = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
+                                   : kAltsRecordProtocolFrameLimit;
+  /* Creates alts_grpc_record_protocol with AEAD crypter ownership transferred.
+   */
+  tsi_result result =
+      is_integrity_only
+          ? alts_grpc_integrity_only_record_protocol_create(
+                crypter, overflow_limit, is_client, is_protect, record_protocol)
+          : alts_grpc_privacy_integrity_record_protocol_create(
+                crypter, overflow_limit, is_client, is_protect,
+                record_protocol);
+  if (result != TSI_OK) {
+    gsec_aead_crypter_destroy(crypter);
+    return result;
+  }
+  return TSI_OK;
+}
+
+/* --- tsi_zero_copy_grpc_protector methods implementation. --- */
+
+static tsi_result alts_zero_copy_grpc_protector_protect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_zero_copy_grpc_protector* protector =
+      reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+  /* Calls alts_grpc_record_protocol protect repeatly.  */
+  while (unprotected_slices->length > protector->max_unprotected_data_size) {
+    grpc_slice_buffer_move_first(unprotected_slices,
+                                 protector->max_unprotected_data_size,
+                                 &protector->unprotected_staging_sb);
+    tsi_result status = alts_grpc_record_protocol_protect(
+        protector->record_protocol, &protector->unprotected_staging_sb,
+        protected_slices);
+    if (status != TSI_OK) {
+      return status;
+    }
+  }
+  return alts_grpc_record_protocol_protect(
+      protector->record_protocol, unprotected_slices, protected_slices);
+}
+
+static tsi_result alts_zero_copy_grpc_protector_unprotect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to zero-copy grpc unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_zero_copy_grpc_protector* protector =
+      reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+  grpc_slice_buffer_move_into(protected_slices, &protector->protected_sb);
+  /* Keep unprotecting each frame if possible.  */
+  while (protector->protected_sb.length >= kZeroCopyFrameLengthFieldSize) {
+    if (protector->parsed_frame_size == 0) {
+      /* We have not parsed frame size yet. Parses frame size.  */
+      if (!read_frame_size(&protector->protected_sb,
+                           &protector->parsed_frame_size)) {
+        grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
+        return TSI_DATA_CORRUPTED;
+      }
+    }
+    if (protector->protected_sb.length < protector->parsed_frame_size) break;
+    /* At this point, protected_sb contains at least one frame of data.  */
+    tsi_result status;
+    if (protector->protected_sb.length == protector->parsed_frame_size) {
+      status = alts_grpc_record_protocol_unprotect(protector->unrecord_protocol,
+                                                   &protector->protected_sb,
+                                                   unprotected_slices);
+    } else {
+      grpc_slice_buffer_move_first(&protector->protected_sb,
+                                   protector->parsed_frame_size,
+                                   &protector->protected_staging_sb);
+      status = alts_grpc_record_protocol_unprotect(
+          protector->unrecord_protocol, &protector->protected_staging_sb,
+          unprotected_slices);
+    }
+    protector->parsed_frame_size = 0;
+    if (status != TSI_OK) {
+      grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
+      return status;
+    }
+  }
+  return TSI_OK;
+}
+
+static void alts_zero_copy_grpc_protector_destroy(
+    tsi_zero_copy_grpc_protector* self) {
+  if (self == nullptr) {
+    return;
+  }
+  alts_zero_copy_grpc_protector* protector =
+      reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+  alts_grpc_record_protocol_destroy(protector->record_protocol);
+  alts_grpc_record_protocol_destroy(protector->unrecord_protocol);
+  grpc_slice_buffer_destroy_internal(&protector->unprotected_staging_sb);
+  grpc_slice_buffer_destroy_internal(&protector->protected_sb);
+  grpc_slice_buffer_destroy_internal(&protector->protected_staging_sb);
+  gpr_free(protector);
+}
+
+static const tsi_zero_copy_grpc_protector_vtable
+    alts_zero_copy_grpc_protector_vtable = {
+        alts_zero_copy_grpc_protector_protect,
+        alts_zero_copy_grpc_protector_unprotect,
+        alts_zero_copy_grpc_protector_destroy};
+
+tsi_result alts_zero_copy_grpc_protector_create(
+    const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+    bool is_integrity_only, size_t* max_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  if (grpc_core::ExecCtx::Get() == nullptr || key == nullptr ||
+      protector == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to alts_zero_copy_grpc_protector create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Creates alts_zero_copy_protector.  */
+  alts_zero_copy_grpc_protector* impl =
+      static_cast<alts_zero_copy_grpc_protector*>(
+          gpr_zalloc(sizeof(alts_zero_copy_grpc_protector)));
+  /* Creates alts_grpc_record_protocol objects.  */
+  tsi_result status = create_alts_grpc_record_protocol(
+      key, key_size, is_rekey, is_client, is_integrity_only,
+      /*is_protect=*/true, &impl->record_protocol);
+  if (status == TSI_OK) {
+    status = create_alts_grpc_record_protocol(
+        key, key_size, is_rekey, is_client, is_integrity_only,
+        /*is_protect=*/false, &impl->unrecord_protocol);
+    if (status == TSI_OK) {
+      /* Sets maximum frame size.  */
+      size_t max_protected_frame_size_to_set = kDefaultFrameLength;
+      if (max_protected_frame_size != nullptr) {
+        *max_protected_frame_size =
+            GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
+        *max_protected_frame_size =
+            GPR_MAX(*max_protected_frame_size, kMinFrameLength);
+        max_protected_frame_size_to_set = *max_protected_frame_size;
+      }
+      impl->max_protected_frame_size = max_protected_frame_size_to_set;
+      impl->max_unprotected_data_size =
+          alts_grpc_record_protocol_max_unprotected_data_size(
+              impl->record_protocol, max_protected_frame_size_to_set);
+      GPR_ASSERT(impl->max_unprotected_data_size > 0);
+      /* Allocates internal slice buffers.  */
+      grpc_slice_buffer_init(&impl->unprotected_staging_sb);
+      grpc_slice_buffer_init(&impl->protected_sb);
+      grpc_slice_buffer_init(&impl->protected_staging_sb);
+      impl->parsed_frame_size = 0;
+      impl->base.vtable = &alts_zero_copy_grpc_protector_vtable;
+      *protector = &impl->base;
+      return TSI_OK;
+    }
+  }
+
+  /* Cleanup if create failed.  */
+  alts_grpc_record_protocol_destroy(impl->record_protocol);
+  alts_grpc_record_protocol_destroy(impl->unrecord_protocol);
+  gpr_free(impl);
+  return TSI_INTERNAL_ERROR;
+}
diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
new file mode 100644
index 0000000..71e953c
--- /dev/null
+++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/transport_security_grpc.h"
+
+/**
+ * This method creates an ALTS zero-copy grpc protector.
+ *
+ * - key: a symmetric key used to seal/unseal frames.
+ * - key_size: the size of symmetric key.
+ * - is_rekey: use rekeying AEAD crypter.
+ * - is_client: a flag indicating if the protector will be used at client or
+ *   server side.
+ * - is_integrity_only: a flag indicating if the protector instance will be
+ *   used for integrity-only or privacy-integrity mode.
+ * - max_protected_frame_size: an in/out parameter indicating max frame size
+ *   to be used by the protector. If it is nullptr, the default frame size will
+ *   be used. Otherwise, the provided frame size will be adjusted (if not
+ *   falling into a valid frame range) and used.
+ * - protector: a pointer to the zero-copy protector returned from the method.
+ *
+ * This method returns TSI_OK on success or a specific error code otherwise.
+ */
+tsi_result alts_zero_copy_grpc_protector_create(
+    const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+    bool is_integrity_only, size_t* max_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H \
+        */
diff --git a/src/core/tsi/alts_transport_security.cc b/src/core/tsi/alts_transport_security.cc
index 1654d89..2fd4081 100644
--- a/src/core/tsi/alts_transport_security.cc
+++ b/src/core/tsi/alts_transport_security.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/alts_transport_security.h"
 
 #include <string.h>
@@ -54,7 +56,7 @@
     grpc_tsi_alts_wait_for_cq_drain();
     grpc_completion_queue_destroy(g_alts_resource.cq);
     grpc_channel_destroy(g_alts_resource.channel);
-    gpr_thd_join(g_alts_resource.thread_id);
+    g_alts_resource.thread.Join();
   }
   gpr_cv_destroy(&g_alts_resource.cv);
   gpr_mu_destroy(&g_alts_resource.mu);
diff --git a/src/core/tsi/alts_transport_security.h b/src/core/tsi/alts_transport_security.h
index 37febd1..d6b8e11 100644
--- a/src/core/tsi/alts_transport_security.h
+++ b/src/core/tsi/alts_transport_security.h
@@ -19,13 +19,15 @@
 #ifndef GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H
 #define GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/grpc.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 
 typedef struct alts_shared_resource {
-  gpr_thd_id thread_id;
+  grpc_core::Thread thread;
   grpc_channel* channel;
   grpc_completion_queue* cq;
   gpr_mu mu;
diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc
index b5b7203..ad08b50 100644
--- a/src/core/tsi/fake_transport_security.cc
+++ b/src/core/tsi/fake_transport_security.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/fake_transport_security.h"
 
 #include <stdlib.h>
@@ -23,7 +25,6 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/slice/slice_internal.h"
diff --git a/src/core/tsi/fake_transport_security.h b/src/core/tsi/fake_transport_security.h
index 3848e7c..3779182 100644
--- a/src/core/tsi/fake_transport_security.h
+++ b/src/core/tsi/fake_transport_security.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H
 #define GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/transport_security_interface.h"
 
 /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */
diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc
index 29fd59a..971170b 100644
--- a/src/core/tsi/ssl_transport_security.cc
+++ b/src/core/tsi/ssl_transport_security.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include "src/core/tsi/ssl_transport_security.h"
-
 #include <grpc/support/port_platform.h>
 
+#include "src/core/tsi/ssl_transport_security.h"
+
 #include <limits.h>
 #include <string.h>
 
diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h
index bf211e1..edebadc 100644
--- a/src/core/tsi/ssl_transport_security.h
+++ b/src/core/tsi/ssl_transport_security.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H
 #define GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/transport_security_interface.h"
 
 /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */
diff --git a/src/core/tsi/ssl_types.h b/src/core/tsi/ssl_types.h
index 3788643..b15d02b 100644
--- a/src/core/tsi/ssl_types.h
+++ b/src/core/tsi/ssl_types.h
@@ -27,6 +27,8 @@
  *                      function
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <openssl/ssl.h>
 
 #ifdef OPENSSL_IS_BORINGSSL
diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc
index 0c8e3e9..129533f 100644
--- a/src/core/tsi/transport_security.cc
+++ b/src/core/tsi/transport_security.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/transport_security.h"
 
 #include <grpc/support/alloc.h>
diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h
index ed662d4..b1ec82d 100644
--- a/src/core/tsi/transport_security.h
+++ b/src/core/tsi/transport_security.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_H
 #define GRPC_CORE_TSI_TRANSPORT_SECURITY_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdbool.h>
 
 #include "src/core/lib/debug/trace.h"
diff --git a/src/core/tsi/transport_security_adapter.cc b/src/core/tsi/transport_security_adapter.cc
index 5f094b3..25608f0 100644
--- a/src/core/tsi/transport_security_adapter.cc
+++ b/src/core/tsi/transport_security_adapter.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/transport_security_adapter.h"
 
 #include <string.h>
diff --git a/src/core/tsi/transport_security_adapter.h b/src/core/tsi/transport_security_adapter.h
index 9818fce..f83ecc5 100644
--- a/src/core/tsi/transport_security_adapter.h
+++ b/src/core/tsi/transport_security_adapter.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H
 #define GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/transport_security_interface.h"
 
 /* Create a tsi handshaker that takes an implementation of old interface and
diff --git a/src/core/tsi/transport_security_grpc.cc b/src/core/tsi/transport_security_grpc.cc
index 76f7ae7..c73a6e3 100644
--- a/src/core/tsi/transport_security_grpc.cc
+++ b/src/core/tsi/transport_security_grpc.cc
@@ -16,6 +16,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include "src/core/tsi/transport_security_grpc.h"
 
 /* This method creates a tsi_zero_copy_grpc_protector object.  */
diff --git a/src/core/tsi/transport_security_grpc.h b/src/core/tsi/transport_security_grpc.h
index 0156ff1..d3bb04d 100644
--- a/src/core/tsi/transport_security_grpc.h
+++ b/src/core/tsi/transport_security_grpc.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H
 #define GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H
 
+#include <grpc/support/port_platform.h>
+
 #include <grpc/slice_buffer.h>
 #include "src/core/tsi/transport_security.h"
 
diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h
index e925598..8c10866 100644
--- a/src/core/tsi/transport_security_interface.h
+++ b/src/core/tsi/transport_security_interface.h
@@ -19,6 +19,8 @@
 #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H
 #define GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H
 
+#include <grpc/support/port_platform.h>
+
 #include <stdint.h>
 #include <stdlib.h>
 
diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc
index dcfac2a..867f31f 100644
--- a/src/cpp/client/channel_cc.cc
+++ b/src/cpp/client/channel_cc.cc
@@ -16,34 +16,33 @@
  *
  */
 
-#include <grpc++/channel.h>
+#include <grpcpp/channel.h>
 
 #include <chrono>
 #include <condition_variable>
 #include <memory>
 #include <mutex>
 
-#include <grpc++/client_context.h>
-#include <grpc++/completion_queue.h>
-#include <grpc++/impl/call.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/impl/rpc_method.h>
-#include <grpc++/security/credentials.h>
-#include <grpc++/support/channel_arguments.h>
-#include <grpc++/support/config.h>
-#include <grpc++/support/status.h>
-#include <grpc++/support/time.h>
 #include <grpc/grpc.h>
 #include <grpc/slice.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
-
+#include <grpcpp/client_context.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/impl/rpc_method.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/time.h>
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/profiling/timers.h"
 
 namespace grpc {
diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc
index 5c90838..07a04e4 100644
--- a/src/cpp/client/client_context.cc
+++ b/src/cpp/client/client_context.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/client_context.h>
+#include <grpcpp/client_context.h>
 
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
@@ -24,10 +24,10 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/security/credentials.h>
-#include <grpc++/server_context.h>
-#include <grpc++/support/time.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/support/time.h>
 
 namespace grpc {
 
diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc
index 9323315..67a46ce 100644
--- a/src/cpp/client/create_channel.cc
+++ b/src/cpp/client/create_channel.cc
@@ -18,10 +18,10 @@
 
 #include <memory>
 
-#include <grpc++/channel.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/support/channel_arguments.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/channel_arguments.h>
 
 #include "src/cpp/client/create_channel_internal.h"
 
diff --git a/src/cpp/client/create_channel_internal.cc b/src/cpp/client/create_channel_internal.cc
index 89e2f8d..aa96edfc 100644
--- a/src/cpp/client/create_channel_internal.cc
+++ b/src/cpp/client/create_channel_internal.cc
@@ -18,7 +18,7 @@
 
 #include <memory>
 
-#include <grpc++/channel.h>
+#include <grpcpp/channel.h>
 
 struct grpc_channel;
 
diff --git a/src/cpp/client/create_channel_internal.h b/src/cpp/client/create_channel_internal.h
index 2cb3163..86e8167 100644
--- a/src/cpp/client/create_channel_internal.h
+++ b/src/cpp/client/create_channel_internal.h
@@ -21,7 +21,7 @@
 
 #include <memory>
 
-#include <grpc++/support/config.h>
+#include <grpcpp/support/config.h>
 
 struct grpc_channel;
 
diff --git a/src/cpp/client/create_channel_posix.cc b/src/cpp/client/create_channel_posix.cc
index cea002f..f9285c9 100644
--- a/src/cpp/client/create_channel_posix.cc
+++ b/src/cpp/client/create_channel_posix.cc
@@ -16,11 +16,11 @@
  *
  */
 
-#include <grpc++/channel.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/impl/grpc_library.h>
 #include <grpc/grpc.h>
 #include <grpc/grpc_posix.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/impl/grpc_library.h>
 
 #include "src/cpp/client/create_channel_internal.h"
 
diff --git a/src/cpp/client/credentials_cc.cc b/src/cpp/client/credentials_cc.cc
index 8d69242..2a0f06f 100644
--- a/src/cpp/client/credentials_cc.cc
+++ b/src/cpp/client/credentials_cc.cc
@@ -16,8 +16,8 @@
  *
  */
 
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/security/credentials.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/security/credentials.h>
 
 namespace grpc {
 
diff --git a/src/cpp/client/cronet_credentials.cc b/src/cpp/client/cronet_credentials.cc
index a874a6e..5c65ad0 100644
--- a/src/cpp/client/cronet_credentials.cc
+++ b/src/cpp/client/cronet_credentials.cc
@@ -16,11 +16,11 @@
  *
  */
 
-#include <grpc++/security/credentials.h>
+#include <grpcpp/security/credentials.h>
 
-#include <grpc++/channel.h>
-#include <grpc++/support/channel_arguments.h>
 #include <grpc/grpc_cronet.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/support/channel_arguments.h>
 #include "src/cpp/client/create_channel_internal.h"
 
 namespace grpc {
diff --git a/src/cpp/client/generic_stub.cc b/src/cpp/client/generic_stub.cc
index 4b4b8dd..67ef46b 100644
--- a/src/cpp/client/generic_stub.cc
+++ b/src/cpp/client/generic_stub.cc
@@ -16,9 +16,9 @@
  *
  */
 
-#include <grpc++/generic/generic_stub.h>
+#include <grpcpp/generic/generic_stub.h>
 
-#include <grpc++/impl/rpc_method.h>
+#include <grpcpp/impl/rpc_method.h>
 
 namespace grpc {
 
diff --git a/src/cpp/client/insecure_credentials.cc b/src/cpp/client/insecure_credentials.cc
index 2112844..04dc5c0 100644
--- a/src/cpp/client/insecure_credentials.cc
+++ b/src/cpp/client/insecure_credentials.cc
@@ -16,13 +16,13 @@
  *
  */
 
-#include <grpc++/security/credentials.h>
+#include <grpcpp/security/credentials.h>
 
-#include <grpc++/channel.h>
-#include <grpc++/support/channel_arguments.h>
-#include <grpc++/support/config.h>
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
 #include "src/cpp/client/create_channel_internal.h"
 
 namespace grpc {
diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc
index 4fb128d..19d67c2 100644
--- a/src/cpp/client/secure_credentials.cc
+++ b/src/cpp/client/secure_credentials.cc
@@ -17,11 +17,11 @@
  */
 
 #include "src/cpp/client/secure_credentials.h"
-#include <grpc++/channel.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/support/channel_arguments.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/channel_arguments.h>
 #include "src/cpp/client/create_channel_internal.h"
 #include "src/cpp/common/secure_auth_context.h"
 
@@ -168,7 +168,7 @@
 void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) {
   if (wrapper == nullptr) return;
   MetadataCredentialsPluginWrapper* w =
-      reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper);
+      static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
   delete w;
 }
 
@@ -180,7 +180,7 @@
     const char** error_details) {
   GPR_ASSERT(wrapper);
   MetadataCredentialsPluginWrapper* w =
-      reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper);
+      static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
   if (!w->plugin_) {
     *num_creds_md = 0;
     *status = GRPC_STATUS_OK;
diff --git a/src/cpp/client/secure_credentials.h b/src/cpp/client/secure_credentials.h
index ed9afb3..85cb542 100644
--- a/src/cpp/client/secure_credentials.h
+++ b/src/cpp/client/secure_credentials.h
@@ -21,8 +21,8 @@
 
 #include <grpc/grpc_security.h>
 
-#include <grpc++/security/credentials.h>
-#include <grpc++/support/config.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/config.h>
 
 #include "src/cpp/server/thread_pool_interface.h"
 
diff --git a/src/cpp/codegen/codegen_init.cc b/src/cpp/codegen/codegen_init.cc
index 2da1556..684d721 100644
--- a/src/cpp/codegen/codegen_init.cc
+++ b/src/cpp/codegen/codegen_init.cc
@@ -16,8 +16,8 @@
  *
  */
 
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
 
 /// Null-initializes the global gRPC variables for the codegen library. These
 /// stay null in the absence of of grpc++ library. In this case, no gRPC
diff --git a/src/cpp/common/alarm.cc b/src/cpp/common/alarm.cc
index 0eecbb6..15a373d 100644
--- a/src/cpp/common/alarm.cc
+++ b/src/cpp/common/alarm.cc
@@ -15,15 +15,15 @@
  *
  */
 
-#include <grpc++/alarm.h>
+#include <grpcpp/alarm.h>
 
 #include <memory>
 
-#include <grpc++/completion_queue.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/support/time.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/time.h>
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/surface/completion_queue.h"
diff --git a/src/cpp/common/auth_property_iterator.cc b/src/cpp/common/auth_property_iterator.cc
index 4f0948c..fbb18e9 100644
--- a/src/cpp/common/auth_property_iterator.cc
+++ b/src/cpp/common/auth_property_iterator.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/security/auth_context.h>
+#include <grpcpp/security/auth_context.h>
 
 #include <grpc/grpc_security.h>
 
diff --git a/src/cpp/common/channel_arguments.cc b/src/cpp/common/channel_arguments.cc
index b696774..50ee9d8 100644
--- a/src/cpp/common/channel_arguments.cc
+++ b/src/cpp/common/channel_arguments.cc
@@ -15,14 +15,14 @@
  * limitations under the License.
  *
  */
-#include <grpc++/support/channel_arguments.h>
+#include <grpcpp/support/channel_arguments.h>
 
 #include <sstream>
 
-#include <grpc++/grpc++.h>
-#include <grpc++/resource_quota.h>
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/log.h>
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/resource_quota.h>
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/socket_mutator.h"
diff --git a/src/cpp/common/channel_filter.cc b/src/cpp/common/channel_filter.cc
index 874f31b..422e7bb 100644
--- a/src/cpp/common/channel_filter.cc
+++ b/src/cpp/common/channel_filter.cc
@@ -21,7 +21,7 @@
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/cpp/common/channel_filter.h"
 
-#include <grpc++/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/slice.h>
 
 namespace grpc {
 
diff --git a/src/cpp/common/channel_filter.h b/src/cpp/common/channel_filter.h
index a1d2208..60eefcb 100644
--- a/src/cpp/common/channel_filter.h
+++ b/src/cpp/common/channel_filter.h
@@ -19,9 +19,9 @@
 #ifndef GRPCXX_CHANNEL_FILTER_H
 #define GRPCXX_CHANNEL_FILTER_H
 
-#include <grpc++/impl/codegen/config.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
+#include <grpcpp/impl/codegen/config.h>
 
 #include <functional>
 #include <vector>
@@ -283,7 +283,7 @@
 
   static void DestroyChannelElement(grpc_channel_element* elem) {
     ChannelDataType* channel_data =
-        reinterpret_cast<ChannelDataType*>(elem->channel_data);
+        static_cast<ChannelDataType*>(elem->channel_data);
     channel_data->Destroy(elem);
     channel_data->~ChannelDataType();
   }
@@ -291,7 +291,7 @@
   static void StartTransportOp(grpc_channel_element* elem,
                                grpc_transport_op* op) {
     ChannelDataType* channel_data =
-        reinterpret_cast<ChannelDataType*>(elem->channel_data);
+        static_cast<ChannelDataType*>(elem->channel_data);
     TransportOp op_wrapper(op);
     channel_data->StartTransportOp(elem, &op_wrapper);
   }
@@ -299,7 +299,7 @@
   static void GetChannelInfo(grpc_channel_element* elem,
                              const grpc_channel_info* channel_info) {
     ChannelDataType* channel_data =
-        reinterpret_cast<ChannelDataType*>(elem->channel_data);
+        static_cast<ChannelDataType*>(elem->channel_data);
     channel_data->GetInfo(elem, channel_info);
   }
 
@@ -315,21 +315,21 @@
   static void DestroyCallElement(grpc_call_element* elem,
                                  const grpc_call_final_info* final_info,
                                  grpc_closure* then_call_closure) {
-    CallDataType* call_data = reinterpret_cast<CallDataType*>(elem->call_data);
+    CallDataType* call_data = static_cast<CallDataType*>(elem->call_data);
     call_data->Destroy(elem, final_info, then_call_closure);
     call_data->~CallDataType();
   }
 
   static void StartTransportStreamOpBatch(grpc_call_element* elem,
                                           grpc_transport_stream_op_batch* op) {
-    CallDataType* call_data = reinterpret_cast<CallDataType*>(elem->call_data);
+    CallDataType* call_data = static_cast<CallDataType*>(elem->call_data);
     TransportStreamOpBatch op_wrapper(op);
     call_data->StartTransportStreamOpBatch(elem, &op_wrapper);
   }
 
   static void SetPollsetOrPollsetSet(grpc_call_element* elem,
                                      grpc_polling_entity* pollent) {
-    CallDataType* call_data = reinterpret_cast<CallDataType*>(elem->call_data);
+    CallDataType* call_data = static_cast<CallDataType*>(elem->call_data);
     call_data->SetPollsetOrPollsetSet(elem, pollent);
   }
 };
diff --git a/src/cpp/common/completion_queue_cc.cc b/src/cpp/common/completion_queue_cc.cc
index eb6dc8c..6893201 100644
--- a/src/cpp/common/completion_queue_cc.cc
+++ b/src/cpp/common/completion_queue_cc.cc
@@ -15,14 +15,14 @@
  *
  */
 
-#include <grpc++/completion_queue.h>
+#include <grpcpp/completion_queue.h>
 
 #include <memory>
 
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/support/time.h>
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/time.h>
 
 namespace grpc {
 
diff --git a/src/cpp/common/core_codegen.cc b/src/cpp/common/core_codegen.cc
index 936d699..aa9788d 100644
--- a/src/cpp/common/core_codegen.cc
+++ b/src/cpp/common/core_codegen.cc
@@ -16,11 +16,10 @@
  *
  */
 
-#include <grpc++/impl/codegen/core_codegen.h>
+#include <grpcpp/impl/codegen/core_codegen.h>
 
 #include <stdlib.h>
 
-#include <grpc++/support/config.h>
 #include <grpc/byte_buffer.h>
 #include <grpc/byte_buffer_reader.h>
 #include <grpc/grpc.h>
@@ -30,6 +29,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 #include <grpc/support/sync.h>
+#include <grpcpp/support/config.h>
 
 #include "src/core/lib/profiling/timers.h"
 
diff --git a/src/cpp/common/insecure_create_auth_context.cc b/src/cpp/common/insecure_create_auth_context.cc
index 28de47b..4e5cbd0 100644
--- a/src/cpp/common/insecure_create_auth_context.cc
+++ b/src/cpp/common/insecure_create_auth_context.cc
@@ -17,8 +17,8 @@
  */
 #include <memory>
 
-#include <grpc++/security/auth_context.h>
 #include <grpc/grpc.h>
+#include <grpcpp/security/auth_context.h>
 
 namespace grpc {
 
diff --git a/src/cpp/common/resource_quota_cc.cc b/src/cpp/common/resource_quota_cc.cc
index 0e9be15..daeb0ba 100644
--- a/src/cpp/common/resource_quota_cc.cc
+++ b/src/cpp/common/resource_quota_cc.cc
@@ -16,8 +16,8 @@
  *
  */
 
-#include <grpc++/resource_quota.h>
 #include <grpc/grpc.h>
+#include <grpcpp/resource_quota.h>
 
 namespace grpc {
 
diff --git a/src/cpp/common/rpc_method.cc b/src/cpp/common/rpc_method.cc
index d00339a..a47dd3e 100644
--- a/src/cpp/common/rpc_method.cc
+++ b/src/cpp/common/rpc_method.cc
@@ -16,6 +16,6 @@
  *
  */
 
-#include <grpc++/impl/rpc_method.h>
+#include <grpcpp/impl/rpc_method.h>
 
 namespace grpc {}  // namespace grpc
diff --git a/src/cpp/common/secure_auth_context.h b/src/cpp/common/secure_auth_context.h
index 0aea642..1426179 100644
--- a/src/cpp/common/secure_auth_context.h
+++ b/src/cpp/common/secure_auth_context.h
@@ -19,7 +19,7 @@
 #ifndef GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
 #define GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
 
-#include <grpc++/security/auth_context.h>
+#include <grpcpp/security/auth_context.h>
 
 struct grpc_auth_context;
 
diff --git a/src/cpp/common/secure_channel_arguments.cc b/src/cpp/common/secure_channel_arguments.cc
index f6cd584..2fb8ea4 100644
--- a/src/cpp/common/secure_channel_arguments.cc
+++ b/src/cpp/common/secure_channel_arguments.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/support/channel_arguments.h>
+#include <grpcpp/support/channel_arguments.h>
 
 #include <grpc/grpc_security.h>
 #include "src/core/lib/channel/channel_args.h"
diff --git a/src/cpp/common/secure_create_auth_context.cc b/src/cpp/common/secure_create_auth_context.cc
index 4b9e39a..bc1387c 100644
--- a/src/cpp/common/secure_create_auth_context.cc
+++ b/src/cpp/common/secure_create_auth_context.cc
@@ -17,9 +17,9 @@
  */
 #include <memory>
 
-#include <grpc++/security/auth_context.h>
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
+#include <grpcpp/security/auth_context.h>
 #include "src/cpp/common/secure_auth_context.h"
 
 namespace grpc {
diff --git a/src/cpp/common/version_cc.cc b/src/cpp/common/version_cc.cc
index 8bc9260..fb1723c 100644
--- a/src/cpp/common/version_cc.cc
+++ b/src/cpp/common/version_cc.cc
@@ -19,8 +19,8 @@
 /* This file is autogenerated from:
    templates/src/core/surface/version.c.template */
 
-#include <grpc++/grpc++.h>
+#include <grpcpp/grpcpp.h>
 
 namespace grpc {
-grpc::string Version() { return "1.10.0-dev"; }
+grpc::string Version() { return "1.11.0-dev"; }
 }  // namespace grpc
diff --git a/src/cpp/ext/proto_server_reflection.cc b/src/cpp/ext/proto_server_reflection.cc
index e612211..f26f007 100644
--- a/src/cpp/ext/proto_server_reflection.cc
+++ b/src/cpp/ext/proto_server_reflection.cc
@@ -19,7 +19,7 @@
 #include <unordered_set>
 #include <vector>
 
-#include <grpc++/grpc++.h>
+#include <grpcpp/grpcpp.h>
 
 #include "src/cpp/ext/proto_server_reflection.h"
 
diff --git a/src/cpp/ext/proto_server_reflection.h b/src/cpp/ext/proto_server_reflection.h
index ee2327c..bf40c3c 100644
--- a/src/cpp/ext/proto_server_reflection.h
+++ b/src/cpp/ext/proto_server_reflection.h
@@ -22,7 +22,7 @@
 #include <unordered_set>
 #include <vector>
 
-#include <grpc++/grpc++.h>
+#include <grpcpp/grpcpp.h>
 #include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h"
 
 namespace grpc {
diff --git a/src/cpp/ext/proto_server_reflection_plugin.cc b/src/cpp/ext/proto_server_reflection_plugin.cc
index c299661..ee3ac3f 100644
--- a/src/cpp/ext/proto_server_reflection_plugin.cc
+++ b/src/cpp/ext/proto_server_reflection_plugin.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include <grpc++/ext/proto_server_reflection_plugin.h>
-#include <grpc++/impl/server_builder_plugin.h>
-#include <grpc++/impl/server_initializer.h>
-#include <grpc++/server.h>
+#include <grpcpp/ext/proto_server_reflection_plugin.h>
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/impl/server_initializer.h>
+#include <grpcpp/server.h>
 
 #include "src/cpp/ext/proto_server_reflection.h"
 
diff --git a/src/cpp/server/async_generic_service.cc b/src/cpp/server/async_generic_service.cc
index 998f516..3061376 100644
--- a/src/cpp/server/async_generic_service.cc
+++ b/src/cpp/server/async_generic_service.cc
@@ -16,9 +16,9 @@
  *
  */
 
-#include <grpc++/generic/async_generic_service.h>
+#include <grpcpp/generic/async_generic_service.h>
 
-#include <grpc++/server.h>
+#include <grpcpp/server.h>
 
 namespace grpc {
 
diff --git a/src/cpp/server/channel_argument_option.cc b/src/cpp/server/channel_argument_option.cc
index dcad253..4d6be90 100644
--- a/src/cpp/server/channel_argument_option.cc
+++ b/src/cpp/server/channel_argument_option.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/impl/channel_argument_option.h>
+#include <grpcpp/impl/channel_argument_option.h>
 
 namespace grpc {
 
diff --git a/src/cpp/server/dynamic_thread_pool.cc b/src/cpp/server/dynamic_thread_pool.cc
index 81c78fe..ef99d64 100644
--- a/src/cpp/server/dynamic_thread_pool.cc
+++ b/src/cpp/server/dynamic_thread_pool.cc
@@ -19,20 +19,23 @@
 #include "src/cpp/server/dynamic_thread_pool.h"
 
 #include <mutex>
-#include <thread>
 
 #include <grpc/support/log.h>
 
+#include "src/core/lib/gprpp/thd.h"
+
 namespace grpc {
 
 DynamicThreadPool::DynamicThread::DynamicThread(DynamicThreadPool* pool)
     : pool_(pool),
-      thd_(new std::thread(&DynamicThreadPool::DynamicThread::ThreadFunc,
-                           this)) {}
-DynamicThreadPool::DynamicThread::~DynamicThread() {
-  thd_->join();
-  thd_.reset();
+      thd_("grpcpp_dynamic_pool",
+           [](void* th) {
+             static_cast<DynamicThreadPool::DynamicThread*>(th)->ThreadFunc();
+           },
+           this) {
+  thd_.Start();
 }
+DynamicThreadPool::DynamicThread::~DynamicThread() { thd_.Join(); }
 
 void DynamicThreadPool::DynamicThread::ThreadFunc() {
   pool_->ThreadFunc();
diff --git a/src/cpp/server/dynamic_thread_pool.h b/src/cpp/server/dynamic_thread_pool.h
index 9237c6e..5df8cf2 100644
--- a/src/cpp/server/dynamic_thread_pool.h
+++ b/src/cpp/server/dynamic_thread_pool.h
@@ -24,10 +24,10 @@
 #include <memory>
 #include <mutex>
 #include <queue>
-#include <thread>
 
-#include <grpc++/support/config.h>
+#include <grpcpp/support/config.h>
 
+#include "src/core/lib/gprpp/thd.h"
 #include "src/cpp/server/thread_pool_interface.h"
 
 namespace grpc {
@@ -47,7 +47,7 @@
 
    private:
     DynamicThreadPool* pool_;
-    std::unique_ptr<std::thread> thd_;
+    grpc_core::Thread thd_;
     void ThreadFunc();
   };
   std::mutex mu_;
diff --git a/src/cpp/server/health/default_health_check_service.cc b/src/cpp/server/health/default_health_check_service.cc
index 10dbd3c..0b45a8b 100644
--- a/src/cpp/server/health/default_health_check_service.cc
+++ b/src/cpp/server/health/default_health_check_service.cc
@@ -19,10 +19,10 @@
 #include <memory>
 #include <mutex>
 
-#include <grpc++/impl/codegen/method_handler_impl.h>
 #include <grpc/slice.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/method_handler_impl.h>
 
 #include "src/cpp/server/health/default_health_check_service.h"
 #include "src/cpp/server/health/health.pb.h"
diff --git a/src/cpp/server/health/default_health_check_service.h b/src/cpp/server/health/default_health_check_service.h
index 99d6680..a1ce5aa 100644
--- a/src/cpp/server/health/default_health_check_service.h
+++ b/src/cpp/server/health/default_health_check_service.h
@@ -21,9 +21,9 @@
 
 #include <mutex>
 
-#include <grpc++/health_check_service_interface.h>
-#include <grpc++/impl/codegen/service_type.h>
-#include <grpc++/support/byte_buffer.h>
+#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/support/byte_buffer.h>
 
 namespace grpc {
 
diff --git a/src/cpp/server/health/health_check_service.cc b/src/cpp/server/health/health_check_service.cc
index a34b533..a0fa2d6 100644
--- a/src/cpp/server/health/health_check_service.cc
+++ b/src/cpp/server/health/health_check_service.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/health_check_service_interface.h>
+#include <grpcpp/health_check_service_interface.h>
 
 namespace grpc {
 namespace {
diff --git a/src/cpp/server/health/health_check_service_server_builder_option.cc b/src/cpp/server/health/health_check_service_server_builder_option.cc
index a722eea..7148265 100644
--- a/src/cpp/server/health/health_check_service_server_builder_option.cc
+++ b/src/cpp/server/health/health_check_service_server_builder_option.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/ext/health_check_service_server_builder_option.h>
+#include <grpcpp/ext/health_check_service_server_builder_option.h>
 
 namespace grpc {
 
diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc
index 87605f8..7d749dd 100644
--- a/src/cpp/server/insecure_server_credentials.cc
+++ b/src/cpp/server/insecure_server_credentials.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/security/server_credentials.h>
+#include <grpcpp/security/server_credentials.h>
 
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index 0fbe4cc..1887109 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -20,8 +20,8 @@
 #include <map>
 #include <memory>
 
-#include <grpc++/impl/codegen/slice.h>
-#include <grpc++/security/auth_metadata_processor.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/security/auth_metadata_processor.h>
 
 #include "src/cpp/common/secure_auth_context.h"
 #include "src/cpp/server/secure_server_credentials.h"
@@ -29,14 +29,14 @@
 namespace grpc {
 
 void AuthMetadataProcessorAyncWrapper::Destroy(void* wrapper) {
-  auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+  auto* w = static_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
   delete w;
 }
 
 void AuthMetadataProcessorAyncWrapper::Process(
     void* wrapper, grpc_auth_context* context, const grpc_metadata* md,
     size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) {
-  auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+  auto* w = static_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
   if (!w->processor_) {
     // Early exit.
     cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr);
diff --git a/src/cpp/server/secure_server_credentials.h b/src/cpp/server/secure_server_credentials.h
index 212f0d1..8a81af2 100644
--- a/src/cpp/server/secure_server_credentials.h
+++ b/src/cpp/server/secure_server_credentials.h
@@ -21,7 +21,7 @@
 
 #include <memory>
 
-#include <grpc++/security/server_credentials.h>
+#include <grpcpp/security/server_credentials.h>
 
 #include <grpc/grpc_security.h>
 
diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc
index 790095e..e951801 100644
--- a/src/cpp/server/server_builder.cc
+++ b/src/cpp/server/server_builder.cc
@@ -16,13 +16,13 @@
  *
  */
 
-#include <grpc++/server_builder.h>
+#include <grpcpp/server_builder.h>
 
-#include <grpc++/impl/service_type.h>
-#include <grpc++/resource_quota.h>
-#include <grpc++/server.h>
 #include <grpc/support/cpu.h>
 #include <grpc/support/log.h>
+#include <grpcpp/impl/service_type.h>
+#include <grpcpp/resource_quota.h>
+#include <grpcpp/server.h>
 
 #include "src/core/lib/gpr/useful.h"
 #include "src/cpp/server/thread_pool_interface.h"
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
index 4f8f4e0..5638636 100644
--- a/src/cpp/server/server_cc.cc
+++ b/src/cpp/server/server_cc.cc
@@ -15,27 +15,27 @@
  *
  */
 
-#include <grpc++/server.h>
+#include <grpcpp/server.h>
 
 #include <cstdlib>
 #include <sstream>
 #include <utility>
 
-#include <grpc++/completion_queue.h>
-#include <grpc++/generic/async_generic_service.h>
-#include <grpc++/impl/codegen/async_unary_call.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/impl/method_handler_impl.h>
-#include <grpc++/impl/rpc_service_method.h>
-#include <grpc++/impl/server_initializer.h>
-#include <grpc++/impl/service_type.h>
-#include <grpc++/security/server_credentials.h>
-#include <grpc++/server_context.h>
-#include <grpc++/support/time.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/generic/async_generic_service.h>
+#include <grpcpp/impl/codegen/async_unary_call.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/impl/method_handler_impl.h>
+#include <grpcpp/impl/rpc_service_method.h>
+#include <grpcpp/impl/server_initializer.h>
+#include <grpcpp/impl/service_type.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/support/time.h>
 
 #include "src/core/ext/transport/inproc/inproc_transport.h"
 #include "src/core/lib/profiling/timers.h"
diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc
index 878be1c..6f5bde0 100644
--- a/src/cpp/server/server_context.cc
+++ b/src/cpp/server/server_context.cc
@@ -16,20 +16,20 @@
  *
  */
 
-#include <grpc++/server_context.h>
+#include <grpcpp/server_context.h>
 
 #include <algorithm>
 #include <mutex>
 #include <utility>
 
-#include <grpc++/completion_queue.h>
-#include <grpc++/impl/call.h>
-#include <grpc++/support/time.h>
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include <grpc/load_reporting.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/support/time.h>
 
 #include "src/core/lib/surface/call.h"
 
diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc
index 158dfa8..c3b3a8b 100644
--- a/src/cpp/server/server_credentials.cc
+++ b/src/cpp/server/server_credentials.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/security/server_credentials.h>
+#include <grpcpp/security/server_credentials.h>
 
 namespace grpc {
 
diff --git a/src/cpp/server/server_posix.cc b/src/cpp/server/server_posix.cc
index d3ef5cb..7c221ed 100644
--- a/src/cpp/server/server_posix.cc
+++ b/src/cpp/server/server_posix.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/server_posix.h>
+#include <grpcpp/server_posix.h>
 
 #include <grpc/grpc_posix.h>
 
diff --git a/src/cpp/thread_manager/thread_manager.cc b/src/cpp/thread_manager/thread_manager.cc
index 23264f1..02ac56a 100644
--- a/src/cpp/thread_manager/thread_manager.cc
+++ b/src/cpp/thread_manager/thread_manager.cc
@@ -20,18 +20,22 @@
 
 #include <climits>
 #include <mutex>
-#include <thread>
 
 #include <grpc/support/log.h>
 
+#include "src/core/lib/gprpp/thd.h"
+
 namespace grpc {
 
 ThreadManager::WorkerThread::WorkerThread(ThreadManager* thd_mgr)
     : thd_mgr_(thd_mgr) {
   // Make thread creation exclusive with respect to its join happening in
   // ~WorkerThread().
-  std::lock_guard<std::mutex> lock(wt_mu_);
-  thd_ = std::thread(&ThreadManager::WorkerThread::Run, this);
+  thd_ = grpc_core::Thread(
+      "grpcpp_sync_server",
+      [](void* th) { static_cast<ThreadManager::WorkerThread*>(th)->Run(); },
+      this);
+  thd_.Start();
 }
 
 void ThreadManager::WorkerThread::Run() {
@@ -41,8 +45,7 @@
 
 ThreadManager::WorkerThread::~WorkerThread() {
   // Don't join until the thread is fully constructed.
-  std::lock_guard<std::mutex> lock(wt_mu_);
-  thd_.join();
+  thd_.Join();
 }
 
 ThreadManager::ThreadManager(int min_pollers, int max_pollers)
diff --git a/src/cpp/thread_manager/thread_manager.h b/src/cpp/thread_manager/thread_manager.h
index a206e0b..5a40f2d 100644
--- a/src/cpp/thread_manager/thread_manager.h
+++ b/src/cpp/thread_manager/thread_manager.h
@@ -23,9 +23,10 @@
 #include <list>
 #include <memory>
 #include <mutex>
-#include <thread>
 
-#include <grpc++/support/config.h>
+#include <grpcpp/support/config.h>
+
+#include "src/core/lib/gprpp/thd.h"
 
 namespace grpc {
 
@@ -84,8 +85,8 @@
   virtual void Wait();
 
  private:
-  // Helper wrapper class around std::thread. This takes a ThreadManager object
-  // and starts a new std::thread to calls the Run() function.
+  // Helper wrapper class around grpc_core::Thread. Takes a ThreadManager object
+  // and starts a new grpc_core::Thread to calls the Run() function.
   //
   // The Run() function calls ThreadManager::MainWorkLoop() function and once
   // that completes, it marks the WorkerThread completed by calling
@@ -101,8 +102,7 @@
     void Run();
 
     ThreadManager* const thd_mgr_;
-    std::mutex wt_mu_;
-    std::thread thd_;
+    grpc_core::Thread thd_;
   };
 
   // The main funtion in ThreadManager
diff --git a/src/cpp/util/byte_buffer_cc.cc b/src/cpp/util/byte_buffer_cc.cc
index 180c813..fbc1768 100644
--- a/src/cpp/util/byte_buffer_cc.cc
+++ b/src/cpp/util/byte_buffer_cc.cc
@@ -16,10 +16,10 @@
  *
  */
 
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/support/byte_buffer.h>
 #include <grpc/byte_buffer.h>
 #include <grpc/byte_buffer_reader.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/byte_buffer.h>
 
 namespace grpc {
 
diff --git a/src/cpp/util/error_details.cc b/src/cpp/util/error_details.cc
index f06b475..42c887a 100644
--- a/src/cpp/util/error_details.cc
+++ b/src/cpp/util/error_details.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/support/error_details.h>
+#include <grpcpp/support/error_details.h>
 
 #include "src/proto/grpc/status/status.pb.h"
 
diff --git a/src/cpp/util/slice_cc.cc b/src/cpp/util/slice_cc.cc
index 3ae17e8..c72dbdb 100644
--- a/src/cpp/util/slice_cc.cc
+++ b/src/cpp/util/slice_cc.cc
@@ -16,8 +16,8 @@
  *
  */
 
-#include <grpc++/support/slice.h>
 #include <grpc/slice.h>
+#include <grpcpp/support/slice.h>
 
 namespace grpc {
 
@@ -32,15 +32,15 @@
 Slice::Slice(size_t len) : slice_(grpc_slice_malloc(len)) {}
 
 Slice::Slice(const void* buf, size_t len)
-    : slice_(grpc_slice_from_copied_buffer(reinterpret_cast<const char*>(buf),
-                                           len)) {}
+    : slice_(
+          grpc_slice_from_copied_buffer(static_cast<const char*>(buf), len)) {}
 
 Slice::Slice(const grpc::string& str)
     : slice_(grpc_slice_from_copied_buffer(str.c_str(), str.length())) {}
 
 Slice::Slice(const void* buf, size_t len, StaticSlice)
-    : slice_(grpc_slice_from_static_buffer(reinterpret_cast<const char*>(buf),
-                                           len)) {}
+    : slice_(
+          grpc_slice_from_static_buffer(static_cast<const char*>(buf), len)) {}
 
 Slice::Slice(const Slice& other) : slice_(grpc_slice_ref(other.slice_)) {}
 
diff --git a/src/cpp/util/status.cc b/src/cpp/util/status.cc
index 0f79336..93696d8 100644
--- a/src/cpp/util/status.cc
+++ b/src/cpp/util/status.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/support/status.h>
+#include <grpcpp/support/status.h>
 
 namespace grpc {
 
diff --git a/src/cpp/util/string_ref.cc b/src/cpp/util/string_ref.cc
index 30c2ab6..8b09a82 100644
--- a/src/cpp/util/string_ref.cc
+++ b/src/cpp/util/string_ref.cc
@@ -16,7 +16,7 @@
  *
  */
 
-#include <grpc++/support/string_ref.h>
+#include <grpcpp/support/string_ref.h>
 
 namespace grpc {
 
diff --git a/src/cpp/util/time_cc.cc b/src/cpp/util/time_cc.cc
index d475c25..6c9c228 100644
--- a/src/cpp/util/time_cc.cc
+++ b/src/cpp/util/time_cc.cc
@@ -16,9 +16,9 @@
  *
  */
 
-#include <grpc++/support/config.h>
-#include <grpc++/support/time.h>
 #include <grpc/support/time.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/time.h>
 
 using std::chrono::duration_cast;
 using std::chrono::high_resolution_clock;
diff --git a/src/csharp/Grpc.Core.Tests/Interceptors/ClientInterceptorTest.cs b/src/csharp/Grpc.Core.Tests/Interceptors/ClientInterceptorTest.cs
new file mode 100644
index 0000000..02f6f6f
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/Interceptors/ClientInterceptorTest.cs
@@ -0,0 +1,228 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Interceptors;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using Grpc.Core.Tests;
+using NUnit.Framework;
+
+namespace Grpc.Core.Interceptors.Tests
+{
+    public class ClientInterceptorTest
+    {
+        const string Host = "127.0.0.1";
+
+        [Test]
+        public void AddRequestHeaderInClientInterceptor()
+        {
+            const string HeaderKey = "x-client-interceptor";
+            const string HeaderValue = "hello-world";
+            var helper = new MockServiceHelper(Host);
+            helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+            {
+                var interceptorHeader = context.RequestHeaders.Last(m => (m.Key == HeaderKey)).Value;
+                Assert.AreEqual(interceptorHeader, HeaderValue);
+                return Task.FromResult("PASS");
+            });
+            var server = helper.GetServer();
+            server.Start();
+            var callInvoker = helper.GetChannel().Intercept(metadata =>
+            {
+                metadata = metadata ?? new Metadata();
+                metadata.Add(new Metadata.Entry(HeaderKey, HeaderValue));
+                return metadata;
+            });
+            Assert.AreEqual("PASS", callInvoker.BlockingUnaryCall(new Method<string, string>(MethodType.Unary, MockServiceHelper.ServiceName, "Unary", Marshallers.StringMarshaller, Marshallers.StringMarshaller), Host, new CallOptions(), ""));
+        }
+
+        [Test]
+        public void CheckInterceptorOrderInClientInterceptors()
+        {
+            var helper = new MockServiceHelper(Host);
+            helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+            {
+                return Task.FromResult("PASS");
+            });
+            var server = helper.GetServer();
+            server.Start();
+            var stringBuilder = new StringBuilder();
+            var callInvoker = helper.GetChannel().Intercept(metadata => {
+                stringBuilder.Append("interceptor1");
+                return metadata;
+            }).Intercept(new CallbackInterceptor(() => stringBuilder.Append("array1")),
+                new CallbackInterceptor(() => stringBuilder.Append("array2")),
+                new CallbackInterceptor(() => stringBuilder.Append("array3")))
+            .Intercept(metadata =>
+            {
+                stringBuilder.Append("interceptor2");
+                return metadata;
+            }).Intercept(metadata =>
+            {
+                stringBuilder.Append("interceptor3");
+                return metadata;
+            });
+            Assert.AreEqual("PASS", callInvoker.BlockingUnaryCall(new Method<string, string>(MethodType.Unary, MockServiceHelper.ServiceName, "Unary", Marshallers.StringMarshaller, Marshallers.StringMarshaller), Host, new CallOptions(), ""));
+            Assert.AreEqual("interceptor3interceptor2array1array2array3interceptor1", stringBuilder.ToString());
+        }
+
+        [Test]
+        public void CheckNullInterceptorRegistrationFails()
+        {
+            var helper = new MockServiceHelper(Host);
+            helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+            {
+                return Task.FromResult("PASS");
+            });
+            Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(default(Interceptor)));
+            Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(new[]{default(Interceptor)}));
+            Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(new[]{new CallbackInterceptor(()=>{}), null}));
+            Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(default(Interceptor[])));
+        }
+
+        [Test]
+        public async Task CountNumberOfRequestsInClientInterceptors()
+        {
+            var helper = new MockServiceHelper(Host);
+            helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
+            {
+                var stringBuilder = new StringBuilder();
+                await requestStream.ForEachAsync(request =>
+                {
+                    stringBuilder.Append(request);
+                    return TaskUtils.CompletedTask;
+                });
+                await Task.Delay(100);
+                return stringBuilder.ToString();
+            });
+
+            var callInvoker = helper.GetChannel().Intercept(new ClientStreamingCountingInterceptor());
+
+            var server = helper.GetServer();
+            server.Start();
+            var call = callInvoker.AsyncClientStreamingCall(new Method<string, string>(MethodType.ClientStreaming, MockServiceHelper.ServiceName, "ClientStreaming", Marshallers.StringMarshaller, Marshallers.StringMarshaller), Host, new CallOptions());
+            await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
+            Assert.AreEqual("3", await call.ResponseAsync);
+
+            Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
+            Assert.IsNotNull(call.GetTrailers());
+        }
+
+        private class CallbackInterceptor : Interceptor
+        {
+            readonly Action callback;
+
+            public CallbackInterceptor(Action callback)
+            {
+                this.callback = GrpcPreconditions.CheckNotNull(callback, nameof(callback));
+            }
+
+            public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
+            {
+                callback();
+                return continuation(request, context);
+            }
+
+            public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
+            {
+                callback();
+                return continuation(request, context);
+            }
+
+            public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                callback();
+                return continuation(request, context);
+            }
+
+            public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                callback();
+                return continuation(context);
+            }
+
+            public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                callback();
+                return continuation(context);
+            }
+        }
+
+        private class ClientStreamingCountingInterceptor : Interceptor
+        {
+            public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                var response = continuation(context);
+                int counter = 0;
+                var requestStream = new WrappedClientStreamWriter<TRequest>(response.RequestStream,
+                    message => { counter++; return message; }, null);
+                var responseAsync = response.ResponseAsync.ContinueWith(
+                    unaryResponse => (TResponse)(object)counter.ToString()  // Cast to object first is needed to satisfy the type-checker    
+                );
+                return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, responseAsync, response.ResponseHeadersAsync, response.GetStatus, response.GetTrailers, response.Dispose);
+            }
+        }
+
+        private class WrappedClientStreamWriter<T> : IClientStreamWriter<T>
+        {
+            readonly IClientStreamWriter<T> writer;
+            readonly Func<T, T> onMessage;
+            readonly Action onResponseStreamEnd;
+            public WrappedClientStreamWriter(IClientStreamWriter<T> writer, Func<T, T> onMessage, Action onResponseStreamEnd)
+            {
+                this.writer = writer;
+                this.onMessage = onMessage;
+                this.onResponseStreamEnd = onResponseStreamEnd;
+            }
+            public Task CompleteAsync()
+            {
+                if (onResponseStreamEnd != null)
+                {
+                    return writer.CompleteAsync().ContinueWith(x => onResponseStreamEnd());
+                }
+                return writer.CompleteAsync();
+            }
+            public Task WriteAsync(T message)
+            {
+                if (onMessage != null)
+                {
+                    message = onMessage(message);
+                }
+                return writer.WriteAsync(message);
+            }
+            public WriteOptions WriteOptions
+            {
+                get
+                {
+                    return writer.WriteOptions;
+                }
+                set
+                {
+                    writer.WriteOptions = value;
+                }
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/Interceptors/ServerInterceptorTest.cs b/src/csharp/Grpc.Core.Tests/Interceptors/ServerInterceptorTest.cs
new file mode 100644
index 0000000..e76f21d
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/Interceptors/ServerInterceptorTest.cs
@@ -0,0 +1,126 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Interceptors;
+using Grpc.Core.Internal;
+using Grpc.Core.Tests;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Interceptors.Tests
+{
+    public class ServerInterceptorTest
+    {
+        const string Host = "127.0.0.1";
+
+        [Test]
+        public void AddRequestHeaderInServerInterceptor()
+        {
+            var helper = new MockServiceHelper(Host);
+            const string MetadataKey = "x-interceptor";
+            const string MetadataValue = "hello world";
+            var interceptor = new ServerCallContextInterceptor(ctx => ctx.RequestHeaders.Add(new Metadata.Entry(MetadataKey, MetadataValue)));
+            helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+            {
+                var interceptorHeader = context.RequestHeaders.Last(m => (m.Key == MetadataKey)).Value;
+                Assert.AreEqual(interceptorHeader, MetadataValue);
+                return Task.FromResult("PASS");
+            });
+            helper.ServiceDefinition = helper.ServiceDefinition.Intercept(interceptor);
+            var server = helper.GetServer();
+            server.Start();
+            var channel = helper.GetChannel();
+            Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
+        }
+
+        [Test]
+        public void VerifyInterceptorOrdering()
+        {
+            var helper = new MockServiceHelper(Host);
+            helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+            {
+                return Task.FromResult("PASS");
+            });
+            var stringBuilder = new StringBuilder();
+            helper.ServiceDefinition = helper.ServiceDefinition
+                .Intercept(new ServerCallContextInterceptor(ctx => stringBuilder.Append("A")))
+                .Intercept(new ServerCallContextInterceptor(ctx => stringBuilder.Append("B1")),
+                    new ServerCallContextInterceptor(ctx => stringBuilder.Append("B2")),
+                    new ServerCallContextInterceptor(ctx => stringBuilder.Append("B3")))
+                .Intercept(new ServerCallContextInterceptor(ctx => stringBuilder.Append("C")));
+            var server = helper.GetServer();
+            server.Start();
+            var channel = helper.GetChannel();
+            Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
+            Assert.AreEqual("CB1B2B3A", stringBuilder.ToString());
+        }
+
+        [Test]
+        public void CheckNullInterceptorRegistrationFails()
+        {
+            var helper = new MockServiceHelper(Host);
+            var sd = helper.ServiceDefinition;
+            Assert.Throws<ArgumentNullException>(() => sd.Intercept(default(Interceptor)));
+            Assert.Throws<ArgumentNullException>(() => sd.Intercept(new[]{default(Interceptor)}));
+            Assert.Throws<ArgumentNullException>(() => sd.Intercept(new[]{new ServerCallContextInterceptor(ctx=>{}), null}));
+            Assert.Throws<ArgumentNullException>(() => sd.Intercept(default(Interceptor[])));
+        }
+
+        private class ServerCallContextInterceptor : Interceptor
+        {
+            readonly Action<ServerCallContext> interceptor;
+
+            public ServerCallContextInterceptor(Action<ServerCallContext> interceptor)
+            {
+                GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
+                this.interceptor = interceptor;
+            }
+
+            public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
+            {
+                interceptor(context);
+                return continuation(request, context);
+            }
+
+            public override Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context, ClientStreamingServerMethod<TRequest, TResponse> continuation)
+            {
+                interceptor(context);
+                return continuation(requestStream, context);
+            }
+
+            public override Task ServerStreamingServerHandler<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, ServerStreamingServerMethod<TRequest, TResponse> continuation)
+            {
+                interceptor(context);
+                return continuation(request, responseStream, context);
+            }
+
+            public override Task DuplexStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
+            {
+                interceptor(context);
+                return continuation(requestStream, responseStream, context);
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
index 7f4677d..a925f86 100644
--- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
+++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
@@ -37,7 +37,6 @@
         public const string ServiceName = "tests.Test";
 
         readonly string host;
-        readonly ServerServiceDefinition serviceDefinition;
         readonly IEnumerable<ChannelOption> channelOptions;
 
         readonly Method<string, string> unaryMethod;
@@ -87,7 +86,7 @@
                 marshaller,
                 marshaller);
 
-            serviceDefinition = ServerServiceDefinition.CreateBuilder()
+            ServiceDefinition = ServerServiceDefinition.CreateBuilder()
                 .AddMethod(unaryMethod, (request, context) => unaryHandler(request, context))
                 .AddMethod(clientStreamingMethod, (requestStream, context) => clientStreamingHandler(requestStream, context))
                 .AddMethod(serverStreamingMethod, (request, responseStream, context) => serverStreamingHandler(request, responseStream, context))
@@ -131,7 +130,7 @@
                 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
                 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
                 {
-                    Services = { serviceDefinition },
+                    Services = { ServiceDefinition },
                     Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
                 };
             }
@@ -178,13 +177,7 @@
             }
         }
 
-        public ServerServiceDefinition ServiceDefinition
-        {
-            get
-            {
-                return this.serviceDefinition;
-            }
-        }
+        public ServerServiceDefinition ServiceDefinition { get; set; }
       
         public UnaryServerMethod<string, string> UnaryHandler
         {
diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs
index 2d41b29..fac3407 100644
--- a/src/csharp/Grpc.Core/ClientBase.cs
+++ b/src/csharp/Grpc.Core/ClientBase.cs
@@ -16,6 +16,8 @@
 
 #endregion
 
+using System;
+using Grpc.Core.Interceptors;
 using Grpc.Core.Internal;
 using Grpc.Core.Utils;
 
@@ -147,6 +149,52 @@
         /// </summary>
         protected internal class ClientBaseConfiguration
         {
+            private class ClientBaseConfigurationInterceptor : Interceptor
+            {
+                readonly Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor;
+
+                /// <summary>
+                /// Creates a new instance of ClientBaseConfigurationInterceptor given the specified header and host interceptor function.
+                /// </summary>
+                public ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor)
+                {
+                    this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
+                }
+
+                private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
+                    where TRequest : class
+                    where TResponse : class
+                {
+                    var newHostAndCallOptions = interceptor(context.Method, context.Host, context.Options);
+                    return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Item1, newHostAndCallOptions.Item2);
+                }
+
+                public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
+                {
+                    return continuation(request, GetNewContext(context));
+                }
+
+                public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
+                {
+                    return continuation(request, GetNewContext(context));
+                }
+
+                public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
+                {
+                    return continuation(request, GetNewContext(context));
+                }
+
+                public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
+                {
+                    return continuation(GetNewContext(context));
+                }
+
+                public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
+                {
+                    return continuation(GetNewContext(context));
+                }
+            }
+
             readonly CallInvoker undecoratedCallInvoker;
             readonly string host;
 
@@ -158,12 +206,12 @@
 
             internal CallInvoker CreateDecoratedCallInvoker()
             {
-                return new InterceptingCallInvoker(undecoratedCallInvoker, hostInterceptor: (h) => host);
+                return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => Tuple.Create(this.host, options)));
             }
 
             internal ClientBaseConfiguration WithHost(string host)
             {
-                GrpcPreconditions.CheckNotNull(host, "host");
+                GrpcPreconditions.CheckNotNull(host, nameof(host));
                 return new ClientBaseConfiguration(this.undecoratedCallInvoker, host);
             }
         }
diff --git a/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs b/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs
new file mode 100644
index 0000000..421b5d3
--- /dev/null
+++ b/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs
@@ -0,0 +1,144 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using System.Linq;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Interceptors
+{
+    /// <summary>
+    /// Extends the CallInvoker class to provide the interceptor facility on the client side.
+    /// This is an EXPERIMENTAL API.
+    /// </summary>
+    public static class CallInvokerExtensions
+    {
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
+        /// the invoker with the given interceptor.
+        /// </summary>
+        /// <param name="invoker">The underlying invoker to intercept.</param>
+        /// <param name="interceptor">The interceptor to intercept calls to the invoker with.</param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by calling
+        /// "invoker.Intercept(a, b, c)".  The order of invocation will be "a", "b", and then "c".
+        /// Interceptors can be later added to an existing intercepted CallInvoker, effectively
+        /// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static CallInvoker Intercept(this CallInvoker invoker, Interceptor interceptor)
+        {
+            return new InterceptingCallInvoker(invoker, interceptor);
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
+        /// the invoker with the given interceptors.
+        /// </summary>
+        /// <param name="invoker">The channel to intercept.</param>
+        /// <param name="interceptors">
+        /// An array of interceptors to intercept the calls to the invoker with.
+        /// Control is passed to the interceptors in the order specified.
+        /// </param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by calling
+        /// "invoker.Intercept(a, b, c)".  The order of invocation will be "a", "b", and then "c".
+        /// Interceptors can be later added to an existing intercepted CallInvoker, effectively
+        /// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static CallInvoker Intercept(this CallInvoker invoker, params Interceptor[] interceptors)
+        {
+            GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
+            GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));
+
+            foreach (var interceptor in interceptors.Reverse())
+            {
+                invoker = Intercept(invoker, interceptor);
+            }
+
+            return invoker;
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
+        /// the invoker with the given interceptor.
+        /// </summary>
+        /// <param name="invoker">The underlying invoker to intercept.</param>
+        /// <param name="interceptor">
+        /// An interceptor delegate that takes the request metadata to be sent with an outgoing call
+        /// and returns a <see cref="Grpc.Core.Metadata" /> instance that will replace the existing
+        /// invocation metadata.
+        /// </param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by
+        /// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static CallInvoker Intercept(this CallInvoker invoker, Func<Metadata, Metadata> interceptor)
+        {
+            return new InterceptingCallInvoker(invoker, new MetadataInterceptor(interceptor));
+        }
+
+        private class MetadataInterceptor : Interceptor
+        {
+            readonly Func<Metadata, Metadata> interceptor;
+
+            /// <summary>
+            /// Creates a new instance of MetadataInterceptor given the specified interceptor function.
+            /// </summary>
+            public MetadataInterceptor(Func<Metadata, Metadata> interceptor)
+            {
+                this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
+            }
+
+            private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
+                where TRequest : class
+                where TResponse : class
+            {
+                var metadata = context.Options.Headers ?? new Metadata();
+                return new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, context.Options.WithHeaders(interceptor(metadata)));
+            }
+
+            public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
+            {
+                return continuation(request, GetNewContext(context));
+            }
+
+            public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
+            {
+                return continuation(request, GetNewContext(context));
+            }
+
+            public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                return continuation(request, GetNewContext(context));
+            }
+
+            public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                return continuation(GetNewContext(context));
+            }
+
+            public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
+            {
+                return continuation(GetNewContext(context));
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs b/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs
new file mode 100644
index 0000000..00b2fa8
--- /dev/null
+++ b/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs
@@ -0,0 +1,88 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+
+namespace Grpc.Core.Interceptors
+{
+    /// <summary>
+    /// Provides extension methods to make it easy to register interceptors on Channel objects.
+    /// This is an EXPERIMENTAL API.
+    /// </summary>
+    public static class ChannelExtensions
+    {
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
+        /// the channel with the given interceptor.
+        /// </summary>
+        /// <param name="channel">The channel to intercept.</param>
+        /// <param name="interceptor">The interceptor to intercept the channel with.</param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by calling
+        /// "channel.Intercept(a, b, c)".  The order of invocation will be "a", "b", and then "c".
+        /// Interceptors can be later added to an existing intercepted channel, effectively
+        /// building a chain like "channel.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static CallInvoker Intercept(this Channel channel, Interceptor interceptor)
+        {
+            return new DefaultCallInvoker(channel).Intercept(interceptor);
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
+        /// the channel with the given interceptors.
+        /// </summary>
+        /// <param name="channel">The channel to intercept.</param>
+        /// <param name="interceptors">
+        /// An array of interceptors to intercept the channel with.
+        /// Control is passed to the interceptors in the order specified.
+        /// </param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by calling
+        /// "channel.Intercept(a, b, c)".  The order of invocation will be "a", "b", and then "c".
+        /// Interceptors can be later added to an existing intercepted channel, effectively
+        /// building a chain like "channel.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static CallInvoker Intercept(this Channel channel, params Interceptor[] interceptors)
+        {
+            return new DefaultCallInvoker(channel).Intercept(interceptors);
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
+        /// the invoker with the given interceptor.
+        /// </summary>
+        /// <param name="channel">The channel to intercept.</param>
+        /// <param name="interceptor">
+        /// An interceptor delegate that takes the request metadata to be sent with an outgoing call
+        /// and returns a <see cref="Grpc.Core.Metadata" /> instance that will replace the existing
+        /// invocation metadata.
+        /// </param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by
+        /// building a chain like "channel.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static CallInvoker Intercept(this Channel channel, Func<Metadata, Metadata> interceptor)
+        {
+            return new DefaultCallInvoker(channel).Intercept(interceptor);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs b/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs
new file mode 100644
index 0000000..de06a77
--- /dev/null
+++ b/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs
@@ -0,0 +1,65 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using System.Reflection;
+using System.Threading.Tasks;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core.Interceptors
+{
+    /// <summary>
+    /// Carries along the context associated with intercepted invocations on the client side.
+    /// This is an EXPERIMENTAL API.
+    /// </summary>
+    public struct ClientInterceptorContext<TRequest, TResponse>
+        where TRequest : class
+        where TResponse : class
+    {
+        /// <summary>
+        /// Creates a new instance of <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}" />
+        /// with the specified method, host, and call options.
+        /// </summary>
+        /// <param name="method">A <see cref="Grpc.Core.Method{TRequest, TResponse}"/> object representing the method to be invoked.</param>
+        /// <param name="host">The host to dispatch the current call to.</param>
+        /// <param name="options">A <see cref="Grpc.Core.CallOptions"/> instance containing the call options of the current call.</param>
+        public ClientInterceptorContext(Method<TRequest, TResponse> method, string host, CallOptions options)
+        {
+            Method = method;
+            Host = host;
+            Options = options;
+        }
+
+        /// <summary>
+        /// Gets the <see cref="Grpc.Core.Method{TRequest, TResponse}"/> instance
+        /// representing the method to be invoked.
+        /// </summary>
+        public Method<TRequest, TResponse> Method { get; }
+
+        /// <summary>
+        /// Gets the host that the currect invocation will be dispatched to.
+        /// </summary>
+        public string Host { get; }
+
+        /// <summary>
+        /// Gets the <see cref="Grpc.Core.CallOptions"/> structure representing the
+        /// call options associated with the current invocation.
+        /// </summary>
+        public CallOptions Options { get; }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Interceptors/InterceptingCallInvoker.cs b/src/csharp/Grpc.Core/Interceptors/InterceptingCallInvoker.cs
new file mode 100644
index 0000000..84d2a0b
--- /dev/null
+++ b/src/csharp/Grpc.Core/Interceptors/InterceptingCallInvoker.cs
@@ -0,0 +1,96 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Interceptors
+{
+    /// <summary>
+    /// Decorates an underlying <see cref="Grpc.Core.CallInvoker" /> to
+    /// intercept calls through a given interceptor.
+    /// </summary>
+    internal class InterceptingCallInvoker : CallInvoker
+    {
+        readonly CallInvoker invoker;
+        readonly Interceptor interceptor;
+
+        /// <summary>
+        /// Creates a new instance of <see cref="Grpc.Core.Interceptors.InterceptingCallInvoker" />
+        /// with the given underlying invoker and interceptor instances.
+        /// </summary>
+        public InterceptingCallInvoker(CallInvoker invoker, Interceptor interceptor)
+        {
+            this.invoker = GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
+            this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
+        }
+
+        /// <summary>
+        /// Intercepts a simple blocking call with the registered interceptor.
+        /// </summary>
+        public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
+        {
+            return interceptor.BlockingUnaryCall(
+                request,
+                new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
+                (req, ctx) => invoker.BlockingUnaryCall(ctx.Method, ctx.Host, ctx.Options, req));
+        }
+
+        /// <summary>
+        /// Intercepts a simple asynchronous call with the registered interceptor.
+        /// </summary>
+        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
+        {
+            return interceptor.AsyncUnaryCall(
+                request,
+                new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
+                (req, ctx) => invoker.AsyncUnaryCall(ctx.Method, ctx.Host, ctx.Options, req));
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous server streaming call with the registered interceptor.
+        /// </summary>
+        public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
+        {
+            return interceptor.AsyncServerStreamingCall(
+                request,
+                new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
+                (req, ctx) => invoker.AsyncServerStreamingCall(ctx.Method, ctx.Host, ctx.Options, req));
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous client streaming call with the registered interceptor.
+        /// </summary>
+        public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
+        {
+            return interceptor.AsyncClientStreamingCall(
+                new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
+                ctx => invoker.AsyncClientStreamingCall(ctx.Method, ctx.Host, ctx.Options));
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous duplex streaming call with the registered interceptor.
+        /// </summary>
+        public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
+        {
+            return interceptor.AsyncDuplexStreamingCall(
+                new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
+                ctx => invoker.AsyncDuplexStreamingCall(ctx.Method, ctx.Host, ctx.Options));
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Interceptors/Interceptor.cs b/src/csharp/Grpc.Core/Interceptors/Interceptor.cs
new file mode 100644
index 0000000..56a30c3
--- /dev/null
+++ b/src/csharp/Grpc.Core/Interceptors/Interceptor.cs
@@ -0,0 +1,406 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using System.Reflection;
+using System.Threading.Tasks;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core.Interceptors
+{
+    /// <summary>
+    /// Serves as the base class for gRPC interceptors.
+    /// This is an EXPERIMENTAL API.
+    /// </summary>
+    public abstract class Interceptor
+    {
+        /// <summary>
+        /// Represents a continuation for intercepting simple blocking invocations.
+        /// A delegate of this type is passed to the BlockingUnaryCall method
+        /// when an outgoing invocation is being intercepted and calling the
+        /// delegate will invoke the next interceptor in the chain, or the underlying
+        /// call invoker if called from the last interceptor. The interceptor is
+        /// allowed to call it zero, one, or multiple times, passing it the appropriate
+        /// context and request values as it sees fit.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
+        /// <param name="request">The request value to continue the invocation with.</param>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// instance to pass to the next step in the invocation process.
+        /// </param>
+        /// <returns>
+        /// The response value of the invocation to return to the caller.
+        /// The interceptor can choose to return the return value of the
+        /// continuation delegate or an arbitrary value as it sees fit.
+        /// </returns>
+        public delegate TResponse BlockingUnaryCallContinuation<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context)
+            where TRequest : class
+            where TResponse : class;
+
+        /// <summary>
+        /// Represents a continuation for intercepting simple asynchronous invocations.
+        /// A delegate of this type is passed to the AsyncUnaryCall method
+        /// when an outgoing invocation is being intercepted and calling the
+        /// delegate will invoke the next interceptor in the chain, or the underlying
+        /// call invoker if called from the last interceptor. The interceptor is
+        /// allowed to call it zero, one, or multiple times, passing it the appropriate
+        /// request value and context as it sees fit.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
+        /// <param name="request">The request value to continue the invocation with.</param>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// instance to pass to the next step in the invocation process.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncUnaryCall{TResponse}" />
+        /// representing an asynchronous invocation of a unary RPC.
+        /// The interceptor can choose to return the same object returned from
+        /// the continuation delegate or an arbitrarily constructed instance as it sees fit.
+        /// </returns>
+        public delegate AsyncUnaryCall<TResponse> AsyncUnaryCallContinuation<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context)
+            where TRequest : class
+            where TResponse : class;
+
+        /// <summary>
+        /// Represents a continuation for intercepting asynchronous server-streaming invocations.
+        /// A delegate of this type is passed to the AsyncServerStreamingCall method
+        /// when an outgoing invocation is being intercepted and calling the
+        /// delegate will invoke the next interceptor in the chain, or the underlying
+        /// call invoker if called from the last interceptor. The interceptor is
+        /// allowed to call it zero, one, or multiple times, passing it the appropriate
+        /// request value and context as it sees fit.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
+        /// <param name="request">The request value to continue the invocation with.</param>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// instance to pass to the next step in the invocation process.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncServerStreamingCall{TResponse}" />
+        /// representing an asynchronous invocation of a server-streaming RPC.
+        /// The interceptor can choose to return the same object returned from
+        /// the continuation delegate or an arbitrarily constructed instance as it sees fit.
+        /// </returns>
+        public delegate AsyncServerStreamingCall<TResponse> AsyncServerStreamingCallContinuation<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context)
+            where TRequest : class
+            where TResponse : class;
+
+        /// <summary>
+        /// Represents a continuation for intercepting asynchronous client-streaming invocations.
+        /// A delegate of this type is passed to the AsyncClientStreamingCall method
+        /// when an outgoing invocation is being intercepted and calling the
+        /// delegate will invoke the next interceptor in the chain, or the underlying
+        /// call invoker if called from the last interceptor. The interceptor is
+        /// allowed to call it zero, one, or multiple times, passing it the appropriate
+        /// request value and context as it sees fit.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// instance to pass to the next step in the invocation process.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncClientStreamingCall{TRequest, TResponse}" />
+        /// representing an asynchronous invocation of a client-streaming RPC.
+        /// The interceptor can choose to return the same object returned from
+        /// the continuation delegate or an arbitrarily constructed instance as it sees fit.
+        /// </returns>
+        public delegate AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCallContinuation<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
+            where TRequest : class
+            where TResponse : class;
+
+        /// <summary>
+        /// Represents a continuation for intercepting asynchronous duplex invocations.
+        /// A delegate of this type is passed to the AsyncDuplexStreamingCall method
+        /// when an outgoing invocation is being intercepted and calling the
+        /// delegate will invoke the next interceptor in the chain, or the underlying
+        /// call invoker if called from the last interceptor. The interceptor is
+        /// allowed to call it zero, one, or multiple times, passing it the appropriate
+        /// request value and context as it sees fit.
+        /// </summary>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// instance to pass to the next step in the invocation process.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncDuplexStreamingCall{TRequest, TResponse}" />
+        /// representing an asynchronous invocation of a duplex-streaming RPC.
+        /// The interceptor can choose to return the same object returned from
+        /// the continuation delegate or an arbitrarily constructed instance as it sees fit.
+        /// </returns>
+        public delegate AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCallContinuation<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
+            where TRequest : class
+            where TResponse : class;
+
+        /// <summary>
+        /// Intercepts a blocking invocation of a simple remote call.
+        /// </summary>
+        /// <param name="request">The request message of the invocation.</param>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// associated with the current invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// The callback that continues the invocation process.
+        /// This can be invoked zero or more times by the interceptor.
+        /// The interceptor can invoke the continuation passing the given
+        /// request value and context arguments, or substitute them as it sees fit.
+        /// </param>
+        /// <returns>
+        /// The response message of the current invocation.
+        /// The interceptor can simply return the return value of the
+        /// continuation delegate passed to it intact, or an arbitrary
+        /// value as it sees fit.
+        /// </returns>
+        public virtual TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(request, context);
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous invocation of a simple remote call.
+        /// </summary>
+        /// <param name="request">The request message of the invocation.</param>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// associated with the current invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// The callback that continues the invocation process.
+        /// This can be invoked zero or more times by the interceptor.
+        /// The interceptor can invoke the continuation passing the given
+        /// request value and context arguments, or substitute them as it sees fit.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncUnaryCall{TResponse}" />
+        /// representing an asynchronous unary invocation.
+        /// The interceptor can simply return the return value of the
+        /// continuation delegate passed to it intact, or construct its
+        /// own substitute as it sees fit.
+        /// </returns>
+        public virtual AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(request, context);
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous invocation of a streaming remote call.
+        /// </summary>
+        /// <param name="request">The request message of the invocation.</param>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// associated with the current invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// The callback that continues the invocation process.
+        /// This can be invoked zero or more times by the interceptor.
+        /// The interceptor can invoke the continuation passing the given
+        /// request value and context arguments, or substitute them as it sees fit.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncServerStreamingCall{TResponse}" />
+        /// representing an asynchronous server-streaming invocation.
+        /// The interceptor can simply return the return value of the
+        /// continuation delegate passed to it intact, or construct its
+        /// own substitute as it sees fit.
+        /// </returns>
+        public virtual AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(request, context);
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous invocation of a client streaming call.
+        /// </summary>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// associated with the current invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// The callback that continues the invocation process.
+        /// This can be invoked zero or more times by the interceptor.
+        /// The interceptor can invoke the continuation passing the given
+        /// context argument, or substitute as it sees fit.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncClientStreamingCall{TRequest, TResponse}" />
+        /// representing an asynchronous client-streaming invocation.
+        /// The interceptor can simply return the return value of the
+        /// continuation delegate passed to it intact, or construct its
+        /// own substitute as it sees fit.
+        /// </returns>
+        public virtual AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(context);
+        }
+
+        /// <summary>
+        /// Intercepts an asynchronous invocation of a duplex streaming call.
+        /// </summary>
+        /// <param name="context">
+        /// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
+        /// associated with the current invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// The callback that continues the invocation process.
+        /// This can be invoked zero or more times by the interceptor.
+        /// The interceptor can invoke the continuation passing the given
+        /// context argument, or substitute as it sees fit.
+        /// </param>
+        /// <returns>
+        /// An instance of <see cref="Grpc.Core.AsyncDuplexStreamingCall{TRequest, TResponse}" />
+        /// representing an asynchronous duplex-streaming invocation.
+        /// The interceptor can simply return the return value of the
+        /// continuation delegate passed to it intact, or construct its
+        /// own substitute as it sees fit.
+        /// </returns>
+        public virtual AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(context);
+        }
+
+        /// <summary>
+        /// Server-side handler for intercepting and incoming unary call.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this method.</typeparam>
+        /// <param name="request">The request value of the incoming invocation.</param>
+        /// <param name="context">
+        /// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
+        /// the context of the invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// A delegate that asynchronously proceeds with the invocation, calling
+        /// the next interceptor in the chain, or the service request handler,
+        /// in case of the last interceptor and return the response value of
+        /// the RPC. The interceptor can choose to call it zero or more times
+        /// at its discretion.
+        /// </param>
+        /// <returns>
+        /// A future representing the response value of the RPC. The interceptor
+        /// can simply return the return value from the continuation intact,
+        /// or an arbitrary response value as it sees fit.
+        /// </returns>
+        public virtual Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(request, context);
+        }
+
+        /// <summary>
+        /// Server-side handler for intercepting client streaming call.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this method.</typeparam>
+        /// <param name="requestStream">The request stream of the incoming invocation.</param>
+        /// <param name="context">
+        /// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
+        /// the context of the invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// A delegate that asynchronously proceeds with the invocation, calling
+        /// the next interceptor in the chain, or the service request handler,
+        /// in case of the last interceptor and return the response value of
+        /// the RPC. The interceptor can choose to call it zero or more times
+        /// at its discretion.
+        /// </param>
+        /// <returns>
+        /// A future representing the response value of the RPC. The interceptor
+        /// can simply return the return value from the continuation intact,
+        /// or an arbitrary response value as it sees fit. The interceptor has
+        /// the ability to wrap or substitute the request stream when calling
+        /// the continuation.
+        /// </returns>
+        public virtual Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context, ClientStreamingServerMethod<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(requestStream, context);
+        }
+
+        /// <summary>
+        /// Server-side handler for intercepting server streaming call.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this method.</typeparam>
+        /// <param name="request">The request value of the incoming invocation.</param>
+        /// <param name="responseStream">The response stream of the incoming invocation.</param>
+        /// <param name="context">
+        /// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
+        /// the context of the invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// A delegate that asynchronously proceeds with the invocation, calling
+        /// the next interceptor in the chain, or the service request handler,
+        /// in case of the last interceptor and the interceptor can choose to
+        /// call it zero or more times at its discretion. The interceptor has
+        /// the ability to wrap or substitute the request value and the response stream
+        /// when calling the continuation.
+        /// </param>
+        public virtual Task ServerStreamingServerHandler<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, ServerStreamingServerMethod<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(request, responseStream, context);
+        }
+
+        /// <summary>
+        /// Server-side handler for intercepting bidirectional streaming calls.
+        /// </summary>
+        /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+        /// <typeparam name="TResponse">Response message type for this method.</typeparam>
+        /// <param name="requestStream">The request stream of the incoming invocation.</param>
+        /// <param name="responseStream">The response stream of the incoming invocation.</param>
+        /// <param name="context">
+        /// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
+        /// the context of the invocation.
+        /// </param>
+        /// <param name="continuation">
+        /// A delegate that asynchronously proceeds with the invocation, calling
+        /// the next interceptor in the chain, or the service request handler,
+        /// in case of the last interceptor and the interceptor can choose to
+        /// call it zero or more times at its discretion. The interceptor has
+        /// the ability to wrap or substitute the request and response streams
+        /// when calling the continuation.
+        /// </param>
+        public virtual Task DuplexStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
+            where TRequest : class
+            where TResponse : class
+        {
+            return continuation(requestStream, responseStream, context);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs b/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs
new file mode 100644
index 0000000..b9b5324
--- /dev/null
+++ b/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs
@@ -0,0 +1,82 @@
+#region Copyright notice and license
+
+// Copyright 2018 gRPC authors.
+//
+// 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.
+
+#endregion
+
+using System;
+using System.Linq;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Interceptors
+{
+    /// <summary>
+    /// Extends the ServerServiceDefinition class to add methods used to register interceptors on the server side.
+    /// This is an EXPERIMENTAL API.
+    /// </summary>
+    public static class ServerServiceDefinitionExtensions
+    {
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.ServerServiceDefinition" /> instance that
+        /// intercepts incoming calls to the underlying service handler through the given interceptor.
+        /// This is an EXPERIMENTAL API.
+        /// </summary>
+        /// <param name="serverServiceDefinition">The <see cref="Grpc.Core.ServerServiceDefinition" /> instance to register interceptors on.</param>
+        /// <param name="interceptor">The interceptor to intercept the incoming invocations with.</param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by calling
+        /// "serverServiceDefinition.Intercept(a, b, c)".  The order of invocation will be "a", "b", and then "c".
+        /// Interceptors can be later added to an existing intercepted service definition, effectively
+        /// building a chain like "serverServiceDefinition.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static ServerServiceDefinition Intercept(this ServerServiceDefinition serverServiceDefinition, Interceptor interceptor)
+        {
+            GrpcPreconditions.CheckNotNull(serverServiceDefinition, nameof(serverServiceDefinition));
+            GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
+            return new ServerServiceDefinition(serverServiceDefinition.CallHandlers.ToDictionary(x => x.Key, x => x.Value.Intercept(interceptor)));
+        }
+
+        /// <summary>
+        /// Returns a <see cref="Grpc.Core.ServerServiceDefinition" /> instance that
+        /// intercepts incoming calls to the underlying service handler through the given interceptors.
+        /// This is an EXPERIMENTAL API.
+        /// </summary>
+        /// <param name="serverServiceDefinition">The <see cref="Grpc.Core.ServerServiceDefinition" /> instance to register interceptors on.</param>
+        /// <param name="interceptors">
+        /// An array of interceptors to intercept the incoming invocations with.
+        /// Control is passed to the interceptors in the order specified.
+        /// </param>
+        /// <remarks>
+        /// Multiple interceptors can be added on top of each other by calling
+        /// "serverServiceDefinition.Intercept(a, b, c)".  The order of invocation will be "a", "b", and then "c".
+        /// Interceptors can be later added to an existing intercepted service definition, effectively
+        /// building a chain like "serverServiceDefinition.Intercept(c).Intercept(b).Intercept(a)".  Note that
+        /// in this case, the last interceptor added will be the first to take control.
+        /// </remarks>
+        public static ServerServiceDefinition Intercept(this ServerServiceDefinition serverServiceDefinition, params Interceptor[] interceptors)
+        {
+            GrpcPreconditions.CheckNotNull(serverServiceDefinition, nameof(serverServiceDefinition));
+            GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));
+
+            foreach (var interceptor in interceptors.Reverse())
+            {
+                serverServiceDefinition = Intercept(serverServiceDefinition, interceptor);
+            }
+
+            return serverServiceDefinition;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs b/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs
deleted file mode 100644
index eb4c7d9..0000000
--- a/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-#region Copyright notice and license
-
-// Copyright 2015-2016 gRPC authors.
-//
-// 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.
-
-#endregion
-
-using System;
-using System.Threading.Tasks;
-using Grpc.Core;
-using Grpc.Core.Utils;
-
-namespace Grpc.Core.Internal
-{
-    /// <summary>
-    /// Decorates an underlying <c>CallInvoker</c> to intercept call invocations.
-    /// </summary>
-    internal class InterceptingCallInvoker : CallInvoker
-    {
-        readonly CallInvoker callInvoker;
-        readonly Func<string, string> hostInterceptor;
-        readonly Func<CallOptions, CallOptions> callOptionsInterceptor;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Grpc.Core.Internal.InterceptingCallInvoker"/> class.
-        /// </summary>
-        public InterceptingCallInvoker(CallInvoker callInvoker,
-            Func<string, string> hostInterceptor = null,
-            Func<CallOptions, CallOptions> callOptionsInterceptor = null)
-        {
-            this.callInvoker = GrpcPreconditions.CheckNotNull(callInvoker);
-            this.hostInterceptor = hostInterceptor;
-            this.callOptionsInterceptor = callOptionsInterceptor;
-        }
-
-        /// <summary>
-        /// Intercepts a unary call.
-        /// </summary>
-        public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
-        {
-            host = InterceptHost(host);
-            options = InterceptCallOptions(options);
-            return callInvoker.BlockingUnaryCall(method, host, options, request);
-        }
-
-        /// <summary>
-        /// Invokes a simple remote call asynchronously.
-        /// </summary>
-        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
-        {
-            host = InterceptHost(host);
-            options = InterceptCallOptions(options);
-            return callInvoker.AsyncUnaryCall(method, host, options, request);
-        }
-
-        /// <summary>
-        /// Invokes a server streaming call asynchronously.
-        /// In server streaming scenario, client sends on request and server responds with a stream of responses.
-        /// </summary>
-        public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
-        {
-            host = InterceptHost(host);
-            options = InterceptCallOptions(options);
-            return callInvoker.AsyncServerStreamingCall(method, host, options, request);
-        }
-
-        /// <summary>
-        /// Invokes a client streaming call asynchronously.
-        /// In client streaming scenario, client sends a stream of requests and server responds with a single response.
-        /// </summary>
-        public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
-        {
-            host = InterceptHost(host);
-            options = InterceptCallOptions(options);
-            return callInvoker.AsyncClientStreamingCall(method, host, options);
-        }
-
-        /// <summary>
-        /// Invokes a duplex streaming call asynchronously.
-        /// In duplex streaming scenario, client sends a stream of requests and server responds with a stream of responses.
-        /// The response stream is completely independent and both side can be sending messages at the same time.
-        /// </summary>
-        public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
-        {
-            host = InterceptHost(host);
-            options = InterceptCallOptions(options);
-            return callInvoker.AsyncDuplexStreamingCall(method, host, options);
-        }
-
-        private string InterceptHost(string host)
-        {
-            if (hostInterceptor == null)
-            {
-                return host;
-            }
-            return hostInterceptor(host);
-        }
-
-        private CallOptions InterceptCallOptions(CallOptions options)
-        {
-            if (callOptionsInterceptor == null)
-            {
-                return options;
-            }
-            return callOptionsInterceptor(options);
-        }
-    }
-}
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index 98995a0..81522cf 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -21,6 +21,7 @@
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Grpc.Core.Interceptors;
 using Grpc.Core.Internal;
 using Grpc.Core.Logging;
 using Grpc.Core.Utils;
@@ -30,6 +31,7 @@
     internal interface IServerCallHandler
     {
         Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq);
+        IServerCallHandler Intercept(Interceptor interceptor);
     }
 
     internal class UnaryServerCallHandler<TRequest, TResponse> : IServerCallHandler
@@ -74,7 +76,7 @@
             {
                 if (!(e is RpcException))
                 {
-                    Logger.Warning(e, "Exception occured in handler.");
+                    Logger.Warning(e, "Exception occurred in the handler or an interceptor.");
                 }
                 status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
             }
@@ -89,6 +91,11 @@
             }
             await finishedTask.ConfigureAwait(false);
         }
+
+        public IServerCallHandler Intercept(Interceptor interceptor)
+        {
+            return new UnaryServerCallHandler<TRequest, TResponse>(method, (request, context) => interceptor.UnaryServerHandler(request, context, handler));
+        }
     }
 
     internal class ServerStreamingServerCallHandler<TRequest, TResponse> : IServerCallHandler
@@ -131,7 +138,7 @@
             {
                 if (!(e is RpcException))
                 {
-                    Logger.Warning(e, "Exception occured in handler.");
+                    Logger.Warning(e, "Exception occurred in the handler or an interceptor.");
                 }
                 status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
             }
@@ -147,6 +154,11 @@
             }
             await finishedTask.ConfigureAwait(false);
         }
+
+        public IServerCallHandler Intercept(Interceptor interceptor)
+        {
+            return new ServerStreamingServerCallHandler<TRequest, TResponse>(method, (request, responseStream, context) => interceptor.ServerStreamingServerHandler(request, responseStream, context, handler));
+        }
     }
 
     internal class ClientStreamingServerCallHandler<TRequest, TResponse> : IServerCallHandler
@@ -189,7 +201,7 @@
             {
                 if (!(e is RpcException))
                 {
-                    Logger.Warning(e, "Exception occured in handler.");
+                    Logger.Warning(e, "Exception occurred in the handler or an interceptor.");
                 }
                 status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
             }
@@ -205,6 +217,11 @@
             }
             await finishedTask.ConfigureAwait(false);
         }
+
+        public IServerCallHandler Intercept(Interceptor interceptor)
+        {
+            return new ClientStreamingServerCallHandler<TRequest, TResponse>(method, (requestStream, context) => interceptor.ClientStreamingServerHandler(requestStream, context, handler));
+        }
     }
 
     internal class DuplexStreamingServerCallHandler<TRequest, TResponse> : IServerCallHandler
@@ -245,7 +262,7 @@
             {
                 if (!(e is RpcException))
                 {
-                    Logger.Warning(e, "Exception occured in handler.");
+                    Logger.Warning(e, "Exception occurred in the handler or an interceptor.");
                 }
                 status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
             }
@@ -260,6 +277,11 @@
             }
             await finishedTask.ConfigureAwait(false);
         }
+
+        public IServerCallHandler Intercept(Interceptor interceptor)
+        {
+            return new DuplexStreamingServerCallHandler<TRequest, TResponse>(method, (requestStream, responseStream, context) => interceptor.DuplexStreamingServerHandler(requestStream, responseStream, context, handler));
+        }
     }
 
     internal class UnimplementedMethodCallHandler : IServerCallHandler
@@ -288,6 +310,11 @@
         {
             return callHandlerImpl.HandleCall(newRpc, cq);
         }
+
+        public IServerCallHandler Intercept(Interceptor interceptor)
+        {
+            return this;  // Do not intercept unimplemented methods.
+        }
     }
 
     internal static class HandlerUtils
diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
index 59868c1..07c6aa1 100644
--- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs
+++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
@@ -19,7 +19,10 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Linq;
+using Grpc.Core.Interceptors;
 using Grpc.Core.Internal;
+using Grpc.Core.Utils;
 
 namespace Grpc.Core
 {
@@ -32,7 +35,7 @@
     {
         readonly ReadOnlyDictionary<string, IServerCallHandler> callHandlers;
 
-        private ServerServiceDefinition(Dictionary<string, IServerCallHandler> callHandlers)
+        internal ServerServiceDefinition(Dictionary<string, IServerCallHandler> callHandlers)
         {
             this.callHandlers = new ReadOnlyDictionary<string, IServerCallHandler>(callHandlers);
         }
diff --git a/src/csharp/Grpc.Core/Version.csproj.include b/src/csharp/Grpc.Core/Version.csproj.include
index 539d3a9..9b55f24 100755
--- a/src/csharp/Grpc.Core/Version.csproj.include
+++ b/src/csharp/Grpc.Core/Version.csproj.include
@@ -1,7 +1,7 @@
 <!-- This file is generated -->
 <Project>
   <PropertyGroup>
-    <GrpcCsharpVersion>1.10.0-dev</GrpcCsharpVersion>
+    <GrpcCsharpVersion>1.11.0-dev</GrpcCsharpVersion>
     <GoogleProtobufVersion>3.3.0</GoogleProtobufVersion>
   </PropertyGroup>
 </Project>
diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs
index f1aef46..2902aee 100644
--- a/src/csharp/Grpc.Core/VersionInfo.cs
+++ b/src/csharp/Grpc.Core/VersionInfo.cs
@@ -33,11 +33,11 @@
         /// <summary>
         /// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
         /// </summary>
-        public const string CurrentAssemblyFileVersion = "1.10.0.0";
+        public const string CurrentAssemblyFileVersion = "1.11.0.0";
 
         /// <summary>
         /// Current version of gRPC C#
         /// </summary>
-        public const string CurrentVersion = "1.10.0-dev";
+        public const string CurrentVersion = "1.11.0-dev";
     }
 }
diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs
index e29b108..045708b 100644
--- a/src/csharp/Grpc.Examples/MathGrpc.cs
+++ b/src/csharp/Grpc.Examples/MathGrpc.cs
@@ -20,9 +20,6 @@
 #pragma warning disable 1591
 #region Designer generated code
 
-using System;
-using System.Threading;
-using System.Threading.Tasks;
 using grpc = global::Grpc.Core;
 
 namespace Math {
@@ -159,7 +156,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Math.DivReply Div(global::Math.DivArgs request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Math.DivReply Div(global::Math.DivArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Div(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -183,7 +180,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return DivAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -208,7 +205,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return DivMany(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -234,7 +231,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Fib(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -258,7 +255,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Sum(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
index 24a7259..1d80bcd 100644
--- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
+++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
@@ -20,9 +20,6 @@
 #pragma warning disable 1591
 #region Designer generated code
 
-using System;
-using System.Threading;
-using System.Threading.Tasks;
 using grpc = global::Grpc.Core;
 
 namespace Grpc.Health.V1 {
@@ -79,7 +76,7 @@
       {
       }
 
-      public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Check(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -87,7 +84,7 @@
       {
         return CallInvoker.BlockingUnaryCall(__Method_Check, null, options, request);
       }
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CheckAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
diff --git a/src/csharp/Grpc.IntegrationTesting/Control.cs b/src/csharp/Grpc.IntegrationTesting/Control.cs
index 8e5da7b..8795728 100644
--- a/src/csharp/Grpc.IntegrationTesting/Control.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Control.cs
@@ -32,7 +32,7 @@
             "U2VjdXJpdHlQYXJhbXMSEwoLdXNlX3Rlc3RfY2EYASABKAgSHAoUc2VydmVy",
             "X2hvc3Rfb3ZlcnJpZGUYAiABKAkSEQoJY3JlZF90eXBlGAMgASgJIk0KCkNo",
             "YW5uZWxBcmcSDAoEbmFtZRgBIAEoCRITCglzdHJfdmFsdWUYAiABKAlIABIT",
-            "CglpbnRfdmFsdWUYAyABKAVIAEIHCgV2YWx1ZSLVBAoMQ2xpZW50Q29uZmln",
+            "CglpbnRfdmFsdWUYAyABKAVIAEIHCgV2YWx1ZSLvBAoMQ2xpZW50Q29uZmln",
             "EhYKDnNlcnZlcl90YXJnZXRzGAEgAygJEi0KC2NsaWVudF90eXBlGAIgASgO",
             "MhguZ3JwYy50ZXN0aW5nLkNsaWVudFR5cGUSNQoPc2VjdXJpdHlfcGFyYW1z",
             "GAMgASgLMhwuZ3JwYy50ZXN0aW5nLlNlY3VyaXR5UGFyYW1zEiQKHG91dHN0",
@@ -45,59 +45,60 @@
             "dG9ncmFtUGFyYW1zEhEKCWNvcmVfbGlzdBgNIAMoBRISCgpjb3JlX2xpbWl0",
             "GA4gASgFEhgKEG90aGVyX2NsaWVudF9hcGkYDyABKAkSLgoMY2hhbm5lbF9h",
             "cmdzGBAgAygLMhguZ3JwYy50ZXN0aW5nLkNoYW5uZWxBcmcSFgoOdGhyZWFk",
-            "c19wZXJfY3EYESABKAUSGwoTbWVzc2FnZXNfcGVyX3N0cmVhbRgSIAEoBSI4",
-            "CgxDbGllbnRTdGF0dXMSKAoFc3RhdHMYASABKAsyGS5ncnBjLnRlc3Rpbmcu",
-            "Q2xpZW50U3RhdHMiFQoETWFyaxINCgVyZXNldBgBIAEoCCJoCgpDbGllbnRB",
-            "cmdzEisKBXNldHVwGAEgASgLMhouZ3JwYy50ZXN0aW5nLkNsaWVudENvbmZp",
-            "Z0gAEiIKBG1hcmsYAiABKAsyEi5ncnBjLnRlc3RpbmcuTWFya0gAQgkKB2Fy",
-            "Z3R5cGUi/QIKDFNlcnZlckNvbmZpZxItCgtzZXJ2ZXJfdHlwZRgBIAEoDjIY",
-            "LmdycGMudGVzdGluZy5TZXJ2ZXJUeXBlEjUKD3NlY3VyaXR5X3BhcmFtcxgC",
-            "IAEoCzIcLmdycGMudGVzdGluZy5TZWN1cml0eVBhcmFtcxIMCgRwb3J0GAQg",
-            "ASgFEhwKFGFzeW5jX3NlcnZlcl90aHJlYWRzGAcgASgFEhIKCmNvcmVfbGlt",
-            "aXQYCCABKAUSMwoOcGF5bG9hZF9jb25maWcYCSABKAsyGy5ncnBjLnRlc3Rp",
-            "bmcuUGF5bG9hZENvbmZpZxIRCgljb3JlX2xpc3QYCiADKAUSGAoQb3RoZXJf",
-            "c2VydmVyX2FwaRgLIAEoCRIWCg50aHJlYWRzX3Blcl9jcRgMIAEoBRIcChNy",
-            "ZXNvdXJjZV9xdW90YV9zaXplGOkHIAEoBRIvCgxjaGFubmVsX2FyZ3MY6gcg",
-            "AygLMhguZ3JwYy50ZXN0aW5nLkNoYW5uZWxBcmciaAoKU2VydmVyQXJncxIr",
-            "CgVzZXR1cBgBIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJDb25maWdIABIi",
-            "CgRtYXJrGAIgASgLMhIuZ3JwYy50ZXN0aW5nLk1hcmtIAEIJCgdhcmd0eXBl",
-            "IlUKDFNlcnZlclN0YXR1cxIoCgVzdGF0cxgBIAEoCzIZLmdycGMudGVzdGlu",
-            "Zy5TZXJ2ZXJTdGF0cxIMCgRwb3J0GAIgASgFEg0KBWNvcmVzGAMgASgFIg0K",
-            "C0NvcmVSZXF1ZXN0Ih0KDENvcmVSZXNwb25zZRINCgVjb3JlcxgBIAEoBSIG",
-            "CgRWb2lkIv0BCghTY2VuYXJpbxIMCgRuYW1lGAEgASgJEjEKDWNsaWVudF9j",
-            "b25maWcYAiABKAsyGi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmlnEhMKC251",
-            "bV9jbGllbnRzGAMgASgFEjEKDXNlcnZlcl9jb25maWcYBCABKAsyGi5ncnBj",
-            "LnRlc3RpbmcuU2VydmVyQ29uZmlnEhMKC251bV9zZXJ2ZXJzGAUgASgFEhYK",
-            "Dndhcm11cF9zZWNvbmRzGAYgASgFEhkKEWJlbmNobWFya19zZWNvbmRzGAcg",
-            "ASgFEiAKGHNwYXduX2xvY2FsX3dvcmtlcl9jb3VudBgIIAEoBSI2CglTY2Vu",
-            "YXJpb3MSKQoJc2NlbmFyaW9zGAEgAygLMhYuZ3JwYy50ZXN0aW5nLlNjZW5h",
-            "cmlvIoQEChVTY2VuYXJpb1Jlc3VsdFN1bW1hcnkSCwoDcXBzGAEgASgBEhsK",
-            "E3Fwc19wZXJfc2VydmVyX2NvcmUYAiABKAESGgoSc2VydmVyX3N5c3RlbV90",
-            "aW1lGAMgASgBEhgKEHNlcnZlcl91c2VyX3RpbWUYBCABKAESGgoSY2xpZW50",
-            "X3N5c3RlbV90aW1lGAUgASgBEhgKEGNsaWVudF91c2VyX3RpbWUYBiABKAES",
-            "EgoKbGF0ZW5jeV81MBgHIAEoARISCgpsYXRlbmN5XzkwGAggASgBEhIKCmxh",
-            "dGVuY3lfOTUYCSABKAESEgoKbGF0ZW5jeV85ORgKIAEoARITCgtsYXRlbmN5",
-            "Xzk5ORgLIAEoARIYChBzZXJ2ZXJfY3B1X3VzYWdlGAwgASgBEiYKHnN1Y2Nl",
-            "c3NmdWxfcmVxdWVzdHNfcGVyX3NlY29uZBgNIAEoARIiChpmYWlsZWRfcmVx",
-            "dWVzdHNfcGVyX3NlY29uZBgOIAEoARIgChhjbGllbnRfcG9sbHNfcGVyX3Jl",
-            "cXVlc3QYDyABKAESIAoYc2VydmVyX3BvbGxzX3Blcl9yZXF1ZXN0GBAgASgB",
-            "EiIKGnNlcnZlcl9xdWVyaWVzX3Blcl9jcHVfc2VjGBEgASgBEiIKGmNsaWVu",
-            "dF9xdWVyaWVzX3Blcl9jcHVfc2VjGBIgASgBIoMDCg5TY2VuYXJpb1Jlc3Vs",
-            "dBIoCghzY2VuYXJpbxgBIAEoCzIWLmdycGMudGVzdGluZy5TY2VuYXJpbxIu",
-            "CglsYXRlbmNpZXMYAiABKAsyGy5ncnBjLnRlc3RpbmcuSGlzdG9ncmFtRGF0",
-            "YRIvCgxjbGllbnRfc3RhdHMYAyADKAsyGS5ncnBjLnRlc3RpbmcuQ2xpZW50",
-            "U3RhdHMSLwoMc2VydmVyX3N0YXRzGAQgAygLMhkuZ3JwYy50ZXN0aW5nLlNl",
-            "cnZlclN0YXRzEhQKDHNlcnZlcl9jb3JlcxgFIAMoBRI0CgdzdW1tYXJ5GAYg",
-            "ASgLMiMuZ3JwYy50ZXN0aW5nLlNjZW5hcmlvUmVzdWx0U3VtbWFyeRIWCg5j",
-            "bGllbnRfc3VjY2VzcxgHIAMoCBIWCg5zZXJ2ZXJfc3VjY2VzcxgIIAMoCBI5",
-            "Cg9yZXF1ZXN0X3Jlc3VsdHMYCSADKAsyIC5ncnBjLnRlc3RpbmcuUmVxdWVz",
-            "dFJlc3VsdENvdW50KkEKCkNsaWVudFR5cGUSDwoLU1lOQ19DTElFTlQQABIQ",
-            "CgxBU1lOQ19DTElFTlQQARIQCgxPVEhFUl9DTElFTlQQAipbCgpTZXJ2ZXJU",
-            "eXBlEg8KC1NZTkNfU0VSVkVSEAASEAoMQVNZTkNfU0VSVkVSEAESGAoUQVNZ",
-            "TkNfR0VORVJJQ19TRVJWRVIQAhIQCgxPVEhFUl9TRVJWRVIQAypyCgdScGNU",
-            "eXBlEgkKBVVOQVJZEAASDQoJU1RSRUFNSU5HEAESGQoVU1RSRUFNSU5HX0ZS",
-            "T01fQ0xJRU5UEAISGQoVU1RSRUFNSU5HX0ZST01fU0VSVkVSEAMSFwoTU1RS",
-            "RUFNSU5HX0JPVEhfV0FZUxAEYgZwcm90bzM="));
+            "c19wZXJfY3EYESABKAUSGwoTbWVzc2FnZXNfcGVyX3N0cmVhbRgSIAEoBRIY",
+            "ChB1c2VfY29hbGVzY2VfYXBpGBMgASgIIjgKDENsaWVudFN0YXR1cxIoCgVz",
+            "dGF0cxgBIAEoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cyIVCgRNYXJr",
+            "Eg0KBXJlc2V0GAEgASgIImgKCkNsaWVudEFyZ3MSKwoFc2V0dXAYASABKAsy",
+            "Gi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmlnSAASIgoEbWFyaxgCIAEoCzIS",
+            "LmdycGMudGVzdGluZy5NYXJrSABCCQoHYXJndHlwZSL9AgoMU2VydmVyQ29u",
+            "ZmlnEi0KC3NlcnZlcl90eXBlGAEgASgOMhguZ3JwYy50ZXN0aW5nLlNlcnZl",
+            "clR5cGUSNQoPc2VjdXJpdHlfcGFyYW1zGAIgASgLMhwuZ3JwYy50ZXN0aW5n",
+            "LlNlY3VyaXR5UGFyYW1zEgwKBHBvcnQYBCABKAUSHAoUYXN5bmNfc2VydmVy",
+            "X3RocmVhZHMYByABKAUSEgoKY29yZV9saW1pdBgIIAEoBRIzCg5wYXlsb2Fk",
+            "X2NvbmZpZxgJIAEoCzIbLmdycGMudGVzdGluZy5QYXlsb2FkQ29uZmlnEhEK",
+            "CWNvcmVfbGlzdBgKIAMoBRIYChBvdGhlcl9zZXJ2ZXJfYXBpGAsgASgJEhYK",
+            "DnRocmVhZHNfcGVyX2NxGAwgASgFEhwKE3Jlc291cmNlX3F1b3RhX3NpemUY",
+            "6QcgASgFEi8KDGNoYW5uZWxfYXJncxjqByADKAsyGC5ncnBjLnRlc3Rpbmcu",
+            "Q2hhbm5lbEFyZyJoCgpTZXJ2ZXJBcmdzEisKBXNldHVwGAEgASgLMhouZ3Jw",
+            "Yy50ZXN0aW5nLlNlcnZlckNvbmZpZ0gAEiIKBG1hcmsYAiABKAsyEi5ncnBj",
+            "LnRlc3RpbmcuTWFya0gAQgkKB2FyZ3R5cGUiVQoMU2VydmVyU3RhdHVzEigK",
+            "BXN0YXRzGAEgASgLMhkuZ3JwYy50ZXN0aW5nLlNlcnZlclN0YXRzEgwKBHBv",
+            "cnQYAiABKAUSDQoFY29yZXMYAyABKAUiDQoLQ29yZVJlcXVlc3QiHQoMQ29y",
+            "ZVJlc3BvbnNlEg0KBWNvcmVzGAEgASgFIgYKBFZvaWQi/QEKCFNjZW5hcmlv",
+            "EgwKBG5hbWUYASABKAkSMQoNY2xpZW50X2NvbmZpZxgCIAEoCzIaLmdycGMu",
+            "dGVzdGluZy5DbGllbnRDb25maWcSEwoLbnVtX2NsaWVudHMYAyABKAUSMQoN",
+            "c2VydmVyX2NvbmZpZxgEIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJDb25m",
+            "aWcSEwoLbnVtX3NlcnZlcnMYBSABKAUSFgoOd2FybXVwX3NlY29uZHMYBiAB",
+            "KAUSGQoRYmVuY2htYXJrX3NlY29uZHMYByABKAUSIAoYc3Bhd25fbG9jYWxf",
+            "d29ya2VyX2NvdW50GAggASgFIjYKCVNjZW5hcmlvcxIpCglzY2VuYXJpb3MY",
+            "ASADKAsyFi5ncnBjLnRlc3RpbmcuU2NlbmFyaW8ihAQKFVNjZW5hcmlvUmVz",
+            "dWx0U3VtbWFyeRILCgNxcHMYASABKAESGwoTcXBzX3Blcl9zZXJ2ZXJfY29y",
+            "ZRgCIAEoARIaChJzZXJ2ZXJfc3lzdGVtX3RpbWUYAyABKAESGAoQc2VydmVy",
+            "X3VzZXJfdGltZRgEIAEoARIaChJjbGllbnRfc3lzdGVtX3RpbWUYBSABKAES",
+            "GAoQY2xpZW50X3VzZXJfdGltZRgGIAEoARISCgpsYXRlbmN5XzUwGAcgASgB",
+            "EhIKCmxhdGVuY3lfOTAYCCABKAESEgoKbGF0ZW5jeV85NRgJIAEoARISCgps",
+            "YXRlbmN5Xzk5GAogASgBEhMKC2xhdGVuY3lfOTk5GAsgASgBEhgKEHNlcnZl",
+            "cl9jcHVfdXNhZ2UYDCABKAESJgoec3VjY2Vzc2Z1bF9yZXF1ZXN0c19wZXJf",
+            "c2Vjb25kGA0gASgBEiIKGmZhaWxlZF9yZXF1ZXN0c19wZXJfc2Vjb25kGA4g",
+            "ASgBEiAKGGNsaWVudF9wb2xsc19wZXJfcmVxdWVzdBgPIAEoARIgChhzZXJ2",
+            "ZXJfcG9sbHNfcGVyX3JlcXVlc3QYECABKAESIgoac2VydmVyX3F1ZXJpZXNf",
+            "cGVyX2NwdV9zZWMYESABKAESIgoaY2xpZW50X3F1ZXJpZXNfcGVyX2NwdV9z",
+            "ZWMYEiABKAEigwMKDlNjZW5hcmlvUmVzdWx0EigKCHNjZW5hcmlvGAEgASgL",
+            "MhYuZ3JwYy50ZXN0aW5nLlNjZW5hcmlvEi4KCWxhdGVuY2llcxgCIAEoCzIb",
+            "LmdycGMudGVzdGluZy5IaXN0b2dyYW1EYXRhEi8KDGNsaWVudF9zdGF0cxgD",
+            "IAMoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cxIvCgxzZXJ2ZXJfc3Rh",
+            "dHMYBCADKAsyGS5ncnBjLnRlc3RpbmcuU2VydmVyU3RhdHMSFAoMc2VydmVy",
+            "X2NvcmVzGAUgAygFEjQKB3N1bW1hcnkYBiABKAsyIy5ncnBjLnRlc3Rpbmcu",
+            "U2NlbmFyaW9SZXN1bHRTdW1tYXJ5EhYKDmNsaWVudF9zdWNjZXNzGAcgAygI",
+            "EhYKDnNlcnZlcl9zdWNjZXNzGAggAygIEjkKD3JlcXVlc3RfcmVzdWx0cxgJ",
+            "IAMoCzIgLmdycGMudGVzdGluZy5SZXF1ZXN0UmVzdWx0Q291bnQqQQoKQ2xp",
+            "ZW50VHlwZRIPCgtTWU5DX0NMSUVOVBAAEhAKDEFTWU5DX0NMSUVOVBABEhAK",
+            "DE9USEVSX0NMSUVOVBACKlsKClNlcnZlclR5cGUSDwoLU1lOQ19TRVJWRVIQ",
+            "ABIQCgxBU1lOQ19TRVJWRVIQARIYChRBU1lOQ19HRU5FUklDX1NFUlZFUhAC",
+            "EhAKDE9USEVSX1NFUlZFUhADKnIKB1JwY1R5cGUSCQoFVU5BUlkQABINCglT",
+            "VFJFQU1JTkcQARIZChVTVFJFQU1JTkdfRlJPTV9DTElFTlQQAhIZChVTVFJF",
+            "QU1JTkdfRlJPTV9TRVJWRVIQAxIXChNTVFJFQU1JTkdfQk9USF9XQVlTEARi",
+            "BnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Grpc.Testing.PayloadsReflection.Descriptor, global::Grpc.Testing.StatsReflection.Descriptor, },
           new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Grpc.Testing.ClientType), typeof(global::Grpc.Testing.ServerType), typeof(global::Grpc.Testing.RpcType), }, new pbr::GeneratedClrTypeInfo[] {
@@ -106,7 +107,7 @@
             new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.LoadParams), global::Grpc.Testing.LoadParams.Parser, new[]{ "ClosedLoop", "Poisson" }, new[]{ "Load" }, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.SecurityParams), global::Grpc.Testing.SecurityParams.Parser, new[]{ "UseTestCa", "ServerHostOverride", "CredType" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ChannelArg), global::Grpc.Testing.ChannelArg.Parser, new[]{ "Name", "StrValue", "IntValue" }, new[]{ "Value" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit", "OtherClientApi", "ChannelArgs", "ThreadsPerCq", "MessagesPerStream" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit", "OtherClientApi", "ChannelArgs", "ThreadsPerCq", "MessagesPerStream", "UseCoalesceApi" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientStatus), global::Grpc.Testing.ClientStatus.Parser, new[]{ "Stats" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.Mark), global::Grpc.Testing.Mark.Parser, new[]{ "Reset" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientArgs), global::Grpc.Testing.ClientArgs.Parser, new[]{ "Setup", "Mark" }, new[]{ "Argtype" }, null, null),
@@ -989,6 +990,7 @@
       channelArgs_ = other.channelArgs_.Clone();
       threadsPerCq_ = other.threadsPerCq_;
       messagesPerStream_ = other.messagesPerStream_;
+      useCoalesceApi_ = other.useCoalesceApi_;
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1198,6 +1200,20 @@
       }
     }
 
+    /// <summary>Field number for the "use_coalesce_api" field.</summary>
+    public const int UseCoalesceApiFieldNumber = 19;
+    private bool useCoalesceApi_;
+    /// <summary>
+    /// Use coalescing API when possible.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool UseCoalesceApi {
+      get { return useCoalesceApi_; }
+      set {
+        useCoalesceApi_ = value;
+      }
+    }
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public override bool Equals(object other) {
       return Equals(other as ClientConfig);
@@ -1227,6 +1243,7 @@
       if(!channelArgs_.Equals(other.channelArgs_)) return false;
       if (ThreadsPerCq != other.ThreadsPerCq) return false;
       if (MessagesPerStream != other.MessagesPerStream) return false;
+      if (UseCoalesceApi != other.UseCoalesceApi) return false;
       return true;
     }
 
@@ -1249,6 +1266,7 @@
       hash ^= channelArgs_.GetHashCode();
       if (ThreadsPerCq != 0) hash ^= ThreadsPerCq.GetHashCode();
       if (MessagesPerStream != 0) hash ^= MessagesPerStream.GetHashCode();
+      if (UseCoalesceApi != false) hash ^= UseCoalesceApi.GetHashCode();
       return hash;
     }
 
@@ -1314,6 +1332,10 @@
         output.WriteRawTag(144, 1);
         output.WriteInt32(MessagesPerStream);
       }
+      if (UseCoalesceApi != false) {
+        output.WriteRawTag(152, 1);
+        output.WriteBool(UseCoalesceApi);
+      }
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1361,6 +1383,9 @@
       if (MessagesPerStream != 0) {
         size += 2 + pb::CodedOutputStream.ComputeInt32Size(MessagesPerStream);
       }
+      if (UseCoalesceApi != false) {
+        size += 2 + 1;
+      }
       return size;
     }
 
@@ -1423,6 +1448,9 @@
       if (other.MessagesPerStream != 0) {
         MessagesPerStream = other.MessagesPerStream;
       }
+      if (other.UseCoalesceApi != false) {
+        UseCoalesceApi = other.UseCoalesceApi;
+      }
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1510,6 +1538,10 @@
             MessagesPerStream = input.ReadInt32();
             break;
           }
+          case 152: {
+            UseCoalesceApi = input.ReadBool();
+            break;
+          }
         }
       }
     }
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index c02c984..ba2107a 100755
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -19,7 +19,7 @@
   <ItemGroup>
     <PackageReference Include="Google.Protobuf" Version="$(GoogleProtobufVersion)" />
     <PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
-    <PackageReference Include="Moq" Version="4.7.0" />
+    <PackageReference Include="Moq" Version="4.8.2" />
     <PackageReference Include="NUnit" Version="3.6.0" />
     <PackageReference Include="NUnitLite" Version="3.6.0" />
   </ItemGroup>
diff --git a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
index f71d6d1..d18b9e7 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
@@ -26,9 +26,6 @@
 #pragma warning disable 1591
 #region Designer generated code
 
-using System;
-using System.Threading;
-using System.Threading.Tasks;
 using grpc = global::Grpc.Core;
 
 namespace Grpc.Testing {
@@ -121,7 +118,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetAllGauges(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -144,7 +141,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetGauge(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -166,7 +163,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return GetGaugeAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
diff --git a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
index d2e4f2e..46b328a 100644
--- a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
@@ -22,9 +22,6 @@
 #pragma warning disable 1591
 #region Designer generated code
 
-using System;
-using System.Threading;
-using System.Threading.Tasks;
 using grpc = global::Grpc.Core;
 
 namespace Grpc.Testing {
@@ -177,7 +174,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -201,7 +198,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -225,7 +222,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingCall(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -248,7 +245,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingFromClient(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -271,7 +268,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.SimpleResponse> StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.SimpleResponse> StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingFromServer(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -294,7 +291,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingBothWays(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingBothWays(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingBothWays(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -470,7 +467,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return RunServer(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -500,7 +497,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return RunClient(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -526,7 +523,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CoreCount(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -548,7 +545,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CoreCountAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -570,7 +567,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return QuitWorker(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -592,7 +589,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return QuitWorkerAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -692,7 +689,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.Void ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Void ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ReportScenario(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -714,7 +711,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> ReportScenarioAsync(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> ReportScenarioAsync(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ReportScenarioAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
index c0d147c..6c4b77f 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
@@ -23,9 +23,6 @@
 #pragma warning disable 1591
 #region Designer generated code
 
-using System;
-using System.Threading;
-using System.Threading.Tasks;
 using grpc = global::Grpc.Core;
 
 namespace Grpc.Testing {
@@ -244,7 +241,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return EmptyCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -266,7 +263,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return EmptyCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -288,7 +285,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -310,7 +307,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -334,7 +331,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CacheableUnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -360,7 +357,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return CacheableUnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -385,7 +382,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingOutputCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -408,7 +405,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StreamingInputCall(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -431,7 +428,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return FullDuplexCall(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -456,7 +453,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return HalfDuplexCall(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -481,7 +478,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -505,7 +502,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -613,7 +610,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The response received from the server.</returns>
-      public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCall(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -635,7 +632,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return UnimplementedCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -734,7 +731,7 @@
       {
       }
 
-      public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Start(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -742,7 +739,7 @@
       {
         return CallInvoker.BlockingUnaryCall(__Method_Start, null, options, request);
       }
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StartAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -750,7 +747,7 @@
       {
         return CallInvoker.AsyncUnaryCall(__Method_Start, null, options, request);
       }
-      public virtual global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return Stop(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
@@ -758,7 +755,7 @@
       {
         return CallInvoker.BlockingUnaryCall(__Method_Stop, null, options, request);
       }
-      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return StopAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
       }
diff --git a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
index 0195186..e2263cf 100644
--- a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
+++ b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
@@ -22,9 +22,6 @@
 #pragma warning disable 1591
 #region Designer generated code
 
-using System;
-using System.Threading;
-using System.Threading.Tasks;
 using grpc = global::Grpc.Core;
 
 namespace Grpc.Reflection.V1Alpha {
@@ -97,7 +94,7 @@
       /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
       /// <param name="cancellationToken">An optional token for canceling the call.</param>
       /// <returns>The call object.</returns>
-      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
       {
         return ServerReflectionInfo(new grpc::CallOptions(headers, deadline, cancellationToken));
       }
diff --git a/src/csharp/build_packages_dotnetcli.bat b/src/csharp/build_packages_dotnetcli.bat
index 4087d8b..4dd4947 100755
--- a/src/csharp/build_packages_dotnetcli.bat
+++ b/src/csharp/build_packages_dotnetcli.bat
@@ -13,7 +13,7 @@
 @rem limitations under the License.
 
 @rem Current package versions
-set VERSION=1.10.0-dev
+set VERSION=1.11.0-dev
 
 @rem Adjust the location of nuget.exe
 set NUGET=C:\nuget\nuget.exe
diff --git a/src/csharp/build_packages_dotnetcli.sh b/src/csharp/build_packages_dotnetcli.sh
index 8ccc537..e3f8463 100755
--- a/src/csharp/build_packages_dotnetcli.sh
+++ b/src/csharp/build_packages_dotnetcli.sh
@@ -39,7 +39,7 @@
 dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts
 dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts
 
-nuget pack Grpc.nuspec -Version "1.10.0-dev" -OutputDirectory ../../artifacts
-nuget pack Grpc.Tools.nuspec -Version "1.10.0-dev" -OutputDirectory ../../artifacts
+nuget pack Grpc.nuspec -Version "1.11.0-dev" -OutputDirectory ../../artifacts
+nuget pack Grpc.Tools.nuspec -Version "1.11.0-dev" -OutputDirectory ../../artifacts
 
 (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)
diff --git a/src/csharp/experimental/README.md b/src/csharp/experimental/README.md
new file mode 100644
index 0000000..f892a2e
--- /dev/null
+++ b/src/csharp/experimental/README.md
@@ -0,0 +1,16 @@
+This directory contains useful resources for getting gRPC C# to work on
+not-yet-supported platforms.
+
+# Unity & Xamarin
+gRPC C# currently doesn't support Unity or Xamarin, but some proof-of-concept
+work has been done. Some of the resources are shared in this directory to
+ease community work on Unity & Xamarin support.
+
+## Crosscompiling `grpc_csharp_ext` for Android
+
+* Install [Android NDK](https://developer.android.com/ndk/index.html)
+* Run `./build_native_ext_for_android.sh` to crosscompile using cmake.
+
+## Crosscompiling `grpc_csharp_ext` for iOS
+
+TBD
diff --git a/src/csharp/experimental/build_native_ext_for_android.sh b/src/csharp/experimental/build_native_ext_for_android.sh
new file mode 100755
index 0000000..3063d78
--- /dev/null
+++ b/src/csharp/experimental/build_native_ext_for_android.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Copyright 2018 gRPC authors.
+#
+# 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.
+
+# Helper script to crosscompile grpc_csharp_ext native extension for Android.
+
+cd "$(dirname "$0")/../../../cmake"
+
+mkdir -p build
+cd build
+
+# set to the location where Android SDK is installed
+ANDROID_NDK_PATH="$HOME/android-ndk-r16b"
+
+cmake ../.. \
+  -DCMAKE_SYSTEM_NAME=Android \
+  -DCMAKE_SYSTEM_VERSION=15 \
+  -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a \
+  -DCMAKE_ANDROID_NDK="${ANDROID_NDK_PATH}" \
+  -DCMAKE_ANDROID_STL_TYPE=c++_static \
+  -DRUN_HAVE_POSIX_REGEX=0 \
+  -DRUN_HAVE_STD_REGEX=0 \
+  -DRUN_HAVE_STEADY_CLOCK=0 \
+  -DCMAKE_BUILD_TYPE=Release
+
+make grpc_csharp_ext
diff --git a/src/csharp/global.json b/src/csharp/global.json
index e4b797e..815be4b 100644
--- a/src/csharp/global.json
+++ b/src/csharp/global.json
@@ -1,5 +1,5 @@
 {
   "sdk": {
-    "version": "1.0.0"
+    "version": "2.1.4"
   }
 }
diff --git a/src/csharp/tests.json b/src/csharp/tests.json
index 469328a..60f67ff 100644
--- a/src/csharp/tests.json
+++ b/src/csharp/tests.json
@@ -1,5 +1,7 @@
 {
   "Grpc.Core.Tests": [
+    "Grpc.Core.Interceptors.Tests.ClientInterceptorTest",
+    "Grpc.Core.Interceptors.Tests.ServerInterceptorTest",
     "Grpc.Core.Internal.Tests.AsyncCallServerTest",
     "Grpc.Core.Internal.Tests.AsyncCallTest",
     "Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest",
@@ -59,4 +61,4 @@
     "Grpc.Reflection.Tests.ReflectionClientServerTest",
     "Grpc.Reflection.Tests.SymbolRegistryTest"
   ]
-}
\ No newline at end of file
+}
diff --git "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec" "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
index 037ad4d..954beed 100644
--- "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
+++ "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
@@ -42,7 +42,7 @@
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.10.0-dev'
+  v = '1.11.0-dev'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
diff --git a/src/objective-c/GRPCClient/GRPCCall+MobileLog.h b/src/objective-c/GRPCClient/GRPCCall+MobileLog.h
new file mode 100644
index 0000000..53b347d
--- /dev/null
+++ b/src/objective-c/GRPCClient/GRPCCall+MobileLog.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+#import "GRPCCall.h"
+
+@interface GRPCCall (MobileLog)
+// Set the object to be passed down along channel stack with channel arg
+// GRPC_ARG_MOBILE_LOG_CONFIG. The setting may be used by custom channel
+// filters for metrics logging.
++ (void)setLogConfig:(id)logConfig;
+
+// Obtain the object to be passed down along channel stack with channel arg
+// GRPC_ARG_MOBILE_LOG_CONFIG.
++ (id)logConfig;
+@end
diff --git a/src/objective-c/GRPCClient/GRPCCall+MobileLog.m b/src/objective-c/GRPCClient/GRPCCall+MobileLog.m
new file mode 100644
index 0000000..4dedb7d
--- /dev/null
+++ b/src/objective-c/GRPCClient/GRPCCall+MobileLog.m
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+#import "GRPCCall+MobileLog.h"
+
+static id globalLogConfig = nil;
+
+@implementation GRPCCall (MobileLog)
+
++ (void)setLogConfig:(id)logConfig {
+  globalLogConfig = logConfig;
+}
+
++ (id)logConfig {
+  return globalLogConfig;
+}
+
+@end
diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index ac4596d..0249260 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -108,6 +108,9 @@
   // The dispatch queue to be used for enqueuing responses to user. Defaulted to the main dispatch
   // queue
   dispatch_queue_t _responseQueue;
+
+  // Whether the call is finished. If it is, should not call finishWithError again.
+  BOOL _finished;
 }
 
 @synthesize state = _state;
@@ -206,6 +209,8 @@
   } else {
     [_responseWriteable enqueueSuccessfulCompletion];
   }
+
+  [GRPCConnectivityMonitor unregisterObserver:self];
 }
 
 - (void)cancelCall {
@@ -214,9 +219,10 @@
 }
 
 - (void)cancel {
-  [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
-                                            code:GRPCErrorCodeCancelled
-                                        userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]];
+  [self maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain
+                                                 code:GRPCErrorCodeCancelled
+                                             userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]];
+
   if (!self.isWaitingForToken) {
     [self cancelCall];
   } else {
@@ -224,6 +230,19 @@
   }
 }
 
+- (void)maybeFinishWithError:(NSError *)errorOrNil {
+  BOOL toFinish = NO;
+  @synchronized(self) {
+    if (_finished == NO) {
+      _finished = YES;
+      toFinish = YES;
+    }
+  }
+  if (toFinish == YES) {
+    [self finishWithError:errorOrNil];
+  }
+}
+
 - (void)dealloc {
   __block GRPCWrappedCall *wrappedCall = _wrappedCall;
   dispatch_async(_callQueue, ^{
@@ -250,11 +269,13 @@
   if (self.state == GRXWriterStatePaused) {
     return;
   }
-  __weak GRPCCall *weakSelf = self;
-  __weak GRXConcurrentWriteable *weakWriteable = _responseWriteable;
 
   dispatch_async(_callQueue, ^{
-    [weakSelf startReadWithHandler:^(grpc_byte_buffer *message) {
+    __weak GRPCCall *weakSelf = self;
+    __weak GRXConcurrentWriteable *weakWriteable = self->_responseWriteable;
+    [self startReadWithHandler:^(grpc_byte_buffer *message) {
+      __strong GRPCCall *strongSelf = weakSelf;
+      __strong GRXConcurrentWriteable *strongWriteable = weakWriteable;
       if (message == NULL) {
         // No more messages from the server
         return;
@@ -266,14 +287,14 @@
         // don't want to throw, because the app shouldn't crash for a behavior
         // that's on the hands of any server to have. Instead we finish and ask
         // the server to cancel.
-        [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
-                                                      code:GRPCErrorCodeResourceExhausted
-                                                  userInfo:@{NSLocalizedDescriptionKey: @"Client does not have enough memory to hold the server response."}]];
-        [weakSelf cancelCall];
+        [strongSelf maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain
+                                                             code:GRPCErrorCodeResourceExhausted
+                                                         userInfo:@{NSLocalizedDescriptionKey: @"Client does not have enough memory to hold the server response."}]];
+        [strongSelf cancelCall];
         return;
       }
-      [weakWriteable enqueueValue:data completionHandler:^{
-        [weakSelf startNextRead];
+      [strongWriteable enqueueValue:data completionHandler:^{
+        [strongSelf startNextRead];
       }];
     }];
   });
@@ -333,12 +354,17 @@
     _requestWriter.state = GRXWriterStatePaused;
   }
 
-  __weak GRPCCall *weakSelf = self;
   dispatch_async(_callQueue, ^{
-    [weakSelf writeMessage:value withErrorHandler:^{
-      [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
-                                                    code:GRPCErrorCodeInternal
-                                                userInfo:nil]];
+    __weak GRPCCall *weakSelf = self;
+    [self writeMessage:value withErrorHandler:^{
+      __strong GRPCCall *strongSelf = weakSelf;
+      if (strongSelf != nil) {
+        [strongSelf maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain
+                                                             code:GRPCErrorCodeInternal
+                                                         userInfo:nil]];
+        // Wrapped call must be canceled when error is reported to upper layers
+        [strongSelf cancelCall];
+      }
     }];
   });
 }
@@ -360,12 +386,15 @@
   if (errorOrNil) {
     [self cancel];
   } else {
-    __weak GRPCCall *weakSelf = self;
     dispatch_async(_callQueue, ^{
-      [weakSelf finishRequestWithErrorHandler:^{
-        [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
-                                                      code:GRPCErrorCodeInternal
-                                                  userInfo:nil]];
+      __weak GRPCCall *weakSelf = self;
+      [self finishRequestWithErrorHandler:^{
+        __strong GRPCCall *strongSelf = weakSelf;
+        [strongSelf maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain
+                                                             code:GRPCErrorCodeInternal
+                                                         userInfo:nil]];
+        // Wrapped call must be canceled when error is reported to upper layers
+        [strongSelf cancelCall];
       }];
     });
   }
@@ -387,30 +416,37 @@
 }
 
 - (void)invokeCall {
+  __weak GRPCCall *weakSelf = self;
   [self invokeCallWithHeadersHandler:^(NSDictionary *headers) {
     // Response headers received.
-    self.responseHeaders = headers;
-    [self startNextRead];
-  } completionHandler:^(NSError *error, NSDictionary *trailers) {
-    self.responseTrailers = trailers;
-
-    if (error) {
-      NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
-      if (error.userInfo) {
-        [userInfo addEntriesFromDictionary:error.userInfo];
-      }
-      userInfo[kGRPCTrailersKey] = self.responseTrailers;
-      // TODO(jcanizales): The C gRPC library doesn't guarantee that the headers block will be
-      // called before this one, so an error might end up with trailers but no headers. We
-      // shouldn't call finishWithError until ater both blocks are called. It is also when this is
-      // done that we can provide a merged view of response headers and trailers in a thread-safe
-      // way.
-      if (self.responseHeaders) {
-        userInfo[kGRPCHeadersKey] = self.responseHeaders;
-      }
-      error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
+    __strong GRPCCall *strongSelf = weakSelf;
+    if (strongSelf) {
+      strongSelf.responseHeaders = headers;
+      [strongSelf startNextRead];
     }
-    [self finishWithError:error];
+  } completionHandler:^(NSError *error, NSDictionary *trailers) {
+    __strong GRPCCall *strongSelf = weakSelf;
+    if (strongSelf) {
+      strongSelf.responseTrailers = trailers;
+
+      if (error) {
+        NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+        if (error.userInfo) {
+          [userInfo addEntriesFromDictionary:error.userInfo];
+        }
+        userInfo[kGRPCTrailersKey] = strongSelf.responseTrailers;
+        // TODO(jcanizales): The C gRPC library doesn't guarantee that the headers block will be
+        // called before this one, so an error might end up with trailers but no headers. We
+        // shouldn't call finishWithError until ater both blocks are called. It is also when this is
+        // done that we can provide a merged view of response headers and trailers in a thread-safe
+        // way.
+        if (strongSelf.responseHeaders) {
+          userInfo[kGRPCHeadersKey] = strongSelf.responseHeaders;
+        }
+        error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
+      }
+      [strongSelf maybeFinishWithError:error];
+    }
   }];
   // Now that the RPC has been initiated, request writes can start.
   @synchronized(_requestWriter) {
@@ -439,16 +475,8 @@
     // TODO(jcanizales): Check this on init.
     [NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host];
   }
-  _connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host];
-  __weak typeof(self) weakSelf = self;
-  void (^handler)(void) = ^{
-    typeof(self) strongSelf = weakSelf;
-    [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
-                                                    code:GRPCErrorCodeUnavailable
-                                                userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]];
-  };
-  [_connectivityMonitor handleLossWithHandler:handler
-                      wifiStatusChangeHandler:nil];
+  [GRPCConnectivityMonitor registerObserver:self
+                                   selector:@selector(connectivityChanged:)];
 }
 
 - (void)startWithWriteable:(id<GRXWriteable>)writeable {
@@ -512,4 +540,12 @@
   }
 }
 
+- (void)connectivityChanged:(NSNotification *)note {
+  [self maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain
+                                                 code:GRPCErrorCodeUnavailable
+                                             userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]];
+  // Cancel underlying call upon this notification
+  [self cancelCall];
+}
+
 @end
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m
index 26efe90..b53d841 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.m
@@ -32,6 +32,24 @@
 #endif
 #import "GRPCCompletionQueue.h"
 
+static void* copy_pointer_arg(void *p) {
+  // Add ref count to the object when making copy
+  id obj = (__bridge id)p;
+  return (__bridge_retained void *)obj;
+}
+
+static void destroy_pointer_arg(void *p) {
+  // Decrease ref count to the object when destroying
+  CFRelease((CFTreeRef)p);
+}
+
+static int cmp_pointer_arg(void *p, void *q) {
+  return p == q;
+}
+
+static const grpc_arg_pointer_vtable objc_arg_vtable = {
+  copy_pointer_arg, destroy_pointer_arg, cmp_pointer_arg};
+
 static void FreeChannelArgs(grpc_channel_args *channel_args) {
   for (size_t i = 0; i < channel_args->num_args; ++i) {
     grpc_arg *arg = &channel_args->args[i];
@@ -75,6 +93,10 @@
     } else if ([value respondsToSelector:@selector(intValue)]) {
       arg->type = GRPC_ARG_INTEGER;
       arg->value.integer = [value intValue];
+    } else if (value != nil) {
+      arg->type = GRPC_ARG_POINTER;
+      arg->value.pointer.p = (__bridge_retained void *)value;
+      arg->value.pointer.vtable = &objc_arg_vtable;
     } else {
       [NSException raise:NSInvalidArgumentException
                   format:@"Invalid value type: %@", [value class]];
@@ -188,8 +210,7 @@
   if (timeout < 0) {
     timeout = 0;
   }
-  grpc_slice host_slice;
-  memset(&host_slice, 0, sizeof(host_slice));
+  grpc_slice host_slice = grpc_empty_slice();
   if (serverName) {
     host_slice = grpc_slice_from_copied_string(serverName.UTF8String);
   }
diff --git a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h
index cb55e46..394d217 100644
--- a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h
+++ b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h
@@ -19,44 +19,30 @@
 #import <Foundation/Foundation.h>
 #import <SystemConfiguration/SystemConfiguration.h>
 
-@interface GRPCReachabilityFlags : NSObject
+typedef NS_ENUM(NSInteger, GRPCConnectivityStatus) {
+  GRPCConnectivityUnknown = 0,
+  GRPCConnectivityNoNetwork = 1,
+  GRPCConnectivityCellular = 2,
+  GRPCConnectivityWiFi = 3,
+};
 
-+ (nonnull instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags;
+extern NSString * _Nonnull kGRPCConnectivityNotification;
 
-/**
- * One accessor method to query each of the different flags. Example:
-
-@property(nonatomic, readonly) BOOL isCell;
-
- */
-#define GRPC_XMACRO_ITEM(methodName, FlagName) \
-@property(nonatomic, readonly) BOOL methodName;
-
-#include "GRPCReachabilityFlagNames.xmacro.h"
-#undef GRPC_XMACRO_ITEM
-
-@property(nonatomic, readonly) BOOL isHostReachable;
-@end
-
+// This interface monitors OS reachability interface for any network status
+// change. Parties interested in these events should register themselves as
+// observer.
 @interface GRPCConnectivityMonitor : NSObject
 
-+ (nullable instancetype)monitorWithHost:(nonnull NSString *)hostName;
-
 - (nonnull instancetype)init NS_UNAVAILABLE;
 
-/**
- * Queue on which callbacks will be dispatched. Default is the main queue. Set it before calling
- * handleLossWithHandler:.
- */
-// TODO(jcanizales): Default to a serial background queue instead.
-@property(nonatomic, strong, null_resettable) dispatch_queue_t queue;
+// Register an object as observer of network status change. \a observer
+// must have a notification method with one parameter of type
+// (NSNotification *) and should pass it to parameter \a selector. The
+// parameter of this notification method is not used for now.
++ (void)registerObserver:(_Nonnull id)observer
+                selector:(_Nonnull SEL)selector;
 
-/**
- * Calls handler every time the connectivity to this instance's host is lost. If this instance is
- * released before that happens, the handler won't be called.
- * Only one handler is active at a time, so if this method is called again before the previous
- * handler has been called, it might never be called at all (or yes, if it has already been queued).
- */
-- (void)handleLossWithHandler:(nullable void (^)(void))lossHandler
-      wifiStatusChangeHandler:(nullable void (^)(void))wifiStatusChangeHandler;
+// Ungegister an object from observers of network status change.
++ (void)unregisterObserver:(_Nonnull id)observer;
+
 @end
diff --git a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m
index c8e10dd..7f31c7e 100644
--- a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m
+++ b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m
@@ -18,175 +18,74 @@
 
 #import "GRPCConnectivityMonitor.h"
 
-#pragma mark Flags
+#include <netinet/in.h>
 
-@implementation GRPCReachabilityFlags {
-  SCNetworkReachabilityFlags _flags;
-}
+NSString *kGRPCConnectivityNotification = @"kGRPCConnectivityNotification";
 
-+ (instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags {
-  return [[self alloc] initWithFlags:flags];
-}
+static SCNetworkReachabilityRef reachability;
+static GRPCConnectivityStatus currentStatus;
 
-- (instancetype)initWithFlags:(SCNetworkReachabilityFlags)flags {
-  if ((self = [super init])) {
-    _flags = flags;
+// Aggregate information in flags into network status.
+GRPCConnectivityStatus CalculateConnectivityStatus(SCNetworkReachabilityFlags flags) {
+  GRPCConnectivityStatus result = GRPCConnectivityUnknown;
+  if (((flags & kSCNetworkReachabilityFlagsReachable) == 0) ||
+      ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0)) {
+    return GRPCConnectivityNoNetwork;
   }
-  return self;
-}
-
-/*
- * One accessor method implementation per flag. Example:
-
-- (BOOL)isCell { \
-  return !!(_flags & kSCNetworkReachabilityFlagsIsWWAN); \
-}
-
- */
-#define GRPC_XMACRO_ITEM(methodName, FlagName) \
-- (BOOL)methodName { \
-  return !!(_flags & kSCNetworkReachabilityFlags ## FlagName); \
-}
-#include "GRPCReachabilityFlagNames.xmacro.h"
-#undef GRPC_XMACRO_ITEM
-
-- (BOOL)isHostReachable {
-  // Note: connectionOnDemand means it'll be reachable only if using the CFSocketStream API or APIs
-  // on top of it.
-  // connectionRequired means we can't tell until a connection is attempted (e.g. for VPN on
-  // demand).
-  return self.reachable && !self.interventionRequired && !self.connectionOnDemand;
-}
-
-- (NSString *)description {
-  NSMutableArray *activeOptions = [NSMutableArray arrayWithCapacity:9];
-
-  /*
-   * For each flag, add its name to the array if it's ON. Example:
-
-  if (self.isCell) {
-    [activeOptions addObject:@"isCell"];
-  }
-
-   */
-  #define GRPC_XMACRO_ITEM(methodName, FlagName) \
-    if (self.methodName) {                       \
-      [activeOptions addObject:@ #methodName];   \
-    }
-  #include "GRPCReachabilityFlagNames.xmacro.h"
-  #undef GRPC_XMACRO_ITEM
-
-  return activeOptions.count == 0 ? @"(none)" : [activeOptions componentsJoinedByString:@", "];
-}
-
-- (BOOL)isEqual:(id)object {
-  return [object isKindOfClass:[GRPCReachabilityFlags class]] &&
-      _flags == ((GRPCReachabilityFlags *)object)->_flags;
-}
-
-- (NSUInteger)hash {
-  return _flags;
-}
-@end
-
-#pragma mark Connectivity Monitor
-
-// Assumes the third argument is a block that accepts a GRPCReachabilityFlags object, and passes the
-// received ones to it.
-static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,
-                                        SCNetworkReachabilityFlags flags,
-                                        void *info) {
-  #pragma unused (target)
-  // This can be called many times with the same info. The info is retained by SCNetworkReachability
-  // while this function is being executed.
-  void (^handler)(GRPCReachabilityFlags *) = (__bridge void (^)(GRPCReachabilityFlags *))info;
-  handler([[GRPCReachabilityFlags alloc] initWithFlags:flags]);
-}
-
-@implementation GRPCConnectivityMonitor {
-  SCNetworkReachabilityRef _reachabilityRef;
-  GRPCReachabilityFlags *_previousReachabilityFlags;
-}
-
-- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability {
-  if (!reachability) {
-    return nil;
-  }
-  if ((self = [super init])) {
-    _reachabilityRef = CFRetain(reachability);
-    _queue = dispatch_get_main_queue();
-    _previousReachabilityFlags = nil;
-  }
-  return self;
-}
-
-+ (nullable instancetype)monitorWithHost:(nonnull NSString *)host {
-  const char *hostName = host.UTF8String;
-  if (!hostName) {
-    [NSException raise:NSInvalidArgumentException
-                format:@"host.UTF8String returns NULL for %@", host];
-  }
-  SCNetworkReachabilityRef reachability =
-      SCNetworkReachabilityCreateWithName(NULL, hostName);
-
-  GRPCConnectivityMonitor *returnValue = [[self alloc] initWithReachability:reachability];
-  if (reachability) {
-    CFRelease(reachability);
-  }
-  return returnValue;
-}
-
-- (void)handleLossWithHandler:(nullable void (^)(void))lossHandler
-      wifiStatusChangeHandler:(nullable void (^)(void))wifiStatusChangeHandler {
-  __weak typeof(self) weakSelf = self;
-  [self startListeningWithHandler:^(GRPCReachabilityFlags *flags) {
-    typeof(self) strongSelf = weakSelf;
-    if (strongSelf) {
-      if (lossHandler && !flags.reachable) {
-        lossHandler();
+  result = GRPCConnectivityWiFi;
 #if TARGET_OS_IPHONE
-      } else if (wifiStatusChangeHandler &&
-                 strongSelf->_previousReachabilityFlags &&
-                 (flags.isWWAN ^
-                  strongSelf->_previousReachabilityFlags.isWWAN)) {
-        wifiStatusChangeHandler();
-#endif
-      }
-      strongSelf->_previousReachabilityFlags = flags;
-    }
-  }];
-}
-
-- (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler {
-  // Copy to ensure the handler block is in the heap (and so can't be deallocated when this method
-  // returns).
-  void (^copiedHandler)(GRPCReachabilityFlags *) = [handler copy];
-  SCNetworkReachabilityContext context = {
-    .version = 0,
-    .info = (__bridge void *)copiedHandler,
-    .retain = CFRetain,
-    .release = CFRelease,
-  };
-  // The following will retain context.info, and release it when the callback is set to NULL.
-  SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context);
-  SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue);
-}
-
-- (void)stopListening {
-  // This releases the block on context.info.
-  SCNetworkReachabilitySetCallback(_reachabilityRef, NULL, NULL);
-  SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, NULL);
-}
-
-- (void)setQueue:(dispatch_queue_t)queue {
-  _queue = queue ?: dispatch_get_main_queue();
-}
-
-- (void)dealloc {
-  if (_reachabilityRef) {
-    [self stopListening];
-    CFRelease(_reachabilityRef);
+  if (flags & kSCNetworkReachabilityFlagsIsWWAN) {
+    return result = GRPCConnectivityCellular;
   }
+#endif
+  return result;
+}
+
+static void ReachabilityCallback(
+    SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
+  GRPCConnectivityStatus newStatus = CalculateConnectivityStatus(flags);
+
+  if (newStatus != currentStatus) {
+    [[NSNotificationCenter defaultCenter] postNotificationName:kGRPCConnectivityNotification
+                                                        object:nil];
+    currentStatus = newStatus;
+  }
+}
+
+@implementation GRPCConnectivityMonitor
+
++ (void)initialize {
+  if (self == [GRPCConnectivityMonitor self]) {
+    struct sockaddr_in addr = {0};
+    addr.sin_len = sizeof(addr);
+    addr.sin_family = AF_INET;
+    reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&addr);
+    currentStatus = GRPCConnectivityUnknown;
+
+    SCNetworkConnectionFlags flags;
+    if (SCNetworkReachabilityGetFlags(reachability, &flags)) {
+      currentStatus = CalculateConnectivityStatus(flags);
+    }
+
+    SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
+    if (!SCNetworkReachabilitySetCallback(reachability, ReachabilityCallback, &context) ||
+        !SCNetworkReachabilityScheduleWithRunLoop(
+            reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)) {
+      NSLog(@"gRPC connectivity monitor fail to set");
+    }
+  }
+}
+
++ (void)registerObserver:(_Nonnull id)observer
+                selector:(SEL)selector {
+  [[NSNotificationCenter defaultCenter] addObserver:observer
+                                           selector:selector
+                                               name:kGRPCConnectivityNotification
+                                             object:nil];
+}
+
++ (void)unregisterObserver:(_Nonnull id)observer {
+  [[NSNotificationCenter defaultCenter] removeObserver:observer];
 }
 
 @end
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index ceae960..8568e33 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -21,6 +21,7 @@
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
 #import <GRPCClient/GRPCCall.h>
+#import <GRPCClient/GRPCCall+MobileLog.h>
 #ifdef GRPC_COMPILE_WITH_CRONET
 #import <GRPCClient/GRPCCall+ChannelArg.h>
 #import <GRPCClient/GRPCCall+Cronet.h>
@@ -36,12 +37,6 @@
 
 static NSMutableDictionary *kHostCache;
 
-// This connectivity monitor flushes the host cache when connectivity status
-// changes or when connection switch between Wifi and Cellular data, so that a
-// new call will use a new channel. Otherwise, a new call will still use the
-// cached channel which is no longer available and will cause gRPC to hang.
-static GRPCConnectivityMonitor *connectivityMonitor = nil;
-
 @implementation GRPCHost {
   // TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
   GRPCChannel *_channel;
@@ -89,17 +84,7 @@
       kHostCache[address] = self;
       _compressAlgorithm = GRPC_COMPRESS_NONE;
     }
-    // Keep a single monitor to flush the cache if the connectivity status changes
-    // Thread safety guarded by @synchronized(kHostCache)
-    if (!connectivityMonitor) {
-      connectivityMonitor =
-      [GRPCConnectivityMonitor monitorWithHost:hostURL.host];
-      void (^handler)(void) = ^{
-        [GRPCHost flushChannelCache];
-      };
-      [connectivityMonitor handleLossWithHandler:handler
-                         wifiStatusChangeHandler:handler];
-    }
+    [GRPCConnectivityMonitor registerObserver:self selector:@selector(connectivityChange:)];
   }
   return self;
 }
@@ -231,6 +216,11 @@
         [NSNumber numberWithInt:_compressAlgorithm];
   }
 
+  id logConfig = [GRPCCall logConfig];
+  if (logConfig != nil) {
+    args[@GRPC_ARG_MOBILE_LOG_CONFIG] = logConfig;
+  }
+
   return args;
 }
 
@@ -275,6 +265,13 @@
   }
 }
 
+// Flushes the host cache when connectivity status changes or when connection switch between Wifi
+// and Cellular data, so that a new call will use a new channel. Otherwise, a new call will still
+// use the cached channel which is no longer available and will cause gRPC to hang.
+- (void)connectivityChange:(NSNotification *)note {
+  [GRPCHost flushChannelCache];
+}
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/src/objective-c/GRPCClient/private/version.h b/src/objective-c/GRPCClient/private/version.h
index 5c134e3..405c2ff 100644
--- a/src/objective-c/GRPCClient/private/version.h
+++ b/src/objective-c/GRPCClient/private/version.h
@@ -23,4 +23,4 @@
 // `tools/buildgen/generate_projects.sh`.
 
 
-#define GRPC_OBJC_VERSION_STRING @"1.10.0-dev"
+#define GRPC_OBJC_VERSION_STRING @"1.11.0-dev"
diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.mm b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.mm
index d91b5cf..33ccdb5 100644
--- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.mm
+++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.mm
@@ -224,8 +224,7 @@
 }
 
 - (void)testBinaryMetadata {
-  // NOT SUPPORTED
-  //[self testIndividualCase:(char *)"binary_metadata"];
+  [self testIndividualCase:(char *)"binary_metadata"];
 }
 
 - (void)testCallCreds {
diff --git a/src/objective-c/tests/version.h b/src/objective-c/tests/version.h
index 5140aa2..6f6cd25 100644
--- a/src/objective-c/tests/version.h
+++ b/src/objective-c/tests/version.h
@@ -23,5 +23,5 @@
 // `tools/buildgen/generate_projects.sh`.
 
 
-#define GRPC_OBJC_VERSION_STRING @"1.10.0-dev"
+#define GRPC_OBJC_VERSION_STRING @"1.11.0-dev"
 #define GRPC_C_VERSION_STRING @"6.0.0-dev"
diff --git a/src/php/composer.json b/src/php/composer.json
index ea21417..dbf0cc3 100644
--- a/src/php/composer.json
+++ b/src/php/composer.json
@@ -2,7 +2,7 @@
   "name": "grpc/grpc-dev",
   "description": "gRPC library for PHP - for Developement use only",
   "license": "Apache-2.0",
-  "version": "1.10.0",
+  "version": "1.11.0",
   "require": {
     "php": ">=5.5.0",
     "google/protobuf": "^v3.3.0"
diff --git a/src/php/ext/grpc/call_credentials.h b/src/php/ext/grpc/call_credentials.h
old mode 100755
new mode 100644
diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h
old mode 100755
new mode 100644
diff --git a/src/php/ext/grpc/channel_credentials.h b/src/php/ext/grpc/channel_credentials.h
old mode 100755
new mode 100644
diff --git a/src/php/ext/grpc/server.h b/src/php/ext/grpc/server.h
old mode 100755
new mode 100644
diff --git a/src/php/ext/grpc/server_credentials.h b/src/php/ext/grpc/server_credentials.h
old mode 100755
new mode 100644
diff --git a/src/php/ext/grpc/timeval.h b/src/php/ext/grpc/timeval.h
old mode 100755
new mode 100644
diff --git a/src/php/ext/grpc/version.h b/src/php/ext/grpc/version.h
index 408f2a4..dd2a701 100644
--- a/src/php/ext/grpc/version.h
+++ b/src/php/ext/grpc/version.h
@@ -20,6 +20,6 @@
 #ifndef VERSION_H
 #define VERSION_H
 
-#define PHP_GRPC_VERSION "1.10.0dev"
+#define PHP_GRPC_VERSION "1.11.0dev"
 
 #endif /* VERSION_H */
diff --git a/src/python/grpcio/grpc/_grpcio_metadata.py b/src/python/grpcio/grpc/_grpcio_metadata.py
index 6032828..4a69d85 100644
--- a/src/python/grpcio/grpc/_grpcio_metadata.py
+++ b/src/python/grpcio/grpc/_grpcio_metadata.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc/_grpcio_metadata.py.template`!!!
 
-__version__ = """1.10.0.dev0"""
+__version__ = """1.11.0.dev0"""
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 2da3c0b..dd3a8f9 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -41,9 +41,6 @@
     'src/core/lib/gpr/sync.cc',
     'src/core/lib/gpr/sync_posix.cc',
     'src/core/lib/gpr/sync_windows.cc',
-    'src/core/lib/gpr/thd.cc',
-    'src/core/lib/gpr/thd_posix.cc',
-    'src/core/lib/gpr/thd_windows.cc',
     'src/core/lib/gpr/time.cc',
     'src/core/lib/gpr/time_posix.cc',
     'src/core/lib/gpr/time_precise.cc',
@@ -53,6 +50,8 @@
     'src/core/lib/gpr/tmpfile_posix.cc',
     'src/core/lib/gpr/tmpfile_windows.cc',
     'src/core/lib/gpr/wrap_memcpy.cc',
+    'src/core/lib/gprpp/thd_posix.cc',
+    'src/core/lib/gprpp/thd_windows.cc',
     'src/core/lib/profiling/basic_timers.cc',
     'src/core/lib/profiling/stap_timers.cc',
     'src/core/lib/surface/init.cc',
@@ -156,7 +155,6 @@
     'src/core/lib/slice/percent_encoding.cc',
     'src/core/lib/slice/slice.cc',
     'src/core/lib/slice/slice_buffer.cc',
-    'src/core/lib/slice/slice_hash_table.cc',
     'src/core/lib/slice/slice_intern.cc',
     'src/core/lib/slice/slice_string_helpers.cc',
     'src/core/lib/surface/api_trace.cc',
@@ -187,6 +185,7 @@
     'src/core/lib/transport/service_config.cc',
     'src/core/lib/transport/static_metadata.cc',
     'src/core/lib/transport/status_conversion.cc',
+    'src/core/lib/transport/status_metadata.cc',
     'src/core/lib/transport/timeout_encoding.cc',
     'src/core/lib/transport/transport.cc',
     'src/core/lib/transport/transport_op_string.cc',
@@ -221,6 +220,7 @@
     'src/core/ext/filters/http/server/http_server_filter.cc',
     'src/core/lib/http/httpcli_security_connector.cc',
     'src/core/lib/security/context/security_context.cc',
+    'src/core/lib/security/credentials/alts/alts_credentials.cc',
     'src/core/lib/security/credentials/composite/composite_credentials.cc',
     'src/core/lib/security/credentials/credentials.cc',
     'src/core/lib/security/credentials/credentials_metadata.cc',
@@ -234,23 +234,55 @@
     'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc',
     'src/core/lib/security/credentials/plugin/plugin_credentials.cc',
     'src/core/lib/security/credentials/ssl/ssl_credentials.cc',
+    'src/core/lib/security/security_connector/alts_security_connector.cc',
+    'src/core/lib/security/security_connector/security_connector.cc',
     'src/core/lib/security/transport/client_auth_filter.cc',
-    'src/core/lib/security/transport/lb_targets_info.cc',
     'src/core/lib/security/transport/secure_endpoint.cc',
-    'src/core/lib/security/transport/security_connector.cc',
     'src/core/lib/security/transport/security_handshaker.cc',
     'src/core/lib/security/transport/server_auth_filter.cc',
+    'src/core/lib/security/transport/target_authority_table.cc',
     'src/core/lib/security/transport/tsi_error.cc',
     'src/core/lib/security/util/json_util.cc',
     'src/core/lib/surface/init_secure.cc',
-    'src/core/tsi/alts_transport_security.cc',
-    'src/core/tsi/fake_transport_security.cc',
-    'src/core/tsi/ssl_transport_security.cc',
-    'src/core/tsi/transport_security_grpc.cc',
+    'src/core/tsi/alts/crypt/aes_gcm.cc',
+    'src/core/tsi/alts/crypt/gsec.cc',
+    'src/core/tsi/alts/frame_protector/alts_counter.cc',
+    'src/core/tsi/alts/frame_protector/alts_crypter.cc',
+    'src/core/tsi/alts/frame_protector/alts_frame_protector.cc',
+    'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc',
+    'src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc',
+    'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc',
+    'src/core/tsi/alts/frame_protector/frame_handler.cc',
+    'src/core/tsi/alts/handshaker/alts_handshaker_client.cc',
+    'src/core/tsi/alts/handshaker/alts_tsi_event.cc',
+    'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc',
+    'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc',
+    'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc',
+    'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc',
+    'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc',
+    'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc',
+    'src/core/lib/security/credentials/alts/check_gcp_environment.cc',
+    'src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc',
+    'src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc',
+    'src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc',
+    'src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc',
+    'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc',
+    'src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc',
+    'src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc',
+    'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc',
+    'src/core/tsi/alts/handshaker/alts_tsi_utils.cc',
+    'src/core/tsi/alts/handshaker/transport_security_common_api.cc',
+    'src/core/tsi/alts/handshaker/altscontext.pb.c',
+    'src/core/tsi/alts/handshaker/handshaker.pb.c',
+    'src/core/tsi/alts/handshaker/transport_security_common.pb.c',
+    'third_party/nanopb/pb_common.c',
+    'third_party/nanopb/pb_decode.c',
+    'third_party/nanopb/pb_encode.c',
     'src/core/tsi/transport_security.cc',
     'src/core/tsi/transport_security_adapter.cc',
-    'src/core/ext/transport/chttp2/server/chttp2_server.cc',
-    'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc',
+    'src/core/ext/transport/chttp2/client/insecure/channel_create.cc',
+    'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc',
+    'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
     'src/core/ext/filters/client_channel/backup_poller.cc',
     'src/core/ext/filters/client_channel/channel_connectivity.cc',
     'src/core/ext/filters/client_channel/client_channel.cc',
@@ -262,21 +294,26 @@
     'src/core/ext/filters/client_channel/lb_policy.cc',
     'src/core/ext/filters/client_channel/lb_policy_factory.cc',
     'src/core/ext/filters/client_channel/lb_policy_registry.cc',
+    'src/core/ext/filters/client_channel/method_params.cc',
     'src/core/ext/filters/client_channel/parse_address.cc',
     'src/core/ext/filters/client_channel/proxy_mapper.cc',
     'src/core/ext/filters/client_channel/proxy_mapper_registry.cc',
     'src/core/ext/filters/client_channel/resolver.cc',
     'src/core/ext/filters/client_channel/resolver_registry.cc',
     'src/core/ext/filters/client_channel/retry_throttle.cc',
+    'src/core/ext/filters/client_channel/status_util.cc',
     'src/core/ext/filters/client_channel/subchannel.cc',
     'src/core/ext/filters/client_channel/subchannel_index.cc',
     'src/core/ext/filters/client_channel/uri_parser.cc',
     'src/core/ext/filters/deadline/deadline_filter.cc',
-    'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
+    'src/core/tsi/alts_transport_security.cc',
+    'src/core/tsi/fake_transport_security.cc',
+    'src/core/tsi/ssl_transport_security.cc',
+    'src/core/tsi/transport_security_grpc.cc',
+    'src/core/ext/transport/chttp2/server/chttp2_server.cc',
+    'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc',
     'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc',
     'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc',
-    'src/core/ext/transport/chttp2/client/insecure/channel_create.cc',
-    'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc',
     'src/core/ext/transport/inproc/inproc_plugin.cc',
     'src/core/ext/transport/inproc/inproc_transport.cc',
     'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc',
@@ -285,9 +322,6 @@
     'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc',
     'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc',
     'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
-    'third_party/nanopb/pb_common.c',
-    'third_party/nanopb/pb_decode.c',
-    'third_party/nanopb/pb_encode.c',
     'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc',
     'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc',
     'src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc',
diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py
index a654eb0..32e8249 100644
--- a/src/python/grpcio/grpc_version.py
+++ b/src/python/grpcio/grpc_version.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
 
-VERSION = '1.10.0.dev0'
+VERSION = '1.11.0.dev0'
diff --git a/src/python/grpcio_health_checking/grpc_version.py b/src/python/grpcio_health_checking/grpc_version.py
index d3185c6..ad4c85c 100644
--- a/src/python/grpcio_health_checking/grpc_version.py
+++ b/src/python/grpcio_health_checking/grpc_version.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_health_checking/grpc_version.py.template`!!!
 
-VERSION = '1.10.0.dev0'
+VERSION = '1.11.0.dev0'
diff --git a/src/python/grpcio_reflection/grpc_version.py b/src/python/grpcio_reflection/grpc_version.py
index 7203d0d..6322d84 100644
--- a/src/python/grpcio_reflection/grpc_version.py
+++ b/src/python/grpcio_reflection/grpc_version.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_reflection/grpc_version.py.template`!!!
 
-VERSION = '1.10.0.dev0'
+VERSION = '1.11.0.dev0'
diff --git a/src/python/grpcio_testing/grpc_version.py b/src/python/grpcio_testing/grpc_version.py
index bf9e55e..1e75fea 100644
--- a/src/python/grpcio_testing/grpc_version.py
+++ b/src/python/grpcio_testing/grpc_version.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_testing/grpc_version.py.template`!!!
 
-VERSION = '1.10.0.dev0'
+VERSION = '1.11.0.dev0'
diff --git a/src/python/grpcio_tests/grpc_version.py b/src/python/grpcio_tests/grpc_version.py
index 2583e42..0cd7bd2 100644
--- a/src/python/grpcio_tests/grpc_version.py
+++ b/src/python/grpcio_tests/grpc_version.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_tests/grpc_version.py.template`!!!
 
-VERSION = '1.10.0.dev0'
+VERSION = '1.11.0.dev0'
diff --git a/src/ruby/lib/grpc/version.rb b/src/ruby/lib/grpc/version.rb
index 9d9f2f4..256a543 100644
--- a/src/ruby/lib/grpc/version.rb
+++ b/src/ruby/lib/grpc/version.rb
@@ -14,5 +14,5 @@
 
 # GRPC contains the General RPC module.
 module GRPC
-  VERSION = '1.10.0.dev'
+  VERSION = '1.11.0.dev'
 end
diff --git a/src/ruby/tools/version.rb b/src/ruby/tools/version.rb
index 2682294..8dc1623 100644
--- a/src/ruby/tools/version.rb
+++ b/src/ruby/tools/version.rb
@@ -14,6 +14,6 @@
 
 module GRPC
   module Tools
-    VERSION = '1.10.0.dev'
+    VERSION = '1.11.0.dev'
   end
 end
diff --git a/summerofcode/ideas.md b/summerofcode/ideas.md
index d89bc37..de59be8 100644
--- a/summerofcode/ideas.md
+++ b/summerofcode/ideas.md
@@ -29,8 +29,8 @@
 
 1. Support static type-checking of both gRPC Python itself and of code that uses gRPC Python. No one likes dynamic typing and Python is finally outgrowing it! There are probably errors in the implementation of gRPC Python that [pytype](https://github.com/google/pytype) or [mypy](http://mypy-lang.org/) could detect. There are certainly errors in other code that uses gRPC Python that they could detect.
     * **Required skills:** Python programming language, open source development across multiple repositories and projects.
-    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Kailash Sethuraman](https://github.com/hsaliak), [Ken Payson](https://github.com/kpayson64), [Mehrdad Afshari](https://github.com/mehrdada).
+    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Kailash Sethuraman](https://github.com/hsaliak).
 
 1. [Enable building of gRPC Python with Bazel](https://github.com/grpc/grpc/issues/8079). Bazel is the designated replacement for our constellation of crufty build scripts, but it's still under active development itself. Up for a challenge? gRPC Python could easily be the most complex codebase to be built with Bazel.
     * **Required skills:** Python programming language, Bazel toolchain, Cython, open source development across multiple repositories and projects.
-    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Ken Payson](https://github.com/kpayson64), [Mehrdad Afshari](https://github.com/mehrdada).
+    * **Likely mentors:** [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle).
diff --git a/templates/CMakeLists.txt.template b/templates/CMakeLists.txt.template
index c279bef..ef4d7d7 100644
--- a/templates/CMakeLists.txt.template
+++ b/templates/CMakeLists.txt.template
@@ -84,6 +84,7 @@
 
   # Options
   option(gRPC_BUILD_TESTS "Build tests" OFF)
+  option(gRPC_BUILD_CODEGEN "Build codegen" ON)
 
   set(gRPC_INSTALL_default ON)
   if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
@@ -125,6 +126,8 @@
       set(_gRPC_PLATFORM_LINUX ON)
     elseif(<%text>${CMAKE_SYSTEM_NAME}</%text> MATCHES "Darwin")
       set(_gRPC_PLATFORM_MAC ON)
+    elseif(<%text>${CMAKE_SYSTEM_NAME}</%text> MATCHES "Android")
+      set(_gRPC_PLATFORM_ANDROID ON)
     else()
       set(_gRPC_PLATFORM_POSIX ON)
     endif()
@@ -135,6 +138,8 @@
 
   ## Some libraries are shared even with BUILD_SHARED_LIBRARIES=OFF
   set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
+  
+  add_definitions(-DPB_FIELD_16BIT)
 
   if (MSVC)
     include(cmake/msvc_static_runtime.cmake)
@@ -168,6 +173,8 @@
 
   if(_gRPC_PLATFORM_MAC)
     set(_gRPC_ALLTARGETS_LIBRARIES <%text>${CMAKE_DL_LIBS}</%text> m pthread)
+  elseif(_gRPC_PLATFORM_ANDROID)
+    set(_gRPC_ALLTARGETS_LIBRARIES <%text>${CMAKE_DL_LIBS}</%text> m)
   elseif(UNIX)
     set(_gRPC_ALLTARGETS_LIBRARIES <%text>${CMAKE_DL_LIBS}</%text> rt m pthread)
   endif()
@@ -304,6 +311,13 @@
   ${cc_binary(tgt)}
   ${get_platforms_condition_end(tgt.platforms)}\
   endif (gRPC_BUILD_TESTS)
+  % elif tgt.build in ["protoc"]:
+  if (gRPC_BUILD_CODEGEN)
+  ${get_platforms_condition_begin(tgt.platforms)}\
+  ${cc_binary(tgt)}
+  ${cc_install(tgt)}
+  ${get_platforms_condition_end(tgt.platforms)}\
+  endif (gRPC_BUILD_CODEGEN)
   % else:
   ${get_platforms_condition_begin(tgt.platforms)}\
   ${cc_binary(tgt)}
@@ -314,6 +328,9 @@
   % endfor
 
   <%def name="cc_library(lib)">
+  % if any(proto_re.match(src) for src in lib.src):
+  if (gRPC_BUILD_CODEGEN)
+  % endif
   add_library(${lib.name}${' SHARED' if lib.get('dll', None) == 'only' else ''}
   % for src in lib.src:
   % if not proto_re.match(src):
@@ -376,6 +393,14 @@
   % endfor
   )
   % endif
+  % if lib.name in ["gpr"]:
+  if (_gRPC_PLATFORM_ANDROID)
+    target_link_libraries(gpr
+      android
+      log
+    )
+  endif (_gRPC_PLATFORM_ANDROID)
+  % endif
 
   % if len(lib.get('public_headers', [])) > 0:
   foreach(_hdr
@@ -390,6 +415,9 @@
     )
   endforeach()
   % endif
+  % if any(proto_re.match(src) for src in lib.src):
+  endif (gRPC_BUILD_CODEGEN)
+  % endif
   </%def>
 
   <%def name="cc_binary(tgt)">
diff --git a/templates/Makefile.template b/templates/Makefile.template
index b8e26b6..196d12f 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -221,6 +221,8 @@
   %  endif
   % endfor
 
+  DEFINES += PB_FIELD_16BIT
+
   CPPFLAGS += $(CPPFLAGS_$(CONFIG))
   CFLAGS += $(CFLAGS_$(CONFIG))
   CXXFLAGS += $(CXXFLAGS_$(CONFIG))
@@ -1378,6 +1380,11 @@
   % endif
   % endfor
 
+  install-grpc-cli: grpc_cli
+  	$(E) "[INSTALL] Installing grpc cli"
+  	$(Q) $(INSTALL) -d $(prefix)/bin
+  	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_cli $(prefix)/bin/grpc_cli
+
   install-pkg-config_c: pc_c pc_c_unsecure
   	$(E) "[INSTALL] Installing C pkg-config files"
   	$(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig
diff --git a/templates/gRPC-C++.podspec.template b/templates/gRPC-C++.podspec.template
index 78adb27..12d5fc1 100644
--- a/templates/gRPC-C++.podspec.template
+++ b/templates/gRPC-C++.podspec.template
@@ -30,6 +30,9 @@
           out += lib.get(group, [])
     return out
 
+  def filter_grpcpp(files):
+    return [file for file in files if not file.startswith("include/grpc++")]
+
   def grpc_private_files(libs):
     out = grpc_lib_files(libs, ("grpc", "gpr"), ("headers", "src"))
     return out
@@ -59,6 +62,9 @@
     # Since some C++ source files directly included private headers in C core, we include all the
     # C core headers in C++ Implementation subspec as well.
     out += [file for file in grpc_private_headers(libs) if not file.startswith("third_party/nanopb/")]
+
+    out = filter_grpcpp(out)
+
     return out
 
   def grpcpp_private_headers(libs, filegroups):
@@ -71,6 +77,8 @@
     # Since some C++ source files directly included private headers in C core, we intentionally
     # keep the C core headers in \a out. But we should exclude nanopb headers.
     out = [file for file in out if not file.startswith("third_party/nanopb/")]
+
+    out = filter_grpcpp(out)
     return out
 
   def grpcpp_public_headers(libs, filegroups):
@@ -81,6 +89,9 @@
     excl_files += grpcpp_proto_files(filegroups)
 
     out = [file for file in out if file not in excl_files]
+
+    out = filter_grpcpp(out)
+
     return out
 
   def grpc_test_util_files(libs):
@@ -91,6 +102,8 @@
     out = grpc_lib_files(libs, ("grpc_test_util", "gpr_test_util"), ("headers",))
     return out
 
+  # Tests subspec is currently disabled since the tests currently use `grpc++` include style instead of `grpcpp`.
+  # TODO (mxyan): enable Tests subspec after the inclusion style is updated in `test/` directory.
   def grpcpp_test_util_files(libs, filegroups):
     out = grpc_lib_files(libs, ("grpc++_test_util",), ("src", "headers"))
     excl_files = grpc_test_util_files(libs) + grpcpp_private_files(libs, filegroups)
@@ -118,7 +131,7 @@
     s.name     = 'gRPC-C++'
     # TODO (mxyan): use version that match gRPC version when pod is stabilized
     # version = '${settings.version}'
-    version = '0.0.1'
+    version = '0.0.2'
     s.version  = version
     s.summary  = 'gRPC C++ library'
     s.homepage = 'https://grpc.io'
@@ -136,8 +149,14 @@
     s.osx.deployment_target = '10.9'
     s.requires_arc = false
 
-    # Add include prefix `grpc++` (i.e. `#include <grpc++/xxx.h>`).
-    s.header_dir = 'grpc++'
+    name = 'grpcpp'
+    # Use `grpcpp` as framework name so that `#include <grpcpp/xxx.h>` works when built as
+    # framework.
+    s.module_name = name
+
+    # Add include prefix `grpcpp` so that `#include <grpcpp/xxx.h>` works when built as static
+    # library.
+    s.header_dir = name
 
     s.pod_target_xcconfig = {
       'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(PODS_TARGET_SRCROOT)/include"',
@@ -157,8 +176,10 @@
 
     s.default_subspecs = 'Interface', 'Implementation'
 
+    s.header_mappings_dir = 'include/grpcpp'
+
     s.subspec 'Interface' do |ss|
-      ss.header_mappings_dir = 'include/grpc++'
+      ss.header_mappings_dir = 'include/grpcpp'
 
       ss.source_files = ${ruby_multiline_list(grpcpp_public_headers(libs, filegroups), 22)}
     end
@@ -174,16 +195,6 @@
       ss.private_header_files = ${ruby_multiline_list(grpcpp_private_headers(libs, filegroups), 30)}
     end
 
-    s.subspec 'Tests' do |ss|
-      ss.header_mappings_dir = '.'
-
-      ss.dependency "#{s.name}/Interface", version
-      ss.dependency "#{s.name}/Implementation", version
-      ss.dependency "gRPC-Core/Tests", grpc_version
-
-      ss.source_files = ${ruby_multiline_list(grpcpp_test_util_files(libs, filegroups), 22)}
-    end
-
     s.prepare_command = <<-END_OF_COMMAND
       find src/cpp/ -type f -exec sed -E -i'.back' 's;#include "third_party/nanopb/(.*)";#include <nanopb/\\1>;g' {} \\\;
       find src/cpp/ -name "*.back" -type f -delete
diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template
index c28b78d..3e80d60 100644
--- a/templates/gRPC-Core.podspec.template
+++ b/templates/gRPC-Core.podspec.template
@@ -144,7 +144,7 @@
     }
 
     s.default_subspecs = 'Interface', 'Implementation'
-    s.compiler_flags = '-DGRPC_ARES=0'
+    s.compiler_flags = '-DGRPC_ARES=0', '-DPB_FIELD_16BIT'
     s.libraries = 'c++'
 
     # Like many other C libraries, gRPC-Core has its public headers under `include/<libname>/` and its
diff --git a/templates/grpc.gyp.template b/templates/grpc.gyp.template
index 3363082..2ea0d06 100644
--- a/templates/grpc.gyp.template
+++ b/templates/grpc.gyp.template
@@ -60,11 +60,11 @@
       % endfor
       'cflags_c': [
         '-Werror',
-        '-std=c99'
+        '-std=c99',
       ],
       'cflags_cc': [
         '-Werror',
-        '-std=c++11'
+        '-std=c++11',
       ],
       'include_dirs': [
         '.',
@@ -127,7 +127,7 @@
               % endfor
               '-stdlib=libc++',
               '-std=c++11',
-              '-Wno-error=deprecated-declarations'
+              '-Wno-error=deprecated-declarations',
             ],
             % endif
           },
diff --git a/templates/src/core/lib/surface/version.cc.template b/templates/src/core/lib/surface/version.cc.template
index d9fa447..0cb3154 100644
--- a/templates/src/core/lib/surface/version.cc.template
+++ b/templates/src/core/lib/surface/version.cc.template
@@ -21,6 +21,8 @@
   /* This file is autogenerated from:
      templates/src/core/surface/version.c.template */
 
+  #include <grpc/support/port_platform.h>
+
   #include <grpc/grpc.h>
 
   const char* grpc_version_string(void) { return "${settings.core_version}"; }
diff --git a/templates/src/core/plugin_registry.template b/templates/src/core/plugin_registry.template
index 805ae90..a00d204 100644
--- a/templates/src/core/plugin_registry.template
+++ b/templates/src/core/plugin_registry.template
@@ -22,6 +22,8 @@
    *
    */
 
+  #include <grpc/support/port_platform.h>
+
   #include <grpc/grpc.h>
 
   %for plugin in selected.plugins:
diff --git a/templates/src/cpp/common/version_cc.cc.template b/templates/src/cpp/common/version_cc.cc.template
index 9882878..a3b0cf6 100644
--- a/templates/src/cpp/common/version_cc.cc.template
+++ b/templates/src/cpp/common/version_cc.cc.template
@@ -21,7 +21,7 @@
   /* This file is autogenerated from:
      templates/src/core/surface/version.c.template */
 
-  #include <grpc++/grpc++.h>
+  #include <grpcpp/grpcpp.h>
 
   namespace grpc {
   grpc::string Version() { return "${settings.cpp_version}"; }
diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_dart/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_dart/Dockerfile.template
new file mode 100644
index 0000000..8b71716
--- /dev/null
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_dart/Dockerfile.template
@@ -0,0 +1,20 @@
+%YAML 1.2
+--- |
+  # Copyright 2017 gRPC authors.
+  #
+  # 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.
+
+  FROM google/dart:latest
+
+  # Define the default command.
+  CMD ["bash"]
diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template b/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template
new file mode 100644
index 0000000..99f60bf
--- /dev/null
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh.template
@@ -0,0 +1,28 @@
+%YAML 1.2
+--- |
+  #!/bin/bash
+  # Copyright 2017 gRPC authors.
+  #
+  # 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.
+  #
+  # Builds Dart interop server and client in a base image.
+  set -e
+
+  mkdir -p /var/local/git
+  git clone /var/local/jenkins/grpc-dart /var/local/git/grpc-dart
+
+  # copy service account keys if available
+  cp -r /var/local/jenkins/service_account $HOME || true
+
+  cd /var/local/git/grpc-dart/interop
+  /usr/lib/dart/bin/pub get
diff --git a/test/core/bad_client/bad_client.cc b/test/core/bad_client/bad_client.cc
index 6055ccb..c03ebcf 100644
--- a/test/core/bad_client/bad_client.cc
+++ b/test/core/bad_client/bad_client.cc
@@ -29,7 +29,7 @@
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/gpr/murmur_hash.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/completion_queue.h"
@@ -220,11 +220,12 @@
   /* Check a ground truth */
   GPR_ASSERT(grpc_server_has_open_connections(a.server));
 
-  gpr_thd_id id;
   gpr_event_init(&a.done_thd);
   a.validator = server_validator;
   /* Start validator */
-  gpr_thd_new(&id, "grpc_bad_client", thd_func, &a, nullptr);
+
+  grpc_core::Thread server_validator_thd("grpc_bad_client", thd_func, &a);
+  server_validator_thd.Start();
   for (int i = 0; i < num_args; i++) {
     grpc_run_client_side_validator(&args[i], i == (num_args - 1) ? flags : 0,
                                    &sfd, client_cq);
@@ -234,6 +235,7 @@
 
   /* Shutdown. */
   shutdown_client(&sfd.client);
+  server_validator_thd.Join();
 
   shutdown_cq = grpc_completion_queue_create_for_pluck(nullptr);
   grpc_server_shutdown_and_notify(a.server, shutdown_cq, nullptr);
diff --git a/test/core/client_channel/BUILD b/test/core/client_channel/BUILD
index c4d12fe..d430b72 100644
--- a/test/core/client_channel/BUILD
+++ b/test/core/client_channel/BUILD
@@ -54,3 +54,14 @@
     ],
 )
 
+grpc_cc_test(
+    name = "status_util_test",
+    srcs = ["status_util_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+    external_deps = [
+        "gtest",
+    ],
+)
diff --git a/test/core/client_channel/resolvers/fake_resolver_test.cc b/test/core/client_channel/resolvers/fake_resolver_test.cc
index 03af895..14caa3e 100644
--- a/test/core/client_channel/resolvers/fake_resolver_test.cc
+++ b/test/core/client_channel/resolvers/fake_resolver_test.cc
@@ -234,6 +234,11 @@
                             grpc_timeout_milliseconds_to_deadline(100)) ==
              nullptr);
   // Clean up.
+  // Note: Need to explicitly unref the resolver and flush the exec_ctx
+  // to make sure that the final resolver callback (with error set to
+  // "Resolver Shutdown") is invoked before on_res_arg goes out of scope.
+  resolver.reset();
+  grpc_core::ExecCtx::Get()->Flush();
   GRPC_COMBINER_UNREF(combiner, "test_fake_resolver");
 }
 
diff --git a/test/core/client_channel/status_util_test.cc b/test/core/client_channel/status_util_test.cc
new file mode 100644
index 0000000..f944990
--- /dev/null
+++ b/test/core/client_channel/status_util_test.cc
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "src/core/ext/filters/client_channel/status_util.h"
+
+#include <gtest/gtest.h>
+
+namespace grpc_core {
+namespace internal {
+namespace {
+
+TEST(StatusCodeSet, Basic) {
+  StatusCodeSet set;
+  EXPECT_TRUE(set.Empty());
+  EXPECT_FALSE(set.Contains(GRPC_STATUS_OK));
+  EXPECT_FALSE(set.Contains(GRPC_STATUS_UNAVAILABLE));
+  set.Add(GRPC_STATUS_OK);
+  EXPECT_FALSE(set.Empty());
+  EXPECT_TRUE(set.Contains(GRPC_STATUS_OK));
+  EXPECT_FALSE(set.Contains(GRPC_STATUS_UNAVAILABLE));
+  set.Add(GRPC_STATUS_UNAVAILABLE);
+  EXPECT_FALSE(set.Empty());
+  EXPECT_TRUE(set.Contains(GRPC_STATUS_OK));
+  EXPECT_TRUE(set.Contains(GRPC_STATUS_UNAVAILABLE));
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/core/end2end/BUILD b/test/core/end2end/BUILD
index f8281bf..952f350 100644
--- a/test/core/end2end/BUILD
+++ b/test/core/end2end/BUILD
@@ -71,4 +71,95 @@
     ],
 )
 
+grpc_cc_test(
+    name = "bad_server_response_test",
+    srcs = ["bad_server_response_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "connection_refused_test",
+    srcs = ["connection_refused_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "dualstack_socket_test",
+    srcs = ["dualstack_socket_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "goaway_server_test",
+    srcs = ["goaway_server_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "invalid_call_argument_test",
+    srcs = ["invalid_call_argument_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "multiple_server_queues_test",
+    srcs = ["multiple_server_queues_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "no_server_test",
+    srcs = ["no_server_test.cc"],
+    language = "C++",
+    deps = [
+        ":cq_verifier",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
 grpc_end2end_tests()
diff --git a/test/core/end2end/bad_server_response_test.cc b/test/core/end2end/bad_server_response_test.cc
index 1af168e..3d133cf 100644
--- a/test/core/end2end/bad_server_response_test.cc
+++ b/test/core/end2end/bad_server_response_test.cc
@@ -31,7 +31,8 @@
 
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
@@ -253,15 +254,17 @@
   gpr_free(pa);
 }
 
-static void poll_server_until_read_done(test_tcp_server* server,
-                                        gpr_event* signal_when_done) {
+static grpc_core::Thread* poll_server_until_read_done(
+    test_tcp_server* server, gpr_event* signal_when_done) {
   gpr_atm_rel_store(&state.done_atm, 0);
   state.write_done = 0;
-  gpr_thd_id id;
   poll_args* pa = static_cast<poll_args*>(gpr_malloc(sizeof(*pa)));
   pa->server = server;
   pa->signal_when_done = signal_when_done;
-  gpr_thd_new(&id, "grpc_poll_server", actually_poll_server, pa, nullptr);
+  auto* th = grpc_core::New<grpc_core::Thread>("grpc_poll_server",
+                                               actually_poll_server, pa);
+  th->Start();
+  return th;
 }
 
 static void run_test(const char* response_payload,
@@ -281,9 +284,11 @@
   state.response_payload_length = response_payload_length;
 
   /* poll server until sending out the response */
-  poll_server_until_read_done(&test_server, &ev);
+  grpc_core::UniquePtr<grpc_core::Thread> thdptr(
+      poll_server_until_read_done(&test_server, &ev));
   start_rpc(server_port, expected_status, expected_detail);
   gpr_event_wait(&ev, gpr_inf_future(GPR_CLOCK_REALTIME));
+  thdptr->Join();
 
   /* clean up */
   grpc_endpoint_shutdown(state.tcp,
diff --git a/test/core/end2end/cq_verifier_uv.cc b/test/core/end2end/cq_verifier_uv.cc
index e23b3ae..45d827e 100644
--- a/test/core/end2end/cq_verifier_uv.cc
+++ b/test/core/end2end/cq_verifier_uv.cc
@@ -58,7 +58,7 @@
 void cq_verifier_destroy(cq_verifier* v) {
   cq_verify(v);
   uv_close((uv_handle_t*)&v->timer, timer_close_cb);
-  while (reinterpret_cast<timer_state>(v->timer.data) != TIMER_CLOSED) {
+  while (static_cast<timer_state>(v->timer.data) != TIMER_CLOSED) {
     uv_run(uv_default_loop(), UV_RUN_NOWAIT);
   }
   gpr_free(v);
@@ -85,7 +85,7 @@
   ev = grpc_completion_queue_next(v->cq, gpr_inf_past(GPR_CLOCK_MONOTONIC),
                                   NULL);
   // Stop the loop if the timer goes off or we get a non-timeout event
-  while ((reinterpret_cast<timer_state>(v->timer.data) != TIMER_TRIGGERED) &&
+  while ((static_cast<timer_state>(v->timer.data) != TIMER_TRIGGERED) &&
          ev.type == GRPC_QUEUE_TIMEOUT) {
     uv_run(uv_default_loop(), UV_RUN_ONCE);
     ev = grpc_completion_queue_next(v->cq, gpr_inf_past(GPR_CLOCK_MONOTONIC),
diff --git a/test/core/end2end/dualstack_socket_test.cc b/test/core/end2end/dualstack_socket_test.cc
index 411d0f2..eb1d043 100644
--- a/test/core/end2end/dualstack_socket_test.cc
+++ b/test/core/end2end/dualstack_socket_test.cc
@@ -166,7 +166,7 @@
   } else {
     /* Give up faster when failure is expected.
        BUG: Setting this to 1000 reveals a memory leak (b/18608927). */
-    deadline = grpc_timeout_milliseconds_to_deadline(3000);
+    deadline = grpc_timeout_milliseconds_to_deadline(8000);
   }
 
   /* Send a trivial request. */
diff --git a/test/core/end2end/end2end_nosec_tests.cc b/test/core/end2end/end2end_nosec_tests.cc
index 6318550..78ddcdb 100644
--- a/test/core/end2end/end2end_nosec_tests.cc
+++ b/test/core/end2end/end2end_nosec_tests.cc
@@ -118,6 +118,36 @@
 extern void request_with_payload_pre_init(void);
 extern void resource_quota_server(grpc_end2end_test_config config);
 extern void resource_quota_server_pre_init(void);
+extern void retry(grpc_end2end_test_config config);
+extern void retry_pre_init(void);
+extern void retry_cancellation(grpc_end2end_test_config config);
+extern void retry_cancellation_pre_init(void);
+extern void retry_disabled(grpc_end2end_test_config config);
+extern void retry_disabled_pre_init(void);
+extern void retry_exceeds_buffer_size_in_initial_batch(grpc_end2end_test_config config);
+extern void retry_exceeds_buffer_size_in_initial_batch_pre_init(void);
+extern void retry_exceeds_buffer_size_in_subsequent_batch(grpc_end2end_test_config config);
+extern void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void);
+extern void retry_non_retriable_status(grpc_end2end_test_config config);
+extern void retry_non_retriable_status_pre_init(void);
+extern void retry_recv_initial_metadata(grpc_end2end_test_config config);
+extern void retry_recv_initial_metadata_pre_init(void);
+extern void retry_recv_message(grpc_end2end_test_config config);
+extern void retry_recv_message_pre_init(void);
+extern void retry_server_pushback_delay(grpc_end2end_test_config config);
+extern void retry_server_pushback_delay_pre_init(void);
+extern void retry_server_pushback_disabled(grpc_end2end_test_config config);
+extern void retry_server_pushback_disabled_pre_init(void);
+extern void retry_streaming(grpc_end2end_test_config config);
+extern void retry_streaming_pre_init(void);
+extern void retry_streaming_after_commit(grpc_end2end_test_config config);
+extern void retry_streaming_after_commit_pre_init(void);
+extern void retry_streaming_succeeds_before_replay_finished(grpc_end2end_test_config config);
+extern void retry_streaming_succeeds_before_replay_finished_pre_init(void);
+extern void retry_throttled(grpc_end2end_test_config config);
+extern void retry_throttled_pre_init(void);
+extern void retry_too_many_attempts(grpc_end2end_test_config config);
+extern void retry_too_many_attempts_pre_init(void);
 extern void server_finishes_request(grpc_end2end_test_config config);
 extern void server_finishes_request_pre_init(void);
 extern void shutdown_finishes_calls(grpc_end2end_test_config config);
@@ -197,6 +227,21 @@
   request_with_flags_pre_init();
   request_with_payload_pre_init();
   resource_quota_server_pre_init();
+  retry_pre_init();
+  retry_cancellation_pre_init();
+  retry_disabled_pre_init();
+  retry_exceeds_buffer_size_in_initial_batch_pre_init();
+  retry_exceeds_buffer_size_in_subsequent_batch_pre_init();
+  retry_non_retriable_status_pre_init();
+  retry_recv_initial_metadata_pre_init();
+  retry_recv_message_pre_init();
+  retry_server_pushback_delay_pre_init();
+  retry_server_pushback_disabled_pre_init();
+  retry_streaming_pre_init();
+  retry_streaming_after_commit_pre_init();
+  retry_streaming_succeeds_before_replay_finished_pre_init();
+  retry_throttled_pre_init();
+  retry_too_many_attempts_pre_init();
   server_finishes_request_pre_init();
   shutdown_finishes_calls_pre_init();
   shutdown_finishes_tags_pre_init();
@@ -265,6 +310,21 @@
     request_with_flags(config);
     request_with_payload(config);
     resource_quota_server(config);
+    retry(config);
+    retry_cancellation(config);
+    retry_disabled(config);
+    retry_exceeds_buffer_size_in_initial_batch(config);
+    retry_exceeds_buffer_size_in_subsequent_batch(config);
+    retry_non_retriable_status(config);
+    retry_recv_initial_metadata(config);
+    retry_recv_message(config);
+    retry_server_pushback_delay(config);
+    retry_server_pushback_disabled(config);
+    retry_streaming(config);
+    retry_streaming_after_commit(config);
+    retry_streaming_succeeds_before_replay_finished(config);
+    retry_throttled(config);
+    retry_too_many_attempts(config);
     server_finishes_request(config);
     shutdown_finishes_calls(config);
     shutdown_finishes_tags(config);
@@ -460,6 +520,66 @@
       resource_quota_server(config);
       continue;
     }
+    if (0 == strcmp("retry", argv[i])) {
+      retry(config);
+      continue;
+    }
+    if (0 == strcmp("retry_cancellation", argv[i])) {
+      retry_cancellation(config);
+      continue;
+    }
+    if (0 == strcmp("retry_disabled", argv[i])) {
+      retry_disabled(config);
+      continue;
+    }
+    if (0 == strcmp("retry_exceeds_buffer_size_in_initial_batch", argv[i])) {
+      retry_exceeds_buffer_size_in_initial_batch(config);
+      continue;
+    }
+    if (0 == strcmp("retry_exceeds_buffer_size_in_subsequent_batch", argv[i])) {
+      retry_exceeds_buffer_size_in_subsequent_batch(config);
+      continue;
+    }
+    if (0 == strcmp("retry_non_retriable_status", argv[i])) {
+      retry_non_retriable_status(config);
+      continue;
+    }
+    if (0 == strcmp("retry_recv_initial_metadata", argv[i])) {
+      retry_recv_initial_metadata(config);
+      continue;
+    }
+    if (0 == strcmp("retry_recv_message", argv[i])) {
+      retry_recv_message(config);
+      continue;
+    }
+    if (0 == strcmp("retry_server_pushback_delay", argv[i])) {
+      retry_server_pushback_delay(config);
+      continue;
+    }
+    if (0 == strcmp("retry_server_pushback_disabled", argv[i])) {
+      retry_server_pushback_disabled(config);
+      continue;
+    }
+    if (0 == strcmp("retry_streaming", argv[i])) {
+      retry_streaming(config);
+      continue;
+    }
+    if (0 == strcmp("retry_streaming_after_commit", argv[i])) {
+      retry_streaming_after_commit(config);
+      continue;
+    }
+    if (0 == strcmp("retry_streaming_succeeds_before_replay_finished", argv[i])) {
+      retry_streaming_succeeds_before_replay_finished(config);
+      continue;
+    }
+    if (0 == strcmp("retry_throttled", argv[i])) {
+      retry_throttled(config);
+      continue;
+    }
+    if (0 == strcmp("retry_too_many_attempts", argv[i])) {
+      retry_too_many_attempts(config);
+      continue;
+    }
     if (0 == strcmp("server_finishes_request", argv[i])) {
       server_finishes_request(config);
       continue;
diff --git a/test/core/end2end/end2end_test.sh b/test/core/end2end/end2end_test.sh
index b1b9a65..5bfb253 100755
--- a/test/core/end2end/end2end_test.sh
+++ b/test/core/end2end/end2end_test.sh
@@ -15,4 +15,8 @@
 # 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.
+if [ -z "$3" ]
+  then
+    export GRPC_POLL_STRATEGY=$3
+fi
 "$1" "$2"
diff --git a/test/core/end2end/end2end_tests.cc b/test/core/end2end/end2end_tests.cc
index 9d8dfd6..fb1e61b 100644
--- a/test/core/end2end/end2end_tests.cc
+++ b/test/core/end2end/end2end_tests.cc
@@ -120,6 +120,36 @@
 extern void request_with_payload_pre_init(void);
 extern void resource_quota_server(grpc_end2end_test_config config);
 extern void resource_quota_server_pre_init(void);
+extern void retry(grpc_end2end_test_config config);
+extern void retry_pre_init(void);
+extern void retry_cancellation(grpc_end2end_test_config config);
+extern void retry_cancellation_pre_init(void);
+extern void retry_disabled(grpc_end2end_test_config config);
+extern void retry_disabled_pre_init(void);
+extern void retry_exceeds_buffer_size_in_initial_batch(grpc_end2end_test_config config);
+extern void retry_exceeds_buffer_size_in_initial_batch_pre_init(void);
+extern void retry_exceeds_buffer_size_in_subsequent_batch(grpc_end2end_test_config config);
+extern void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void);
+extern void retry_non_retriable_status(grpc_end2end_test_config config);
+extern void retry_non_retriable_status_pre_init(void);
+extern void retry_recv_initial_metadata(grpc_end2end_test_config config);
+extern void retry_recv_initial_metadata_pre_init(void);
+extern void retry_recv_message(grpc_end2end_test_config config);
+extern void retry_recv_message_pre_init(void);
+extern void retry_server_pushback_delay(grpc_end2end_test_config config);
+extern void retry_server_pushback_delay_pre_init(void);
+extern void retry_server_pushback_disabled(grpc_end2end_test_config config);
+extern void retry_server_pushback_disabled_pre_init(void);
+extern void retry_streaming(grpc_end2end_test_config config);
+extern void retry_streaming_pre_init(void);
+extern void retry_streaming_after_commit(grpc_end2end_test_config config);
+extern void retry_streaming_after_commit_pre_init(void);
+extern void retry_streaming_succeeds_before_replay_finished(grpc_end2end_test_config config);
+extern void retry_streaming_succeeds_before_replay_finished_pre_init(void);
+extern void retry_throttled(grpc_end2end_test_config config);
+extern void retry_throttled_pre_init(void);
+extern void retry_too_many_attempts(grpc_end2end_test_config config);
+extern void retry_too_many_attempts_pre_init(void);
 extern void server_finishes_request(grpc_end2end_test_config config);
 extern void server_finishes_request_pre_init(void);
 extern void shutdown_finishes_calls(grpc_end2end_test_config config);
@@ -200,6 +230,21 @@
   request_with_flags_pre_init();
   request_with_payload_pre_init();
   resource_quota_server_pre_init();
+  retry_pre_init();
+  retry_cancellation_pre_init();
+  retry_disabled_pre_init();
+  retry_exceeds_buffer_size_in_initial_batch_pre_init();
+  retry_exceeds_buffer_size_in_subsequent_batch_pre_init();
+  retry_non_retriable_status_pre_init();
+  retry_recv_initial_metadata_pre_init();
+  retry_recv_message_pre_init();
+  retry_server_pushback_delay_pre_init();
+  retry_server_pushback_disabled_pre_init();
+  retry_streaming_pre_init();
+  retry_streaming_after_commit_pre_init();
+  retry_streaming_succeeds_before_replay_finished_pre_init();
+  retry_throttled_pre_init();
+  retry_too_many_attempts_pre_init();
   server_finishes_request_pre_init();
   shutdown_finishes_calls_pre_init();
   shutdown_finishes_tags_pre_init();
@@ -269,6 +314,21 @@
     request_with_flags(config);
     request_with_payload(config);
     resource_quota_server(config);
+    retry(config);
+    retry_cancellation(config);
+    retry_disabled(config);
+    retry_exceeds_buffer_size_in_initial_batch(config);
+    retry_exceeds_buffer_size_in_subsequent_batch(config);
+    retry_non_retriable_status(config);
+    retry_recv_initial_metadata(config);
+    retry_recv_message(config);
+    retry_server_pushback_delay(config);
+    retry_server_pushback_disabled(config);
+    retry_streaming(config);
+    retry_streaming_after_commit(config);
+    retry_streaming_succeeds_before_replay_finished(config);
+    retry_throttled(config);
+    retry_too_many_attempts(config);
     server_finishes_request(config);
     shutdown_finishes_calls(config);
     shutdown_finishes_tags(config);
@@ -468,6 +528,66 @@
       resource_quota_server(config);
       continue;
     }
+    if (0 == strcmp("retry", argv[i])) {
+      retry(config);
+      continue;
+    }
+    if (0 == strcmp("retry_cancellation", argv[i])) {
+      retry_cancellation(config);
+      continue;
+    }
+    if (0 == strcmp("retry_disabled", argv[i])) {
+      retry_disabled(config);
+      continue;
+    }
+    if (0 == strcmp("retry_exceeds_buffer_size_in_initial_batch", argv[i])) {
+      retry_exceeds_buffer_size_in_initial_batch(config);
+      continue;
+    }
+    if (0 == strcmp("retry_exceeds_buffer_size_in_subsequent_batch", argv[i])) {
+      retry_exceeds_buffer_size_in_subsequent_batch(config);
+      continue;
+    }
+    if (0 == strcmp("retry_non_retriable_status", argv[i])) {
+      retry_non_retriable_status(config);
+      continue;
+    }
+    if (0 == strcmp("retry_recv_initial_metadata", argv[i])) {
+      retry_recv_initial_metadata(config);
+      continue;
+    }
+    if (0 == strcmp("retry_recv_message", argv[i])) {
+      retry_recv_message(config);
+      continue;
+    }
+    if (0 == strcmp("retry_server_pushback_delay", argv[i])) {
+      retry_server_pushback_delay(config);
+      continue;
+    }
+    if (0 == strcmp("retry_server_pushback_disabled", argv[i])) {
+      retry_server_pushback_disabled(config);
+      continue;
+    }
+    if (0 == strcmp("retry_streaming", argv[i])) {
+      retry_streaming(config);
+      continue;
+    }
+    if (0 == strcmp("retry_streaming_after_commit", argv[i])) {
+      retry_streaming_after_commit(config);
+      continue;
+    }
+    if (0 == strcmp("retry_streaming_succeeds_before_replay_finished", argv[i])) {
+      retry_streaming_succeeds_before_replay_finished(config);
+      continue;
+    }
+    if (0 == strcmp("retry_throttled", argv[i])) {
+      retry_throttled(config);
+      continue;
+    }
+    if (0 == strcmp("retry_too_many_attempts", argv[i])) {
+      retry_too_many_attempts(config);
+      continue;
+    }
     if (0 == strcmp("server_finishes_request", argv[i])) {
       server_finishes_request(config);
       continue;
diff --git a/test/core/end2end/fixtures/h2_census.cc b/test/core/end2end/fixtures/h2_census.cc
index 27b897c..b3b4171 100644
--- a/test/core/end2end/fixtures/h2_census.cc
+++ b/test/core/end2end/fixtures/h2_census.cc
@@ -30,7 +30,6 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/h2_compress.cc b/test/core/end2end/fixtures/h2_compress.cc
index b4ec78d..565c96c 100644
--- a/test/core/end2end/fixtures/h2_compress.cc
+++ b/test/core/end2end/fixtures/h2_compress.cc
@@ -30,7 +30,6 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/h2_full+pipe.cc b/test/core/end2end/fixtures/h2_full+pipe.cc
index e97d078..ed173c1 100644
--- a/test/core/end2end/fixtures/h2_full+pipe.cc
+++ b/test/core/end2end/fixtures/h2_full+pipe.cc
@@ -34,7 +34,6 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
diff --git a/test/core/end2end/fixtures/h2_full+trace.cc b/test/core/end2end/fixtures/h2_full+trace.cc
index 12aa69b..afb86ea 100644
--- a/test/core/end2end/fixtures/h2_full+trace.cc
+++ b/test/core/end2end/fixtures/h2_full+trace.cc
@@ -35,7 +35,6 @@
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/h2_full+workarounds.cc b/test/core/end2end/fixtures/h2_full+workarounds.cc
index c6b358d..bd9ddff 100644
--- a/test/core/end2end/fixtures/h2_full+workarounds.cc
+++ b/test/core/end2end/fixtures/h2_full+workarounds.cc
@@ -31,7 +31,6 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/h2_full.cc b/test/core/end2end/fixtures/h2_full.cc
index 32e3e55..ca61ec8 100644
--- a/test/core/end2end/fixtures/h2_full.cc
+++ b/test/core/end2end/fixtures/h2_full.cc
@@ -29,7 +29,6 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/h2_http_proxy.cc b/test/core/end2end/fixtures/h2_http_proxy.cc
index 017682f..90d0627 100644
--- a/test/core/end2end/fixtures/h2_http_proxy.cc
+++ b/test/core/end2end/fixtures/h2_http_proxy.cc
@@ -31,7 +31,6 @@
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/env.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/end2end/fixtures/http_proxy_fixture.h"
@@ -72,11 +71,12 @@
   /* If testing for proxy auth, add credentials to proxy uri */
   const grpc_arg* proxy_auth_arg =
       grpc_channel_args_find(client_args, GRPC_ARG_HTTP_PROXY_AUTH_CREDS);
-  if (proxy_auth_arg == nullptr || proxy_auth_arg->type != GRPC_ARG_STRING) {
+  const char* proxy_auth_str = grpc_channel_arg_get_string(proxy_auth_arg);
+  if (proxy_auth_str == nullptr) {
     gpr_asprintf(&proxy_uri, "http://%s",
                  grpc_end2end_http_proxy_get_proxy_name(ffd->proxy));
   } else {
-    gpr_asprintf(&proxy_uri, "http://%s@%s", proxy_auth_arg->value.string,
+    gpr_asprintf(&proxy_uri, "http://%s@%s", proxy_auth_str,
                  grpc_end2end_http_proxy_get_proxy_name(ffd->proxy));
   }
   gpr_setenv("http_proxy", proxy_uri);
diff --git a/test/core/end2end/fixtures/h2_load_reporting.cc b/test/core/end2end/fixtures/h2_load_reporting.cc
index 6adc0c1..ec9eedb 100644
--- a/test/core/end2end/fixtures/h2_load_reporting.cc
+++ b/test/core/end2end/fixtures/h2_load_reporting.cc
@@ -31,7 +31,6 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/h2_proxy.cc b/test/core/end2end/fixtures/h2_proxy.cc
index 93dde4f..c97188f 100644
--- a/test/core/end2end/fixtures/h2_proxy.cc
+++ b/test/core/end2end/fixtures/h2_proxy.cc
@@ -29,7 +29,6 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/end2end/fixtures/proxy.h"
@@ -49,7 +48,17 @@
 
 static grpc_channel* create_proxy_client(const char* target,
                                          grpc_channel_args* client_args) {
-  return grpc_insecure_channel_create(target, client_args, nullptr);
+  // Disable retries in proxy client.
+  grpc_arg arg;
+  arg.type = GRPC_ARG_INTEGER;
+  arg.key = const_cast<char*>(GRPC_ARG_ENABLE_RETRIES);
+  arg.value.integer = 0;
+  grpc_channel_args* new_args =
+      grpc_channel_args_copy_and_add(client_args, &arg, 1);
+  grpc_channel* channel =
+      grpc_insecure_channel_create(target, new_args, nullptr);
+  grpc_channel_args_destroy(new_args);
+  return channel;
 }
 
 static const grpc_end2end_proxy_def proxy_def = {create_proxy_server,
diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.cc b/test/core/end2end/fixtures/h2_sockpair+trace.cc
index 5dd5c2a..d539dda 100644
--- a/test/core/end2end/fixtures/h2_sockpair+trace.cc
+++ b/test/core/end2end/fixtures/h2_sockpair+trace.cc
@@ -36,7 +36,6 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/surface/channel.h"
diff --git a/test/core/end2end/fixtures/h2_sockpair.cc b/test/core/end2end/fixtures/h2_sockpair.cc
index 52a7b95..75f6402 100644
--- a/test/core/end2end/fixtures/h2_sockpair.cc
+++ b/test/core/end2end/fixtures/h2_sockpair.cc
@@ -30,7 +30,6 @@
 #include "src/core/ext/filters/http/server/http_server_filter.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/surface/channel.h"
diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.cc b/test/core/end2end/fixtures/h2_sockpair_1byte.cc
index 0d3cb34..9296319 100644
--- a/test/core/end2end/fixtures/h2_sockpair_1byte.cc
+++ b/test/core/end2end/fixtures/h2_sockpair_1byte.cc
@@ -30,7 +30,6 @@
 #include "src/core/ext/filters/http/server/http_server_filter.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/surface/channel.h"
diff --git a/test/core/end2end/fixtures/h2_uds.cc b/test/core/end2end/fixtures/h2_uds.cc
index a97b14f..1b081f9 100644
--- a/test/core/end2end/fixtures/h2_uds.cc
+++ b/test/core/end2end/fixtures/h2_uds.cc
@@ -33,7 +33,6 @@
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/http_proxy_fixture.cc b/test/core/end2end/fixtures/http_proxy_fixture.cc
index 289843e..5835337 100644
--- a/test/core/end2end/fixtures/http_proxy_fixture.cc
+++ b/test/core/end2end/fixtures/http_proxy_fixture.cc
@@ -33,7 +33,7 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/host_port.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -53,7 +53,7 @@
 
 struct grpc_end2end_http_proxy {
   char* proxy_name;
-  gpr_thd_id thd;
+  grpc_core::Thread thd;
   grpc_tcp_server* server;
   grpc_channel_args* channel_args;
   gpr_mu* mu;
@@ -413,12 +413,13 @@
   // If proxy auth is being used, check if the header is present and as expected
   const grpc_arg* proxy_auth_arg = grpc_channel_args_find(
       conn->proxy->channel_args, GRPC_ARG_HTTP_PROXY_AUTH_CREDS);
-  if (proxy_auth_arg != nullptr && proxy_auth_arg->type == GRPC_ARG_STRING) {
+  char* proxy_auth_str = grpc_channel_arg_get_string(proxy_auth_arg);
+  if (proxy_auth_str != nullptr) {
     bool client_authenticated = false;
     for (size_t i = 0; i < conn->http_request.hdr_count; i++) {
       if (strcmp(conn->http_request.hdrs[i].key, "Proxy-Authorization") == 0) {
         client_authenticated = proxy_auth_header_matches(
-            conn->http_request.hdrs[i].value, proxy_auth_arg->value.string);
+            conn->http_request.hdrs[i].value, proxy_auth_str);
         break;
       }
     }
@@ -549,10 +550,8 @@
   grpc_tcp_server_start(proxy->server, &proxy->pollset, 1, on_accept, proxy);
 
   // Start proxy thread.
-  gpr_thd_options opt = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&opt);
-  GPR_ASSERT(
-      gpr_thd_new(&proxy->thd, "grpc_http_proxy", thread_main, proxy, &opt));
+  proxy->thd = grpc_core::Thread("grpc_http_proxy", thread_main, proxy);
+  proxy->thd.Start();
   return proxy;
 }
 
@@ -565,7 +564,7 @@
 void grpc_end2end_http_proxy_destroy(grpc_end2end_http_proxy* proxy) {
   gpr_unref(&proxy->users);  // Signal proxy thread to shutdown.
   grpc_core::ExecCtx exec_ctx;
-  gpr_thd_join(proxy->thd);
+  proxy->thd.Join();
   grpc_tcp_server_shutdown_listeners(proxy->server);
   grpc_tcp_server_unref(proxy->server);
   gpr_free(proxy->proxy_name);
diff --git a/test/core/end2end/fixtures/inproc.cc b/test/core/end2end/fixtures/inproc.cc
index 4ddcc78..d47de42 100644
--- a/test/core/end2end/fixtures/inproc.cc
+++ b/test/core/end2end/fixtures/inproc.cc
@@ -29,7 +29,6 @@
 #include "src/core/ext/transport/inproc/inproc_transport.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/port.h"
diff --git a/test/core/end2end/fixtures/proxy.cc b/test/core/end2end/fixtures/proxy.cc
index bc3b0ca..042c858 100644
--- a/test/core/end2end/fixtures/proxy.cc
+++ b/test/core/end2end/fixtures/proxy.cc
@@ -25,12 +25,12 @@
 #include <grpc/support/sync.h>
 
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/port.h"
 
 struct grpc_end2end_proxy {
-  gpr_thd_id thd;
+  grpc_core::Thread thd;
   char* proxy_port;
   char* server_port;
   grpc_completion_queue* cq;
@@ -76,7 +76,6 @@
 grpc_end2end_proxy* grpc_end2end_proxy_create(const grpc_end2end_proxy_def* def,
                                               grpc_channel_args* client_args,
                                               grpc_channel_args* server_args) {
-  gpr_thd_options opt = gpr_thd_options_default();
   int proxy_port = grpc_pick_unused_port_or_die();
   int server_port = grpc_pick_unused_port_or_die();
 
@@ -98,9 +97,8 @@
   grpc_server_start(proxy->server);
 
   grpc_call_details_init(&proxy->new_call_details);
-  gpr_thd_options_set_joinable(&opt);
-  GPR_ASSERT(
-      gpr_thd_new(&proxy->thd, "grpc_end2end_proxy", thread_main, proxy, &opt));
+  proxy->thd = grpc_core::Thread("grpc_end2end_proxy", thread_main, proxy);
+  proxy->thd.Start();
 
   request_call(proxy);
 
@@ -123,7 +121,7 @@
 void grpc_end2end_proxy_destroy(grpc_end2end_proxy* proxy) {
   grpc_server_shutdown_and_notify(proxy->server, proxy->cq,
                                   new_closure(shutdown_complete, proxy));
-  gpr_thd_join(proxy->thd);
+  proxy->thd.Join();
   gpr_free(proxy->proxy_port);
   gpr_free(proxy->server_port);
   grpc_server_destroy(proxy->server);
diff --git a/test/core/end2end/fuzzers/hpack.dictionary b/test/core/end2end/fuzzers/hpack.dictionary
index 3ed82e1..c719d0f 100644
--- a/test/core/end2end/fuzzers/hpack.dictionary
+++ b/test/core/end2end/fuzzers/hpack.dictionary
@@ -21,7 +21,13 @@
 "\x0Auser-agent"
 "\x04host"
 "\x08lb-token"
+"\x1Agrpc-previous-rpc-attempts"
+"\x16grpc-retry-pushback-ms"
 "\x0Cgrpc-timeout"
+"\x011"
+"\x012"
+"\x013"
+"\x014"
 "\x00"
 "\x13grpc.wait_for_ready"
 "\x0Cgrpc.timeout"
@@ -32,8 +38,6 @@
 "\x04gzip"
 "\x0Bstream/gzip"
 "\x010"
-"\x011"
-"\x012"
 "\x08identity"
 "\x08trailers"
 "\x10application/grpc"
diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py
index e7cf97b..4e20b0b 100755
--- a/test/core/end2end/gen_build_yaml.py
+++ b/test/core/end2end/gen_build_yaml.py
@@ -24,15 +24,24 @@
 
 FixtureOptions = collections.namedtuple(
     'FixtureOptions',
-    'fullstack includes_proxy dns_resolver name_resolution secure platforms ci_mac tracing exclude_configs exclude_iomgrs large_writes enables_compression supports_compression is_inproc is_http2 supports_proxy_auth supports_write_buffering')
+    'fullstack includes_proxy dns_resolver name_resolution secure platforms ci_mac tracing exclude_configs exclude_iomgrs large_writes enables_compression supports_compression is_inproc is_http2 supports_proxy_auth supports_write_buffering client_channel')
 default_unsecure_fixture_options = FixtureOptions(
-    True, False, True, True, False, ['windows', 'linux', 'mac', 'posix'], True, False, [], [], True, False, True, False, True, False, True)
-socketpair_unsecure_fixture_options = default_unsecure_fixture_options._replace(fullstack=False, dns_resolver=False)
-default_secure_fixture_options = default_unsecure_fixture_options._replace(secure=True)
-uds_fixture_options = default_unsecure_fixture_options._replace(dns_resolver=False, platforms=['linux', 'mac', 'posix'], exclude_iomgrs=['uv'])
+    True, False, True, True, False, ['windows', 'linux', 'mac', 'posix'],
+    True, False, [], [], True, False, True, False, True, False, True, True)
+socketpair_unsecure_fixture_options = default_unsecure_fixture_options._replace(
+    fullstack=False, dns_resolver=False, client_channel=False)
+default_secure_fixture_options = default_unsecure_fixture_options._replace(
+    secure=True)
+uds_fixture_options = default_unsecure_fixture_options._replace(
+    dns_resolver=False, platforms=['linux', 'mac', 'posix'],
+    exclude_iomgrs=['uv'])
 fd_unsecure_fixture_options = default_unsecure_fixture_options._replace(
-    dns_resolver=False, fullstack=False, platforms=['linux', 'mac', 'posix'], exclude_iomgrs=['uv'])
-inproc_fixture_options = default_unsecure_fixture_options._replace(dns_resolver=False, fullstack=False, name_resolution=False, supports_compression=False, is_inproc=True, is_http2=False, supports_write_buffering=False)
+    dns_resolver=False, fullstack=False, platforms=['linux', 'mac', 'posix'],
+    exclude_iomgrs=['uv'], client_channel=False)
+inproc_fixture_options = default_unsecure_fixture_options._replace(
+    dns_resolver=False, fullstack=False, name_resolution=False,
+    supports_compression=False, is_inproc=True, is_http2=False,
+    supports_write_buffering=False, client_channel=False)
 
 # maps fixture name to whether it requires the security library
 END2END_FIXTURES = {
@@ -68,9 +77,12 @@
 
 TestOptions = collections.namedtuple(
     'TestOptions',
-    'needs_fullstack needs_dns needs_names proxyable secure traceable cpu_cost exclude_iomgrs large_writes flaky allows_compression needs_compression exclude_inproc needs_http2 needs_proxy_auth needs_write_buffering')
-default_test_options = TestOptions(False, False, False, True, False, True, 1.0, [], False, False, True, False, False, False, False, False)
-connectivity_test_options = default_test_options._replace(needs_fullstack=True)
+    'needs_fullstack needs_dns needs_names proxyable secure traceable cpu_cost exclude_iomgrs large_writes flaky allows_compression needs_compression exclude_inproc needs_http2 needs_proxy_auth needs_write_buffering needs_client_channel')
+default_test_options = TestOptions(
+    False, False, False, True, False, True, 1.0, [], False, False, True,
+    False, False, False, False, False, False)
+connectivity_test_options = default_test_options._replace(
+    needs_fullstack=True)
 
 LOWCPU = 0.1
 
@@ -80,9 +92,8 @@
     'bad_hostname': default_test_options._replace(needs_names=True),
     'bad_ping': connectivity_test_options._replace(proxyable=False),
     'binary_metadata': default_test_options._replace(cpu_cost=LOWCPU),
-    'resource_quota_server': default_test_options._replace(large_writes=True,
-                                                           proxyable=False,
-                                                           allows_compression=False),
+    'resource_quota_server': default_test_options._replace(
+        large_writes=True, proxyable=False, allows_compression=False),
     'call_creds': default_test_options._replace(secure=True),
     'cancel_after_accept': default_test_options._replace(cpu_cost=LOWCPU),
     'cancel_after_client_done': default_test_options._replace(cpu_cost=LOWCPU),
@@ -91,18 +102,21 @@
     'cancel_before_invoke': default_test_options._replace(cpu_cost=LOWCPU),
     'cancel_in_a_vacuum': default_test_options._replace(cpu_cost=LOWCPU),
     'cancel_with_status': default_test_options._replace(cpu_cost=LOWCPU),
-    'compressed_payload': default_test_options._replace(proxyable=False,needs_compression=True),
+    'compressed_payload': default_test_options._replace(proxyable=False,
+                                                        needs_compression=True),
     'connectivity': connectivity_test_options._replace(needs_names=True,
         proxyable=False, cpu_cost=LOWCPU, exclude_iomgrs=['uv']),
-    'default_host': default_test_options._replace(needs_fullstack=True,
-                                                  needs_dns=True,needs_names=True),
-    'disappearing_server': connectivity_test_options._replace(flaky=True,needs_names=True),
+    'default_host': default_test_options._replace(
+        needs_fullstack=True, needs_dns=True, needs_names=True),
+    'disappearing_server': connectivity_test_options._replace(flaky=True,
+                                                              needs_names=True),
     'empty_batch': default_test_options._replace(cpu_cost=LOWCPU),
     'filter_causes_close': default_test_options._replace(cpu_cost=LOWCPU),
     'filter_call_init_fails': default_test_options,
     'filter_latency': default_test_options._replace(cpu_cost=LOWCPU),
     'filter_status_code': default_test_options._replace(cpu_cost=LOWCPU),
-    'graceful_server_shutdown': default_test_options._replace(cpu_cost=LOWCPU,exclude_inproc=True),
+    'graceful_server_shutdown': default_test_options._replace(
+        cpu_cost=LOWCPU, exclude_inproc=True),
     'hpack_size': default_test_options._replace(proxyable=False,
                                                 traceable=False,
                                                 cpu_cost=LOWCPU),
@@ -127,30 +141,75 @@
     'payload': default_test_options,
     'load_reporting_hook': default_test_options,
     'ping_pong_streaming': default_test_options._replace(cpu_cost=LOWCPU),
-    'ping': connectivity_test_options._replace(proxyable=False, cpu_cost=LOWCPU),
+    'ping': connectivity_test_options._replace(proxyable=False,
+                                               cpu_cost=LOWCPU),
     'proxy_auth': default_test_options._replace(needs_proxy_auth=True),
     'registered_call': default_test_options,
     'request_with_flags': default_test_options._replace(
         proxyable=False, cpu_cost=LOWCPU),
     'request_with_payload': default_test_options._replace(cpu_cost=LOWCPU),
+    # TODO(roth): Remove proxyable=False for all retry tests once we
+    # have a way for the proxy to propagate the fact that trailing
+    # metadata is available when initial metadata is returned.
+    # See https://github.com/grpc/grpc/issues/14467 for context.
+    'retry': default_test_options._replace(cpu_cost=LOWCPU,
+                                           needs_client_channel=True,
+                                           proxyable=False),
+    'retry_cancellation': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_disabled': default_test_options._replace(cpu_cost=LOWCPU,
+                                                    needs_client_channel=True,
+                                                    proxyable=False),
+    'retry_exceeds_buffer_size_in_initial_batch': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_exceeds_buffer_size_in_subsequent_batch':
+        default_test_options._replace(cpu_cost=LOWCPU,
+                                      needs_client_channel=True,
+                                      proxyable=False),
+    'retry_non_retriable_status': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_recv_initial_metadata': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_recv_message': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_server_pushback_delay': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_server_pushback_disabled': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_streaming': default_test_options._replace(cpu_cost=LOWCPU,
+                                                     needs_client_channel=True,
+                                                     proxyable=False),
+    'retry_streaming_after_commit': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
+    'retry_streaming_succeeds_before_replay_finished':
+        default_test_options._replace(cpu_cost=LOWCPU,
+                                      needs_client_channel=True,
+                                      proxyable=False),
+    'retry_throttled': default_test_options._replace(cpu_cost=LOWCPU,
+                                                     needs_client_channel=True,
+                                                     proxyable=False),
+    'retry_too_many_attempts': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_client_channel=True, proxyable=False),
     'server_finishes_request': default_test_options._replace(cpu_cost=LOWCPU),
     'shutdown_finishes_calls': default_test_options._replace(cpu_cost=LOWCPU),
     'shutdown_finishes_tags': default_test_options._replace(cpu_cost=LOWCPU),
     'simple_cacheable_request': default_test_options._replace(cpu_cost=LOWCPU),
-    'stream_compression_compressed_payload': default_test_options._replace(proxyable=False,
-                                                               exclude_inproc=True),
-    'stream_compression_payload': default_test_options._replace(exclude_inproc=True),
-    'stream_compression_ping_pong_streaming': default_test_options._replace(exclude_inproc=True),
+    'stream_compression_compressed_payload': default_test_options._replace(
+        proxyable=False, exclude_inproc=True),
+    'stream_compression_payload': default_test_options._replace(
+        exclude_inproc=True),
+    'stream_compression_ping_pong_streaming': default_test_options._replace(
+        exclude_inproc=True),
     'simple_delayed_request': connectivity_test_options,
     'simple_metadata': default_test_options,
     'simple_request': default_test_options,
     'streaming_error_response': default_test_options._replace(cpu_cost=LOWCPU),
     'trailing_metadata': default_test_options,
     'workaround_cronet_compression': default_test_options,
-    'write_buffering': default_test_options._replace(cpu_cost=LOWCPU,
-                                                     needs_write_buffering=True),
-    'write_buffering_at_end': default_test_options._replace(cpu_cost=LOWCPU,
-                                                     needs_write_buffering=True),
+    'write_buffering': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_write_buffering=True),
+    'write_buffering_at_end': default_test_options._replace(
+        cpu_cost=LOWCPU, needs_write_buffering=True),
 }
 
 
@@ -191,6 +250,9 @@
   if END2END_TESTS[t].needs_write_buffering:
     if not END2END_FIXTURES[f].supports_write_buffering:
       return False
+  if END2END_TESTS[t].needs_client_channel:
+    if not END2END_FIXTURES[f].client_channel:
+      return False
   return True
 
 
diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl
index 1d759e1..8e723fd 100755
--- a/test/core/end2end/generate_tests.bzl
+++ b/test/core/end2end/generate_tests.bzl
@@ -13,6 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+POLLERS = ['epollex', 'epollsig', 'epoll1', 'poll', 'poll-cv']
+
 load("//bazel:grpc_build_system.bzl", "grpc_sh_test", "grpc_cc_binary", "grpc_cc_library")
 
 """Generates the appropriate build.json data for all the end2end tests."""
@@ -22,7 +24,7 @@
                     name_resolution=True, secure=True, tracing=False,
                     platforms=['windows', 'linux', 'mac', 'posix'],
                     is_inproc=False, is_http2=True, supports_proxy_auth=False,
-                    supports_write_buffering=True):
+                    supports_write_buffering=True, client_channel=True):
   return struct(
     fullstack=fullstack,
     includes_proxy=includes_proxy,
@@ -33,8 +35,9 @@
     is_inproc=is_inproc,
     is_http2=is_http2,
     supports_proxy_auth=supports_proxy_auth,
-    supports_write_buffering=supports_write_buffering
-    #platforms=platforms
+    supports_write_buffering=supports_write_buffering,
+    client_channel=client_channel,
+    #platforms=platforms,
   )
 
 
@@ -45,6 +48,7 @@
     'h2_load_reporting': fixture_options(),
     'h2_fakesec': fixture_options(),
     'h2_fd': fixture_options(dns_resolver=False, fullstack=False,
+                             client_channel=False,
                              platforms=['linux', 'mac', 'posix']),
     'h2_full': fixture_options(),
     'h2_full+pipe': fixture_options(platforms=['linux']),
@@ -53,24 +57,28 @@
     'h2_http_proxy': fixture_options(supports_proxy_auth=True),
     'h2_oauth2': fixture_options(),
     'h2_proxy': fixture_options(includes_proxy=True),
-    'h2_sockpair_1byte': fixture_options(fullstack=False, dns_resolver=False),
-    'h2_sockpair': fixture_options(fullstack=False, dns_resolver=False),
+    'h2_sockpair_1byte': fixture_options(fullstack=False, dns_resolver=False,
+                                         client_channel=False),
+    'h2_sockpair': fixture_options(fullstack=False, dns_resolver=False,
+                                   client_channel=False),
     'h2_sockpair+trace': fixture_options(fullstack=False, dns_resolver=False,
-                                         tracing=True),
+                                         tracing=True, client_channel=False),
     'h2_ssl': fixture_options(secure=True),
     'h2_ssl_proxy': fixture_options(includes_proxy=True, secure=True),
     'h2_uds': fixture_options(dns_resolver=False,
                               platforms=['linux', 'mac', 'posix']),
     'inproc': fixture_options(fullstack=False, dns_resolver=False,
                               name_resolution=False, is_inproc=True,
-                              is_http2=False, supports_write_buffering=False),
+                              is_http2=False, supports_write_buffering=False,
+                              client_channel=False),
 }
 
 
 def test_options(needs_fullstack=False, needs_dns=False, needs_names=False,
                  proxyable=True, secure=False, traceable=False,
                  exclude_inproc=False, needs_http2=False,
-                 needs_proxy_auth=False, needs_write_buffering=False):
+                 needs_proxy_auth=False, needs_write_buffering=False,
+                 needs_client_channel=False):
   return struct(
     needs_fullstack=needs_fullstack,
     needs_dns=needs_dns,
@@ -81,7 +89,8 @@
     exclude_inproc=exclude_inproc,
     needs_http2=needs_http2,
     needs_proxy_auth=needs_proxy_auth,
-    needs_write_buffering=needs_write_buffering
+    needs_write_buffering=needs_write_buffering,
+    needs_client_channel=needs_client_channel,
   )
 
 
@@ -116,7 +125,8 @@
     'invoke_large_request': test_options(),
     'keepalive_timeout': test_options(proxyable=False, needs_http2=True),
     'large_metadata': test_options(),
-    'max_concurrent_streams': test_options(proxyable=False, exclude_inproc=True),
+    'max_concurrent_streams': test_options(proxyable=False,
+                                           exclude_inproc=True),
     'max_connection_age': test_options(exclude_inproc=True),
     'max_connection_idle': test_options(needs_fullstack=True, proxyable=False),
     'max_message_length': test_options(),
@@ -132,6 +142,37 @@
     'registered_call': test_options(),
     'request_with_flags': test_options(proxyable=False),
     'request_with_payload': test_options(),
+    # TODO(roth): Remove proxyable=False for all retry tests once we
+    # have a way for the proxy to propagate the fact that trailing
+    # metadata is available when initial metadata is returned.
+    # See https://github.com/grpc/grpc/issues/14467 for context.
+    'retry': test_options(needs_client_channel=True, proxyable=False),
+    'retry_cancellation': test_options(needs_client_channel=True,
+                                       proxyable=False),
+    'retry_disabled': test_options(needs_client_channel=True, proxyable=False),
+    'retry_exceeds_buffer_size_in_initial_batch': test_options(
+        needs_client_channel=True, proxyable=False),
+    'retry_exceeds_buffer_size_in_subsequent_batch': test_options(
+        needs_client_channel=True, proxyable=False),
+    'retry_non_retriable_status': test_options(needs_client_channel=True,
+                                               proxyable=False),
+    'retry_recv_initial_metadata': test_options(needs_client_channel=True,
+                                                proxyable=False),
+    'retry_recv_message': test_options(needs_client_channel=True,
+                                       proxyable=False),
+    'retry_server_pushback_delay': test_options(needs_client_channel=True,
+                                                proxyable=False),
+    'retry_server_pushback_disabled': test_options(needs_client_channel=True,
+                                                   proxyable=False),
+    'retry_streaming': test_options(needs_client_channel=True, proxyable=False),
+    'retry_streaming_after_commit': test_options(needs_client_channel=True,
+                                                 proxyable=False),
+    'retry_streaming_succeeds_before_replay_finished': test_options(
+        needs_client_channel=True, proxyable=False),
+    'retry_throttled': test_options(needs_client_channel=True,
+                                    proxyable=False),
+    'retry_too_many_attempts': test_options(needs_client_channel=True,
+                                            proxyable=False),
     'server_finishes_request': test_options(),
     'shutdown_finishes_calls': test_options(),
     'shutdown_finishes_tags': test_options(),
@@ -140,7 +181,8 @@
     'simple_metadata': test_options(),
     'simple_request': test_options(),
     'streaming_error_response': test_options(),
-    'stream_compression_compressed_payload': test_options(proxyable=False, exclude_inproc=True),
+    'stream_compression_compressed_payload': test_options(proxyable=False,
+                                                          exclude_inproc=True),
     'stream_compression_payload': test_options(exclude_inproc=True),
     'stream_compression_ping_pong_streaming': test_options(exclude_inproc=True),
     'trailing_metadata': test_options(),
@@ -181,6 +223,9 @@
   if topt.needs_write_buffering:
     if not fopt.supports_write_buffering:
       return False
+  if topt.needs_client_channel:
+    if not fopt.client_channel:
+      return False
   return True
 
 
@@ -219,9 +264,14 @@
     for t, topt in END2END_TESTS.items():
       #print(compatible(fopt, topt), f, t, fopt, topt)
       if not compatible(fopt, topt): continue
-      grpc_sh_test(
-        name = '%s_test@%s' % (f, t),
-        srcs = ['end2end_test.sh'],
-        args = ['$(location %s_test)' % f, t],
-        data = [':%s_test' % f],
-      )
+      for poller in POLLERS:
+        native.sh_test(
+          name = '%s_test@%s@poller=%s' % (f, t, poller),
+          data = [':%s_test' % f],
+          srcs = ['end2end_test.sh'],
+          args = [
+            '$(location %s_test)' % f, 
+            t,
+            poller,
+          ],
+        )
diff --git a/test/core/end2end/tests/bad_ping.cc b/test/core/end2end/tests/bad_ping.cc
index 9fff3bf..f305ea5 100644
--- a/test/core/end2end/tests/bad_ping.cc
+++ b/test/core/end2end/tests/bad_ping.cc
@@ -25,7 +25,6 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
 #include "test/core/end2end/cq_verifier.h"
 
diff --git a/test/core/end2end/tests/connectivity.cc b/test/core/end2end/tests/connectivity.cc
index a517ffa..caa4265 100644
--- a/test/core/end2end/tests/connectivity.cc
+++ b/test/core/end2end/tests/connectivity.cc
@@ -22,7 +22,7 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/end2end/cq_verifier.h"
 
 static void* tag(intptr_t t) { return (void*)t; }
@@ -50,8 +50,6 @@
   grpc_connectivity_state state;
   cq_verifier* cqv = cq_verifier_create(f.cq);
   child_events ce;
-  gpr_thd_options thdopt = gpr_thd_options_default();
-  gpr_thd_id thdid;
 
   grpc_channel_args client_args;
   grpc_arg arg_array[1];
@@ -67,9 +65,8 @@
   ce.channel = f.client;
   ce.cq = f.cq;
   gpr_event_init(&ce.started);
-  gpr_thd_options_set_joinable(&thdopt);
-  GPR_ASSERT(
-      gpr_thd_new(&thdid, "grpc_connectivity", child_thread, &ce, &thdopt));
+  grpc_core::Thread thd("grpc_connectivity", child_thread, &ce);
+  thd.Start();
 
   gpr_event_wait(&ce.started, gpr_inf_future(GPR_CLOCK_MONOTONIC));
 
@@ -86,7 +83,7 @@
       f.client, GRPC_CHANNEL_IDLE, gpr_now(GPR_CLOCK_MONOTONIC), f.cq, tag(1));
 
   /* eventually the child thread completion should trigger */
-  gpr_thd_join(thdid);
+  thd.Join();
 
   /* check that we're still in idle, and start connecting */
   GPR_ASSERT(grpc_channel_check_connectivity_state(f.client, 1) ==
diff --git a/test/core/end2end/tests/ping.cc b/test/core/end2end/tests/ping.cc
index 8fce295..f523cbb 100644
--- a/test/core/end2end/tests/ping.cc
+++ b/test/core/end2end/tests/ping.cc
@@ -22,7 +22,6 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
 #include "test/core/end2end/cq_verifier.h"
 
diff --git a/test/core/end2end/tests/retry.cc b/test/core/end2end/tests/retry.cc
new file mode 100644
index 0000000..38ecc6f
--- /dev/null
+++ b/test/core/end2end/tests/retry.cc
@@ -0,0 +1,325 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests a basic retry scenario:
+// - 2 retries allowed for ABORTED status
+// - first attempt returns ABORTED
+// - second attempt returns OK
+static void test_retry(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  // Make sure the "grpc-previous-rpc-attempts" header was not sent in the
+  // initial attempt.
+  for (size_t i = 0; i < request_metadata_recv.count; ++i) {
+    GPR_ASSERT(!grpc_slice_eq(request_metadata_recv.metadata[i].key,
+                              GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS));
+  }
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  // Make sure the "grpc-previous-rpc-attempts" header was sent in the retry.
+  bool found_retry_header = false;
+  for (size_t i = 0; i < request_metadata_recv.count; ++i) {
+    if (grpc_slice_eq(request_metadata_recv.metadata[i].key,
+                      GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS)) {
+      GPR_ASSERT(
+          grpc_slice_eq(request_metadata_recv.metadata[i].value, GRPC_MDSTR_1));
+      found_retry_header = true;
+      break;
+    }
+  }
+  GPR_ASSERT(found_retry_header);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response_payload;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 0);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry(config);
+}
+
+void retry_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_cancellation.cc b/test/core/end2end/tests/retry_cancellation.cc
new file mode 100644
index 0000000..0504092
--- /dev/null
+++ b/test/core/end2end/tests/retry_cancellation.cc
@@ -0,0 +1,277 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests retry cancellation.
+static void test_retry_cancellation(grpc_end2end_test_config config,
+                                    cancellation_mode mode) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    },\n"
+      "    \"timeout\": \"5s\"\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  char* name;
+  gpr_asprintf(&name, "retry_cancellation/%s", mode.name);
+  grpc_end2end_test_fixture f = begin_test(config, name, &client_args, nullptr);
+  gpr_free(name);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  // Client starts a batch with all 6 ops.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // Server gets a call and fails with retryable status.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+
+  // Server gets a second call (the retry).
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  // Initiate cancellation.
+  GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, nullptr));
+
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == mode.expect_status);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_cancellation(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); ++i) {
+    test_retry_cancellation(config, cancellation_modes[i]);
+  }
+}
+
+void retry_cancellation_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_disabled.cc b/test/core/end2end/tests/retry_disabled.cc
new file mode 100644
index 0000000..cb30502
--- /dev/null
+++ b/test/core/end2end/tests/retry_disabled.cc
@@ -0,0 +1,262 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we don't retry when retries are disabled via the
+// GRPC_ARG_ENABLE_RETRIES channel arg, even when there is retry
+// configuration in the service config.
+// - 1 retry allowed for ABORTED status
+// - first attempt returns ABORTED but does not retry
+static void test_retry_disabled(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg args[2];
+  args[0].type = GRPC_ARG_STRING;
+  args[0].key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  args[0].value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  args[1].type = GRPC_ARG_INTEGER;
+  args[1].key = const_cast<char*>(GRPC_ARG_ENABLE_RETRIES);
+  args[1].value.integer = 0;
+  grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_disabled", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_disabled(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_disabled(config);
+}
+
+void retry_disabled_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc b/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc
new file mode 100644
index 0000000..3908f29
--- /dev/null
+++ b/test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc
@@ -0,0 +1,266 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we don't make any further attempts after we exceed the
+// max buffer size.
+// - 1 retry allowed for ABORTED status
+// - buffer size set to 2 bytes
+// - client sends a 3-byte message
+// - first attempt gets ABORTED but is not retried
+static void test_retry_exceeds_buffer_size_in_initial_batch(
+    grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg args[2];
+  args[0].type = GRPC_ARG_STRING;
+  args[0].key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  args[0].value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  args[1].type = GRPC_ARG_INTEGER;
+  args[1].key = const_cast<char*>(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE);
+  args[1].value.integer = 2;
+  grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_exceeds_buffer_size_in_initial_batch",
+                 &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_exceeds_buffer_size_in_initial_batch(
+    grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_exceeds_buffer_size_in_initial_batch(config);
+}
+
+void retry_exceeds_buffer_size_in_initial_batch_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc b/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc
new file mode 100644
index 0000000..409fac4
--- /dev/null
+++ b/test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc
@@ -0,0 +1,279 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Similar to the retry_exceeds_buffer_size_in_initial_batch test, but we
+// don't exceed the buffer size until the second batch.
+// - 1 retry allowed for ABORTED status
+// - buffer size set to 100 KiB (larger than initial metadata)
+// - client sends a 100 KiB message
+// - first attempt gets ABORTED but is not retried
+static void test_retry_exceeds_buffer_size_in_subsequent_batch(
+    grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  const size_t buf_size = 102401;
+  char* buf = static_cast<char*>(gpr_malloc(buf_size * sizeof(*buf)));
+  memset(buf, 'a', buf_size - 1);
+  buf[buf_size - 1] = '\0';
+  // TODO(markdroth): buf is not a static string, so fix the next line
+  grpc_slice request_payload_slice = grpc_slice_from_static_string(buf);
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg args[2];
+  args[0].type = GRPC_ARG_STRING;
+  args[0].key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  args[0].value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  args[1].type = GRPC_ARG_INTEGER;
+  args[1].key = const_cast<char*>(GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE);
+  args[1].value.integer = 102400;
+  grpc_channel_args client_args = {GPR_ARRAY_SIZE(args), args};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_exceeds_buffer_size_in_subsequent_batch",
+                 &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+  gpr_free(buf);
+}
+
+void retry_exceeds_buffer_size_in_subsequent_batch(
+    grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_exceeds_buffer_size_in_subsequent_batch(config);
+}
+
+void retry_exceeds_buffer_size_in_subsequent_batch_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_non_retriable_status.cc b/test/core/end2end/tests/retry_non_retriable_status.cc
new file mode 100644
index 0000000..6d59db0
--- /dev/null
+++ b/test/core/end2end/tests/retry_non_retriable_status.cc
@@ -0,0 +1,257 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we don't retry for non-retryable status codes.
+// - 1 retry allowed for ABORTED status
+// - first attempt gets INVALID_ARGUMENT, so no retry is done
+static void test_retry_non_retriable_status(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_non_retriable_status", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_INVALID_ARGUMENT;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_non_retriable_status(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_non_retriable_status(config);
+}
+
+void retry_non_retriable_status_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_recv_initial_metadata.cc b/test/core/end2end/tests/retry_recv_initial_metadata.cc
new file mode 100644
index 0000000..14215e4
--- /dev/null
+++ b/test/core/end2end/tests/retry_recv_initial_metadata.cc
@@ -0,0 +1,268 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that receiving initial metadata commits the call.
+// - 1 retry allowed for ABORTED status
+// - first attempt receives initial metadata before trailing metadata,
+//   so no retry is done even though status was ABORTED
+static void test_retry_recv_initial_metadata(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_recv_initial_metadata", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  // Server sends initial metadata in its own batch, before sending
+  // trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(103), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_recv_initial_metadata(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_recv_initial_metadata(config);
+}
+
+void retry_recv_initial_metadata_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_recv_message.cc b/test/core/end2end/tests/retry_recv_message.cc
new file mode 100644
index 0000000..86171fd
--- /dev/null
+++ b/test/core/end2end/tests/retry_recv_message.cc
@@ -0,0 +1,261 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that receiving a message commits the call.
+// - 1 retry allowed for ABORTED status
+// - first attempt receives a message and therefore does not retry even
+//   though the final status is ABORTED
+static void test_retry_recv_message(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_recv_message", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response_payload;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(103), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_recv_message(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_recv_message(config);
+}
+
+void retry_recv_message_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_server_pushback_delay.cc b/test/core/end2end/tests/retry_server_pushback_delay.cc
new file mode 100644
index 0000000..1da9860
--- /dev/null
+++ b/test/core/end2end/tests/retry_server_pushback_delay.cc
@@ -0,0 +1,318 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we honor server push-back delay.
+// - 2 retries allowed for ABORTED status
+// - first attempt gets ABORTED with a long delay
+// - second attempt succeeds
+static void test_retry_server_pushback_delay(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_metadata pushback_md;
+  memset(&pushback_md, 0, sizeof(pushback_md));
+  pushback_md.key = GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS;
+  pushback_md.value = grpc_slice_from_static_string("2000");
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_server_pushback_delay", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 1;
+  op->data.send_status_from_server.trailing_metadata = &pushback_md;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  gpr_timespec before_retry = gpr_now(GPR_CLOCK_MONOTONIC);
+
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  gpr_timespec after_retry = gpr_now(GPR_CLOCK_MONOTONIC);
+  gpr_timespec retry_delay = gpr_time_sub(after_retry, before_retry);
+  // Configured back-off was 1 second, server push-back said 2 seconds.
+  // To avoid flakiness, we allow some fudge factor here.
+  gpr_log(GPR_INFO, "retry delay was {.tv_sec=%" PRId64 ", .tv_nsec=%d}",
+          retry_delay.tv_sec, retry_delay.tv_nsec);
+  GPR_ASSERT(retry_delay.tv_sec >= 1);
+  if (retry_delay.tv_sec == 1) {
+    GPR_ASSERT(retry_delay.tv_nsec >= 900000000);
+  }
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 0);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_server_pushback_delay(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_server_pushback_delay(config);
+}
+
+void retry_server_pushback_delay_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_server_pushback_disabled.cc b/test/core/end2end/tests/retry_server_pushback_disabled.cc
new file mode 100644
index 0000000..13d4f01
--- /dev/null
+++ b/test/core/end2end/tests/retry_server_pushback_disabled.cc
@@ -0,0 +1,306 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we don't retry when disabled by server push-back.
+// - 2 retries allowed for ABORTED status
+// - first attempt gets ABORTED
+// - second attempt gets ABORTED but server push back disables retrying
+static void test_retry_server_pushback_disabled(
+    grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_metadata pushback_md;
+  memset(&pushback_md, 0, sizeof(pushback_md));
+  pushback_md.key = GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS;
+  pushback_md.value = grpc_slice_from_static_string("-1");
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f = begin_test(
+      config, "retry_server_pushback_disabled", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 1;
+  op->data.send_status_from_server.trailing_metadata = &pushback_md;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_server_pushback_disabled(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_server_pushback_disabled(config);
+}
+
+void retry_server_pushback_disabled_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_streaming.cc b/test/core/end2end/tests/retry_streaming.cc
new file mode 100644
index 0000000..e96e57e
--- /dev/null
+++ b/test/core/end2end/tests/retry_streaming.cc
@@ -0,0 +1,424 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests retrying a streaming RPC.  This is the same as
+// the basic retry test, except that the client sends two messages on the
+// call before the initial attempt fails.
+// FIXME: We should also test the case where the retry is committed after
+// replaying 1 of 2 previously-completed send_message ops.  However,
+// there's no way to trigger that from an end2end test, because the
+// replayed ops happen under the hood -- they are not surfaced to the
+// C-core API, and therefore we have no way to inject the commit at the
+// right point.
+static void test_retry_streaming(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice request2_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_slice request3_payload_slice = grpc_slice_from_static_string("baz");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("quux");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* request2_payload =
+      grpc_raw_byte_buffer_create(&request2_payload_slice, 1);
+  grpc_byte_buffer* request3_payload =
+      grpc_raw_byte_buffer_create(&request3_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* request2_payload_recv = nullptr;
+  grpc_byte_buffer* request3_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_streaming", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  // Client starts a batch for receiving initial metadata, a message,
+  // and trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // Client sends initial metadata and a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  // Server gets a call with received initial metadata.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  // Server receives a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  // Client sends a second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request2_payload;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), true);
+  cq_verify(cqv);
+
+  // Server receives the second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request2_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), true);
+  cq_verify(cqv);
+
+  // Server sends both initial and trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(104), true);
+  cq_verify(cqv);
+
+  // Clean up from first attempt.
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+  GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice));
+  grpc_byte_buffer_destroy(request_payload_recv);
+  request_payload_recv = nullptr;
+  GPR_ASSERT(
+      byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice));
+  grpc_byte_buffer_destroy(request2_payload_recv);
+  request2_payload_recv = nullptr;
+
+  // Server gets a second call (the retry).
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  // Server receives a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  cq_verify(cqv);
+
+  // Server receives a second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request2_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(203), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(203), true);
+  cq_verify(cqv);
+
+  // Client sends a third message and a close.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request3_payload;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(4), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(4), true);
+  cq_verify(cqv);
+
+  // Server receives a third message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request3_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(204), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(204), true);
+  cq_verify(cqv);
+
+  // Server receives a close and sends initial metadata, a message, and
+  // trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response_payload;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  // Returning a retriable code, but because we are also sending a
+  // message, the client will commit instead of retrying again.
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(205), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(205), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(request2_payload);
+  grpc_byte_buffer_destroy(request3_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice));
+  grpc_byte_buffer_destroy(request_payload_recv);
+  GPR_ASSERT(
+      byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice));
+  grpc_byte_buffer_destroy(request2_payload_recv);
+  GPR_ASSERT(
+      byte_buffer_eq_slice(request3_payload_recv, request3_payload_slice));
+  grpc_byte_buffer_destroy(request3_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_streaming(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_streaming(config);
+}
+
+void retry_streaming_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_streaming_after_commit.cc b/test/core/end2end/tests/retry_streaming_after_commit.cc
new file mode 100644
index 0000000..43eee86
--- /dev/null
+++ b/test/core/end2end/tests/retry_streaming_after_commit.cc
@@ -0,0 +1,354 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we can continue to send/recv messages on a streaming call
+// after retries are committed.
+static void test_retry_streaming_after_commit(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice request2_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("baz");
+  grpc_slice response2_payload_slice = grpc_slice_from_static_string("quux");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* request2_payload =
+      grpc_raw_byte_buffer_create(&request2_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* response2_payload =
+      grpc_raw_byte_buffer_create(&response2_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* request2_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_byte_buffer* response2_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_streaming_after_commit", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  // Client starts a batch for receiving initial metadata and a message.
+  // This will commit retries.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // Client sends initial metadata and a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), true);
+  cq_verify(cqv);
+
+  // Server gets a call with received initial metadata.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  // Server receives a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  // Server sends initial metadata and a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response_payload;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), true);
+  // Client receives initial metadata and a message.
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  // Client sends a second message and a close.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request2_payload;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(4), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(4), true);
+  cq_verify(cqv);
+
+  // Server receives a second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request2_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(104), true);
+  cq_verify(cqv);
+
+  // Server receives a close, sends a second message, and sends status.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response2_payload;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  // Returning a retriable code, but because retries are already
+  // committed, the client will not retry.
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(105), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(105), true);
+  cq_verify(cqv);
+
+  // Client receives a second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response2_payload_recv;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(5), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(5), true);
+  cq_verify(cqv);
+
+  // Client receives status.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(request2_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(response2_payload);
+  GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice));
+  grpc_byte_buffer_destroy(request_payload_recv);
+  GPR_ASSERT(
+      byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice));
+  grpc_byte_buffer_destroy(request2_payload_recv);
+  GPR_ASSERT(
+      byte_buffer_eq_slice(response_payload_recv, response_payload_slice));
+  grpc_byte_buffer_destroy(response_payload_recv);
+  GPR_ASSERT(
+      byte_buffer_eq_slice(response2_payload_recv, response2_payload_slice));
+  grpc_byte_buffer_destroy(response2_payload_recv);
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_streaming_after_commit(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_streaming_after_commit(config);
+}
+
+void retry_streaming_after_commit_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc b/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc
new file mode 100644
index 0000000..5c92f64
--- /dev/null
+++ b/test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc
@@ -0,0 +1,400 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we correctly clean up if the second attempt finishes
+// before we have finished replaying all of the send ops.
+static void test_retry_streaming_succeeds_before_replay_finished(
+    grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice request2_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_slice request3_payload_slice = grpc_slice_from_static_string("baz");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("quux");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* request2_payload =
+      grpc_raw_byte_buffer_create(&request2_payload_slice, 1);
+  grpc_byte_buffer* request3_payload =
+      grpc_raw_byte_buffer_create(&request3_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* request2_payload_recv = nullptr;
+  grpc_byte_buffer* request3_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 3,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_streaming", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  // Client starts a batch for receiving initial metadata, a message,
+  // and trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  // Client sends initial metadata and a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), true);
+  cq_verify(cqv);
+
+  // Server gets a call with received initial metadata.
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  // Server receives a message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  // Client sends a second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request2_payload;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), true);
+  cq_verify(cqv);
+
+  // Server receives the second message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request2_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), true);
+  cq_verify(cqv);
+
+  // Client sends a third message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request3_payload;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(4), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(4), true);
+  cq_verify(cqv);
+
+  // Server receives the third message.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request3_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(104), true);
+  cq_verify(cqv);
+
+  // Server sends both initial and trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(105), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(105), true);
+  cq_verify(cqv);
+
+  // Clean up from first attempt.
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+  GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice));
+  grpc_byte_buffer_destroy(request_payload_recv);
+  request_payload_recv = nullptr;
+  GPR_ASSERT(
+      byte_buffer_eq_slice(request2_payload_recv, request2_payload_slice));
+  grpc_byte_buffer_destroy(request2_payload_recv);
+  request2_payload_recv = nullptr;
+  GPR_ASSERT(
+      byte_buffer_eq_slice(request3_payload_recv, request3_payload_slice));
+  grpc_byte_buffer_destroy(request3_payload_recv);
+  request3_payload_recv = nullptr;
+
+  // Server gets a second call (the retry).
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  // Server receives the first message (and does not receive any others).
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &request_payload_recv;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  cq_verify(cqv);
+
+  // Server sends initial metadata, a message, and trailing metadata.
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = response_payload;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  // Returning a retriable code, but because we are also sending a
+  // message, the client will commit instead of retrying again.
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(205), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(205), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(request2_payload);
+  grpc_byte_buffer_destroy(request3_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  GPR_ASSERT(byte_buffer_eq_slice(request_payload_recv, request_payload_slice));
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_streaming_succeeds_before_replay_finished(
+    grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_streaming_succeeds_before_replay_finished(config);
+}
+
+void retry_streaming_succeeds_before_replay_finished_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_throttled.cc b/test/core/end2end/tests/retry_throttled.cc
new file mode 100644
index 0000000..8cd0848
--- /dev/null
+++ b/test/core/end2end/tests/retry_throttled.cc
@@ -0,0 +1,264 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we don't retry when throttled.
+// - 1 retry allowed for ABORTED status
+// - first attempt gets ABORTED but is over limit, so no retry is done
+static void test_retry_throttled(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ],\n"
+      // A single failure will cause us to be throttled.
+      // (This is not a very realistic config, but it works for the
+      // purposes of this test.)
+      "  \"retryThrottling\": {\n"
+      "    \"maxTokens\": 2,\n"
+      "    \"tokenRatio\": 1.0,\n"
+      "  }\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_throttled", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_throttled(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_throttled(config);
+}
+
+void retry_throttled_pre_init(void) {}
diff --git a/test/core/end2end/tests/retry_too_many_attempts.cc b/test/core/end2end/tests/retry_too_many_attempts.cc
new file mode 100644
index 0000000..5225c9b
--- /dev/null
+++ b/test/core/end2end/tests/retry_too_many_attempts.cc
@@ -0,0 +1,299 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/tests/cancel_test_helpers.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char* test_name,
+                                            grpc_channel_args* client_args,
+                                            grpc_channel_args* server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture* f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+static void end_test(grpc_end2end_test_fixture* f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+// Tests that we stop retrying after the configured number of attempts.
+// - 1 retry allowed for ABORTED status
+// - first attempt gets ABORTED
+// - second attempt gets ABORTED but does not retry
+static void test_retry_too_many_attempts(grpc_end2end_test_config config) {
+  grpc_call* c;
+  grpc_call* s;
+  grpc_op ops[6];
+  grpc_op* op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_slice request_payload_slice = grpc_slice_from_static_string("foo");
+  grpc_slice response_payload_slice = grpc_slice_from_static_string("bar");
+  grpc_byte_buffer* request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer* response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  grpc_byte_buffer* request_payload_recv = nullptr;
+  grpc_byte_buffer* response_payload_recv = nullptr;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+  char* peer;
+
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = const_cast<char*>(GRPC_ARG_SERVICE_CONFIG);
+  arg.value.string = const_cast<char*>(
+      "{\n"
+      "  \"methodConfig\": [ {\n"
+      "    \"name\": [\n"
+      "      { \"service\": \"service\", \"method\": \"method\" }\n"
+      "    ],\n"
+      "    \"retryPolicy\": {\n"
+      "      \"maxAttempts\": 2,\n"
+      "      \"initialBackoff\": \"1s\",\n"
+      "      \"maxBackoff\": \"120s\",\n"
+      "      \"backoffMultiplier\": 1.6,\n"
+      "      \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
+      "    }\n"
+      "  } ]\n"
+      "}");
+  grpc_channel_args client_args = {1, &arg};
+  grpc_end2end_test_fixture f =
+      begin_test(config, "retry_too_many_attempts", &client_args, nullptr);
+
+  cq_verifier* cqv = cq_verifier_create(f.cq);
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(
+      f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
+      nullptr);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = request_payload;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &response_payload_recv;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), true);
+  cq_verify(cqv);
+
+  grpc_call_unref(s);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_call_details_init(&call_details);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(201));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), true);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != nullptr);
+  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_ABORTED;
+  op->data.send_status_from_server.status_details = &status_details;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(202), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_ABORTED);
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  validate_host_override_string("foo.test.google.fr:1234", call_details.host,
+                                config);
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  grpc_slice_unref(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  grpc_call_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void retry_too_many_attempts(grpc_end2end_test_config config) {
+  GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL);
+  test_retry_too_many_attempts(config);
+}
+
+void retry_too_many_attempts_pre_init(void) {}
diff --git a/test/core/gpr/BUILD b/test/core/gpr/BUILD
index 9aa74cc..5308ea0 100644
--- a/test/core/gpr/BUILD
+++ b/test/core/gpr/BUILD
@@ -29,6 +29,16 @@
 )
 
 grpc_cc_test(
+    name = "arena_test",
+    srcs = ["arena_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//test/core/util:gpr_test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "cpu_test",
     srcs = ["cpu_test.cc"],
     language = "C++",
@@ -119,16 +129,6 @@
 )
 
 grpc_cc_test(
-    name = "thd_test",
-    srcs = ["thd_test.cc"],
-    language = "C++",
-    deps = [
-        "//:gpr",
-        "//test/core/util:gpr_test_util",
-    ],
-)
-
-grpc_cc_test(
     name = "time_test",
     srcs = ["time_test.cc"],
     language = "C++",
diff --git a/test/core/gpr/arena_test.cc b/test/core/gpr/arena_test.cc
index 9eaf57b..111414e 100644
--- a/test/core/gpr/arena_test.cc
+++ b/test/core/gpr/arena_test.cc
@@ -18,16 +18,17 @@
 
 #include "src/core/lib/gpr/arena.h"
 
+#include <inttypes.h>
+#include <string.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
-#include <inttypes.h>
-#include <string.h>
 
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 static void test_noop(void) { gpr_arena_destroy(gpr_arena_create(1)); }
@@ -97,19 +98,18 @@
   gpr_event_init(&args.ev_start);
   args.arena = gpr_arena_create(1024);
 
-  gpr_thd_id thds[CONCURRENT_TEST_THREADS];
+  grpc_core::Thread thds[CONCURRENT_TEST_THREADS];
 
   for (int i = 0; i < CONCURRENT_TEST_THREADS; i++) {
-    gpr_thd_options opt = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&opt);
-    gpr_thd_new(&thds[i], "grpc_concurrent_test", concurrent_test_body, &args,
-                &opt);
+    thds[i] =
+        grpc_core::Thread("grpc_concurrent_test", concurrent_test_body, &args);
+    thds[i].Start();
   }
 
   gpr_event_set(&args.ev_start, (void*)1);
 
-  for (int i = 0; i < CONCURRENT_TEST_THREADS; i++) {
-    gpr_thd_join(thds[i]);
+  for (auto& th : thds) {
+    th.Join();
   }
 
   gpr_arena_destroy(args.arena);
diff --git a/test/core/gpr/cpu_test.cc b/test/core/gpr/cpu_test.cc
index 9f2c3f1..1052d40 100644
--- a/test/core/gpr/cpu_test.cc
+++ b/test/core/gpr/cpu_test.cc
@@ -21,15 +21,17 @@
    gpr_cpu_current_cpu()
 */
 
-#include <grpc/support/alloc.h>
 #include <grpc/support/cpu.h>
-#include <grpc/support/log.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/time.h>
+
 #include <stdio.h>
 #include <string.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 /* Test structure is essentially:
@@ -101,7 +103,6 @@
   uint32_t i;
   int cores_seen = 0;
   struct cpu_test ct;
-  gpr_thd_id thd;
   ct.ncores = gpr_cpu_num_cores();
   GPR_ASSERT(ct.ncores > 0);
   ct.nthreads = static_cast<int>(ct.ncores) * 3;
@@ -110,15 +111,24 @@
   gpr_mu_init(&ct.mu);
   gpr_cv_init(&ct.done_cv);
   ct.is_done = 0;
-  for (i = 0; i < ct.ncores * 3; i++) {
-    GPR_ASSERT(
-        gpr_thd_new(&thd, "grpc_cpu_test", &worker_thread, &ct, nullptr));
+
+  uint32_t nthreads = ct.ncores * 3;
+  grpc_core::Thread* thd =
+      static_cast<grpc_core::Thread*>(gpr_malloc(sizeof(*thd) * nthreads));
+
+  for (i = 0; i < nthreads; i++) {
+    thd[i] = grpc_core::Thread("grpc_cpu_test", &worker_thread, &ct);
+    thd[i].Start();
   }
   gpr_mu_lock(&ct.mu);
   while (!ct.is_done) {
     gpr_cv_wait(&ct.done_cv, &ct.mu, gpr_inf_future(GPR_CLOCK_MONOTONIC));
   }
   gpr_mu_unlock(&ct.mu);
+  for (i = 0; i < nthreads; i++) {
+    thd[i].Join();
+  }
+  gpr_free(thd);
   fprintf(stderr, "Saw cores [");
   fflush(stderr);
   for (i = 0; i < ct.ncores; i++) {
diff --git a/test/core/gpr/mpscq_test.cc b/test/core/gpr/mpscq_test.cc
index 9681346..8c08739 100644
--- a/test/core/gpr/mpscq_test.cc
+++ b/test/core/gpr/mpscq_test.cc
@@ -24,8 +24,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 typedef struct test_node {
@@ -76,18 +76,16 @@
   gpr_log(GPR_DEBUG, "test_mt");
   gpr_event start;
   gpr_event_init(&start);
-  gpr_thd_id thds[100];
+  grpc_core::Thread thds[100];
   thd_args ta[GPR_ARRAY_SIZE(thds)];
   gpr_mpscq q;
   gpr_mpscq_init(&q);
   for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_options options = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&options);
     ta[i].ctr = 0;
     ta[i].q = &q;
     ta[i].start = &start;
-    GPR_ASSERT(
-        gpr_thd_new(&thds[i], "grpc_mt_test", test_thread, &ta[i], &options));
+    thds[i] = grpc_core::Thread("grpc_mt_test", test_thread, &ta[i]);
+    thds[i].Start();
   }
   size_t num_done = 0;
   size_t spins = 0;
@@ -104,8 +102,8 @@
     gpr_free(tn);
   }
   gpr_log(GPR_DEBUG, "spins: %" PRIdPTR, spins);
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_join(thds[i]);
+  for (auto& th : thds) {
+    th.Join();
   }
   gpr_mpscq_destroy(&q);
 }
@@ -147,19 +145,17 @@
   gpr_log(GPR_DEBUG, "test_mt_multipop");
   gpr_event start;
   gpr_event_init(&start);
-  gpr_thd_id thds[100];
-  gpr_thd_id pull_thds[100];
+  grpc_core::Thread thds[50];
+  grpc_core::Thread pull_thds[50];
   thd_args ta[GPR_ARRAY_SIZE(thds)];
   gpr_mpscq q;
   gpr_mpscq_init(&q);
   for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_options options = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&options);
     ta[i].ctr = 0;
     ta[i].q = &q;
     ta[i].start = &start;
-    GPR_ASSERT(gpr_thd_new(&thds[i], "grpc_multipop_test", test_thread, &ta[i],
-                           &options));
+    thds[i] = grpc_core::Thread("grpc_multipop_test", test_thread, &ta[i]);
+    thds[i].Start();
   }
   pull_args pa;
   pa.ta = ta;
@@ -170,18 +166,16 @@
   pa.start = &start;
   gpr_mu_init(&pa.mu);
   for (size_t i = 0; i < GPR_ARRAY_SIZE(pull_thds); i++) {
-    gpr_thd_options options = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&options);
-    GPR_ASSERT(gpr_thd_new(&pull_thds[i], "grpc_multipop_pull", pull_thread,
-                           &pa, &options));
+    pull_thds[i] = grpc_core::Thread("grpc_multipop_pull", pull_thread, &pa);
+    pull_thds[i].Start();
   }
   gpr_event_set(&start, (void*)1);
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(pull_thds); i++) {
-    gpr_thd_join(pull_thds[i]);
+  for (auto& pth : pull_thds) {
+    pth.Join();
   }
   gpr_log(GPR_DEBUG, "spins: %" PRIdPTR, pa.spins);
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_join(thds[i]);
+  for (auto& th : thds) {
+    th.Join();
   }
   gpr_mpscq_destroy(&q);
 }
diff --git a/test/core/gpr/spinlock_test.cc b/test/core/gpr/spinlock_test.cc
index 9f182bc..0ee72ed 100644
--- a/test/core/gpr/spinlock_test.cc
+++ b/test/core/gpr/spinlock_test.cc
@@ -16,24 +16,26 @@
  *
  */
 
-/* Test of gpr synchronization support. */
+/* Test of gpr spin-lock support. */
 
 #include "src/core/lib/gpr/spinlock.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
-#include <stdio.h>
-#include <stdlib.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 /* ------------------------------------------------- */
 /* Tests for gpr_spinlock. */
 struct test {
   int thread_count; /* number of threads */
-  gpr_thd_id* threads;
+  grpc_core::Thread* threads;
 
   int64_t iterations; /* number of iterations per thread */
   int64_t counter;
@@ -46,7 +48,7 @@
 static struct test* test_new(int threads, int64_t iterations, int incr_step) {
   struct test* m = static_cast<struct test*>(gpr_malloc(sizeof(*m)));
   m->thread_count = threads;
-  m->threads = static_cast<gpr_thd_id*>(
+  m->threads = static_cast<grpc_core::Thread*>(
       gpr_malloc(sizeof(*m->threads) * static_cast<size_t>(threads)));
   m->iterations = iterations;
   m->counter = 0;
@@ -66,10 +68,8 @@
 static void test_create_threads(struct test* m, void (*body)(void* arg)) {
   int i;
   for (i = 0; i != m->thread_count; i++) {
-    gpr_thd_options opt = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&opt);
-    GPR_ASSERT(
-        gpr_thd_new(&m->threads[i], "grpc_create_threads", body, m, &opt));
+    m->threads[i] = grpc_core::Thread("grpc_create_threads", body, m);
+    m->threads[i].Start();
   }
 }
 
@@ -77,7 +77,7 @@
 static void test_wait(struct test* m) {
   int i;
   for (i = 0; i != m->thread_count; i++) {
-    gpr_thd_join(m->threads[i]);
+    m->threads[i].Join();
   }
 }
 
diff --git a/test/core/gpr/sync_test.cc b/test/core/gpr/sync_test.cc
index bafd910..24b4562 100644
--- a/test/core/gpr/sync_test.cc
+++ b/test/core/gpr/sync_test.cc
@@ -18,14 +18,16 @@
 
 /* Test of gpr synchronization support. */
 
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
 #include <grpc/support/sync.h>
-#include <grpc/support/time.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 /* ==================Example use of interface===================
@@ -133,7 +135,8 @@
 /* ------------------------------------------------- */
 /* Tests for gpr_mu and gpr_cv, and the queue example. */
 struct test {
-  int threads; /* number of threads */
+  int nthreads; /* number of threads */
+  grpc_core::Thread* threads;
 
   int64_t iterations; /* number of iterations per thread */
   int64_t counter;
@@ -157,13 +160,15 @@
 };
 
 /* Return pointer to a new struct test. */
-static struct test* test_new(int threads, int64_t iterations, int incr_step) {
+static struct test* test_new(int nthreads, int64_t iterations, int incr_step) {
   struct test* m = static_cast<struct test*>(gpr_malloc(sizeof(*m)));
-  m->threads = threads;
+  m->nthreads = nthreads;
+  m->threads = static_cast<grpc_core::Thread*>(
+      gpr_malloc(sizeof(*m->threads) * nthreads));
   m->iterations = iterations;
   m->counter = 0;
   m->thread_count = 0;
-  m->done = threads;
+  m->done = nthreads;
   m->incr_step = incr_step;
   gpr_mu_init(&m->mu);
   gpr_cv_init(&m->cv);
@@ -171,7 +176,7 @@
   queue_init(&m->q);
   gpr_stats_init(&m->stats_counter, 0);
   gpr_ref_init(&m->refcount, 0);
-  gpr_ref_init(&m->thread_refcount, threads);
+  gpr_ref_init(&m->thread_refcount, nthreads);
   gpr_event_init(&m->event);
   return m;
 }
@@ -182,15 +187,16 @@
   gpr_cv_destroy(&m->cv);
   gpr_cv_destroy(&m->done_cv);
   queue_destroy(&m->q);
+  gpr_free(m->threads);
   gpr_free(m);
 }
 
-/* Create m->threads threads, each running (*body)(m) */
+/* Create m->nthreads threads, each running (*body)(m) */
 static void test_create_threads(struct test* m, void (*body)(void* arg)) {
-  gpr_thd_id id;
   int i;
-  for (i = 0; i != m->threads; i++) {
-    GPR_ASSERT(gpr_thd_new(&id, "grpc_create_threads", body, m, nullptr));
+  for (i = 0; i != m->nthreads; i++) {
+    m->threads[i] = grpc_core::Thread("grpc_create_threads", body, m);
+    m->threads[i].Start();
   }
 }
 
@@ -201,9 +207,12 @@
     gpr_cv_wait(&m->done_cv, &m->mu, gpr_inf_future(GPR_CLOCK_MONOTONIC));
   }
   gpr_mu_unlock(&m->mu);
+  for (int i = 0; i != m->nthreads; i++) {
+    m->threads[i].Join();
+  }
 }
 
-/* Get an integer thread id in the raneg 0..threads-1 */
+/* Get an integer thread id in the raneg 0..nthreads-1 */
 static int thread_id(struct test* m) {
   int id;
   gpr_mu_lock(&m->mu);
@@ -245,16 +254,20 @@
     fprintf(stderr, " %ld", static_cast<long>(iterations));
     fflush(stderr);
     m = test_new(10, iterations, incr_step);
+    grpc_core::Thread extra_thd;
     if (extra != nullptr) {
-      gpr_thd_id id;
-      GPR_ASSERT(gpr_thd_new(&id, name, extra, m, nullptr));
+      extra_thd = grpc_core::Thread(name, extra, m);
+      extra_thd.Start();
       m->done++; /* one more thread to wait for */
     }
     test_create_threads(m, body);
     test_wait(m);
-    if (m->counter != m->threads * m->iterations * m->incr_step) {
+    if (extra != nullptr) {
+      extra_thd.Join();
+    }
+    if (m->counter != m->nthreads * m->iterations * m->incr_step) {
       fprintf(stderr, "counter %ld  threads %d  iterations %ld\n",
-              static_cast<long>(m->counter), m->threads,
+              static_cast<long>(m->counter), m->nthreads,
               static_cast<long>(m->iterations));
       fflush(stderr);
       GPR_ASSERT(0);
@@ -296,7 +309,7 @@
   mark_thread_done(m);
 }
 
-/* Increment counter only when (m->counter%m->threads)==m->thread_id; then mark
+/* Increment counter only when (m->counter%m->nthreads)==m->thread_id; then mark
    thread as done.  */
 static void inc_by_turns(void* v /*=m*/) {
   struct test* m = static_cast<struct test*>(v);
@@ -304,7 +317,7 @@
   int id = thread_id(m);
   for (i = 0; i != m->iterations; i++) {
     gpr_mu_lock(&m->mu);
-    while ((m->counter % m->threads) != id) {
+    while ((m->counter % m->nthreads) != id) {
       gpr_cv_wait(&m->cv, &m->mu, gpr_inf_future(GPR_CLOCK_MONOTONIC));
     }
     m->counter++;
@@ -369,12 +382,12 @@
   mark_thread_done(m);
 }
 
-/* Consume elements from m->q until m->threads*m->iterations are seen,
+/* Consume elements from m->q until m->nthreads*m->iterations are seen,
    wait an extra second to confirm that no more elements are arriving,
    then mark thread as done. */
 static void consumer(void* v /*=m*/) {
   struct test* m = static_cast<struct test*>(v);
-  int64_t n = m->iterations * m->threads;
+  int64_t n = m->iterations * m->nthreads;
   int64_t i;
   int value;
   for (i = 0; i != n; i++) {
@@ -424,11 +437,11 @@
 }
 
 /* Wait until m->event is set to (void *)1, then decrement m->refcount by 1
-   (m->threads * m->iterations * m->incr_step) times, and ensure that the last
+   (m->nthreads * m->iterations * m->incr_step) times, and ensure that the last
    decrement caused the counter to reach zero, then mark thread as done.  */
 static void refcheck(void* v /*=m*/) {
   struct test* m = static_cast<struct test*>(v);
-  int64_t n = m->iterations * m->threads * m->incr_step;
+  int64_t n = m->iterations * m->nthreads * m->incr_step;
   int64_t i;
   GPR_ASSERT(gpr_event_wait(&m->event, gpr_inf_future(GPR_CLOCK_REALTIME)) ==
              (void*)1);
diff --git a/test/core/gpr/thd_test.cc b/test/core/gpr/thd_test.cc
deleted file mode 100644
index 18bbaae..0000000
--- a/test/core/gpr/thd_test.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * 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.
- *
- */
-
-/* Test of gpr thread support. */
-
-#include "src/core/lib/gpr/thd.h"
-
-#include <grpc/support/log.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/time.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "test/core/util/test_config.h"
-
-#define NUM_THREADS 300
-
-struct test {
-  gpr_mu mu;
-  int n;
-  int is_done;
-  gpr_cv done_cv;
-};
-
-/* A Thread body.   Decrement t->n, and if is becomes zero, set t->done. */
-static void thd_body(void* v) {
-  struct test* t = static_cast<struct test*>(v);
-  gpr_mu_lock(&t->mu);
-  t->n--;
-  if (t->n == 0) {
-    t->is_done = 1;
-    gpr_cv_signal(&t->done_cv);
-  }
-  gpr_mu_unlock(&t->mu);
-}
-
-static void thd_body_joinable(void* v) {}
-
-/* Test thread options work as expected */
-static void test_options(void) {
-  gpr_thd_options options = gpr_thd_options_default();
-  GPR_ASSERT(!gpr_thd_options_is_joinable(&options));
-  GPR_ASSERT(gpr_thd_options_is_detached(&options));
-  gpr_thd_options_set_joinable(&options);
-  GPR_ASSERT(gpr_thd_options_is_joinable(&options));
-  GPR_ASSERT(!gpr_thd_options_is_detached(&options));
-  gpr_thd_options_set_detached(&options);
-  GPR_ASSERT(!gpr_thd_options_is_joinable(&options));
-  GPR_ASSERT(gpr_thd_options_is_detached(&options));
-}
-
-/* Test that we can create a number of threads and wait for them. */
-static void test(void) {
-  int i;
-  gpr_thd_id thd;
-  gpr_thd_id thds[NUM_THREADS];
-  struct test t;
-  gpr_thd_options options = gpr_thd_options_default();
-  gpr_mu_init(&t.mu);
-  gpr_cv_init(&t.done_cv);
-  t.n = NUM_THREADS;
-  t.is_done = 0;
-  for (i = 0; i < NUM_THREADS; i++) {
-    GPR_ASSERT(gpr_thd_new(&thd, "grpc_thread_test", &thd_body, &t, nullptr));
-  }
-  gpr_mu_lock(&t.mu);
-  while (!t.is_done) {
-    gpr_cv_wait(&t.done_cv, &t.mu, gpr_inf_future(GPR_CLOCK_REALTIME));
-  }
-  gpr_mu_unlock(&t.mu);
-  GPR_ASSERT(t.n == 0);
-  gpr_thd_options_set_joinable(&options);
-  for (i = 0; i < NUM_THREADS; i++) {
-    GPR_ASSERT(gpr_thd_new(&thds[i], "grpc_joinable_thread_test",
-                           &thd_body_joinable, nullptr, &options));
-  }
-  for (i = 0; i < NUM_THREADS; i++) {
-    gpr_thd_join(thds[i]);
-  }
-}
-
-/* ------------------------------------------------- */
-
-int main(int argc, char* argv[]) {
-  grpc_test_init(argc, argv);
-  test_options();
-  test();
-  return 0;
-}
diff --git a/test/core/gpr/time_test.cc b/test/core/gpr/time_test.cc
index e6bcc12..c80aac6 100644
--- a/test/core/gpr/time_test.cc
+++ b/test/core/gpr/time_test.cc
@@ -26,7 +26,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "test/core/util/test_config.h"
 
 static void to_fp(void* arg, const char* buf, size_t len) {
diff --git a/test/core/gpr/tls_test.cc b/test/core/gpr/tls_test.cc
index 1e4534d..0502fc7 100644
--- a/test/core/gpr/tls_test.cc
+++ b/test/core/gpr/tls_test.cc
@@ -18,13 +18,15 @@
 
 /* Test of gpr thread local storage support. */
 
-#include <grpc/support/log.h>
-#include <grpc/support/sync.h>
+#include "src/core/lib/gpr/tls.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "src/core/lib/gpr/thd.h"
-#include "src/core/lib/gpr/tls.h"
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 #define NUM_THREADS 100
@@ -46,21 +48,18 @@
 /* ------------------------------------------------- */
 
 int main(int argc, char* argv[]) {
-  gpr_thd_options opt = gpr_thd_options_default();
-  int i;
-  gpr_thd_id threads[NUM_THREADS];
+  grpc_core::Thread threads[NUM_THREADS];
 
   grpc_test_init(argc, argv);
 
   gpr_tls_init(&test_var);
 
-  gpr_thd_options_set_joinable(&opt);
-
-  for (i = 0; i < NUM_THREADS; i++) {
-    gpr_thd_new(&threads[i], "grpc_tls_test", thd_body, nullptr, &opt);
+  for (auto& th : threads) {
+    th = grpc_core::Thread("grpc_tls_test", thd_body, nullptr);
+    th.Start();
   }
-  for (i = 0; i < NUM_THREADS; i++) {
-    gpr_thd_join(threads[i]);
+  for (auto& th : threads) {
+    th.Join();
   }
 
   gpr_tls_destroy(&test_var);
diff --git a/test/core/gprpp/BUILD b/test/core/gprpp/BUILD
index 1c11e0b..a8a5739 100644
--- a/test/core/gprpp/BUILD
+++ b/test/core/gprpp/BUILD
@@ -24,7 +24,6 @@
     language = "C++",
     deps = [
         "//:gpr",
-        "//:gpr++_base",
         "//test/core/util:gpr_test_util",
     ],
 )
@@ -37,7 +36,7 @@
     ],
     language = "C++",
     deps = [
-        "//:gpr++_base",
+        "//:gpr_base",
         "//test/core/util:gpr_test_util",
     ],
 )
@@ -58,39 +57,49 @@
 grpc_cc_test(
     name = "orphanable_test",
     srcs = ["orphanable_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
     language = "C++",
     deps = [
         "//:orphanable",
         "//test/core/util:gpr_test_util",
     ],
-    external_deps = [
-        "gtest",
-    ],
 )
 
 grpc_cc_test(
     name = "ref_counted_test",
     srcs = ["ref_counted_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
     language = "C++",
     deps = [
         "//:ref_counted",
         "//test/core/util:gpr_test_util",
     ],
-    external_deps = [
-        "gtest",
-    ],
 )
 
 grpc_cc_test(
     name = "ref_counted_ptr_test",
     srcs = ["ref_counted_ptr_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
     language = "C++",
     deps = [
         "//:ref_counted",
         "//:ref_counted_ptr",
         "//test/core/util:gpr_test_util",
     ],
-    external_deps = [
-        "gtest",
+)
+
+grpc_cc_test(
+    name = "thd_test",
+    srcs = ["thd_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//test/core/util:gpr_test_util",
     ],
 )
diff --git a/test/core/gprpp/manual_constructor_test.cc b/test/core/gprpp/manual_constructor_test.cc
index 74777fe..af162ae 100644
--- a/test/core/gprpp/manual_constructor_test.cc
+++ b/test/core/gprpp/manual_constructor_test.cc
@@ -26,7 +26,6 @@
 #include <stdlib.h>
 #include <cstring>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gprpp/abstract.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/core/gprpp/thd_test.cc b/test/core/gprpp/thd_test.cc
new file mode 100644
index 0000000..82dd681
--- /dev/null
+++ b/test/core/gprpp/thd_test.cc
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+/* Test of gpr thread support. */
+
+#include "src/core/lib/gprpp/thd.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "test/core/util/test_config.h"
+
+#define NUM_THREADS 100
+
+struct test {
+  gpr_mu mu;
+  int n;
+  int is_done;
+  gpr_cv done_cv;
+};
+
+/* A Thread body.   Decrement t->n, and if is becomes zero, set t->done. */
+static void thd_body1(void* v) {
+  struct test* t = static_cast<struct test*>(v);
+  gpr_mu_lock(&t->mu);
+  t->n--;
+  if (t->n == 0) {
+    t->is_done = 1;
+    gpr_cv_signal(&t->done_cv);
+  }
+  gpr_mu_unlock(&t->mu);
+}
+
+/* Test that we can create a number of threads, wait for them, and join them. */
+static void test1(void) {
+  grpc_core::Thread thds[NUM_THREADS];
+  struct test t;
+  gpr_mu_init(&t.mu);
+  gpr_cv_init(&t.done_cv);
+  t.n = NUM_THREADS;
+  t.is_done = 0;
+  for (auto& th : thds) {
+    th = grpc_core::Thread("grpc_thread_body1_test", &thd_body1, &t);
+    th.Start();
+  }
+  gpr_mu_lock(&t.mu);
+  while (!t.is_done) {
+    gpr_cv_wait(&t.done_cv, &t.mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+  }
+  gpr_mu_unlock(&t.mu);
+  for (auto& th : thds) {
+    th.Join();
+  }
+  GPR_ASSERT(t.n == 0);
+}
+
+static void thd_body2(void* v) {}
+
+/* Test that we can create a number of threads and join them. */
+static void test2(void) {
+  grpc_core::Thread thds[NUM_THREADS];
+  for (auto& th : thds) {
+    bool ok;
+    th = grpc_core::Thread("grpc_thread_body2_test", &thd_body2, nullptr, &ok);
+    GPR_ASSERT(ok);
+    th.Start();
+  }
+  for (auto& th : thds) {
+    th.Join();
+  }
+}
+
+/* ------------------------------------------------- */
+
+int main(int argc, char* argv[]) {
+  grpc_test_init(argc, argv);
+  test1();
+  test2();
+  return 0;
+}
diff --git a/test/core/handshake/client_ssl.cc b/test/core/handshake/client_ssl.cc
index fe2ab25..8ac763a 100644
--- a/test/core/handshake/client_ssl.cc
+++ b/test/core/handshake/client_ssl.cc
@@ -35,7 +35,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -230,12 +230,11 @@
   GPR_ASSERT(server_socket > 0 && port > 0);
 
   // Launch the TLS server thread.
-  gpr_thd_options thdopt = gpr_thd_options_default();
-  gpr_thd_id thdid;
-  gpr_thd_options_set_joinable(&thdopt);
   server_args args = {server_socket, server_alpn_preferred};
-  GPR_ASSERT(gpr_thd_new(&thdid, "grpc_client_ssl_test", server_thread, &args,
-                         &thdopt));
+  bool ok;
+  grpc_core::Thread thd("grpc_client_ssl_test", server_thread, &args, &ok);
+  GPR_ASSERT(ok);
+  thd.Start();
 
   // Load key pair and establish client SSL credentials.
   grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
@@ -303,7 +302,7 @@
   grpc_slice_unref(key_slice);
   grpc_slice_unref(ca_slice);
 
-  gpr_thd_join(thdid);
+  thd.Join();
 
   grpc_shutdown();
 
diff --git a/test/core/handshake/readahead_handshaker_server_ssl.cc b/test/core/handshake/readahead_handshaker_server_ssl.cc
index 80000ca..9788320 100644
--- a/test/core/handshake/readahead_handshaker_server_ssl.cc
+++ b/test/core/handshake/readahead_handshaker_server_ssl.cc
@@ -30,7 +30,6 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/handshake/server_ssl.cc b/test/core/handshake/server_ssl.cc
index f0465c8..8fa5f7f 100644
--- a/test/core/handshake/server_ssl.cc
+++ b/test/core/handshake/server_ssl.cc
@@ -30,7 +30,6 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/handshake/server_ssl_common.cc b/test/core/handshake/server_ssl_common.cc
index d202a7c..41b2829 100644
--- a/test/core/handshake/server_ssl_common.cc
+++ b/test/core/handshake/server_ssl_common.cc
@@ -32,7 +32,7 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -138,11 +138,10 @@
   gpr_event_init(&client_handshake_complete);
 
   // Launch the gRPC server thread.
-  gpr_thd_options thdopt = gpr_thd_options_default();
-  gpr_thd_id thdid;
-  gpr_thd_options_set_joinable(&thdopt);
-  GPR_ASSERT(
-      gpr_thd_new(&thdid, "grpc_ssl_test", server_thread, &port, &thdopt));
+  bool ok;
+  grpc_core::Thread thd("grpc_ssl_test", server_thread, &port, &ok);
+  GPR_ASSERT(ok);
+  thd.Start();
 
   SSL_load_error_strings();
   OpenSSL_add_ssl_algorithms();
@@ -235,7 +234,7 @@
   EVP_cleanup();
   close(sock);
 
-  gpr_thd_join(thdid);
+  thd.Join();
 
   grpc_shutdown();
 
diff --git a/test/core/handshake/server_ssl_common.h b/test/core/handshake/server_ssl_common.h
index f726a1c..32bc6f9 100644
--- a/test/core/handshake/server_ssl_common.h
+++ b/test/core/handshake/server_ssl_common.h
@@ -26,7 +26,6 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/iomgr/load_file.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/iomgr/BUILD b/test/core/iomgr/BUILD
index 41e2607..349a06d 100644
--- a/test/core/iomgr/BUILD
+++ b/test/core/iomgr/BUILD
@@ -60,6 +60,19 @@
 )
 
 grpc_cc_test(
+    name = "error_test",
+    srcs = ["error_test.cc"],
+    language = "C++",
+    deps = [
+        ":endpoint_tests",
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "ev_epollsig_linux_test",
     srcs = ["ev_epollsig_linux_test.cc"],
     deps = [
diff --git a/test/core/iomgr/combiner_test.cc b/test/core/iomgr/combiner_test.cc
index 8426b3d..cf2c7db 100644
--- a/test/core/iomgr/combiner_test.cc
+++ b/test/core/iomgr/combiner_test.cc
@@ -22,8 +22,8 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/test_config.h"
 
 static void test_no_op(void) {
@@ -97,21 +97,19 @@
   gpr_log(GPR_DEBUG, "test_execute_many");
 
   grpc_combiner* lock = grpc_combiner_create();
-  gpr_thd_id thds[100];
+  grpc_core::Thread thds[100];
   thd_args ta[GPR_ARRAY_SIZE(thds)];
   for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_options options = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&options);
     ta[i].ctr = 0;
     ta[i].lock = lock;
     gpr_event_init(&ta[i].done);
-    GPR_ASSERT(gpr_thd_new(&thds[i], "grpc_execute_many", execute_many_loop,
-                           &ta[i], &options));
+    thds[i] = grpc_core::Thread("grpc_execute_many", execute_many_loop, &ta[i]);
+    thds[i].Start();
   }
   for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
     GPR_ASSERT(gpr_event_wait(&ta[i].done,
                               gpr_inf_future(GPR_CLOCK_REALTIME)) != nullptr);
-    gpr_thd_join(thds[i]);
+    thds[i].Join();
   }
   grpc_core::ExecCtx exec_ctx;
   GRPC_COMBINER_UNREF(lock, "test_execute_many");
diff --git a/test/core/iomgr/error_test.cc b/test/core/iomgr/error_test.cc
index f6292b7..a1628a1 100644
--- a/test/core/iomgr/error_test.cc
+++ b/test/core/iomgr/error_test.cc
@@ -24,7 +24,6 @@
 
 #include <string.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "test/core/util/test_config.h"
 
 static void test_set_get_int() {
diff --git a/test/core/iomgr/ev_epollsig_linux_test.cc b/test/core/iomgr/ev_epollsig_linux_test.cc
index 02d1127..c3ba6d7 100644
--- a/test/core/iomgr/ev_epollsig_linux_test.cc
+++ b/test/core/iomgr/ev_epollsig_linux_test.cc
@@ -30,8 +30,8 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "test/core/util/test_config.h"
 
@@ -259,11 +259,10 @@
   shared.pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
   grpc_pollset_init(shared.pollset, &shared.mu);
 
-  gpr_thd_id thds[10];
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_options opt = gpr_thd_options_default();
-    gpr_thd_options_set_joinable(&opt);
-    gpr_thd_new(&thds[i], "test_thread", test_threading_loop, &shared, &opt);
+  grpc_core::Thread thds[10];
+  for (auto& th : thds) {
+    th = grpc_core::Thread("test_thread", test_threading_loop, &shared);
+    th.Start();
   }
   grpc_wakeup_fd fd;
   GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_fd_init", grpc_wakeup_fd_init(&fd)));
@@ -280,8 +279,8 @@
   }
   GPR_ASSERT(GRPC_LOG_IF_ERROR("wakeup_first",
                                grpc_wakeup_fd_wakeup(shared.wakeup_fd)));
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
-    gpr_thd_join(thds[i]);
+  for (auto& th : thds) {
+    th.Join();
   }
   fd.read_fd = 0;
   grpc_wakeup_fd_destroy(&fd);
diff --git a/test/core/iomgr/resolve_address_posix_test.cc b/test/core/iomgr/resolve_address_posix_test.cc
index 341579f..79b2b50 100644
--- a/test/core/iomgr/resolve_address_posix_test.cc
+++ b/test/core/iomgr/resolve_address_posix_test.cc
@@ -27,8 +27,8 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "test/core/util/test_config.h"
@@ -38,6 +38,7 @@
 }
 
 typedef struct args_struct {
+  grpc_core::Thread thd;
   gpr_event ev;
   grpc_resolved_addresses* addrs;
   gpr_atm done_atm;
@@ -59,6 +60,9 @@
 
 void args_finish(args_struct* args) {
   GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline()));
+  args->thd.Join();
+  // Don't need to explicitly destruct args->thd since
+  // args is actually going to be destructed, not just freed
   grpc_resolved_addresses_destroy(args->addrs);
   grpc_pollset_set_del_pollset(args->pollset_set, args->pollset);
   grpc_pollset_set_destroy(args->pollset_set);
@@ -101,8 +105,8 @@
 
 static void poll_pollset_until_request_done(args_struct* args) {
   gpr_atm_rel_store(&args->done_atm, 0);
-  gpr_thd_id id;
-  gpr_thd_new(&id, "grpc_poll_pollset", actually_poll, args, nullptr);
+  args->thd = grpc_core::Thread("grpc_poll_pollset", actually_poll, args);
+  args->thd.Start();
 }
 
 static void must_succeed(void* argsp, grpc_error* err) {
diff --git a/test/core/iomgr/udp_server_test.cc b/test/core/iomgr/udp_server_test.cc
index 13cbf2f..60f2939 100644
--- a/test/core/iomgr/udp_server_test.cc
+++ b/test/core/iomgr/udp_server_test.cc
@@ -36,6 +36,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/socket_factory_posix.h"
@@ -54,42 +55,70 @@
 int rcv_buf_size = 1024;
 int snd_buf_size = 1024;
 
-static void on_start(grpc_fd* emfd, void* user_data) { g_number_of_starts++; }
+class TestGrpcUdpHandler : public GrpcUdpHandler {
+ public:
+  TestGrpcUdpHandler(grpc_fd* emfd, void* user_data)
+      : GrpcUdpHandler(emfd, user_data), emfd_(emfd) {
+    g_number_of_starts++;
+  }
+  ~TestGrpcUdpHandler() override {}
 
-static bool on_read(grpc_fd* emfd) {
-  char read_buffer[512];
-  ssize_t byte_count;
+ protected:
+  bool Read() override {
+    char read_buffer[512];
+    ssize_t byte_count;
 
-  gpr_mu_lock(g_mu);
-  byte_count =
-      recv(grpc_fd_wrapped_fd(emfd), read_buffer, sizeof(read_buffer), 0);
+    gpr_mu_lock(g_mu);
+    byte_count =
+        recv(grpc_fd_wrapped_fd(emfd()), read_buffer, sizeof(read_buffer), 0);
 
-  g_number_of_reads++;
-  g_number_of_bytes_read += static_cast<int>(byte_count);
+    g_number_of_reads++;
+    g_number_of_bytes_read += static_cast<int>(byte_count);
 
-  GPR_ASSERT(
-      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, nullptr)));
-  gpr_mu_unlock(g_mu);
-  return false;
-}
+    GPR_ASSERT(GRPC_LOG_IF_ERROR("pollset_kick",
+                                 grpc_pollset_kick(g_pollset, nullptr)));
+    gpr_mu_unlock(g_mu);
+    return false;
+  }
 
-static void on_write(grpc_fd* emfd, void* user_data,
-                     grpc_closure* notify_on_write_closure) {
-  gpr_mu_lock(g_mu);
-  g_number_of_writes++;
+  void OnCanWrite(void* user_data,
+                  grpc_closure* notify_on_write_closure) override {
+    gpr_mu_lock(g_mu);
+    g_number_of_writes++;
 
-  GPR_ASSERT(
-      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, nullptr)));
-  gpr_mu_unlock(g_mu);
-}
+    GPR_ASSERT(GRPC_LOG_IF_ERROR("pollset_kick",
+                                 grpc_pollset_kick(g_pollset, nullptr)));
+    gpr_mu_unlock(g_mu);
+  }
 
-static void on_fd_orphaned(grpc_fd* emfd, grpc_closure* closure,
-                           void* user_data) {
-  gpr_log(GPR_INFO, "gRPC FD about to be orphaned: %d",
-          grpc_fd_wrapped_fd(emfd));
-  GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
-  g_number_of_orphan_calls++;
-}
+  void OnFdAboutToOrphan(grpc_closure* orphan_fd_closure,
+                         void* user_data) override {
+    gpr_log(GPR_INFO, "gRPC FD about to be orphaned: %d",
+            grpc_fd_wrapped_fd(emfd()));
+    GRPC_CLOSURE_SCHED(orphan_fd_closure, GRPC_ERROR_NONE);
+    g_number_of_orphan_calls++;
+  }
+
+  grpc_fd* emfd() { return emfd_; }
+
+ private:
+  grpc_fd* emfd_;
+};
+
+class TestGrpcUdpHandlerFactory : public GrpcUdpHandlerFactory {
+ public:
+  GrpcUdpHandler* CreateUdpHandler(grpc_fd* emfd, void* user_data) override {
+    gpr_log(GPR_INFO, "create udp handler for fd %d", grpc_fd_wrapped_fd(emfd));
+    return grpc_core::New<TestGrpcUdpHandler>(emfd, user_data);
+  }
+
+  void DestroyUdpHandler(GrpcUdpHandler* handler) override {
+    gpr_log(GPR_INFO, "Destroy handler");
+    grpc_core::Delete(reinterpret_cast<TestGrpcUdpHandler*>(handler));
+  }
+};
+
+TestGrpcUdpHandlerFactory handler_factory;
 
 struct test_socket_factory {
   grpc_socket_factory base;
@@ -184,13 +213,12 @@
   resolved_addr.len = sizeof(struct sockaddr_in);
   addr->sin_family = AF_INET;
   GPR_ASSERT(grpc_udp_server_add_port(s, &resolved_addr, rcv_buf_size,
-                                      snd_buf_size, on_start, on_read, on_write,
-                                      on_fd_orphaned));
+                                      snd_buf_size, &handler_factory));
 
   grpc_udp_server_destroy(s, nullptr);
 
-  /* The server had a single FD, which should have been orphaned. */
-  GPR_ASSERT(g_number_of_orphan_calls == 1);
+  /* The server haven't start listening, so no udp handler to be notified. */
+  GPR_ASSERT(g_number_of_orphan_calls == 0);
   shutdown_and_destroy_pollset();
 }
 
@@ -216,8 +244,7 @@
   resolved_addr.len = sizeof(struct sockaddr_in);
   addr->sin_family = AF_INET;
   GPR_ASSERT(grpc_udp_server_add_port(s, &resolved_addr, rcv_buf_size,
-                                      snd_buf_size, on_start, on_read, on_write,
-                                      on_fd_orphaned));
+                                      snd_buf_size, &handler_factory));
   GPR_ASSERT(socket_factory->number_of_socket_calls == 1);
   GPR_ASSERT(socket_factory->number_of_bind_calls == 1);
 
@@ -225,8 +252,8 @@
 
   grpc_socket_factory_unref(&socket_factory->base);
 
-  /* The server had a single FD, which should have been orphaned. */
-  GPR_ASSERT(g_number_of_orphan_calls == 1);
+  /* The server haven't start listening, so no udp handler to be notified. */
+  GPR_ASSERT(g_number_of_orphan_calls == 0);
   shutdown_and_destroy_pollset();
 }
 
@@ -244,8 +271,7 @@
   resolved_addr.len = sizeof(struct sockaddr_in);
   addr->sin_family = AF_INET;
   GPR_ASSERT(grpc_udp_server_add_port(s, &resolved_addr, rcv_buf_size,
-                                      snd_buf_size, on_start, on_read, on_write,
-                                      on_fd_orphaned));
+                                      snd_buf_size, &handler_factory));
 
   grpc_udp_server_start(s, nullptr, 0, nullptr);
   GPR_ASSERT(g_number_of_starts == 1);
@@ -278,8 +304,7 @@
   resolved_addr.len = sizeof(struct sockaddr_storage);
   addr->ss_family = AF_INET;
   GPR_ASSERT(grpc_udp_server_add_port(s, &resolved_addr, rcv_buf_size,
-                                      snd_buf_size, on_start, on_read, on_write,
-                                      on_fd_orphaned));
+                                      snd_buf_size, &handler_factory));
 
   svrfd = grpc_udp_server_get_fd(s, 0);
   GPR_ASSERT(svrfd >= 0);
diff --git a/test/core/iomgr/wakeup_fd_cv_test.cc b/test/core/iomgr/wakeup_fd_cv_test.cc
index 68dcb50..9bd7c6e 100644
--- a/test/core/iomgr/wakeup_fd_cv_test.cc
+++ b/test/core/iomgr/wakeup_fd_cv_test.cc
@@ -26,7 +26,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/iomgr_posix.h"
 
@@ -103,8 +103,6 @@
   grpc_wakeup_fd cvfd1, cvfd2, cvfd3;
   struct pollfd pfds[6];
   poll_args pargs;
-  gpr_thd_id t_id;
-  gpr_thd_options opt;
 
   GPR_ASSERT(grpc_wakeup_fd_init(&cvfd1) == GRPC_ERROR_NONE);
   GPR_ASSERT(grpc_wakeup_fd_init(&cvfd2) == GRPC_ERROR_NONE);
@@ -135,79 +133,91 @@
   pargs.timeout = 1000;
   pargs.result = -2;
 
-  opt = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&opt);
-  gpr_thd_new(&t_id, "grpc_background_poll", &background_poll, &pargs, &opt);
+  {
+    grpc_core::Thread thd("grpc_background_poll", &background_poll, &pargs);
+    thd.Start();
+    // Wakeup wakeup_fd not listening for events
+    GPR_ASSERT(grpc_wakeup_fd_wakeup(&cvfd1) == GRPC_ERROR_NONE);
+    thd.Join();
+    GPR_ASSERT(pargs.result == 0);
+    GPR_ASSERT(pfds[0].revents == 0);
+    GPR_ASSERT(pfds[1].revents == 0);
+    GPR_ASSERT(pfds[2].revents == 0);
+    GPR_ASSERT(pfds[3].revents == 0);
+    GPR_ASSERT(pfds[4].revents == 0);
+    GPR_ASSERT(pfds[5].revents == 0);
+  }
 
-  // Wakeup wakeup_fd not listening for events
-  GPR_ASSERT(grpc_wakeup_fd_wakeup(&cvfd1) == GRPC_ERROR_NONE);
-  gpr_thd_join(t_id);
-  GPR_ASSERT(pargs.result == 0);
-  GPR_ASSERT(pfds[0].revents == 0);
-  GPR_ASSERT(pfds[1].revents == 0);
-  GPR_ASSERT(pfds[2].revents == 0);
-  GPR_ASSERT(pfds[3].revents == 0);
-  GPR_ASSERT(pfds[4].revents == 0);
-  GPR_ASSERT(pfds[5].revents == 0);
+  {
+    // Pollin on socket fd
+    pargs.timeout = -1;
+    pargs.result = -2;
+    grpc_core::Thread thd("grpc_background_poll", &background_poll, &pargs);
+    thd.Start();
+    trigger_socket_event();
+    thd.Join();
+    GPR_ASSERT(pargs.result == 1);
+    GPR_ASSERT(pfds[0].revents == 0);
+    GPR_ASSERT(pfds[1].revents == 0);
+    GPR_ASSERT(pfds[2].revents == POLLIN);
+    GPR_ASSERT(pfds[3].revents == 0);
+    GPR_ASSERT(pfds[4].revents == 0);
+    GPR_ASSERT(pfds[5].revents == 0);
+  }
 
-  // Pollin on socket fd
-  pargs.timeout = -1;
-  pargs.result = -2;
-  gpr_thd_new(&t_id, "grpc_background_poll", &background_poll, &pargs, &opt);
-  trigger_socket_event();
-  gpr_thd_join(t_id);
-  GPR_ASSERT(pargs.result == 1);
-  GPR_ASSERT(pfds[0].revents == 0);
-  GPR_ASSERT(pfds[1].revents == 0);
-  GPR_ASSERT(pfds[2].revents == POLLIN);
-  GPR_ASSERT(pfds[3].revents == 0);
-  GPR_ASSERT(pfds[4].revents == 0);
-  GPR_ASSERT(pfds[5].revents == 0);
+  {
+    // Pollin on wakeup fd
+    reset_socket_event();
+    pargs.result = -2;
+    grpc_core::Thread thd("grpc_background_poll", &background_poll, &pargs);
+    thd.Start();
+    GPR_ASSERT(grpc_wakeup_fd_wakeup(&cvfd2) == GRPC_ERROR_NONE);
+    thd.Join();
 
-  // Pollin on wakeup fd
-  reset_socket_event();
-  pargs.result = -2;
-  gpr_thd_new(&t_id, "grpc_background_poll", &background_poll, &pargs, &opt);
-  GPR_ASSERT(grpc_wakeup_fd_wakeup(&cvfd2) == GRPC_ERROR_NONE);
-  gpr_thd_join(t_id);
+    GPR_ASSERT(pargs.result == 1);
+    GPR_ASSERT(pfds[0].revents == 0);
+    GPR_ASSERT(pfds[1].revents == POLLIN);
+    GPR_ASSERT(pfds[2].revents == 0);
+    GPR_ASSERT(pfds[3].revents == 0);
+    GPR_ASSERT(pfds[4].revents == 0);
+    GPR_ASSERT(pfds[5].revents == 0);
+  }
 
-  GPR_ASSERT(pargs.result == 1);
-  GPR_ASSERT(pfds[0].revents == 0);
-  GPR_ASSERT(pfds[1].revents == POLLIN);
-  GPR_ASSERT(pfds[2].revents == 0);
-  GPR_ASSERT(pfds[3].revents == 0);
-  GPR_ASSERT(pfds[4].revents == 0);
-  GPR_ASSERT(pfds[5].revents == 0);
+  {
+    // Pollin on wakeupfd before poll()
+    pargs.result = -2;
+    grpc_core::Thread thd("grpc_background_poll", &background_poll, &pargs);
+    thd.Start();
+    thd.Join();
 
-  // Pollin on wakeupfd before poll()
-  pargs.result = -2;
-  gpr_thd_new(&t_id, "grpc_background_poll", &background_poll, &pargs, &opt);
-  gpr_thd_join(t_id);
+    GPR_ASSERT(pargs.result == 1);
+    GPR_ASSERT(pfds[0].revents == 0);
+    GPR_ASSERT(pfds[1].revents == POLLIN);
+    GPR_ASSERT(pfds[2].revents == 0);
+    GPR_ASSERT(pfds[3].revents == 0);
+    GPR_ASSERT(pfds[4].revents == 0);
+    GPR_ASSERT(pfds[5].revents == 0);
+  }
 
-  GPR_ASSERT(pargs.result == 1);
-  GPR_ASSERT(pfds[0].revents == 0);
-  GPR_ASSERT(pfds[1].revents == POLLIN);
-  GPR_ASSERT(pfds[2].revents == 0);
-  GPR_ASSERT(pfds[3].revents == 0);
-  GPR_ASSERT(pfds[4].revents == 0);
-  GPR_ASSERT(pfds[5].revents == 0);
+  {
+    // No Events
+    pargs.result = -2;
+    pargs.timeout = 1000;
+    reset_socket_event();
+    GPR_ASSERT(grpc_wakeup_fd_consume_wakeup(&cvfd1) == GRPC_ERROR_NONE);
+    GPR_ASSERT(grpc_wakeup_fd_consume_wakeup(&cvfd2) == GRPC_ERROR_NONE);
+    grpc_core::Thread thd("grpc_background_poll", &background_poll, &pargs);
+    thd.Start();
+    thd.Join();
 
-  // No Events
-  pargs.result = -2;
-  pargs.timeout = 1000;
-  reset_socket_event();
-  GPR_ASSERT(grpc_wakeup_fd_consume_wakeup(&cvfd1) == GRPC_ERROR_NONE);
-  GPR_ASSERT(grpc_wakeup_fd_consume_wakeup(&cvfd2) == GRPC_ERROR_NONE);
-  gpr_thd_new(&t_id, "grpc_background_poll", &background_poll, &pargs, &opt);
-  gpr_thd_join(t_id);
-
-  GPR_ASSERT(pargs.result == 0);
-  GPR_ASSERT(pfds[0].revents == 0);
-  GPR_ASSERT(pfds[1].revents == 0);
-  GPR_ASSERT(pfds[2].revents == 0);
-  GPR_ASSERT(pfds[3].revents == 0);
-  GPR_ASSERT(pfds[4].revents == 0);
-  GPR_ASSERT(pfds[5].revents == 0);
+    GPR_ASSERT(pargs.result == 0);
+    GPR_ASSERT(pfds[0].revents == 0);
+    GPR_ASSERT(pfds[1].revents == 0);
+    GPR_ASSERT(pfds[2].revents == 0);
+    GPR_ASSERT(pfds[3].revents == 0);
+    GPR_ASSERT(pfds[4].revents == 0);
+    GPR_ASSERT(pfds[5].revents == 0);
+  }
 }
 
 int main(int argc, char** argv) {
diff --git a/test/core/network_benchmarks/low_level_ping_pong.cc b/test/core/network_benchmarks/low_level_ping_pong.cc
index 33716b9..a983b18 100644
--- a/test/core/network_benchmarks/low_level_ping_pong.cc
+++ b/test/core/network_benchmarks/low_level_ping_pong.cc
@@ -38,8 +38,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/iomgr/socket_utils_posix.h"
 #include "test/core/util/cmdline.h"
@@ -575,7 +575,6 @@
 
 static int run_benchmark(const char* socket_type, thread_args* client_args,
                          thread_args* server_args) {
-  gpr_thd_id tid;
   int rv = 0;
 
   rv = create_socket(socket_type, &client_args->fds, &server_args->fds);
@@ -586,8 +585,11 @@
   gpr_log(GPR_INFO, "Starting test %s %s %zu", client_args->strategy_name,
           socket_type, client_args->msg_size);
 
-  gpr_thd_new(&tid, "server_thread", server_thread_wrap, server_args, nullptr);
+  grpc_core::Thread server("server_thread", server_thread_wrap, server_args);
+  server.Start();
   client_thread(client_args);
+  server.Join();
+
   return 0;
 }
 
diff --git a/test/core/security/BUILD b/test/core/security/BUILD
index 425c617..68de2d1 100644
--- a/test/core/security/BUILD
+++ b/test/core/security/BUILD
@@ -24,7 +24,7 @@
     name = "ssl_server_fuzzer",
     srcs = ["ssl_server_fuzzer.cc"],
     language = "C++",
-    corpus = "corpus",
+    corpus = "corpus/ssl_server_corpus",
     deps = [
         "//:gpr",
         "//:grpc",
@@ -67,6 +67,31 @@
 )
 
 grpc_cc_test(
+    name = "json_token_test",
+    srcs = ["json_token_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "jwt_verifier_test",
+    srcs = ["jwt_verifier_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+
+grpc_cc_test(
     name = "secure_endpoint_test",
     srcs = ["secure_endpoint_test.cc"],
     language = "C++",
@@ -136,3 +161,39 @@
         "//test/core/util:grpc_test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "check_gcp_environment_linux_test",
+    srcs = ["check_gcp_environment_linux_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "check_gcp_environment_windows_test",
+    srcs = ["check_gcp_environment_windows_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "grpc_alts_credentials_options_test",
+    srcs = ["grpc_alts_credentials_options_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_security_connector_test",
+    srcs = ["alts_security_connector_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+)
diff --git a/test/core/security/alts_security_connector_test.cc b/test/core/security/alts_security_connector_test.cc
new file mode 100644
index 0000000..103a493
--- /dev/null
+++ b/test/core/security/alts_security_connector_test.cc
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/security/security_connector/alts_security_connector.h"
+#include "src/core/lib/transport/transport.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/transport_security.h"
+
+using grpc_core::internal::grpc_alts_auth_context_from_tsi_peer;
+
+/* This file contains unit tests of grpc_alts_auth_context_from_tsi_peer(). */
+static void test_invalid_input_failure() {
+  tsi_peer peer;
+  grpc_auth_context* ctx;
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(nullptr, &ctx) ==
+             GRPC_SECURITY_ERROR);
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(&peer, nullptr) ==
+             GRPC_SECURITY_ERROR);
+}
+
+static void test_empty_certificate_type_failure() {
+  tsi_peer peer;
+  grpc_auth_context* ctx = nullptr;
+  GPR_ASSERT(tsi_construct_peer(0, &peer) == TSI_OK);
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(&peer, &ctx) ==
+             GRPC_SECURITY_ERROR);
+  GPR_ASSERT(ctx == nullptr);
+  tsi_peer_destruct(&peer);
+}
+
+static void test_empty_peer_property_failure() {
+  tsi_peer peer;
+  grpc_auth_context* ctx;
+  GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
+                 &peer.properties[0]) == TSI_OK);
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(&peer, &ctx) ==
+             GRPC_SECURITY_ERROR);
+  GPR_ASSERT(ctx == nullptr);
+  tsi_peer_destruct(&peer);
+}
+
+static void test_missing_rpc_protocol_versions_property_failure() {
+  tsi_peer peer;
+  grpc_auth_context* ctx;
+  GPR_ASSERT(tsi_construct_peer(kTsiAltsNumOfPeerProperties, &peer) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
+                 &peer.properties[0]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, "alice",
+                 &peer.properties[1]) == TSI_OK);
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(&peer, &ctx) ==
+             GRPC_SECURITY_ERROR);
+  GPR_ASSERT(ctx == nullptr);
+  tsi_peer_destruct(&peer);
+}
+
+static void test_unknown_peer_property_failure() {
+  tsi_peer peer;
+  grpc_auth_context* ctx;
+  GPR_ASSERT(tsi_construct_peer(kTsiAltsNumOfPeerProperties, &peer) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
+                 &peer.properties[0]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 "unknown", "alice", &peer.properties[1]) == TSI_OK);
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(&peer, &ctx) ==
+             GRPC_SECURITY_ERROR);
+  GPR_ASSERT(ctx == nullptr);
+  tsi_peer_destruct(&peer);
+}
+
+static bool test_identity(const grpc_auth_context* ctx,
+                          const char* expected_property_name,
+                          const char* expected_identity) {
+  grpc_auth_property_iterator it;
+  const grpc_auth_property* prop;
+  GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx));
+  it = grpc_auth_context_peer_identity(ctx);
+  prop = grpc_auth_property_iterator_next(&it);
+  GPR_ASSERT(prop != nullptr);
+  if (strcmp(prop->name, expected_property_name) != 0) {
+    gpr_log(GPR_ERROR, "Expected peer identity property name %s and got %s.",
+            expected_property_name, prop->name);
+    return false;
+  }
+  if (strncmp(prop->value, expected_identity, prop->value_length) != 0) {
+    gpr_log(GPR_ERROR, "Expected peer identity %s and got got %s.",
+            expected_identity, prop->value);
+    return false;
+  }
+  return true;
+}
+
+static void test_alts_peer_to_auth_context_success() {
+  tsi_peer peer;
+  grpc_auth_context* ctx;
+  GPR_ASSERT(tsi_construct_peer(kTsiAltsNumOfPeerProperties, &peer) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
+                 &peer.properties[0]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, "alice",
+                 &peer.properties[1]) == TSI_OK);
+  grpc_gcp_rpc_protocol_versions peer_versions;
+  grpc_gcp_rpc_protocol_versions_set_max(&peer_versions,
+                                         GRPC_PROTOCOL_VERSION_MAX_MAJOR,
+                                         GRPC_PROTOCOL_VERSION_MAX_MINOR);
+  grpc_gcp_rpc_protocol_versions_set_min(&peer_versions,
+                                         GRPC_PROTOCOL_VERSION_MIN_MAJOR,
+                                         GRPC_PROTOCOL_VERSION_MIN_MINOR);
+  grpc_slice serialized_peer_versions;
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_encode(&peer_versions,
+                                                   &serialized_peer_versions));
+
+  GPR_ASSERT(tsi_construct_string_peer_property(
+                 TSI_ALTS_RPC_VERSIONS,
+                 reinterpret_cast<char*>(
+                     GRPC_SLICE_START_PTR(serialized_peer_versions)),
+                 GRPC_SLICE_LENGTH(serialized_peer_versions),
+                 &peer.properties[2]) == TSI_OK);
+  GPR_ASSERT(grpc_alts_auth_context_from_tsi_peer(&peer, &ctx) ==
+             GRPC_SECURITY_OK);
+  GPR_ASSERT(
+      test_identity(ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, "alice"));
+  GRPC_AUTH_CONTEXT_UNREF(ctx, "test");
+  grpc_slice_unref(serialized_peer_versions);
+  tsi_peer_destruct(&peer);
+}
+
+int main(int argc, char** argv) {
+  /* Test. */
+  test_invalid_input_failure();
+  test_empty_certificate_type_failure();
+  test_empty_peer_property_failure();
+  test_unknown_peer_property_failure();
+  test_missing_rpc_protocol_versions_property_failure();
+  test_alts_peer_to_auth_context_success();
+
+  return 0;
+}
diff --git a/test/core/security/check_gcp_environment_linux_test.cc b/test/core/security/check_gcp_environment_linux_test.cc
new file mode 100644
index 0000000..6c436a3
--- /dev/null
+++ b/test/core/security/check_gcp_environment_linux_test.cc
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#if GPR_LINUX
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/tmpfile.h"
+
+static bool check_bios_data_linux_test(const char* data) {
+  /* Create a file with contents data. */
+  char* filename = nullptr;
+  FILE* fp = gpr_tmpfile("check_gcp_environment_test", &filename);
+  GPR_ASSERT(filename != nullptr);
+  GPR_ASSERT(fp != nullptr);
+  GPR_ASSERT(fwrite(data, 1, strlen(data), fp) == strlen(data));
+  fclose(fp);
+  bool result = grpc_core::internal::check_bios_data(
+      reinterpret_cast<const char*>(filename));
+  /* Cleanup. */
+  remove(filename);
+  gpr_free(filename);
+  return result;
+}
+
+static void test_gcp_environment_check_success() {
+  /* Exact match. */
+  GPR_ASSERT(check_bios_data_linux_test("Google"));
+  GPR_ASSERT(check_bios_data_linux_test("Google Compute Engine"));
+  /* With leading and trailing whitespaces. */
+  GPR_ASSERT(check_bios_data_linux_test(" Google  "));
+  GPR_ASSERT(check_bios_data_linux_test("Google  "));
+  GPR_ASSERT(check_bios_data_linux_test("   Google"));
+  GPR_ASSERT(check_bios_data_linux_test("  Google Compute Engine  "));
+  GPR_ASSERT(check_bios_data_linux_test("Google Compute Engine  "));
+  GPR_ASSERT(check_bios_data_linux_test("  Google Compute Engine"));
+  /* With leading and trailing \t and \n. */
+  GPR_ASSERT(check_bios_data_linux_test("\t\tGoogle Compute Engine\t"));
+  GPR_ASSERT(check_bios_data_linux_test("Google Compute Engine\n"));
+  GPR_ASSERT(check_bios_data_linux_test("\n\n\tGoogle Compute Engine \n\t\t"));
+}
+
+static void test_gcp_environment_check_failure() {
+  GPR_ASSERT(!check_bios_data_linux_test("non_existing-file"));
+  GPR_ASSERT(!check_bios_data_linux_test("Google-Chrome"));
+  GPR_ASSERT(!check_bios_data_linux_test("Amazon"));
+  GPR_ASSERT(!check_bios_data_linux_test("Google-Chrome\t\t"));
+  GPR_ASSERT(!check_bios_data_linux_test("Amazon"));
+}
+
+int main(int argc, char** argv) {
+  /* Tests. */
+  test_gcp_environment_check_success();
+  test_gcp_environment_check_failure();
+  return 0;
+}
+
+#else  // GPR_LINUX
+
+int main(int argc, char** argv) { return 0; }
+
+#endif  // GPR_LINUX
diff --git a/test/core/security/check_gcp_environment_windows_test.cc b/test/core/security/check_gcp_environment_windows_test.cc
new file mode 100644
index 0000000..46179b7
--- /dev/null
+++ b/test/core/security/check_gcp_environment_windows_test.cc
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#ifdef GPR_WINDOWS
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/gpr/tmpfile.h"
+
+static bool check_bios_data_windows_test(const char* data) {
+  /* Create a file with contents data. */
+  char* filename = nullptr;
+  FILE* fp = gpr_tmpfile("check_gcp_environment_test", &filename);
+  GPR_ASSERT(filename != nullptr);
+  GPR_ASSERT(fp != nullptr);
+  GPR_ASSERT(fwrite(data, 1, strlen(data), fp) == strlen(data));
+  fclose(fp);
+  bool result = grpc_core::internal::check_bios_data(
+      reinterpret_cast<const char*>(filename));
+  /* Cleanup. */
+  remove(filename);
+  gpr_free(filename);
+  return result;
+}
+
+static void test_gcp_environment_check_success() {
+  GPR_ASSERT(check_bios_data_windows_test("Google"));
+  GPR_ASSERT(check_bios_data_windows_test("Google\n"));
+  GPR_ASSERT(check_bios_data_windows_test("Google\r"));
+  GPR_ASSERT(check_bios_data_windows_test("Google\r\n"));
+  GPR_ASSERT(check_bios_data_windows_test("   Google   \r\n"));
+  GPR_ASSERT(check_bios_data_windows_test(" \t\t Google\r\n"));
+  GPR_ASSERT(check_bios_data_windows_test(" \t\t Google\t\t  \r\n"));
+}
+
+static void test_gcp_environment_check_failure() {
+  GPR_ASSERT(!check_bios_data_windows_test("\t\tAmazon\n"));
+  GPR_ASSERT(!check_bios_data_windows_test("  Amazon\r\n"));
+}
+
+int main(int argc, char** argv) {
+  /* Tests. */
+  test_gcp_environment_check_success();
+  test_gcp_environment_check_failure();
+  return 0;
+}
+#else  // GPR_WINDOWS
+
+int main(int argc, char** argv) { return 0; }
+
+#endif  // GPR_WINDOWS
diff --git a/test/core/security/grpc_alts_credentials_options_test.cc b/test/core/security/grpc_alts_credentials_options_test.cc
new file mode 100644
index 0000000..1217065
--- /dev/null
+++ b/test/core/security/grpc_alts_credentials_options_test.cc
@@ -0,0 +1,118 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+
+#define ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_1 "abc@google.com"
+#define ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_2 "def@google.com"
+
+const size_t kTargetServiceAccountNum = 2;
+
+static void test_add_target_service_account_failure() {
+  /* Initialization. */
+  grpc_alts_credentials_options* options =
+      grpc_alts_credentials_client_options_create();
+  auto client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(options);
+
+  /* Test. */
+  GPR_ASSERT(!grpc_alts_credentials_client_options_add_target_service_account(
+      client_options, nullptr));
+  GPR_ASSERT(!grpc_alts_credentials_client_options_add_target_service_account(
+      nullptr, ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_1));
+
+  /* Cleanup. */
+  grpc_alts_credentials_options_destroy(options);
+}
+
+static void test_copy_client_options_failure() {
+  /* Initialization. */
+  grpc_alts_credentials_options* options =
+      grpc_alts_credentials_client_options_create();
+
+  /* Test. */
+  GPR_ASSERT(grpc_alts_credentials_options_copy(nullptr) == nullptr);
+
+  /* Cleanup. */
+  grpc_alts_credentials_options_destroy(options);
+}
+
+static size_t get_target_service_account_num(
+    grpc_alts_credentials_client_options* options) {
+  size_t num = 0;
+  target_service_account* node = options->target_account_list_head;
+  while (node != nullptr) {
+    num++;
+    node = node->next;
+  }
+  return num;
+}
+
+static void test_client_options_api_success() {
+  /* Initialization. */
+  grpc_alts_credentials_options* options =
+      grpc_alts_credentials_client_options_create();
+  auto client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(options);
+
+  /* Set client options fields. */
+  grpc_alts_credentials_client_options_add_target_service_account(
+      client_options, ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_1);
+  grpc_alts_credentials_client_options_add_target_service_account(
+      client_options, ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_2);
+
+  /* Validate client option fields. */
+  GPR_ASSERT(get_target_service_account_num(client_options) ==
+             kTargetServiceAccountNum);
+  GPR_ASSERT(strcmp(client_options->target_account_list_head->data,
+                    ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_2) == 0);
+  GPR_ASSERT(strcmp(client_options->target_account_list_head->next->data,
+                    ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_1) == 0);
+
+  /* Perform a copy operation and validate its correctness. */
+  grpc_alts_credentials_options* new_options =
+      grpc_alts_credentials_options_copy(options);
+  auto new_client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(new_options);
+
+  GPR_ASSERT(get_target_service_account_num(new_client_options) ==
+             kTargetServiceAccountNum);
+  GPR_ASSERT(strcmp(new_client_options->target_account_list_head->data,
+                    ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_2) == 0);
+  GPR_ASSERT(strcmp(new_client_options->target_account_list_head->next->data,
+                    ALTS_CLIENT_OPTIONS_TEST_TARGET_SERVICE_ACCOUNT_1) == 0);
+
+  /* Cleanup.*/
+  grpc_alts_credentials_options_destroy(options);
+  grpc_alts_credentials_options_destroy(new_options);
+}
+
+int main(int argc, char** argv) {
+  /* Test. */
+  test_add_target_service_account_failure();
+  test_copy_client_options_failure();
+  test_client_options_api_success();
+  return 0;
+}
diff --git a/test/core/security/security_connector_test.cc b/test/core/security/security_connector_test.cc
index e4731fb..ed3849b 100644
--- a/test/core/security/security_connector_test.cc
+++ b/test/core/security/security_connector_test.cc
@@ -28,7 +28,7 @@
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/tmpfile.h"
 #include "src/core/lib/security/context/security_context.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/tsi/ssl_transport_security.h"
 #include "src/core/tsi/transport_security.h"
diff --git a/test/core/security/ssl_server_fuzzer.cc b/test/core/security/ssl_server_fuzzer.cc
index 6e30698..cb74e3b 100644
--- a/test/core/security/ssl_server_fuzzer.cc
+++ b/test/core/security/ssl_server_fuzzer.cc
@@ -22,7 +22,7 @@
 
 #include "src/core/lib/iomgr/load_file.h"
 #include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/memory_counters.h"
 #include "test/core/util/mock_endpoint.h"
diff --git a/test/core/slice/BUILD b/test/core/slice/BUILD
index ba2b553..9a1a506 100644
--- a/test/core/slice/BUILD
+++ b/test/core/slice/BUILD
@@ -23,8 +23,8 @@
 grpc_fuzzer(
     name = "percent_encode_fuzzer",
     srcs = ["percent_encode_fuzzer.cc"],
-    language = "C++",
     corpus = "percent_encode_corpus",
+    language = "C++",
     deps = [
         "//:gpr",
         "//:grpc",
@@ -35,8 +35,8 @@
 grpc_fuzzer(
     name = "percent_decode_fuzzer",
     srcs = ["percent_decode_fuzzer.cc"],
-    language = "C++",
     corpus = "percent_decode_corpus",
+    language = "C++",
     deps = [
         "//:gpr",
         "//:grpc",
@@ -59,8 +59,13 @@
 grpc_cc_test(
     name = "slice_test",
     srcs = ["slice_test.cc"],
-    deps = ["//:grpc", "//test/core/util:grpc_test_util", "//:gpr", "//test/core/util:gpr_test_util"],
     language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
 )
 
 grpc_cc_test(
@@ -78,15 +83,43 @@
 grpc_cc_test(
     name = "slice_buffer_test",
     srcs = ["slice_buffer_test.cc"],
-    deps = ["//:grpc", "//test/core/util:grpc_test_util", "//:gpr", "//test/core/util:gpr_test_util"],
     language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
 )
 
 grpc_cc_test(
     name = "slice_hash_table_test",
     srcs = ["slice_hash_table_test.cc"],
-    deps = ["//:grpc", "//test/core/util:grpc_test_util", "//:gpr", "//test/core/util:gpr_test_util"],
+    external_deps = [
+        "gtest",
+    ],
     language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "slice_weak_hash_table_test",
+    srcs = ["slice_weak_hash_table_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
 )
 
 grpc_cc_test(
diff --git a/test/core/slice/slice_hash_table_test.cc b/test/core/slice/slice_hash_table_test.cc
index 9fad9a6..279b543 100644
--- a/test/core/slice/slice_hash_table_test.cc
+++ b/test/core/slice/slice_hash_table_test.cc
@@ -20,6 +20,10 @@
 
 #include <string.h>
 
+#include <vector>
+
+#include <gtest/gtest.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
@@ -27,56 +31,55 @@
 #include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/test_config.h"
 
-typedef struct {
+namespace grpc_core {
+namespace {
+
+typedef SliceHashTable<UniquePtr<char>> TestHashTable;
+
+struct TestEntry {
   const char* key;
   const char* value;
-} test_entry;
+};
 
-static void populate_entries(const test_entry* input, size_t num_entries,
-                             grpc_slice_hash_table_entry* output) {
-  for (size_t i = 0; i < num_entries; ++i) {
-    output[i].key = grpc_slice_from_copied_string(input[i].key);
-    output[i].value = gpr_strdup(input[i].value);
-  }
-}
-
-static void check_values(const test_entry* input, size_t num_entries,
-                         grpc_slice_hash_table* table) {
-  for (size_t i = 0; i < num_entries; ++i) {
-    grpc_slice key = grpc_slice_from_static_string(input[i].key);
-    const char* actual =
-        static_cast<const char*>(grpc_slice_hash_table_get(table, key));
-    GPR_ASSERT(actual != nullptr);
-    GPR_ASSERT(strcmp(actual, input[i].value) == 0);
+void CheckValues(const std::vector<TestEntry>& input,
+                 const TestHashTable& table) {
+  for (const TestEntry& expected : input) {
+    grpc_slice key = grpc_slice_from_static_string(expected.key);
+    const UniquePtr<char>* actual = table.Get(key);
+    ASSERT_NE(actual, nullptr);
+    EXPECT_STREQ(expected.value, actual->get());
     grpc_slice_unref(key);
   }
 }
 
-static void check_non_existent_value(const char* key_string,
-                                     grpc_slice_hash_table* table) {
+void CheckNonExistentValue(const char* key_string, const TestHashTable& table) {
   grpc_slice key = grpc_slice_from_static_string(key_string);
-  GPR_ASSERT(grpc_slice_hash_table_get(table, key) == nullptr);
+  ASSERT_EQ(nullptr, table.Get(key));
   grpc_slice_unref(key);
 }
 
-static void destroy_string(void* value) { gpr_free(value); }
+void PopulateEntries(const std::vector<TestEntry>& input,
+                     TestHashTable::Entry* output) {
+  for (size_t i = 0; i < input.size(); ++i) {
+    output[i].key = grpc_slice_from_copied_string(input[i].key);
+    output[i].value = UniquePtr<char>(gpr_strdup(input[i].value));
+  }
+}
 
-static grpc_slice_hash_table* create_table_from_entries(
-    const test_entry* test_entries, size_t num_test_entries,
-    int (*value_cmp_fn)(void*, void*)) {
-  // Construct table.
-  grpc_slice_hash_table_entry* entries =
-      static_cast<grpc_slice_hash_table_entry*>(
-          gpr_zalloc(sizeof(*entries) * num_test_entries));
-  populate_entries(test_entries, num_test_entries, entries);
-  grpc_slice_hash_table* table = grpc_slice_hash_table_create(
-      num_test_entries, entries, destroy_string, value_cmp_fn);
+RefCountedPtr<TestHashTable> CreateTableFromEntries(
+    const std::vector<TestEntry>& test_entries,
+    TestHashTable::ValueCmp value_cmp) {
+  TestHashTable::Entry* entries = static_cast<TestHashTable::Entry*>(
+      gpr_zalloc(sizeof(*entries) * test_entries.size()));
+  PopulateEntries(test_entries, entries);
+  RefCountedPtr<TestHashTable> table =
+      TestHashTable::Create(test_entries.size(), entries, value_cmp);
   gpr_free(entries);
   return table;
 }
 
-static void test_slice_hash_table() {
-  const test_entry test_entries[] = {
+TEST(SliceHashTable, Basic) {
+  const std::vector<TestEntry> test_entries = {
       {"key_0", "value_0"},   {"key_1", "value_1"},   {"key_2", "value_2"},
       {"key_3", "value_3"},   {"key_4", "value_4"},   {"key_5", "value_5"},
       {"key_6", "value_6"},   {"key_7", "value_7"},   {"key_8", "value_8"},
@@ -112,129 +115,110 @@
       {"key_96", "value_96"}, {"key_97", "value_97"}, {"key_98", "value_98"},
       {"key_99", "value_99"},
   };
-  const size_t num_entries = GPR_ARRAY_SIZE(test_entries);
-  grpc_slice_hash_table* table =
-      create_table_from_entries(test_entries, num_entries, nullptr);
+  RefCountedPtr<TestHashTable> table =
+      CreateTableFromEntries(test_entries, nullptr);
   // Check contents of table.
-  check_values(test_entries, num_entries, table);
-  check_non_existent_value("XX", table);
-  // Clean up.
-  grpc_core::ExecCtx exec_ctx;
-  grpc_slice_hash_table_unref(table);
+  CheckValues(test_entries, *table);
+  CheckNonExistentValue("XX", *table);
 }
 
-static int value_cmp_fn(void* a, void* b) {
-  const char* a_str = static_cast<const char*>(a);
-  const char* b_str = static_cast<const char*>(b);
-  return strcmp(a_str, b_str);
+int StringCmp(const UniquePtr<char>& a, const UniquePtr<char>& b) {
+  return strcmp(a.get(), b.get());
 }
 
-static int pointer_cmp_fn(void* a, void* b) { return GPR_ICMP(a, b); }
-
-static void test_slice_hash_table_eq() {
-  const test_entry test_entries_a[] = {
-      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_a = GPR_ARRAY_SIZE(test_entries_a);
-  grpc_slice_hash_table* table_a =
-      create_table_from_entries(test_entries_a, num_entries_a, value_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_a, table_a) == 0);
-
-  const test_entry test_entries_b[] = {
-      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_b = GPR_ARRAY_SIZE(test_entries_b);
-  grpc_slice_hash_table* table_b =
-      create_table_from_entries(test_entries_b, num_entries_b, value_cmp_fn);
-
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_a, table_b) == 0);
-  grpc_core::ExecCtx exec_ctx;
-  grpc_slice_hash_table_unref(table_a);
-  grpc_slice_hash_table_unref(table_b);
+int PointerCmp(const UniquePtr<char>& a, const UniquePtr<char>& b) {
+  return GPR_ICMP(a.get(), b.get());
 }
 
-static void test_slice_hash_table_not_eq() {
-  const test_entry test_entries_a[] = {
+TEST(SliceHashTable, CmpEqual) {
+  const std::vector<TestEntry> test_entries_a = {
       {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_a = GPR_ARRAY_SIZE(test_entries_a);
-  grpc_slice_hash_table* table_a =
-      create_table_from_entries(test_entries_a, num_entries_a, value_cmp_fn);
+  RefCountedPtr<TestHashTable> table_a =
+      CreateTableFromEntries(test_entries_a, StringCmp);
+  const std::vector<TestEntry> test_entries_b = {
+      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_b =
+      CreateTableFromEntries(test_entries_b, StringCmp);
+  // table_a equals itself.
+  EXPECT_EQ(0, TestHashTable::Cmp(*table_a, *table_a));
+  // table_a equals table_b.
+  EXPECT_EQ(0, TestHashTable::Cmp(*table_a, *table_b));
+}
 
-  // Different sizes.
-  const test_entry test_entries_b_smaller[] = {{"key_0", "value_0"},
-                                               {"key_1", "value_1"}};
-  const size_t num_entries_b_smaller = GPR_ARRAY_SIZE(test_entries_b_smaller);
-  grpc_slice_hash_table* table_b_smaller = create_table_from_entries(
-      test_entries_b_smaller, num_entries_b_smaller, value_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_a, table_b_smaller) > 0);
+TEST(SliceHashTable, CmpDifferentSizes) {
+  // table_a has 3 entries, table_b has only 2.
+  const std::vector<TestEntry> test_entries_a = {
+      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_a =
+      CreateTableFromEntries(test_entries_a, StringCmp);
+  const std::vector<TestEntry> test_entries_b = {{"key_0", "value_0"},
+                                                 {"key_1", "value_1"}};
+  RefCountedPtr<TestHashTable> table_b =
+      CreateTableFromEntries(test_entries_b, StringCmp);
+  EXPECT_GT(TestHashTable::Cmp(*table_a, *table_b), 0);
+  EXPECT_LT(TestHashTable::Cmp(*table_b, *table_a), 0);
+}
 
-  const test_entry test_entries_b_larger[] = {{"key_0", "value_0"},
-                                              {"key_1", "value_1"},
-                                              {"key_2", "value_2"},
-                                              {"key_3", "value_3"}};
-  const size_t num_entries_b_larger = GPR_ARRAY_SIZE(test_entries_b_larger);
-  grpc_slice_hash_table* table_b_larger = create_table_from_entries(
-      test_entries_b_larger, num_entries_b_larger, value_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_a, table_b_larger) < 0);
-
+TEST(SliceHashTable, CmpDifferentKey) {
   // One key doesn't match and is lexicographically "smaller".
-  const test_entry test_entries_c[] = {
+  const std::vector<TestEntry> test_entries_a = {
+      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_a =
+      CreateTableFromEntries(test_entries_a, StringCmp);
+  const std::vector<TestEntry> test_entries_b = {
       {"key_zz", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_c = GPR_ARRAY_SIZE(test_entries_c);
-  grpc_slice_hash_table* table_c =
-      create_table_from_entries(test_entries_c, num_entries_c, value_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_a, table_c) > 0);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_c, table_a) < 0);
-
-  // One value doesn't match.
-  const test_entry test_entries_d[] = {
-      {"key_0", "value_z"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_d = GPR_ARRAY_SIZE(test_entries_d);
-  grpc_slice_hash_table* table_d =
-      create_table_from_entries(test_entries_d, num_entries_d, value_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_a, table_d) < 0);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_d, table_a) > 0);
-
-  // Same values but different "equals" functions.
-  const test_entry test_entries_e[] = {
-      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_e = GPR_ARRAY_SIZE(test_entries_e);
-  grpc_slice_hash_table* table_e =
-      create_table_from_entries(test_entries_e, num_entries_e, value_cmp_fn);
-  const test_entry test_entries_f[] = {
-      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
-  const size_t num_entries_f = GPR_ARRAY_SIZE(test_entries_f);
-  grpc_slice_hash_table* table_f =
-      create_table_from_entries(test_entries_f, num_entries_f, pointer_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_e, table_f) != 0);
-
-  // Same (empty) key, different values.
-  const test_entry test_entries_g[] = {{"", "value_0"}};
-  const size_t num_entries_g = GPR_ARRAY_SIZE(test_entries_g);
-  grpc_slice_hash_table* table_g =
-      create_table_from_entries(test_entries_g, num_entries_g, value_cmp_fn);
-  const test_entry test_entries_h[] = {{"", "value_1"}};
-  const size_t num_entries_h = GPR_ARRAY_SIZE(test_entries_h);
-  grpc_slice_hash_table* table_h =
-      create_table_from_entries(test_entries_h, num_entries_h, pointer_cmp_fn);
-  GPR_ASSERT(grpc_slice_hash_table_cmp(table_g, table_h) != 0);
-
-  grpc_core::ExecCtx exec_ctx;
-  grpc_slice_hash_table_unref(table_a);
-  grpc_slice_hash_table_unref(table_b_larger);
-  grpc_slice_hash_table_unref(table_b_smaller);
-  grpc_slice_hash_table_unref(table_c);
-  grpc_slice_hash_table_unref(table_d);
-  grpc_slice_hash_table_unref(table_e);
-  grpc_slice_hash_table_unref(table_f);
-  grpc_slice_hash_table_unref(table_g);
-  grpc_slice_hash_table_unref(table_h);
+  RefCountedPtr<TestHashTable> table_b =
+      CreateTableFromEntries(test_entries_b, StringCmp);
+  EXPECT_GT(TestHashTable::Cmp(*table_a, *table_b), 0);
+  EXPECT_LT(TestHashTable::Cmp(*table_b, *table_a), 0);
 }
 
+TEST(SliceHashTable, CmpDifferentValue) {
+  // One value doesn't match.
+  const std::vector<TestEntry> test_entries_a = {
+      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_a =
+      CreateTableFromEntries(test_entries_a, StringCmp);
+  const std::vector<TestEntry> test_entries_b = {
+      {"key_0", "value_z"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_b =
+      CreateTableFromEntries(test_entries_b, StringCmp);
+  EXPECT_LT(TestHashTable::Cmp(*table_a, *table_b), 0);
+  EXPECT_GT(TestHashTable::Cmp(*table_b, *table_a), 0);
+}
+
+TEST(SliceHashTable, CmpDifferentCmpFunctions) {
+  // Same values but different "equals" functions.
+  const std::vector<TestEntry> test_entries_a = {
+      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_a =
+      CreateTableFromEntries(test_entries_a, StringCmp);
+  const std::vector<TestEntry> test_entries_b = {
+      {"key_0", "value_0"}, {"key_1", "value_1"}, {"key_2", "value_2"}};
+  RefCountedPtr<TestHashTable> table_b =
+      CreateTableFromEntries(test_entries_b, PointerCmp);
+  EXPECT_NE(TestHashTable::Cmp(*table_a, *table_b), 0);
+}
+
+TEST(SliceHashTable, CmpEmptyKeysDifferentValue) {
+  // Same (empty) key, different values.
+  const std::vector<TestEntry> test_entries_a = {{"", "value_0"}};
+  RefCountedPtr<TestHashTable> table_a =
+      CreateTableFromEntries(test_entries_a, StringCmp);
+  const std::vector<TestEntry> test_entries_b = {{"", "value_1"}};
+  RefCountedPtr<TestHashTable> table_b =
+      CreateTableFromEntries(test_entries_b, PointerCmp);
+  EXPECT_NE(TestHashTable::Cmp(*table_a, *table_b), 0);
+}
+
+}  // namespace
+}  // namespace grpc_core
+
 int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
   grpc_test_init(argc, argv);
   grpc_core::ExecCtx::GlobalInit();
-  test_slice_hash_table();
-  test_slice_hash_table_eq();
-  test_slice_hash_table_not_eq();
+  int result = RUN_ALL_TESTS();
   grpc_core::ExecCtx::GlobalShutdown();
-  return 0;
+  return result;
 }
diff --git a/test/core/slice/slice_weak_hash_table_test.cc b/test/core/slice/slice_weak_hash_table_test.cc
new file mode 100644
index 0000000..4711d2f
--- /dev/null
+++ b/test/core/slice/slice_weak_hash_table_test.cc
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "src/core/lib/slice/slice_weak_hash_table.h"
+
+#include <cstring>
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "test/core/util/test_config.h"
+
+namespace grpc_core {
+namespace {
+
+grpc_slice BuildRefCountedKey(const char* key_str) {
+  const size_t key_length = strlen(key_str);
+  grpc_slice key = grpc_slice_malloc_large(key_length);
+  memcpy(GRPC_SLICE_START_PTR(key), key_str, key_length);
+  return key;
+}
+
+TEST(SliceWeakHashTable, Basic) {
+  auto table = SliceWeakHashTable<UniquePtr<char>, 10>::Create();
+  // Single key-value insertion.
+  grpc_slice key = BuildRefCountedKey("key");
+  grpc_slice_ref(key);  // Get doesn't own.
+  table->Add(key, UniquePtr<char>(gpr_strdup("value")));
+  ASSERT_NE(table->Get(key), nullptr);
+  ASSERT_STREQ(table->Get(key)->get(), "value");
+  grpc_slice_unref(key);
+  // Unknown key.
+  ASSERT_EQ(table->Get(grpc_slice_from_static_string("unknown_key")), nullptr);
+}
+
+TEST(SliceWeakHashTable, ValueTypeConstructor) {
+  struct Value {
+    Value() : a(123) {}
+    int a;
+  };
+  auto table = SliceWeakHashTable<Value, 1>::Create();
+  grpc_slice key = BuildRefCountedKey("key");
+  grpc_slice_ref(key);  // Get doesn't own.
+  table->Add(key, Value());
+  ASSERT_EQ(table->Get(key)->a, 123);
+  grpc_slice_unref(key);
+}
+
+TEST(SliceWeakHashTable, ForceOverload) {
+  constexpr int kTableSize = 10;
+  auto table = SliceWeakHashTable<UniquePtr<char>, kTableSize>::Create();
+  // Insert a multiple of the maximum size table.
+  for (int i = 0; i < kTableSize * 2; ++i) {
+    std::ostringstream oss;
+    oss << "key-" << i;
+    grpc_slice key = BuildRefCountedKey(oss.str().c_str());
+    oss.clear();
+    oss << "value-" << i;
+    table->Add(key, UniquePtr<char>(gpr_strdup(oss.str().c_str())));
+  }
+  // Verify that some will have been replaced.
+  int num_missing = 0;
+  for (int i = 0; i < kTableSize * 2; ++i) {
+    std::ostringstream oss;
+    oss << "key-" << i;
+    grpc_slice key = BuildRefCountedKey(oss.str().c_str());
+    if (table->Get(key) == nullptr) num_missing++;
+    grpc_slice_unref(key);
+  }
+  // At least kTableSize elements will be missing.
+  ASSERT_GE(num_missing, kTableSize);
+}
+
+}  // namespace
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  grpc_test_init(argc, argv);
+  grpc_core::ExecCtx::GlobalInit();
+  int result = RUN_ALL_TESTS();
+  grpc_core::ExecCtx::GlobalShutdown();
+  return result;
+}
diff --git a/test/core/statistics/rpc_stats_test.cc b/test/core/statistics/rpc_stats_test.cc
index ff48075..a2a648e 100644
--- a/test/core/statistics/rpc_stats_test.cc
+++ b/test/core/statistics/rpc_stats_test.cc
@@ -27,7 +27,6 @@
 #include "src/core/ext/census/census_interface.h"
 #include "src/core/ext/census/census_rpc_stats.h"
 #include "src/core/ext/census/census_tracing.h"
-#include "src/core/lib/gpr/thd.h"
 #include "test/core/util/test_config.h"
 
 /* Ensure all possible state transitions are called without causing problem */
diff --git a/test/core/surface/BUILD b/test/core/surface/BUILD
index d27123d..e848dde 100644
--- a/test/core/surface/BUILD
+++ b/test/core/surface/BUILD
@@ -55,6 +55,18 @@
 )
 
 grpc_cc_test(
+    name = "completion_queue_threading_test",
+    srcs = ["completion_queue_threading_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "concurrent_connectivity_test",
     srcs = ["concurrent_connectivity_test.cc"],
     language = "C++",
@@ -104,6 +116,19 @@
 )
 
 grpc_cc_test(
+    name = "num_external_connectivity_watchers_test",
+    srcs = ["num_external_connectivity_watchers_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//test/core/end2end:ssl_test_data",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "public_headers_must_be_c89",
     srcs = ["public_headers_must_be_c89.c"],
     language = "C",
diff --git a/test/core/surface/byte_buffer_reader_test.cc b/test/core/surface/byte_buffer_reader_test.cc
index 861ed5d..cff05ca 100644
--- a/test/core/surface/byte_buffer_reader_test.cc
+++ b/test/core/surface/byte_buffer_reader_test.cc
@@ -26,7 +26,7 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/compression/message_compress.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/core/surface/completion_queue_threading_test.cc b/test/core/surface/completion_queue_threading_test.cc
index 81319f4..0b82803 100644
--- a/test/core/surface/completion_queue_threading_test.cc
+++ b/test/core/surface/completion_queue_threading_test.cc
@@ -22,8 +22,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "test/core/util/test_config.h"
 
@@ -78,16 +78,14 @@
   grpc_completion_queue* cc;
   void* tags[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS];
   grpc_cq_completion completions[GPR_ARRAY_SIZE(tags)];
-  gpr_thd_id thread_ids[GPR_ARRAY_SIZE(tags)];
+  grpc_core::Thread threads[GPR_ARRAY_SIZE(tags)];
   struct thread_state thread_states[GPR_ARRAY_SIZE(tags)];
-  gpr_thd_options thread_options = gpr_thd_options_default();
   grpc_core::ExecCtx exec_ctx;
   unsigned i, j;
 
   LOG_TEST("test_too_many_plucks");
 
   cc = grpc_completion_queue_create_for_pluck(nullptr);
-  gpr_thd_options_set_joinable(&thread_options);
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
     tags[i] = create_test_tag();
@@ -96,8 +94,9 @@
     }
     thread_states[i].cc = cc;
     thread_states[i].tag = tags[i];
-    gpr_thd_new(thread_ids + i, "grpc_pluck_test", pluck_one, thread_states + i,
-                &thread_options);
+    threads[i] =
+        grpc_core::Thread("grpc_pluck_test", pluck_one, thread_states + i);
+    threads[i].Start();
   }
 
   /* wait until all other threads are plucking */
@@ -113,8 +112,8 @@
                    nullptr, &completions[i]);
   }
 
-  for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
-    gpr_thd_join(thread_ids[i]);
+  for (auto& th : threads) {
+    th.Join();
   }
 
   shutdown_and_destroy(cc);
@@ -220,8 +219,9 @@
           "test_threading", producers, consumers);
 
   /* start all threads: they will wait for phase1 */
+  grpc_core::Thread* threads = static_cast<grpc_core::Thread*>(
+      gpr_malloc(sizeof(*threads) * (producers + consumers)));
   for (i = 0; i < producers + consumers; i++) {
-    gpr_thd_id id;
     gpr_event_init(&options[i].on_started);
     gpr_event_init(&options[i].on_phase1_done);
     gpr_event_init(&options[i].on_finished);
@@ -230,10 +230,13 @@
     options[i].events_triggered = 0;
     options[i].cc = cc;
     options[i].id = optid++;
-    GPR_ASSERT(gpr_thd_new(&id,
-                           i < producers ? "grpc_producer" : "grpc_consumer",
-                           i < producers ? producer_thread : consumer_thread,
-                           options + i, nullptr));
+
+    bool ok;
+    threads[i] = grpc_core::Thread(
+        i < producers ? "grpc_producer" : "grpc_consumer",
+        i < producers ? producer_thread : consumer_thread, options + i, &ok);
+    GPR_ASSERT(ok);
+    threads[i].Start();
     gpr_event_wait(&options[i].on_started, ten_seconds_time());
   }
 
@@ -266,6 +269,11 @@
   /* destroy the completion channel */
   grpc_completion_queue_destroy(cc);
 
+  for (i = 0; i < producers + consumers; i++) {
+    threads[i].Join();
+  }
+  gpr_free(threads);
+
   /* verify that everything was produced and consumed */
   for (i = 0; i < producers + consumers; i++) {
     if (i < producers) {
diff --git a/test/core/surface/concurrent_connectivity_test.cc b/test/core/surface/concurrent_connectivity_test.cc
index 95af4ab..c1298b6 100644
--- a/test/core/surface/concurrent_connectivity_test.cc
+++ b/test/core/surface/concurrent_connectivity_test.cc
@@ -30,7 +30,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/resolve_address.h"
@@ -172,73 +172,77 @@
 
   grpc_init();
 
-  gpr_thd_id threads[NUM_THREADS];
-  gpr_thd_id server;
-
-  char* localhost = gpr_strdup("localhost:54321");
-  gpr_thd_options options = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&options);
-
   /* First round, no server */
-  gpr_log(GPR_DEBUG, "Wave 1");
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_new(&threads[i], "grpc_wave_1", create_loop_destroy, localhost,
-                &options);
-  }
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_join(threads[i]);
-  }
-  gpr_free(localhost);
-
-  /* Second round, actual grpc server */
-  gpr_log(GPR_DEBUG, "Wave 2");
-  int port = grpc_pick_unused_port_or_die();
-  gpr_asprintf(&args.addr, "localhost:%d", port);
-  args.server = grpc_server_create(nullptr, nullptr);
-  grpc_server_add_insecure_http2_port(args.server, args.addr);
-  args.cq = grpc_completion_queue_create_for_next(nullptr);
-  grpc_server_register_completion_queue(args.server, args.cq, nullptr);
-  grpc_server_start(args.server);
-  gpr_thd_new(&server, "grpc_wave_2_server", server_thread, &args, &options);
-
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_new(&threads[i], "grpc_wave_2", create_loop_destroy, args.addr,
-                &options);
-  }
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_join(threads[i]);
-  }
-  grpc_server_shutdown_and_notify(args.server, args.cq, tag(0xd1e));
-
-  gpr_thd_join(server);
-  grpc_server_destroy(args.server);
-  grpc_completion_queue_destroy(args.cq);
-  gpr_free(args.addr);
-
-  /* Third round, bogus tcp server */
-  gpr_log(GPR_DEBUG, "Wave 3");
-  args.pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
-  grpc_pollset_init(args.pollset, &args.mu);
-  gpr_event_init(&args.ready);
-  gpr_thd_new(&server, "grpc_wave_3_server", bad_server_thread, &args,
-              &options);
-  gpr_event_wait(&args.ready, gpr_inf_future(GPR_CLOCK_MONOTONIC));
-
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_new(&threads[i], "grpc_wave_3", create_loop_destroy, args.addr,
-                &options);
-  }
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_join(threads[i]);
-  }
-
-  gpr_atm_rel_store(&args.stop, 1);
-  gpr_thd_join(server);
   {
-    grpc_core::ExecCtx exec_ctx;
-    grpc_pollset_shutdown(
-        args.pollset, GRPC_CLOSURE_CREATE(done_pollset_shutdown, args.pollset,
-                                          grpc_schedule_on_exec_ctx));
+    gpr_log(GPR_DEBUG, "Wave 1");
+    char* localhost = gpr_strdup("localhost:54321");
+    grpc_core::Thread threads[NUM_THREADS];
+    for (auto& th : threads) {
+      th = grpc_core::Thread("grpc_wave_1", create_loop_destroy, localhost);
+      th.Start();
+    }
+    for (auto& th : threads) {
+      th.Join();
+    }
+    gpr_free(localhost);
+  }
+
+  {
+    /* Second round, actual grpc server */
+    gpr_log(GPR_DEBUG, "Wave 2");
+    int port = grpc_pick_unused_port_or_die();
+    gpr_asprintf(&args.addr, "localhost:%d", port);
+    args.server = grpc_server_create(nullptr, nullptr);
+    grpc_server_add_insecure_http2_port(args.server, args.addr);
+    args.cq = grpc_completion_queue_create_for_next(nullptr);
+    grpc_server_register_completion_queue(args.server, args.cq, nullptr);
+    grpc_server_start(args.server);
+    grpc_core::Thread server2("grpc_wave_2_server", server_thread, &args);
+    server2.Start();
+
+    grpc_core::Thread threads[NUM_THREADS];
+    for (auto& th : threads) {
+      th = grpc_core::Thread("grpc_wave_2", create_loop_destroy, args.addr);
+      th.Start();
+    }
+    for (auto& th : threads) {
+      th.Join();
+    }
+    grpc_server_shutdown_and_notify(args.server, args.cq, tag(0xd1e));
+
+    server2.Join();
+    grpc_server_destroy(args.server);
+    grpc_completion_queue_destroy(args.cq);
+    gpr_free(args.addr);
+  }
+
+  {
+    /* Third round, bogus tcp server */
+    gpr_log(GPR_DEBUG, "Wave 3");
+    args.pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
+    grpc_pollset_init(args.pollset, &args.mu);
+    gpr_event_init(&args.ready);
+    grpc_core::Thread server3("grpc_wave_3_server", bad_server_thread, &args);
+    server3.Start();
+    gpr_event_wait(&args.ready, gpr_inf_future(GPR_CLOCK_MONOTONIC));
+
+    grpc_core::Thread threads[NUM_THREADS];
+    for (auto& th : threads) {
+      th = grpc_core::Thread("grpc_wave_3", create_loop_destroy, args.addr);
+      th.Start();
+    }
+    for (auto& th : threads) {
+      th.Join();
+    }
+
+    gpr_atm_rel_store(&args.stop, 1);
+    server3.Join();
+    {
+      grpc_core::ExecCtx exec_ctx;
+      grpc_pollset_shutdown(
+          args.pollset, GRPC_CLOSURE_CREATE(done_pollset_shutdown, args.pollset,
+                                            grpc_schedule_on_exec_ctx));
+    }
   }
 
   grpc_shutdown();
@@ -278,18 +282,17 @@
 int run_concurrent_watches_with_short_timeouts_test() {
   grpc_init();
 
-  gpr_thd_id threads[NUM_THREADS];
+  grpc_core::Thread threads[NUM_THREADS];
 
   char* localhost = gpr_strdup("localhost:54321");
-  gpr_thd_options options = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&options);
 
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_new(&threads[i], "grpc_short_watches", watches_with_short_timeouts,
-                localhost, &options);
+  for (auto& th : threads) {
+    th = grpc_core::Thread("grpc_short_watches", watches_with_short_timeouts,
+                           localhost);
+    th.Start();
   }
-  for (size_t i = 0; i < NUM_THREADS; ++i) {
-    gpr_thd_join(threads[i]);
+  for (auto& th : threads) {
+    th.Join();
   }
   gpr_free(localhost);
 
diff --git a/test/core/surface/num_external_connectivity_watchers_test.cc b/test/core/surface/num_external_connectivity_watchers_test.cc
index 49d28ad..467deee 100644
--- a/test/core/surface/num_external_connectivity_watchers_test.cc
+++ b/test/core/surface/num_external_connectivity_watchers_test.cc
@@ -23,7 +23,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
diff --git a/test/core/surface/secure_channel_create_test.cc b/test/core/surface/secure_channel_create_test.cc
index c10d679..0696217 100644
--- a/test/core/surface/secure_channel_create_test.cc
+++ b/test/core/surface/secure_channel_create_test.cc
@@ -23,7 +23,7 @@
 #include <grpc/support/log.h>
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/lib/surface/channel.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/core/surface/sequential_connectivity_test.cc b/test/core/surface/sequential_connectivity_test.cc
index 428d17f..9aba4c4 100644
--- a/test/core/surface/sequential_connectivity_test.cc
+++ b/test/core/surface/sequential_connectivity_test.cc
@@ -23,7 +23,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
@@ -67,10 +67,8 @@
   grpc_server_start(server);
 
   server_thread_args sta = {server, server_cq};
-  gpr_thd_id server_thread;
-  gpr_thd_options thdopt = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&thdopt);
-  gpr_thd_new(&server_thread, "grpc_server", server_thread_func, &sta, &thdopt);
+  grpc_core::Thread server_thread("grpc_server", server_thread_func, &sta);
+  server_thread.Start();
 
   grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
   grpc_channel* channels[NUM_CONNECTIONS];
@@ -95,7 +93,7 @@
   }
 
   grpc_server_shutdown_and_notify(server, server_cq, nullptr);
-  gpr_thd_join(server_thread);
+  server_thread.Join();
 
   grpc_completion_queue_shutdown(server_cq);
   grpc_completion_queue_shutdown(cq);
diff --git a/test/core/transport/BUILD b/test/core/transport/BUILD
index b31d4ff..2c2d05b 100644
--- a/test/core/transport/BUILD
+++ b/test/core/transport/BUILD
@@ -119,3 +119,15 @@
         "//test/core/util:grpc_test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "status_metadata_test",
+    srcs = ["status_metadata_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+    external_deps = [
+        "gtest",
+    ],
+)
diff --git a/test/core/transport/chttp2/bin_decoder_test.cc b/test/core/transport/chttp2/bin_decoder_test.cc
index 283eebb..751dd90 100644
--- a/test/core/transport/chttp2/bin_decoder_test.cc
+++ b/test/core/transport/chttp2/bin_decoder_test.cc
@@ -67,6 +67,16 @@
   return out;
 }
 
+static size_t base64_infer_length(const char* s) {
+  grpc_slice ss = grpc_slice_from_copied_string(s);
+  size_t out = grpc_chttp2_base64_infer_length_after_decode(ss);
+  grpc_slice_unref_internal(ss);
+  return out;
+}
+
+#define EXPECT_DECODED_LENGTH(s, expected) \
+  GPR_ASSERT((expected) == base64_infer_length((s)));
+
 #define EXPECT_SLICE_EQ(expected, slice)                                    \
   expect_slice_eq(                                                          \
       grpc_slice_from_copied_buffer(expected, sizeof(expected) - 1), slice, \
@@ -131,6 +141,26 @@
     // Test illegal charactors in grpc_chttp2_base64_decode_with_length
     EXPECT_SLICE_EQ("", base64_decode_with_length("Zm:v", 3));
     EXPECT_SLICE_EQ("", base64_decode_with_length("Zm=v", 3));
+
+    EXPECT_DECODED_LENGTH("", 0);
+    EXPECT_DECODED_LENGTH("ab", 1);
+    EXPECT_DECODED_LENGTH("abc", 2);
+    EXPECT_DECODED_LENGTH("abcd", 3);
+    EXPECT_DECODED_LENGTH("abcdef", 4);
+    EXPECT_DECODED_LENGTH("abcdefg", 5);
+    EXPECT_DECODED_LENGTH("abcdefgh", 6);
+
+    EXPECT_DECODED_LENGTH("ab==", 1);
+    EXPECT_DECODED_LENGTH("abc=", 2);
+    EXPECT_DECODED_LENGTH("abcd", 3);
+    EXPECT_DECODED_LENGTH("abcdef==", 4);
+    EXPECT_DECODED_LENGTH("abcdefg=", 5);
+    EXPECT_DECODED_LENGTH("abcdefgh", 6);
+
+    EXPECT_DECODED_LENGTH("a", 0);
+    EXPECT_DECODED_LENGTH("a===", 0);
+    EXPECT_DECODED_LENGTH("abcde", 0);
+    EXPECT_DECODED_LENGTH("abcde===", 0);
   }
   grpc_shutdown();
   return all_ok ? 0 : 1;
diff --git a/test/core/transport/status_metadata_test.cc b/test/core/transport/status_metadata_test.cc
new file mode 100644
index 0000000..a96f11c
--- /dev/null
+++ b/test/core/transport/status_metadata_test.cc
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * 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 "src/core/lib/transport/status_metadata.h"
+
+#include <gtest/gtest.h>
+
+#include "src/core/lib/transport/static_metadata.h"
+
+namespace {
+
+TEST(GetStatusCodeFromMetadata, OK) {
+  EXPECT_EQ(GRPC_STATUS_OK,
+            grpc_get_status_code_from_metadata(GRPC_MDELEM_GRPC_STATUS_0));
+}
+
+TEST(GetStatusCodeFromMetadata, CANCELLED) {
+  EXPECT_EQ(GRPC_STATUS_CANCELLED,
+            grpc_get_status_code_from_metadata(GRPC_MDELEM_GRPC_STATUS_1));
+}
+
+TEST(GetStatusCodeFromMetadata, UNKNOWN) {
+  EXPECT_EQ(GRPC_STATUS_UNKNOWN,
+            grpc_get_status_code_from_metadata(GRPC_MDELEM_GRPC_STATUS_2));
+}
+
+TEST(GetStatusCodeFromMetadata, Other) {
+  grpc_mdelem status_md = grpc_mdelem_from_slices(
+      GRPC_MDSTR_GRPC_STATUS, grpc_slice_from_static_string("10"));
+  EXPECT_EQ(GRPC_STATUS_ABORTED, grpc_get_status_code_from_metadata(status_md));
+  GRPC_MDELEM_UNREF(status_md);
+}
+
+TEST(GetStatusCodeFromMetadata, Unparseable) {
+  grpc_mdelem status_md = grpc_mdelem_from_slices(
+      GRPC_MDSTR_GRPC_STATUS, grpc_slice_from_static_string("NaN"));
+  EXPECT_EQ(GRPC_STATUS_UNKNOWN, grpc_get_status_code_from_metadata(status_md));
+  GRPC_MDELEM_UNREF(status_md);
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/test/core/tsi/BUILD b/test/core/tsi/BUILD
index e28c0b5..8ac3e76 100644
--- a/test/core/tsi/BUILD
+++ b/test/core/tsi/BUILD
@@ -16,7 +16,7 @@
 
 licenses(["notice"])  # Apache v2
 
-grpc_package(name = "test/core/tsi")
+grpc_package(name = "test/core/tsi", visibility = "public")
 
 grpc_cc_library(
     name = "transport_security_test_lib",
diff --git a/test/core/tsi/alts/crypt/BUILD b/test/core/tsi/alts/crypt/BUILD
new file mode 100644
index 0000000..0057d7f
--- /dev/null
+++ b/test/core/tsi/alts/crypt/BUILD
@@ -0,0 +1,38 @@
+# Copyright 2018 gRPC authors.
+# 
+# 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.
+#
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package")
+
+licenses(["notice"])  # Apache v2
+
+grpc_package(name = "crypt", visibility = "public")
+
+grpc_cc_library(
+    name = "alts_crypt_test_util",
+    srcs = ["gsec_test_util.cc"],
+    hdrs = ["gsec_test_util.h"],
+    deps = [
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_crypt_test",
+    srcs = ["aes_gcm_test.cc"],
+    language = "C++",
+    deps = [
+        ":alts_crypt_test_util",
+        "//:grpc",
+    ],
+)
diff --git a/test/core/tsi/alts/crypt/aes_gcm_test.cc b/test/core/tsi/alts/crypt/aes_gcm_test.cc
new file mode 100644
index 0000000..576dd8f
--- /dev/null
+++ b/test/core/tsi/alts/crypt/aes_gcm_test.cc
@@ -0,0 +1,2105 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 "src/core/tsi/alts/crypt/gsec.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+const size_t kTestMinTagLengthForCorruption = 8;
+const size_t kTestNumCrypters = 3;
+const size_t kTestMaxSlices = 5;
+const size_t kTestMaxLength = 1024;
+const size_t kTestNumEncryptions = 100;
+
+/* Struct for pre-generated test vector */
+typedef struct gsec_aead_test_vector {
+  uint8_t* nonce;
+  uint8_t* aad;
+  uint8_t* key;
+  uint8_t* plaintext;
+  uint8_t* ciphertext_and_tag;
+  size_t nonce_length;
+  size_t aad_length;
+  size_t key_length;
+  size_t plaintext_length;
+  size_t ciphertext_and_tag_length;
+} gsec_aead_test_vector;
+
+static void gsec_randomly_slice(uint8_t* input, size_t input_length,
+                                struct iovec** output, size_t* output_length) {
+  if (input_length == 0) {
+    *output = nullptr;
+    *output_length = 0;
+    return;
+  }
+  *output_length = gsec_test_bias_random_uint32(kTestMaxSlices) + 1;
+  *output =
+      static_cast<struct iovec*>(malloc(*output_length * sizeof(**output)));
+  size_t i;
+  for (i = 0; i < *output_length - 1; i++) {
+    size_t slice_length =
+        gsec_test_bias_random_uint32(static_cast<uint32_t>(input_length));
+    struct iovec slice = {input, slice_length};
+    (*output)[i] = slice;
+    input += slice_length;
+    input_length -= slice_length;
+  }
+  struct iovec slice = {input, input_length};
+  (*output)[*output_length - 1] = slice;
+}
+
+static void gsec_assert_ok(grpc_status_code status, const char* error_detail) {
+  char empty_string[] = "";
+  if (error_detail == nullptr) {
+    error_detail = empty_string;
+  }
+  if (status != GRPC_STATUS_OK) {
+    fprintf(stderr, "Status is not ok: %s\n", error_detail);
+  }
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+}
+
+static void gsec_test_random_encrypt_decrypt(gsec_aead_crypter* crypter,
+                                             size_t aad_length,
+                                             size_t message_length) {
+  GPR_ASSERT(crypter != nullptr);
+  size_t nonce_length, tag_length;
+  uint8_t *nonce, *aad, *message;
+  gsec_aead_crypter_nonce_length(crypter, &nonce_length, nullptr);
+  gsec_aead_crypter_tag_length(crypter, &tag_length, nullptr);
+
+  gsec_test_random_array(&nonce, nonce_length);
+  gsec_test_random_array(&aad, aad_length);
+  gsec_test_random_array(&message, message_length);
+
+  /* Test encryption  */
+  size_t ciphertext_and_tag_length, ciphertext_bytes_written = 0;
+  gsec_aead_crypter_max_ciphertext_and_tag_length(
+      crypter, message_length, &ciphertext_and_tag_length, nullptr);
+
+  uint8_t* ciphertext_and_tag =
+      static_cast<uint8_t*>(gpr_malloc(ciphertext_and_tag_length));
+
+  char* error_buffer = nullptr;
+  gsec_assert_ok(
+      gsec_aead_crypter_encrypt(crypter, nonce, nonce_length, aad, aad_length,
+                                message, message_length, ciphertext_and_tag,
+                                ciphertext_and_tag_length,
+                                &ciphertext_bytes_written, &error_buffer),
+      error_buffer);
+  GPR_ASSERT(message_length + tag_length == ciphertext_and_tag_length);
+  GPR_ASSERT(ciphertext_bytes_written == ciphertext_and_tag_length);
+
+  /* Test decryption */
+  size_t plaintext_length, plaintext_bytes_written = 0;
+  gsec_aead_crypter_max_plaintext_length(crypter, ciphertext_bytes_written,
+                                         &plaintext_length, nullptr);
+  uint8_t* plaintext = static_cast<uint8_t*>(gpr_malloc(plaintext_length));
+  grpc_status_code status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length, aad, aad_length, ciphertext_and_tag,
+      ciphertext_bytes_written, plaintext, plaintext_length,
+      &plaintext_bytes_written, nullptr);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(message_length == plaintext_bytes_written);
+  GPR_ASSERT(memcmp(message, plaintext, message_length) == 0);
+
+  /**
+   * The returned plaintext will be zeroed if there was an authentication error.
+   */
+  uint8_t* zero_message = static_cast<uint8_t*>(gpr_zalloc(plaintext_length));
+  if (tag_length >= kTestMinTagLengthForCorruption) {
+    char* error_message;
+    /* Corrupt nonce */
+    if (nonce_length > 0) {
+      plaintext_bytes_written = 0;
+      uint8_t* corrupt_nonce;
+      gsec_test_copy_and_alter_random_byte(nonce, &corrupt_nonce, nonce_length);
+      status = gsec_aead_crypter_decrypt(
+          crypter, corrupt_nonce, nonce_length, aad, aad_length,
+          ciphertext_and_tag, ciphertext_bytes_written, plaintext,
+          plaintext_length, &plaintext_bytes_written, &error_message);
+
+      GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+          status, GRPC_STATUS_FAILED_PRECONDITION, "Checking tag failed.",
+          error_message));
+      GPR_ASSERT(plaintext_bytes_written == 0);
+      GPR_ASSERT(memcmp(zero_message, plaintext, plaintext_length) == 0);
+      gpr_free(corrupt_nonce);
+      gpr_free(error_message);
+    }
+
+    /* Corrupt ciphertext_and_tag */
+    plaintext_bytes_written = 0;
+    uint8_t* corrupt_ciphertext_and_tag;
+    gsec_test_copy_and_alter_random_byte(ciphertext_and_tag,
+                                         &corrupt_ciphertext_and_tag,
+                                         ciphertext_and_tag_length);
+    status = gsec_aead_crypter_decrypt(
+        crypter, nonce, nonce_length, aad, aad_length,
+        corrupt_ciphertext_and_tag, ciphertext_bytes_written, plaintext,
+        plaintext_length, &plaintext_bytes_written, &error_message);
+
+    GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+        status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+        "Checking tag failed"));
+    GPR_ASSERT(plaintext_bytes_written == 0);
+    GPR_ASSERT(memcmp(zero_message, plaintext, plaintext_length) == 0);
+    gpr_free(error_message);
+    gpr_free(corrupt_ciphertext_and_tag);
+
+    /* Corrupt start of ciphertext_and_tag */
+    plaintext_bytes_written = 0;
+    gsec_test_copy(ciphertext_and_tag, &corrupt_ciphertext_and_tag,
+                   ciphertext_and_tag_length);
+    (*corrupt_ciphertext_and_tag)++;
+    status = gsec_aead_crypter_decrypt(
+        crypter, nonce, nonce_length, aad, aad_length,
+        corrupt_ciphertext_and_tag, ciphertext_bytes_written, plaintext,
+        plaintext_length, &plaintext_bytes_written, &error_message);
+    GPR_ASSERT(plaintext_bytes_written == 0);
+    GPR_ASSERT(memcmp(zero_message, plaintext, plaintext_length) == 0);
+    GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+        status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+        "Checking tag failed"));
+    gpr_free(error_message);
+    gpr_free(corrupt_ciphertext_and_tag);
+
+    /* Corrupt end of ciphertext_and_tag */
+    plaintext_bytes_written = 0;
+    gsec_test_copy(ciphertext_and_tag, &corrupt_ciphertext_and_tag,
+                   ciphertext_and_tag_length);
+    (*(corrupt_ciphertext_and_tag + ciphertext_and_tag_length - 1))++;
+
+    status = gsec_aead_crypter_decrypt(
+        crypter, nonce, nonce_length, aad, aad_length,
+        corrupt_ciphertext_and_tag, ciphertext_bytes_written, plaintext,
+        plaintext_length, &plaintext_bytes_written, &error_message);
+
+    GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+        status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+        "Checking tag failed"));
+    GPR_ASSERT(plaintext_bytes_written == 0);
+    GPR_ASSERT(memcmp(zero_message, plaintext, plaintext_length) == 0);
+    gpr_free(error_message);
+    gpr_free(corrupt_ciphertext_and_tag);
+  }
+
+  gpr_free(zero_message);
+  gpr_free(nonce);
+  gpr_free(aad);
+  gpr_free(message);
+  gpr_free(plaintext);
+  gpr_free(ciphertext_and_tag);
+}
+
+static void gsec_test_encrypt_decrypt(gsec_aead_crypter* crypter) {
+  GPR_ASSERT(crypter != nullptr);
+  size_t aad_length, message_length;
+  aad_length = gsec_test_bias_random_uint32(kTestMaxLength);
+  message_length = gsec_test_bias_random_uint32(kTestMaxLength);
+  gsec_test_random_encrypt_decrypt(crypter, aad_length, message_length);
+  gsec_test_random_encrypt_decrypt(crypter, 0, message_length);
+  gsec_test_random_encrypt_decrypt(crypter, aad_length, 0);
+}
+
+static void gsec_test_multiple_random_encrypt_decrypt(
+    gsec_aead_crypter* crypter, size_t* aad_lengths, size_t* message_lengths,
+    size_t count) {
+  GPR_ASSERT(crypter != nullptr);
+  size_t nonce_length, tag_length;
+  uint8_t **nonces, **aads, **messages;
+  nonces = static_cast<uint8_t**>(gpr_malloc(sizeof(uint8_t*) * count));
+  aads = static_cast<uint8_t**>(gpr_malloc(sizeof(uint8_t*) * count));
+  messages = static_cast<uint8_t**>(gpr_malloc(sizeof(uint8_t*) * count));
+
+  gsec_aead_crypter_nonce_length(crypter, &nonce_length, nullptr);
+  gsec_aead_crypter_tag_length(crypter, &tag_length, nullptr);
+
+  size_t ind;
+  for (ind = 0; ind < count; ind++) {
+    size_t aad_length = (aad_lengths == nullptr) ? 0 : aad_lengths[ind];
+    size_t message_length =
+        (message_lengths == nullptr) ? 0 : message_lengths[ind];
+    gsec_test_random_array(&(nonces[ind]), nonce_length);
+    gsec_test_random_array(&(aads[ind]), aad_length);
+    gsec_test_random_array(&(messages[ind]), message_length);
+  }
+
+  size_t* ciphertext_and_tag_lengths =
+      static_cast<size_t*>(gpr_malloc(sizeof(size_t) * count));
+  size_t* ciphertext_bytes_writtens =
+      static_cast<size_t*>(gpr_malloc(sizeof(size_t) * count));
+  size_t* plaintext_lengths =
+      static_cast<size_t*>(gpr_malloc(sizeof(size_t) * count));
+  size_t* plaintext_bytes_writtens =
+      static_cast<size_t*>(gpr_malloc(sizeof(size_t) * count));
+  uint8_t** ciphertext_and_tags =
+      static_cast<uint8_t**>(gpr_malloc(sizeof(uint8_t*) * count));
+  uint8_t** plaintexts =
+      static_cast<uint8_t**>(gpr_malloc(sizeof(uint8_t*) * count));
+
+  /* Do encryption */
+  for (ind = 0; ind < count; ind++) {
+    size_t aad_length = (aad_lengths == nullptr) ? 0 : aad_lengths[ind];
+    size_t message_length =
+        (message_lengths == nullptr) ? 0 : message_lengths[ind];
+    gsec_aead_crypter_max_ciphertext_and_tag_length(
+        crypter, message_length, &(ciphertext_and_tag_lengths[ind]), nullptr);
+    ciphertext_and_tags[ind] =
+        static_cast<uint8_t*>(gpr_malloc(ciphertext_and_tag_lengths[ind]));
+    grpc_status_code status = gsec_aead_crypter_encrypt(
+        crypter, nonces[ind], nonce_length, aads[ind], aad_length,
+        messages[ind], message_length, ciphertext_and_tags[ind],
+        ciphertext_and_tag_lengths[ind], &(ciphertext_bytes_writtens[ind]),
+        nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    GPR_ASSERT(message_length + tag_length == ciphertext_and_tag_lengths[ind]);
+    GPR_ASSERT(ciphertext_bytes_writtens[ind] ==
+               ciphertext_and_tag_lengths[ind]);
+  }
+  /* Do Decryption */
+  for (ind = 0; ind < count; ind++) {
+    size_t aad_length = (aad_lengths == nullptr) ? 0 : aad_lengths[ind];
+    size_t message_length =
+        (message_lengths == nullptr) ? 0 : message_lengths[ind];
+    gsec_aead_crypter_max_plaintext_length(crypter,
+                                           ciphertext_bytes_writtens[ind],
+                                           &(plaintext_lengths[ind]), nullptr);
+    plaintexts[ind] = static_cast<uint8_t*>(gpr_malloc(plaintext_lengths[ind]));
+    grpc_status_code status = gsec_aead_crypter_decrypt(
+        crypter, nonces[ind], nonce_length, aads[ind], aad_length,
+        ciphertext_and_tags[ind], ciphertext_bytes_writtens[ind],
+        plaintexts[ind], plaintext_lengths[ind],
+        &(plaintext_bytes_writtens[ind]), nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    GPR_ASSERT(message_length == plaintext_bytes_writtens[ind]);
+    GPR_ASSERT(memcmp(messages[ind], plaintexts[ind], message_length) == 0);
+  }
+
+  /* Slice the plaintext and encrypt with iovecs */
+  for (ind = 0; ind < count; ind++) {
+    size_t aad_length = (aad_lengths == nullptr) ? 0 : aad_lengths[ind];
+    struct iovec* aad_vecs = nullptr;
+    size_t aad_vecs_length = 0;
+    gsec_randomly_slice(aads[ind], aad_length, &aad_vecs, &aad_vecs_length);
+    size_t message_length =
+        (message_lengths == nullptr) ? 0 : message_lengths[ind];
+    struct iovec* message_vecs = nullptr;
+    size_t message_vecs_length = 0;
+    gsec_randomly_slice(messages[ind], message_length, &message_vecs,
+                        &message_vecs_length);
+
+    size_t ciphertext_length = ciphertext_and_tag_lengths[ind];
+    uint8_t* another_ciphertext =
+        static_cast<uint8_t*>(malloc(ciphertext_length));
+    struct iovec another_ciphertext_vec = {another_ciphertext,
+                                           ciphertext_length};
+
+    char* error_details = nullptr;
+    size_t ciphertext_bytes_written = 0;
+    gsec_assert_ok(
+        gsec_aead_crypter_encrypt_iovec(
+            crypter, nonces[ind], nonce_length, aad_vecs, aad_vecs_length,
+            message_vecs, message_vecs_length, another_ciphertext_vec,
+            &ciphertext_bytes_written, &error_details),
+        error_details);
+    GPR_ASSERT(memcmp(ciphertext_and_tags[ind], another_ciphertext_vec.iov_base,
+                      ciphertext_length) == 0);
+    free(another_ciphertext);
+    free(aad_vecs);
+    free(message_vecs);
+  }
+
+  /* Slice the ciphertext and decrypt with iovecs */
+  for (ind = 0; ind < count; ind++) {
+    size_t message_length =
+        (message_lengths == nullptr) ? 0 : message_lengths[ind];
+    message_length = message_length + 0;
+
+    size_t aad_length = (aad_lengths == nullptr) ? 0 : aad_lengths[ind];
+
+    struct iovec* aad_vecs = nullptr;
+    size_t aad_vecs_length = 0;
+    gsec_randomly_slice(aads[ind], aad_length, &aad_vecs, &aad_vecs_length);
+
+    struct iovec* ciphertext_vecs = nullptr;
+    size_t ciphertext_vecs_length = 0;
+    gsec_randomly_slice(ciphertext_and_tags[ind],
+                        ciphertext_bytes_writtens[ind], &ciphertext_vecs,
+                        &ciphertext_vecs_length);
+
+    size_t decrypted_length = plaintext_lengths[ind];
+    uint8_t* decrypted = static_cast<uint8_t*>(malloc(decrypted_length));
+    struct iovec decrypted_vec = {decrypted, decrypted_length};
+
+    char* error_details = nullptr;
+    gsec_assert_ok(gsec_aead_crypter_decrypt_iovec(
+                       crypter, nonces[ind], nonce_length, aad_vecs,
+                       aad_vecs_length, ciphertext_vecs, ciphertext_vecs_length,
+                       decrypted_vec, &decrypted_length, &error_details),
+                   error_details);
+    GPR_ASSERT(decrypted_vec.iov_len == message_length);
+    GPR_ASSERT(memcmp(decrypted_vec.iov_base, messages[ind], message_length) ==
+               0);
+    free(decrypted);
+    free(aad_vecs);
+    free(ciphertext_vecs);
+  }
+
+  for (ind = 0; ind < count; ind++) {
+    gpr_free(nonces[ind]);
+    gpr_free(aads[ind]);
+    gpr_free(messages[ind]);
+    gpr_free(ciphertext_and_tags[ind]);
+    gpr_free(plaintexts[ind]);
+  }
+  gpr_free(nonces);
+  gpr_free(aads);
+  gpr_free(messages);
+  gpr_free(ciphertext_and_tag_lengths);
+  gpr_free(ciphertext_bytes_writtens);
+  gpr_free(plaintext_lengths);
+  gpr_free(plaintext_bytes_writtens);
+  gpr_free(ciphertext_and_tags);
+  gpr_free(plaintexts);
+}
+
+static void gsec_test_multiple_encrypt_decrypt(gsec_aead_crypter* crypter) {
+  GPR_ASSERT(crypter != nullptr);
+  size_t count = kTestNumEncryptions;
+  size_t* aad_lengths =
+      static_cast<size_t*>(gpr_malloc(sizeof(size_t) * count));
+  size_t* message_lengths =
+      static_cast<size_t*>(gpr_malloc(sizeof(size_t) * count));
+  size_t ind;
+  for (ind = 0; ind < count; ind++) {
+    aad_lengths[ind] = gsec_test_bias_random_uint32(kTestMaxLength);
+    message_lengths[ind] = gsec_test_bias_random_uint32(kTestMaxLength);
+  }
+  gsec_test_multiple_random_encrypt_decrypt(crypter, aad_lengths,
+                                            message_lengths, count);
+  gsec_test_multiple_random_encrypt_decrypt(crypter, aad_lengths, nullptr,
+                                            count);
+  gsec_test_multiple_random_encrypt_decrypt(crypter, nullptr, message_lengths,
+                                            count);
+  gpr_free(aad_lengths);
+  gpr_free(message_lengths);
+}
+
+static void gsec_test_encryption_failure(gsec_aead_crypter* crypter) {
+  GPR_ASSERT(crypter != nullptr);
+  size_t aad_length = kTestMaxLength;
+  size_t message_length = kTestMaxLength;
+  size_t nonce_length;
+
+  char* error_message;
+  uint8_t *nonce, *aad, *message;
+
+  gsec_aead_crypter_nonce_length(crypter, &nonce_length, nullptr);
+  gsec_test_random_array(&nonce, nonce_length);
+  gsec_test_random_array(&aad, aad_length);
+  gsec_test_random_array(&message, message_length);
+
+  size_t ciphertext_and_tag_length, ciphertext_bytes_written = 0;
+  gsec_aead_crypter_max_ciphertext_and_tag_length(
+      crypter, message_length, &ciphertext_and_tag_length, nullptr);
+  uint8_t* ciphertext_and_tag =
+      static_cast<uint8_t*>(gpr_malloc(ciphertext_and_tag_length));
+
+  /* nullptr nonce */
+  grpc_status_code status = gsec_aead_crypter_encrypt(
+      crypter, nullptr, nonce_length, aad, aad_length, message, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length, &ciphertext_bytes_written,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Nonce buffer is nullptr."));
+  gpr_free(error_message);
+
+  /* Big nonce */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length + 1, aad, aad_length, message,
+      message_length, ciphertext_and_tag, ciphertext_and_tag_length,
+      &ciphertext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Nonce buffer has the wrong length."));
+  gpr_free(error_message);
+
+  /* Small nonce */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length - 1, aad, aad_length, message,
+      message_length, ciphertext_and_tag, ciphertext_and_tag_length,
+      &ciphertext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Nonce buffer has the wrong length."));
+  gpr_free(error_message);
+
+  /* nullptr aad */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, nullptr, aad_length, message,
+      message_length, ciphertext_and_tag, ciphertext_and_tag_length,
+      &ciphertext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message, "aad is nullptr."));
+  gpr_free(error_message);
+
+  /* nullptr aad with zero length */
+  gsec_assert_ok(
+      gsec_aead_crypter_encrypt(crypter, nonce, nonce_length, nullptr, 0,
+                                message, message_length, ciphertext_and_tag,
+                                ciphertext_and_tag_length,
+                                &ciphertext_bytes_written, &error_message),
+      error_message);
+
+  /* nullptr plaintext */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, aad, aad_length, nullptr, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length, &ciphertext_bytes_written,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "plaintext is nullptr."));
+  gpr_free(error_message);
+
+  /* nullptr ciphertext */
+  status = gsec_aead_crypter_encrypt(crypter, nonce, nonce_length, aad,
+                                     aad_length, message, message_length,
+                                     nullptr, ciphertext_and_tag_length,
+                                     &ciphertext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "ciphertext is nullptr."));
+  gpr_free(error_message);
+
+  /* Short ciphertext */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, aad, aad_length, message, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length - 1,
+      &ciphertext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "ciphertext is too small to hold a tag."));
+  gpr_free(error_message);
+
+  /* nullptr ciphertext_bytes_written */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, aad, aad_length, message, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length, nullptr, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "bytes_written is nullptr."));
+  gpr_free(error_message);
+
+  /* nullptr plaintext/ciphertext encrypt with zero length */
+  gsec_assert_ok(gsec_aead_crypter_encrypt(
+                     crypter, nonce, nonce_length, aad, aad_length, nullptr, 0,
+                     ciphertext_and_tag, ciphertext_and_tag_length,
+                     &ciphertext_bytes_written, &error_message),
+                 error_message);
+
+  /* Success */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, aad, aad_length, message, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length, &ciphertext_bytes_written,
+      &error_message);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+
+  gpr_free(message);
+  gpr_free(aad);
+  gpr_free(nonce);
+  gpr_free(ciphertext_and_tag);
+}
+
+static void gsec_test_decryption_failure(gsec_aead_crypter* crypter) {
+  GPR_ASSERT(crypter != nullptr);
+  size_t aad_length = kTestMaxLength;
+  size_t message_length = kTestMaxLength;
+  size_t nonce_length, tag_length;
+  uint8_t *nonce, *aad, *message;
+
+  gsec_aead_crypter_nonce_length(crypter, &nonce_length, nullptr);
+  gsec_aead_crypter_tag_length(crypter, &tag_length, nullptr);
+  gsec_test_random_array(&nonce, nonce_length);
+  gsec_test_random_array(&aad, aad_length);
+  gsec_test_random_array(&message, message_length);
+
+  /* Test encryption */
+  size_t ciphertext_and_tag_length, ciphertext_bytes_written = 0;
+  gsec_aead_crypter_max_ciphertext_and_tag_length(
+      crypter, message_length, &ciphertext_and_tag_length, nullptr);
+  uint8_t* ciphertext_and_tag =
+      static_cast<uint8_t*>(gpr_malloc(ciphertext_and_tag_length));
+
+  grpc_status_code status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, aad, aad_length, message, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length, &ciphertext_bytes_written,
+      nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(ciphertext_bytes_written == ciphertext_and_tag_length);
+
+  size_t plaintext_length, plaintext_bytes_written = 0;
+  gsec_aead_crypter_max_plaintext_length(crypter, ciphertext_bytes_written,
+                                         &plaintext_length, nullptr);
+  uint8_t* plaintext = static_cast<uint8_t*>(gpr_malloc(plaintext_length));
+
+  char* error_message;
+  /* nullptr nonce */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nullptr, nonce_length, aad, aad_length, ciphertext_and_tag,
+      ciphertext_and_tag_length, plaintext, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Nonce buffer is nullptr."));
+  gpr_free(error_message);
+
+  /* Big nonce */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length + 1, aad, aad_length, ciphertext_and_tag,
+      ciphertext_and_tag_length, plaintext, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Nonce buffer has the wrong length."));
+  gpr_free(error_message);
+
+  /* Small nonce */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length - 1, aad, aad_length, ciphertext_and_tag,
+      ciphertext_and_tag_length, plaintext, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Nonce buffer has the wrong length."));
+  gpr_free(error_message);
+
+  /* nullptr aad */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length, nullptr, aad_length, ciphertext_and_tag,
+      ciphertext_and_tag_length, plaintext, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message, "aad is nullptr."));
+  gpr_free(error_message);
+
+  /* nullptr aad with zero length */
+  status = gsec_aead_crypter_encrypt(
+      crypter, nonce, nonce_length, nullptr, 0, message, message_length,
+      ciphertext_and_tag, ciphertext_and_tag_length, &ciphertext_bytes_written,
+      &error_message);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length, nullptr, 0, ciphertext_and_tag,
+      ciphertext_and_tag_length, plaintext, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+
+  /* Small ciphertext */
+  if (tag_length > 0) {
+    status = gsec_aead_crypter_decrypt(
+        crypter, nonce, nonce_length, aad, aad_length, ciphertext_and_tag,
+        tag_length - 1, plaintext, plaintext_length, &plaintext_bytes_written,
+        &error_message);
+
+    GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+        status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+        "ciphertext is too small to hold a tag."));
+    gpr_free(error_message);
+  }
+
+  /* nullptr ciphertext */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length, aad, aad_length, nullptr,
+      ciphertext_and_tag_length, plaintext, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "ciphertext is nullptr."));
+  gpr_free(error_message);
+
+  /* nullptr plaintext */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length, aad, aad_length, ciphertext_and_tag,
+      ciphertext_and_tag_length, nullptr, plaintext_length,
+      &plaintext_bytes_written, &error_message);
+
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "plaintext is nullptr, but plaintext_length is positive."));
+  gpr_free(error_message);
+
+  /* Short plaintext */
+  status = gsec_aead_crypter_decrypt(
+      crypter, nonce, nonce_length, aad, aad_length, ciphertext_and_tag,
+      ciphertext_and_tag_length, plaintext, plaintext_length - 1,
+      &plaintext_bytes_written, &error_message);
+
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Not enough plaintext buffer to hold encrypted ciphertext."));
+  gpr_free(error_message);
+
+  /* nullptr plaintext_bytes_written */
+  status = gsec_aead_crypter_decrypt(crypter, nonce, nonce_length, aad,
+                                     aad_length, ciphertext_and_tag,
+                                     ciphertext_and_tag_length, plaintext,
+                                     plaintext_length, nullptr, &error_message);
+
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "bytes_written is nullptr."));
+  gpr_free(error_message);
+
+  gpr_free(message);
+  gpr_free(plaintext);
+  gpr_free(ciphertext_and_tag);
+  gpr_free(aad);
+  gpr_free(nonce);
+}
+
+static void gsec_test_encrypt_decrypt_test_vector(
+    gsec_aead_crypter* crypter, gsec_aead_test_vector* test_vector) {
+  GPR_ASSERT(crypter != nullptr);
+  /* Test byte-based encryption interface. */
+  size_t ciphertext_and_tag_length, ciphertext_bytes_written = 0;
+  gsec_aead_crypter_max_ciphertext_and_tag_length(
+      crypter, test_vector->plaintext_length, &ciphertext_and_tag_length,
+      nullptr);
+  uint8_t* ciphertext_and_tag_bytes =
+      static_cast<uint8_t*>(gpr_malloc(ciphertext_and_tag_length));
+  grpc_status_code status = gsec_aead_crypter_encrypt(
+      crypter, test_vector->nonce, test_vector->nonce_length, test_vector->aad,
+      test_vector->aad_length, test_vector->plaintext,
+      test_vector->plaintext_length, ciphertext_and_tag_bytes,
+      ciphertext_and_tag_length, &ciphertext_bytes_written, nullptr);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(ciphertext_bytes_written == ciphertext_and_tag_length);
+  GPR_ASSERT(memcmp(test_vector->ciphertext_and_tag, ciphertext_and_tag_bytes,
+                    ciphertext_and_tag_length) == 0);
+
+  /* Test byte-based decryption interface */
+  size_t plaintext_length, plaintext_bytes_written = 0;
+  gsec_aead_crypter_max_plaintext_length(crypter, ciphertext_and_tag_length,
+                                         &plaintext_length, nullptr);
+  uint8_t* plaintext_bytes =
+      static_cast<uint8_t*>(gpr_malloc(plaintext_length));
+  status = gsec_aead_crypter_decrypt(
+      crypter, test_vector->nonce, test_vector->nonce_length, test_vector->aad,
+      test_vector->aad_length, test_vector->ciphertext_and_tag,
+      test_vector->ciphertext_and_tag_length, plaintext_bytes, plaintext_length,
+      &plaintext_bytes_written, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(test_vector->plaintext, plaintext_bytes,
+                    plaintext_bytes_written) == 0);
+
+  gpr_free(ciphertext_and_tag_bytes);
+  gpr_free(plaintext_bytes);
+}
+
+static void gsec_test_get_crypter_from_test_vector(
+    gsec_aead_crypter** crypter, gsec_aead_test_vector* test_vector,
+    bool rekey = false) {
+  size_t key_length = test_vector->key_length;
+  GPR_ASSERT(key_length == kAes128GcmKeyLength ||
+             key_length == kAes256GcmKeyLength ||
+             key_length == kAes128GcmRekeyKeyLength);
+  size_t nonce_length = test_vector->nonce_length;
+  GPR_ASSERT(nonce_length == kAesGcmNonceLength);
+  size_t plaintext_length = test_vector->plaintext_length;
+  size_t ciphertext_and_tag_length = test_vector->ciphertext_and_tag_length;
+  GPR_ASSERT(ciphertext_and_tag_length == plaintext_length + kAesGcmTagLength);
+  size_t tag_length = ciphertext_and_tag_length - plaintext_length;
+  gsec_aes_gcm_aead_crypter_create(test_vector->key, key_length, nonce_length,
+                                   tag_length, rekey, crypter, nullptr);
+}
+
+static void gsec_test_verify_crypter_on_test_vector(
+    gsec_aead_test_vector* test_vector, bool rekey = false) {
+  gsec_aead_crypter* crypter;
+  gsec_test_get_crypter_from_test_vector(&crypter, test_vector, rekey);
+  gsec_test_encrypt_decrypt_test_vector(crypter, test_vector);
+  gsec_aead_crypter_destroy(crypter);
+}
+
+static void gsec_aead_malloc_test_vector(
+    gsec_aead_test_vector** test_vector, const uint8_t* key, size_t key_length,
+    const uint8_t* nonce, size_t nonce_length, const uint8_t* aad,
+    size_t aad_length, const uint8_t* plaintext, size_t plaintext_length,
+    const uint8_t* ciphertext_and_tag, size_t ciphertext_and_tag_length) {
+  *test_vector = static_cast<gsec_aead_test_vector*>(
+      gpr_malloc(sizeof(gsec_aead_test_vector)));
+  (*test_vector)->key_length = key_length;
+  (*test_vector)->nonce_length = nonce_length;
+  (*test_vector)->aad_length = aad_length;
+  (*test_vector)->plaintext_length = plaintext_length;
+  (*test_vector)->ciphertext_and_tag_length = ciphertext_and_tag_length;
+  gsec_test_copy(key, &((*test_vector)->key), key_length);
+  gsec_test_copy(nonce, &((*test_vector)->nonce), nonce_length);
+  gsec_test_copy(aad, &((*test_vector)->aad), aad_length);
+  gsec_test_copy(plaintext, &((*test_vector)->plaintext), plaintext_length);
+  gsec_test_copy(ciphertext_and_tag, &((*test_vector)->ciphertext_and_tag),
+                 ciphertext_and_tag_length);
+}
+
+static void gsec_aead_free_test_vector(gsec_aead_test_vector* test_vector) {
+  gpr_free(test_vector->key);
+  gpr_free(test_vector->nonce);
+  gpr_free(test_vector->aad);
+  gpr_free(test_vector->plaintext);
+  gpr_free(test_vector->ciphertext_and_tag);
+  gpr_free(test_vector);
+}
+
+static void gsec_test_create_random_aes_gcm_crypter(gsec_aead_crypter** crypter,
+                                                    size_t key_length,
+                                                    size_t nonce_length,
+                                                    size_t tag_length,
+                                                    bool rekey) {
+  uint8_t* key;
+  gsec_test_random_array(&key, key_length);
+  gsec_aes_gcm_aead_crypter_create(key, key_length, nonce_length, tag_length,
+                                   rekey, crypter, nullptr);
+  gpr_free(key);
+}
+
+static void gsec_test_get_random_aes_gcm_crypters(
+    gsec_aead_crypter*** crypters) {
+  *crypters = static_cast<gsec_aead_crypter**>(
+      gpr_malloc(sizeof(gsec_aead_crypter*) * kTestNumCrypters));
+  gsec_test_create_random_aes_gcm_crypter(
+      &((*crypters)[0]), kAes128GcmKeyLength, kAesGcmNonceLength,
+      kAesGcmTagLength, /*rekey=*/false);
+  gsec_test_create_random_aes_gcm_crypter(
+      &((*crypters)[1]), kAes256GcmKeyLength, kAesGcmNonceLength,
+      kAesGcmTagLength, /*rekey=*/false);
+  gsec_test_create_random_aes_gcm_crypter(
+      &((*crypters)[2]), kAes128GcmRekeyKeyLength, kAesGcmNonceLength,
+      kAesGcmTagLength, /*rekey=*/true);
+}
+
+static void gsec_test_do_generic_crypter_tests() {
+  gsec_aead_crypter** crypters;
+  gsec_test_get_random_aes_gcm_crypters(&crypters);
+  size_t ind;
+  for (ind = 0; ind < kTestNumCrypters; ind++) {
+    gsec_test_encrypt_decrypt(crypters[ind]);
+    gsec_test_multiple_encrypt_decrypt(crypters[ind]);
+    gsec_test_encryption_failure(crypters[ind]);
+    gsec_test_decryption_failure(crypters[ind]);
+  }
+  for (ind = 0; ind < kTestNumCrypters; ind++) {
+    gsec_aead_crypter_destroy(crypters[ind]);
+  }
+  gpr_free(crypters);
+}
+
+static void gsec_test_do_vector_tests_rekey_nist() {
+  // NIST vectors from:
+  // http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
+  //
+  // IEEE vectors from:
+  // http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
+  //
+  // Key expanded by setting expandedKey = (key||(key ^ {0x01, .., 0x01})||key ^
+  // {0x02,..,0x02}))[0:44].
+
+  gsec_aead_test_vector vec;
+
+  // Derived from NIST test vector 1
+  uint8_t nonce_0[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                       0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+  uint8_t aad_0[1] = {};
+  uint8_t key_0[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+                     0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2,
+                     0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2};
+  uint8_t plaintext_0[1] = {};
+  uint8_t ciphertext_0[] = {0x85, 0xE8, 0x73, 0xE0, 0x2,  0xF6, 0xEB, 0xDC,
+                            0x40, 0x60, 0x95, 0x4E, 0xB8, 0x67, 0x55, 0x8};
+  vec = {nonce_0, aad_0, key_0, plaintext_0, ciphertext_0, 12, 0, 44, 0, 16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from NIST test vector 2
+  uint8_t nonce_1[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                       0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+  uint8_t aad_1[1] = {};
+  uint8_t key_1[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+                     0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2,
+                     0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2};
+  uint8_t plaintext_1[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                           0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+  uint8_t ciphertext_1[] = {0x51, 0xE9, 0xA8, 0xCB, 0x23, 0xCA, 0x25, 0x12,
+                            0xC8, 0x25, 0x6A, 0xFF, 0xF8, 0xE7, 0x2D, 0x68,
+                            0x1A, 0xCA, 0x19, 0xA1, 0x14, 0x8A, 0xC1, 0x15,
+                            0xE8, 0x3D, 0xF4, 0x88, 0x8C, 0xC0, 0xD,  0x11};
+  vec = {nonce_1, aad_1, key_1, plaintext_1, ciphertext_1, 12, 0, 44, 16, 32};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from NIST test vector 3
+  uint8_t nonce_2[] = {0xCA, 0xFE, 0xBA, 0xBE, 0xFA, 0xCE,
+                       0xDB, 0xAD, 0xDE, 0xCA, 0xF8, 0x88};
+  uint8_t aad_2[1] = {};
+  uint8_t key_2[] = {0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D,
+                     0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x8,  0xFF, 0xFE,
+                     0xE8, 0x93, 0x87, 0x64, 0x72, 0x1D, 0x6C, 0x6B, 0x8E,
+                     0x95, 0x66, 0x31, 0x82, 0x9,  0xFC, 0xFD, 0xEB, 0x90,
+                     0x84, 0x67, 0x71, 0x1E, 0x6F, 0x68, 0x8D, 0x96};
+  uint8_t plaintext_2[] = {
+      0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x6,  0xE5, 0xA5, 0x59, 0x9,
+      0xC5, 0xAF, 0xF5, 0x26, 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34,
+      0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 0x1C,
+      0x3C, 0xC,  0x95, 0x95, 0x68, 0x9,  0x53, 0x2F, 0xCF, 0xE,  0x24,
+      0x49, 0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0xD,  0xE6,
+      0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55};
+  uint8_t ciphertext_2[] = {
+      0x10, 0x18, 0xED, 0x5A, 0x14, 0x2,  0xA8, 0x65, 0x16, 0xD6, 0x57, 0x6D,
+      0x70, 0xB2, 0xFF, 0xCC, 0xCA, 0x26, 0x1B, 0x94, 0xDF, 0x88, 0xB5, 0x8F,
+      0x53, 0xB6, 0x4D, 0xFB, 0xA4, 0x35, 0xD1, 0x8B, 0x2F, 0x6E, 0x3B, 0x78,
+      0x69, 0xF9, 0x35, 0x3D, 0x4A, 0xC8, 0xCF, 0x9,  0xAF, 0xB1, 0x66, 0x3D,
+      0xAA, 0x7B, 0x40, 0x17, 0xE6, 0xFC, 0x2C, 0x17, 0x7C, 0xC,  0x8,  0x7C,
+      0xD,  0xF1, 0x16, 0x21, 0x29, 0x95, 0x22, 0x13, 0xCE, 0xE1, 0xBC, 0x6E,
+      0x9C, 0x84, 0x95, 0xDD, 0x70, 0x5E, 0x1F, 0x3D};
+  vec = {nonce_2, aad_2, key_2, plaintext_2, ciphertext_2, 12, 0, 44, 64, 80};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from NIST test vector 4
+  uint8_t nonce_3[] = {0xCA, 0xFE, 0xBA, 0xBE, 0xFA, 0xCE,
+                       0xDB, 0xAD, 0xDE, 0xCA, 0xF8, 0x88};
+  uint8_t aad_3[] = {0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE,
+                     0xEF, 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD,
+                     0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2};
+  uint8_t key_3[] = {0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D,
+                     0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x8,  0xFF, 0xFE,
+                     0xE8, 0x93, 0x87, 0x64, 0x72, 0x1D, 0x6C, 0x6B, 0x8E,
+                     0x95, 0x66, 0x31, 0x82, 0x9,  0xFC, 0xFD, 0xEB, 0x90,
+                     0x84, 0x67, 0x71, 0x1E, 0x6F, 0x68, 0x8D, 0x96};
+  uint8_t plaintext_3[] = {
+      0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x6,  0xE5, 0xA5, 0x59, 0x9,  0xC5,
+      0xAF, 0xF5, 0x26, 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA,
+      0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 0x1C, 0x3C, 0xC,  0x95,
+      0x95, 0x68, 0x9,  0x53, 0x2F, 0xCF, 0xE,  0x24, 0x49, 0xA6, 0xB5, 0x25,
+      0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0xD,  0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39};
+  uint8_t ciphertext_3[] = {
+      0x10, 0x18, 0xED, 0x5A, 0x14, 0x2,  0xA8, 0x65, 0x16, 0xD6, 0x57,
+      0x6D, 0x70, 0xB2, 0xFF, 0xCC, 0xCA, 0x26, 0x1B, 0x94, 0xDF, 0x88,
+      0xB5, 0x8F, 0x53, 0xB6, 0x4D, 0xFB, 0xA4, 0x35, 0xD1, 0x8B, 0x2F,
+      0x6E, 0x3B, 0x78, 0x69, 0xF9, 0x35, 0x3D, 0x4A, 0xC8, 0xCF, 0x9,
+      0xAF, 0xB1, 0x66, 0x3D, 0xAA, 0x7B, 0x40, 0x17, 0xE6, 0xFC, 0x2C,
+      0x17, 0x7C, 0xC,  0x8,  0x7C, 0x47, 0x64, 0x56, 0x5D, 0x7,  0x7E,
+      0x91, 0x24, 0x0,  0x1D, 0xDB, 0x27, 0xFC, 0x8,  0x48, 0xC5};
+  vec = {nonce_3, aad_3, key_3, plaintext_3, ciphertext_3, 12, 20, 44, 60, 76};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from adapted NIST test vector 4 for KDF counter boundary (flip
+  // nonce bit 15)
+  uint8_t nonce_4[] = {0xCA, 0x7E, 0xBA, 0xBE, 0xFA, 0xCE,
+                       0xDB, 0xAD, 0xDE, 0xCA, 0xF8, 0x88};
+  uint8_t aad_4[] = {0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE,
+                     0xEF, 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD,
+                     0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2};
+  uint8_t key_4[] = {0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D,
+                     0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x8,  0xFF, 0xFE,
+                     0xE8, 0x93, 0x87, 0x64, 0x72, 0x1D, 0x6C, 0x6B, 0x8E,
+                     0x95, 0x66, 0x31, 0x82, 0x9,  0xFC, 0xFD, 0xEB, 0x90,
+                     0x84, 0x67, 0x71, 0x1E, 0x6F, 0x68, 0x8D, 0x96};
+  uint8_t plaintext_4[] = {
+      0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x6,  0xE5, 0xA5, 0x59, 0x9,  0xC5,
+      0xAF, 0xF5, 0x26, 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA,
+      0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 0x1C, 0x3C, 0xC,  0x95,
+      0x95, 0x68, 0x9,  0x53, 0x2F, 0xCF, 0xE,  0x24, 0x49, 0xA6, 0xB5, 0x25,
+      0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0xD,  0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39};
+  uint8_t ciphertext_4[] = {
+      0xE6, 0x50, 0xD3, 0xC0, 0xFB, 0x87, 0x93, 0x27, 0xF2, 0xD0, 0x32,
+      0x87, 0xFA, 0x93, 0xCD, 0x7,  0x34, 0x2B, 0x13, 0x62, 0x15, 0xAD,
+      0xBC, 0xA0, 0xC,  0x3B, 0xD5, 0x9,  0x9E, 0xC4, 0x18, 0x32, 0xB1,
+      0xD1, 0x8E, 0x4,  0x23, 0xED, 0x26, 0xBB, 0x12, 0xC6, 0xCD, 0x9,
+      0xDE, 0xBB, 0x29, 0x23, 0xA,  0x94, 0xC0, 0xCE, 0xE1, 0x59, 0x3,
+      0x65, 0x6F, 0x85, 0xED, 0xB6, 0xFC, 0x50, 0x9B, 0x1B, 0x28, 0x21,
+      0x63, 0x82, 0x17, 0x2E, 0xCB, 0xCC, 0x31, 0xE1, 0xE9, 0xB1};
+  vec = {nonce_4, aad_4, key_4, plaintext_4, ciphertext_4, 12, 20, 44, 60, 76};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from adapted NIST test vector 4 for KDF counter boundary (flip
+  // nonce bit 16)
+  uint8_t nonce_5[] = {0xCA, 0xFE, 0xBB, 0xBE, 0xFA, 0xCE,
+                       0xDB, 0xAD, 0xDE, 0xCA, 0xF8, 0x88};
+  uint8_t aad_5[] = {0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE,
+                     0xEF, 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD,
+                     0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2};
+  uint8_t key_5[] = {0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D,
+                     0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x8,  0xFF, 0xFE,
+                     0xE8, 0x93, 0x87, 0x64, 0x72, 0x1D, 0x6C, 0x6B, 0x8E,
+                     0x95, 0x66, 0x31, 0x82, 0x9,  0xFC, 0xFD, 0xEB, 0x90,
+                     0x84, 0x67, 0x71, 0x1E, 0x6F, 0x68, 0x8D, 0x96};
+  uint8_t plaintext_5[] = {
+      0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x6,  0xE5, 0xA5, 0x59, 0x9,  0xC5,
+      0xAF, 0xF5, 0x26, 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA,
+      0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 0x1C, 0x3C, 0xC,  0x95,
+      0x95, 0x68, 0x9,  0x53, 0x2F, 0xCF, 0xE,  0x24, 0x49, 0xA6, 0xB5, 0x25,
+      0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0xD,  0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39};
+  uint8_t ciphertext_5[] = {
+      0xC0, 0x12, 0x1E, 0x6C, 0x95, 0x4D, 0x7,  0x67, 0xF9, 0x66, 0x30,
+      0xC3, 0x34, 0x50, 0x99, 0x97, 0x91, 0xB2, 0xDA, 0x2A, 0xD0, 0x5C,
+      0x41, 0x90, 0x16, 0x9C, 0xCA, 0xD9, 0xAC, 0x86, 0xFF, 0x1C, 0x72,
+      0x1E, 0x3D, 0x82, 0xF2, 0xAD, 0x22, 0xAB, 0x46, 0x3B, 0xAB, 0x4A,
+      0x7,  0x54, 0xB7, 0xDD, 0x68, 0xCA, 0x4D, 0xE7, 0xEA, 0x25, 0x31,
+      0xB6, 0x25, 0xED, 0xA0, 0x1F, 0x89, 0x31, 0x2B, 0x2A, 0xB9, 0x57,
+      0xD5, 0xC7, 0xF8, 0x56, 0x8D, 0xD9, 0x5F, 0xCD, 0xCD, 0x1F};
+  vec = {nonce_5, aad_5, key_5, plaintext_5, ciphertext_5, 12, 20, 44, 60, 76};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from adapted NIST test vector 4 for KDF counter boundary (flip
+  // nonce bit 63)
+  uint8_t nonce_6[] = {0xCA, 0xFE, 0xBA, 0xBE, 0xFA, 0xCE,
+                       0xDB, 0x2D, 0xDE, 0xCA, 0xF8, 0x88};
+  uint8_t aad_6[] = {0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE,
+                     0xEF, 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD,
+                     0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2};
+  uint8_t key_6[] = {0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D,
+                     0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x8,  0xFF, 0xFE,
+                     0xE8, 0x93, 0x87, 0x64, 0x72, 0x1D, 0x6C, 0x6B, 0x8E,
+                     0x95, 0x66, 0x31, 0x82, 0x9,  0xFC, 0xFD, 0xEB, 0x90,
+                     0x84, 0x67, 0x71, 0x1E, 0x6F, 0x68, 0x8D, 0x96};
+  uint8_t plaintext_6[] = {
+      0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x6,  0xE5, 0xA5, 0x59, 0x9,  0xC5,
+      0xAF, 0xF5, 0x26, 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA,
+      0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 0x1C, 0x3C, 0xC,  0x95,
+      0x95, 0x68, 0x9,  0x53, 0x2F, 0xCF, 0xE,  0x24, 0x49, 0xA6, 0xB5, 0x25,
+      0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0xD,  0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39};
+  uint8_t ciphertext_6[] = {
+      0x8A, 0xF3, 0x7E, 0xA5, 0x68, 0x4A, 0x4D, 0x81, 0xD4, 0xFD, 0x81,
+      0x72, 0x61, 0xFD, 0x97, 0x43, 0x9,  0x9E, 0x7E, 0x6A, 0x2,  0x5E,
+      0xAA, 0xCF, 0x8E, 0x54, 0xB1, 0x24, 0xFB, 0x57, 0x43, 0x14, 0x9E,
+      0x5,  0xCB, 0x89, 0xF4, 0xA4, 0x94, 0x67, 0xFE, 0x2E, 0x5E, 0x59,
+      0x65, 0xF2, 0x9A, 0x19, 0xF9, 0x94, 0x16, 0xB0, 0x1,  0x6B, 0x54,
+      0x58, 0x5D, 0x12, 0x55, 0x37, 0x83, 0xBA, 0x59, 0xE9, 0xF7, 0x82,
+      0xE8, 0x2E, 0x9,  0x7C, 0x33, 0x6B, 0xF7, 0x98, 0x9F, 0x8};
+  vec = {nonce_6, aad_6, key_6, plaintext_6, ciphertext_6, 12, 20, 44, 60, 76};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from adapted NIST test vector 4 for KDF counter boundary (flip
+  // nonce bit 64)
+  uint8_t nonce_7[] = {0xCA, 0xFE, 0xBA, 0xBE, 0xFA, 0xCE,
+                       0xDB, 0xAD, 0xDF, 0xCA, 0xF8, 0x88};
+  uint8_t aad_7[] = {0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE,
+                     0xEF, 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD,
+                     0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2};
+  uint8_t key_7[] = {0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D,
+                     0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x8,  0xFF, 0xFE,
+                     0xE8, 0x93, 0x87, 0x64, 0x72, 0x1D, 0x6C, 0x6B, 0x8E,
+                     0x95, 0x66, 0x31, 0x82, 0x9,  0xFC, 0xFD, 0xEB, 0x90,
+                     0x84, 0x67, 0x71, 0x1E, 0x6F, 0x68, 0x8D, 0x96};
+  uint8_t plaintext_7[] = {
+      0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x6,  0xE5, 0xA5, 0x59, 0x9,  0xC5,
+      0xAF, 0xF5, 0x26, 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA,
+      0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 0x1C, 0x3C, 0xC,  0x95,
+      0x95, 0x68, 0x9,  0x53, 0x2F, 0xCF, 0xE,  0x24, 0x49, 0xA6, 0xB5, 0x25,
+      0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0xD,  0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39};
+  uint8_t ciphertext_7[] = {
+      0xFB, 0xD5, 0x28, 0x44, 0x8D, 0x3,  0x46, 0xBF, 0xA8, 0x78, 0x63,
+      0x48, 0x64, 0xD4, 0x7,  0xA3, 0x5A, 0x3,  0x9D, 0xE9, 0xDB, 0x2F,
+      0x1F, 0xEB, 0x8E, 0x96, 0x5B, 0x3A, 0xE9, 0x35, 0x6C, 0xE6, 0x28,
+      0x94, 0x41, 0xD7, 0x7F, 0x8F, 0xD,  0xF2, 0x94, 0x89, 0x1F, 0x37,
+      0xEA, 0x43, 0x8B, 0x22, 0x3E, 0x3B, 0xF2, 0xBD, 0xC5, 0x3D, 0x4C,
+      0x5A, 0x74, 0xFB, 0x68, 0xB,  0xB3, 0x12, 0xA8, 0xDE, 0xC6, 0xF7,
+      0x25, 0x2C, 0xBC, 0xD7, 0xF5, 0x79, 0x97, 0x50, 0xAD, 0x78};
+  vec = {nonce_7, aad_7, key_7, plaintext_7, ciphertext_7, 12, 20, 44, 60, 76};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+}
+
+static void gsec_test_do_vector_tests_rekey_ieee() {
+  // IEEE vectors from:
+  // http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
+  //
+  // Key expanded by setting expandedKey = (key||(key ^ {0x01, .., 0x01})||key ^
+  // {0x02,..,0x02}))[0:44].
+
+  gsec_aead_test_vector vec;
+
+  // Derived from IEEE 2.1.1 54-byte auth
+  uint8_t nonce_8[] = {0x12, 0x15, 0x35, 0x24, 0xC0, 0x89,
+                       0x5E, 0x81, 0xB2, 0xC2, 0x84, 0x65};
+  uint8_t aad_8[] = {0xD6, 0x9,  0xB1, 0xF0, 0x56, 0x63, 0x7A, 0xD,  0x46, 0xDF,
+                     0x99, 0x8D, 0x88, 0xE5, 0x22, 0x2A, 0xB2, 0xC2, 0x84, 0x65,
+                     0x12, 0x15, 0x35, 0x24, 0xC0, 0x89, 0x5E, 0x81, 0x8,  0x0,
+                     0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+                     0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+                     0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
+                     0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x0,  0x1};
+  uint8_t key_8[] = {0xAD, 0x7A, 0x2B, 0xD0, 0x3E, 0xAC, 0x83, 0x5A, 0x6F,
+                     0x62, 0xF,  0xDC, 0xB5, 0x6,  0xB3, 0x45, 0xAC, 0x7B,
+                     0x2A, 0xD1, 0x3F, 0xAD, 0x82, 0x5B, 0x6E, 0x63, 0xE,
+                     0xDD, 0xB4, 0x7,  0xB2, 0x44, 0xAF, 0x78, 0x29, 0xD2,
+                     0x3C, 0xAE, 0x81, 0x58, 0x6D, 0x60, 0xD,  0xDE};
+  uint8_t plaintext_8[1] = {};
+  uint8_t ciphertext_8[] = {0x3E, 0xA0, 0xB5, 0x84, 0xF3, 0xC8, 0x5E, 0x93,
+                            0xF9, 0x32, 0xE,  0xA5, 0x91, 0x69, 0x9E, 0xFB};
+  vec = {nonce_8, aad_8, key_8, plaintext_8, ciphertext_8, 12, 70, 44, 0, 16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.1.2 54-byte auth
+  uint8_t nonce_9[] = {0x12, 0x15, 0x35, 0x24, 0xC0, 0x89,
+                       0x5E, 0x81, 0xB2, 0xC2, 0x84, 0x65};
+  uint8_t aad_9[] = {0xD6, 0x9,  0xB1, 0xF0, 0x56, 0x63, 0x7A, 0xD,  0x46, 0xDF,
+                     0x99, 0x8D, 0x88, 0xE5, 0x22, 0x2A, 0xB2, 0xC2, 0x84, 0x65,
+                     0x12, 0x15, 0x35, 0x24, 0xC0, 0x89, 0x5E, 0x81, 0x8,  0x0,
+                     0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+                     0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+                     0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
+                     0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x0,  0x1};
+  uint8_t key_9[] = {0xE3, 0xC0, 0x8A, 0x8F, 0x6,  0xC6, 0xE3, 0xAD, 0x95,
+                     0xA7, 0x5,  0x57, 0xB2, 0x3F, 0x75, 0x48, 0x3C, 0xE3,
+                     0x30, 0x21, 0xA9, 0xC7, 0x2B, 0x70, 0x25, 0x66, 0x62,
+                     0x4,  0xC6, 0x9C, 0xB,  0x72, 0xE1, 0xC2, 0x88, 0x8D,
+                     0x4,  0xC4, 0xE1, 0xAF, 0x97, 0xA5, 0x7,  0x55};
+  uint8_t plaintext_9[1] = {};
+  uint8_t ciphertext_9[] = {0x29, 0x4E, 0x2,  0x8B, 0xF1, 0xFE, 0x6F, 0x14,
+                            0xC4, 0xE8, 0xF7, 0x30, 0x5C, 0x93, 0x3E, 0xB5};
+  vec = {nonce_9, aad_9, key_9, plaintext_9, ciphertext_9, 12, 70, 44, 0, 16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.2.1 60-byte crypt
+  uint8_t nonce_10[] = {0x12, 0x15, 0x35, 0x24, 0xC0, 0x89,
+                        0x5E, 0x81, 0xB2, 0xC2, 0x84, 0x65};
+  uint8_t aad_10[] = {0xD6, 0x9,  0xB1, 0xF0, 0x56, 0x63, 0x7A,
+                      0xD,  0x46, 0xDF, 0x99, 0x8D, 0x88, 0xE5,
+                      0x2E, 0x0,  0xB2, 0xC2, 0x84, 0x65, 0x12,
+                      0x15, 0x35, 0x24, 0xC0, 0x89, 0x5E, 0x81};
+  uint8_t key_10[] = {0xAD, 0x7A, 0x2B, 0xD0, 0x3E, 0xAC, 0x83, 0x5A, 0x6F,
+                      0x62, 0xF,  0xDC, 0xB5, 0x6,  0xB3, 0x45, 0xAC, 0x7B,
+                      0x2A, 0xD1, 0x3F, 0xAD, 0x82, 0x5B, 0x6E, 0x63, 0xE,
+                      0xDD, 0xB4, 0x7,  0xB2, 0x44, 0xAF, 0x78, 0x29, 0xD2,
+                      0x3C, 0xAE, 0x81, 0x58, 0x6D, 0x60, 0xD,  0xDE};
+  uint8_t plaintext_10[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+      0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+      0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x0,  0x2};
+  uint8_t ciphertext_10[] = {
+      0xDB, 0x3D, 0x25, 0x71, 0x9C, 0x6B, 0xA,  0x3C, 0xA6, 0x14, 0x5C,
+      0x15, 0x9D, 0x5C, 0x6E, 0xD9, 0xAF, 0xF9, 0xC6, 0xE0, 0xB7, 0x9F,
+      0x17, 0x1,  0x9E, 0xA9, 0x23, 0xB8, 0x66, 0x5D, 0xDF, 0x52, 0x13,
+      0x7A, 0xD6, 0x11, 0xF0, 0xD1, 0xBF, 0x41, 0x7A, 0x7C, 0xA8, 0x5E,
+      0x45, 0xAF, 0xE1, 0x6,  0xFF, 0x9C, 0x75, 0x69, 0xD3, 0x35, 0xD0,
+      0x86, 0xAE, 0x6C, 0x3,  0xF0, 0x9,  0x87, 0xCC, 0xD6};
+  vec = {nonce_10, aad_10, key_10, plaintext_10, ciphertext_10,
+         12,       28,     44,     48,           64};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.2.2 60-byte crypt
+  uint8_t nonce_11[] = {0x12, 0x15, 0x35, 0x24, 0xC0, 0x89,
+                        0x5E, 0x81, 0xB2, 0xC2, 0x84, 0x65};
+  uint8_t aad_11[] = {0xD6, 0x9,  0xB1, 0xF0, 0x56, 0x63, 0x7A,
+                      0xD,  0x46, 0xDF, 0x99, 0x8D, 0x88, 0xE5,
+                      0x2E, 0x0,  0xB2, 0xC2, 0x84, 0x65, 0x12,
+                      0x15, 0x35, 0x24, 0xC0, 0x89, 0x5E, 0x81};
+  uint8_t key_11[] = {0xE3, 0xC0, 0x8A, 0x8F, 0x6,  0xC6, 0xE3, 0xAD, 0x95,
+                      0xA7, 0x5,  0x57, 0xB2, 0x3F, 0x75, 0x48, 0x3C, 0xE3,
+                      0x30, 0x21, 0xA9, 0xC7, 0x2B, 0x70, 0x25, 0x66, 0x62,
+                      0x4,  0xC6, 0x9C, 0xB,  0x72, 0xE1, 0xC2, 0x88, 0x8D,
+                      0x4,  0xC4, 0xE1, 0xAF, 0x97, 0xA5, 0x7,  0x55};
+  uint8_t plaintext_11[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+      0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+      0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x0,  0x2};
+  uint8_t ciphertext_11[] = {
+      0x16, 0x41, 0xF2, 0x8E, 0xC1, 0x3A, 0xFC, 0xC8, 0xF7, 0x90, 0x33,
+      0x89, 0x78, 0x72, 0x1,  0x5,  0x16, 0x44, 0x91, 0x49, 0x33, 0xE9,
+      0x20, 0x2B, 0xB9, 0xD0, 0x6A, 0xA0, 0x20, 0xC2, 0xA6, 0x7E, 0xF5,
+      0x1D, 0xFE, 0x7B, 0xC0, 0xA,  0x85, 0x6C, 0x55, 0xB8, 0xF8, 0x13,
+      0x3E, 0x77, 0xF6, 0x59, 0x13, 0x25, 0x2,  0xBA, 0xD6, 0x3F, 0x57,
+      0x13, 0xD5, 0x7D, 0xC,  0x11, 0xE0, 0xF8, 0x71, 0xED};
+  vec = {nonce_11, aad_11, key_11, plaintext_11, ciphertext_11,
+         12,       28,     44,     48,           64};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.3.1 60-byte auth
+  uint8_t nonce_12[] = {0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D,
+                        0x0,  0x1,  0x76, 0xD4, 0x57, 0xED};
+  uint8_t aad_12[] = {
+      0xE2, 0x1,  0x6,  0xD7, 0xCD, 0xD,  0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D,
+      0x88, 0xE5, 0x40, 0x0,  0x76, 0xD4, 0x57, 0xED, 0x8,  0x0,  0xF,  0x10,
+      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
+      0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+      0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x0,  0x3};
+  uint8_t key_12[] = {0x7,  0x1B, 0x11, 0x3B, 0xC,  0xA7, 0x43, 0xFE, 0xCC,
+                      0xCF, 0x3D, 0x5,  0x1F, 0x73, 0x73, 0x82, 0x6,  0x1A,
+                      0x10, 0x3A, 0xD,  0xA6, 0x42, 0xFF, 0xCD, 0xCE, 0x3C,
+                      0x4,  0x1E, 0x72, 0x72, 0x83, 0x5,  0x19, 0x13, 0x39,
+                      0xE,  0xA5, 0x41, 0xFC, 0xCE, 0xCD, 0x3F, 0x7};
+  uint8_t plaintext_12[1] = {};
+  uint8_t ciphertext_12[] = {0x58, 0x83, 0x7A, 0x10, 0x56, 0x2B, 0xF,  0x1F,
+                             0x8E, 0xDB, 0xE5, 0x8C, 0xA5, 0x58, 0x11, 0xD3};
+  vec = {nonce_12, aad_12, key_12, plaintext_12, ciphertext_12, 12, 68,
+         44,       0,      16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.3.2 60-byte auth
+  uint8_t nonce_13[] = {0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D,
+                        0x0,  0x1,  0x76, 0xD4, 0x57, 0xED};
+  uint8_t aad_13[] = {
+      0xE2, 0x1,  0x6,  0xD7, 0xCD, 0xD,  0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D,
+      0x88, 0xE5, 0x40, 0x0,  0x76, 0xD4, 0x57, 0xED, 0x8,  0x0,  0xF,  0x10,
+      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
+      0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+      0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x0,  0x3};
+  uint8_t key_13[] = {0x69, 0x1D, 0x3E, 0xE9, 0x9,  0xD7, 0xF5, 0x41, 0x67,
+                      0xFD, 0x1C, 0xA0, 0xB5, 0xD7, 0x69, 0x8,  0x1F, 0x2B,
+                      0xDE, 0x1A, 0xEE, 0x65, 0x5F, 0xDB, 0xAB, 0x80, 0xBD,
+                      0x52, 0x95, 0xAE, 0x6B, 0xE7, 0x6B, 0x1F, 0x3C, 0xEB,
+                      0xB,  0xD5, 0xF7, 0x43, 0x65, 0xFF, 0x1E, 0xA2};
+  uint8_t plaintext_13[1] = {};
+  uint8_t ciphertext_13[] = {0xC2, 0x72, 0x2F, 0xF6, 0xCA, 0x29, 0xA2, 0x57,
+                             0x71, 0x8A, 0x52, 0x9D, 0x1F, 0xC,  0x6A, 0x3B};
+  vec = {nonce_13, aad_13, key_13, plaintext_13, ciphertext_13, 12, 68,
+         44,       0,      16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.4.1 54-byte crypt
+  uint8_t nonce_14[] = {0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D,
+                        0x0,  0x1,  0x76, 0xD4, 0x57, 0xED};
+  uint8_t aad_14[] = {0xE2, 0x1,  0x6,  0xD7, 0xCD, 0xD,  0xF0,
+                      0x76, 0x1E, 0x8D, 0xCD, 0x3D, 0x88, 0xE5,
+                      0x4C, 0x2A, 0x76, 0xD4, 0x57, 0xED};
+  uint8_t key_14[] = {0x7,  0x1B, 0x11, 0x3B, 0xC,  0xA7, 0x43, 0xFE, 0xCC,
+                      0xCF, 0x3D, 0x5,  0x1F, 0x73, 0x73, 0x82, 0x6,  0x1A,
+                      0x10, 0x3A, 0xD,  0xA6, 0x42, 0xFF, 0xCD, 0xCE, 0x3C,
+                      0x4,  0x1E, 0x72, 0x72, 0x83, 0x5,  0x19, 0x13, 0x39,
+                      0xE,  0xA5, 0x41, 0xFC, 0xCE, 0xCD, 0x3F, 0x7};
+  uint8_t plaintext_14[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+      0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x0,  0x4};
+  uint8_t ciphertext_14[] = {
+      0xFD, 0x96, 0xB7, 0x15, 0xB9, 0x3A, 0x13, 0x34, 0x6A, 0xF5, 0x1E, 0x8A,
+      0xCD, 0xF7, 0x92, 0xCD, 0xC7, 0xB2, 0x68, 0x6F, 0x85, 0x74, 0xC7, 0xE,
+      0x6B, 0xC,  0xBF, 0x16, 0x29, 0x1D, 0xED, 0x42, 0x7A, 0xD7, 0x3F, 0xEC,
+      0x48, 0xCD, 0x29, 0x8E, 0x5,  0x28, 0xA1, 0xF4, 0xC6, 0x44, 0xA9, 0x49,
+      0xFC, 0x31, 0xDC, 0x92, 0x79, 0x70, 0x6D, 0xDB, 0xA3, 0x3F};
+  vec = {nonce_14, aad_14, key_14, plaintext_14, ciphertext_14,
+         12,       20,     44,     42,           58};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.4.2 54-byte crypt
+  uint8_t nonce_15[] = {0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D,
+                        0x0,  0x1,  0x76, 0xD4, 0x57, 0xED};
+  uint8_t aad_15[] = {0xE2, 0x1,  0x6,  0xD7, 0xCD, 0xD,  0xF0,
+                      0x76, 0x1E, 0x8D, 0xCD, 0x3D, 0x88, 0xE5,
+                      0x4C, 0x2A, 0x76, 0xD4, 0x57, 0xED};
+  uint8_t key_15[] = {0x69, 0x1D, 0x3E, 0xE9, 0x9,  0xD7, 0xF5, 0x41, 0x67,
+                      0xFD, 0x1C, 0xA0, 0xB5, 0xD7, 0x69, 0x8,  0x1F, 0x2B,
+                      0xDE, 0x1A, 0xEE, 0x65, 0x5F, 0xDB, 0xAB, 0x80, 0xBD,
+                      0x52, 0x95, 0xAE, 0x6B, 0xE7, 0x6B, 0x1F, 0x3C, 0xEB,
+                      0xB,  0xD5, 0xF7, 0x43, 0x65, 0xFF, 0x1E, 0xA2};
+  uint8_t plaintext_15[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+      0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x0,  0x4};
+  uint8_t ciphertext_15[] = {
+      0xB6, 0x8F, 0x63, 0x0,  0xC2, 0xE9, 0xAE, 0x83, 0x3B, 0xDC, 0x7,  0xE,
+      0x24, 0x2,  0x1A, 0x34, 0x77, 0x11, 0x8E, 0x78, 0xCC, 0xF8, 0x4E, 0x11,
+      0xA4, 0x85, 0xD8, 0x61, 0x47, 0x6C, 0x30, 0xF,  0x17, 0x53, 0x53, 0xD5,
+      0xCD, 0xF9, 0x20, 0x8,  0xA4, 0xF8, 0x78, 0xE6, 0xCC, 0x35, 0x77, 0x76,
+      0x80, 0x85, 0xC5, 0xA,  0xE,  0x98, 0xFD, 0xA6, 0xCB, 0xB8};
+  vec = {nonce_15, aad_15, key_15, plaintext_15, ciphertext_15,
+         12,       20,     44,     42,           58};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.5.1 65-byte auth
+  uint8_t nonce_16[] = {0x7C, 0xFD, 0xE9, 0xF9, 0xE3, 0x37,
+                        0x24, 0xC6, 0x89, 0x32, 0xD6, 0x12};
+  uint8_t aad_16[] = {
+      0x84, 0xC5, 0xD5, 0x13, 0xD2, 0xAA, 0xF6, 0xE5, 0xBB, 0xD2, 0x72, 0x77,
+      0x88, 0xE5, 0x23, 0x0,  0x89, 0x32, 0xD6, 0x12, 0x7C, 0xFD, 0xE9, 0xF9,
+      0xE3, 0x37, 0x24, 0xC6, 0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14,
+      0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
+      0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x0,  0x5};
+  uint8_t key_16[] = {0x1,  0x3F, 0xE0, 0xB,  0x5F, 0x11, 0xBE, 0x7F, 0x86,
+                      0x6D, 0xC,  0xBB, 0xC5, 0x5A, 0x7A, 0x90, 0x0,  0x3E,
+                      0xE1, 0xA,  0x5E, 0x10, 0xBF, 0x7E, 0x87, 0x6C, 0xD,
+                      0xBA, 0xC4, 0x5B, 0x7B, 0x91, 0x3,  0x3D, 0xE2, 0x9,
+                      0x5D, 0x13, 0xBC, 0x7D, 0x84, 0x6F, 0xE,  0xB9};
+  uint8_t plaintext_16[1] = {};
+  uint8_t ciphertext_16[] = {0xCC, 0xA2, 0xE,  0xEC, 0xDA, 0x62, 0x83, 0xF0,
+                             0x9B, 0xB3, 0x54, 0x3D, 0xD9, 0x9E, 0xDB, 0x9B};
+  vec = {nonce_16, aad_16, key_16, plaintext_16, ciphertext_16, 12, 81,
+         44,       0,      16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.5.2 65-byte auth
+  uint8_t nonce_17[] = {0x7C, 0xFD, 0xE9, 0xF9, 0xE3, 0x37,
+                        0x24, 0xC6, 0x89, 0x32, 0xD6, 0x12};
+  uint8_t aad_17[] = {
+      0x84, 0xC5, 0xD5, 0x13, 0xD2, 0xAA, 0xF6, 0xE5, 0xBB, 0xD2, 0x72, 0x77,
+      0x88, 0xE5, 0x23, 0x0,  0x89, 0x32, 0xD6, 0x12, 0x7C, 0xFD, 0xE9, 0xF9,
+      0xE3, 0x37, 0x24, 0xC6, 0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14,
+      0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
+      0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x0,  0x5};
+  uint8_t key_17[] = {0x83, 0xC0, 0x93, 0xB5, 0x8D, 0xE7, 0xFF, 0xE1, 0xC0,
+                      0xDA, 0x92, 0x6A, 0xC4, 0x3F, 0xB3, 0x60, 0x9A, 0xC1,
+                      0xC8, 0xF,  0xEE, 0x1B, 0x62, 0x44, 0x97, 0xEF, 0x94,
+                      0x2E, 0x2F, 0x79, 0xA8, 0x23, 0x81, 0xC2, 0x91, 0xB7,
+                      0x8F, 0xE5, 0xFD, 0xE3, 0xC2, 0xD8, 0x90, 0x68};
+  uint8_t plaintext_17[1] = {};
+  uint8_t ciphertext_17[] = {0xB2, 0x32, 0xCC, 0x1D, 0xA5, 0x11, 0x7B, 0xF1,
+                             0x50, 0x3,  0x73, 0x4F, 0xA5, 0x99, 0xD2, 0x71};
+  vec = {nonce_17, aad_17, key_17, plaintext_17, ciphertext_17, 12, 81,
+         44,       0,      16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE  2.6.1 61-byte crypt
+  uint8_t nonce_18[] = {0x7C, 0xFD, 0xE9, 0xF9, 0xE3, 0x37,
+                        0x24, 0xC6, 0x89, 0x32, 0xD6, 0x12};
+  uint8_t aad_18[] = {0x84, 0xC5, 0xD5, 0x13, 0xD2, 0xAA, 0xF6,
+                      0xE5, 0xBB, 0xD2, 0x72, 0x77, 0x88, 0xE5,
+                      0x2F, 0x0,  0x89, 0x32, 0xD6, 0x12, 0x7C,
+                      0xFD, 0xE9, 0xF9, 0xE3, 0x37, 0x24, 0xC6};
+  uint8_t key_18[] = {0x1,  0x3F, 0xE0, 0xB,  0x5F, 0x11, 0xBE, 0x7F, 0x86,
+                      0x6D, 0xC,  0xBB, 0xC5, 0x5A, 0x7A, 0x90, 0x0,  0x3E,
+                      0xE1, 0xA,  0x5E, 0x10, 0xBF, 0x7E, 0x87, 0x6C, 0xD,
+                      0xBA, 0xC4, 0x5B, 0x7B, 0x91, 0x3,  0x3D, 0xE2, 0x9,
+                      0x5D, 0x13, 0xBC, 0x7D, 0x84, 0x6F, 0xE,  0xB9};
+  uint8_t plaintext_18[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+      0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
+      0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x0,  0x6};
+  uint8_t ciphertext_18[] = {
+      0xFF, 0x19, 0x10, 0xD3, 0x5A, 0xD7, 0xE5, 0x65, 0x78, 0x90, 0xC7,
+      0xC5, 0x60, 0x14, 0x6F, 0xD0, 0x38, 0x70, 0x7F, 0x20, 0x4B, 0x66,
+      0xED, 0xBC, 0x3D, 0x16, 0x1F, 0x8A, 0xCE, 0x24, 0x4B, 0x98, 0x59,
+      0x21, 0x2,  0x3C, 0x43, 0x6E, 0x3A, 0x1C, 0x35, 0x32, 0xEC, 0xD5,
+      0xD0, 0x9A, 0x5,  0x6D, 0x70, 0xBE, 0x58, 0x3F, 0xD,  0x10, 0x82,
+      0x9D, 0x93, 0x87, 0xD0, 0x7D, 0x33, 0xD8, 0x72, 0xE4, 0x90};
+  vec = {nonce_18, aad_18, key_18, plaintext_18, ciphertext_18,
+         12,       28,     44,     49,           65};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.6.2 61-byte crypt
+  uint8_t nonce_19[] = {0x7C, 0xFD, 0xE9, 0xF9, 0xE3, 0x37,
+                        0x24, 0xC6, 0x89, 0x32, 0xD6, 0x12};
+  uint8_t aad_19[] = {0x84, 0xC5, 0xD5, 0x13, 0xD2, 0xAA, 0xF6,
+                      0xE5, 0xBB, 0xD2, 0x72, 0x77, 0x88, 0xE5,
+                      0x2F, 0x0,  0x89, 0x32, 0xD6, 0x12, 0x7C,
+                      0xFD, 0xE9, 0xF9, 0xE3, 0x37, 0x24, 0xC6};
+  uint8_t key_19[] = {0x83, 0xC0, 0x93, 0xB5, 0x8D, 0xE7, 0xFF, 0xE1, 0xC0,
+                      0xDA, 0x92, 0x6A, 0xC4, 0x3F, 0xB3, 0x60, 0x9A, 0xC1,
+                      0xC8, 0xF,  0xEE, 0x1B, 0x62, 0x44, 0x97, 0xEF, 0x94,
+                      0x2E, 0x2F, 0x79, 0xA8, 0x23, 0x81, 0xC2, 0x91, 0xB7,
+                      0x8F, 0xE5, 0xFD, 0xE3, 0xC2, 0xD8, 0x90, 0x68};
+  uint8_t plaintext_19[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+      0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
+      0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x0,  0x6};
+  uint8_t ciphertext_19[] = {
+      0xD,  0xB4, 0xCF, 0x95, 0x6B, 0x5F, 0x97, 0xEC, 0xA4, 0xEA, 0xB8,
+      0x2A, 0x69, 0x55, 0x30, 0x7F, 0x9A, 0xE0, 0x2A, 0x32, 0xDD, 0x7D,
+      0x93, 0xF8, 0x3D, 0x66, 0xAD, 0x4,  0xE1, 0xCF, 0xDC, 0x51, 0x82,
+      0xAD, 0x12, 0xAB, 0xDE, 0xA5, 0xBB, 0xB6, 0x19, 0xA1, 0xBD, 0x5F,
+      0xB9, 0xA5, 0x73, 0x59, 0xF,  0xBA, 0x90, 0x8E, 0x9C, 0x7A, 0x46,
+      0xC1, 0xF7, 0xBA, 0x9,  0x5,  0xD1, 0xB5, 0x5F, 0xFD, 0xA4};
+  vec = {nonce_19, aad_19, key_19, plaintext_19, ciphertext_19,
+         12,       28,     44,     49,           65};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.7.1 79-byte crypt
+  uint8_t nonce_20[] = {0x7A, 0xE8, 0xE2, 0xCA, 0x4E, 0xC5,
+                        0x0,  0x1,  0x2E, 0x58, 0x49, 0x5C};
+  uint8_t aad_20[] = {
+      0x68, 0xF2, 0xE7, 0x76, 0x96, 0xCE, 0x7A, 0xE8, 0xE2, 0xCA, 0x4E,
+      0xC5, 0x88, 0xE5, 0x41, 0x0,  0x2E, 0x58, 0x49, 0x5C, 0x8,  0x0,
+      0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+      0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
+      0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+      0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x0,  0x7};
+  uint8_t key_20[] = {0x88, 0xEE, 0x8,  0x7F, 0xD9, 0x5D, 0xA9, 0xFB, 0xF6,
+                      0x72, 0x5A, 0xA9, 0xD7, 0x57, 0xB0, 0xCD, 0x89, 0xEF,
+                      0x9,  0x7E, 0xD8, 0x5C, 0xA8, 0xFA, 0xF7, 0x73, 0x5B,
+                      0xA8, 0xD6, 0x56, 0xB1, 0xCC, 0x8A, 0xEC, 0xA,  0x7D,
+                      0xDB, 0x5F, 0xAB, 0xF9, 0xF4, 0x70, 0x58, 0xAB};
+  uint8_t plaintext_20[1] = {};
+  uint8_t ciphertext_20[] = {0x81, 0x3F, 0xE,  0x63, 0xF,  0x96, 0xFB, 0x2D,
+                             0x3,  0xF,  0x58, 0xD8, 0x3F, 0x5C, 0xDF, 0xD0};
+  vec = {nonce_20, aad_20, key_20, plaintext_20, ciphertext_20, 12, 87,
+         44,       0,      16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.7.2 79-byte crypt
+  uint8_t nonce_21[] = {0x7A, 0xE8, 0xE2, 0xCA, 0x4E, 0xC5,
+                        0x0,  0x1,  0x2E, 0x58, 0x49, 0x5C};
+  uint8_t aad_21[] = {
+      0x68, 0xF2, 0xE7, 0x76, 0x96, 0xCE, 0x7A, 0xE8, 0xE2, 0xCA, 0x4E,
+      0xC5, 0x88, 0xE5, 0x41, 0x0,  0x2E, 0x58, 0x49, 0x5C, 0x8,  0x0,
+      0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+      0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
+      0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+      0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x0,  0x7};
+  uint8_t key_21[] = {0x4C, 0x97, 0x3D, 0xBC, 0x73, 0x64, 0x62, 0x16, 0x74,
+                      0xF8, 0xB5, 0xB8, 0x9E, 0x5C, 0x15, 0x51, 0x1F, 0xCE,
+                      0xD9, 0x21, 0x64, 0x90, 0xFB, 0x1C, 0x1A, 0x2C, 0xAA,
+                      0xF,  0xFE, 0x4,  0x7,  0xE5, 0x4E, 0x95, 0x3F, 0xBE,
+                      0x71, 0x66, 0x60, 0x14, 0x76, 0xFA, 0xB7, 0xBA};
+  uint8_t plaintext_21[1] = {};
+  uint8_t ciphertext_21[] = {0x77, 0xE5, 0xA4, 0x4C, 0x21, 0xEB, 0x7, 0x18,
+                             0x8A, 0xAC, 0xBD, 0x74, 0xD1, 0x98, 0xE, 0x97};
+  vec = {nonce_21, aad_21, key_21, plaintext_21, ciphertext_21, 12, 87,
+         44,       0,      16};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.8.1 61-byte crypt
+  uint8_t nonce_22[] = {0x7A, 0xE8, 0xE2, 0xCA, 0x4E, 0xC5,
+                        0x0,  0x1,  0x2E, 0x58, 0x49, 0x5C};
+  uint8_t aad_22[] = {0x68, 0xF2, 0xE7, 0x76, 0x96, 0xCE, 0x7A,
+                      0xE8, 0xE2, 0xCA, 0x4E, 0xC5, 0x88, 0xE5,
+                      0x4D, 0x0,  0x2E, 0x58, 0x49, 0x5C};
+  uint8_t key_22[] = {0x88, 0xEE, 0x8,  0x7F, 0xD9, 0x5D, 0xA9, 0xFB, 0xF6,
+                      0x72, 0x5A, 0xA9, 0xD7, 0x57, 0xB0, 0xCD, 0x89, 0xEF,
+                      0x9,  0x7E, 0xD8, 0x5C, 0xA8, 0xFA, 0xF7, 0x73, 0x5B,
+                      0xA8, 0xD6, 0x56, 0xB1, 0xCC, 0x8A, 0xEC, 0xA,  0x7D,
+                      0xDB, 0x5F, 0xAB, 0xF9, 0xF4, 0x70, 0x58, 0xAB};
+  uint8_t plaintext_22[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+      0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43,
+      0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x0,  0x8};
+  uint8_t ciphertext_22[] = {
+      0x95, 0x8E, 0xC3, 0xF6, 0xD6, 0xA,  0xFE, 0xDA, 0x99, 0xEF, 0xD8, 0x88,
+      0xF1, 0x75, 0xE5, 0xFC, 0xD4, 0xC8, 0x7B, 0x9B, 0xCC, 0x5C, 0x2F, 0x54,
+      0x26, 0x25, 0x3A, 0x8B, 0x50, 0x62, 0x96, 0xC8, 0xC4, 0x33, 0x9,  0xAB,
+      0x2A, 0xDB, 0x59, 0x39, 0x46, 0x25, 0x41, 0xD9, 0x5E, 0x80, 0x81, 0x1E,
+      0x4,  0xE7, 0x6,  0xB1, 0x49, 0x8F, 0x2C, 0x40, 0x7C, 0x7F, 0xB2, 0x34,
+      0xF8, 0xCC, 0x1,  0xA6, 0x47, 0x55, 0xE,  0xE6, 0xB5, 0x57, 0xB3, 0x5A,
+      0x7E, 0x39, 0x45, 0x38, 0x18, 0x21, 0xF4};
+  vec = {nonce_22, aad_22, key_22, plaintext_22, ciphertext_22,
+         12,       20,     44,     63,           79};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+
+  // Derived from IEEE 2.8.2 61-byte crypt
+  uint8_t nonce_23[] = {0x7A, 0xE8, 0xE2, 0xCA, 0x4E, 0xC5,
+                        0x0,  0x1,  0x2E, 0x58, 0x49, 0x5C};
+  uint8_t aad_23[] = {0x68, 0xF2, 0xE7, 0x76, 0x96, 0xCE, 0x7A,
+                      0xE8, 0xE2, 0xCA, 0x4E, 0xC5, 0x88, 0xE5,
+                      0x4D, 0x0,  0x2E, 0x58, 0x49, 0x5C};
+  uint8_t key_23[] = {0x4C, 0x97, 0x3D, 0xBC, 0x73, 0x64, 0x62, 0x16, 0x74,
+                      0xF8, 0xB5, 0xB8, 0x9E, 0x5C, 0x15, 0x51, 0x1F, 0xCE,
+                      0xD9, 0x21, 0x64, 0x90, 0xFB, 0x1C, 0x1A, 0x2C, 0xAA,
+                      0xF,  0xFE, 0x4,  0x7,  0xE5, 0x4E, 0x95, 0x3F, 0xBE,
+                      0x71, 0x66, 0x60, 0x14, 0x76, 0xFA, 0xB7, 0xBA};
+  uint8_t plaintext_23[] = {
+      0x8,  0x0,  0xF,  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+      0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43,
+      0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x0,  0x8};
+  uint8_t ciphertext_23[] = {
+      0xB4, 0x4D, 0x7,  0x20, 0x11, 0xCD, 0x36, 0xD2, 0x72, 0xA9, 0xB7, 0xA9,
+      0x8D, 0xB9, 0xAA, 0x90, 0xCB, 0xC5, 0xC6, 0x7B, 0x93, 0xDD, 0xCE, 0x67,
+      0xC8, 0x54, 0x50, 0x32, 0x14, 0xE2, 0xE8, 0x96, 0xEC, 0x7E, 0x9D, 0xB6,
+      0x49, 0xED, 0x4B, 0xCF, 0x6F, 0x85, 0xA,  0xAC, 0x2,  0x23, 0xD0, 0xCF,
+      0x92, 0xC8, 0x3D, 0xB8, 0x7,  0x95, 0xC3, 0xA1, 0x7E, 0xCC, 0x12, 0x48,
+      0xBB, 0x0,  0x59, 0x17, 0x12, 0xB1, 0xAE, 0x71, 0xE2, 0x68, 0x16, 0x41,
+      0x96, 0x25, 0x21, 0x62, 0x81, 0xB,  0x0};
+  vec = {nonce_23, aad_23, key_23, plaintext_23, ciphertext_23,
+         12,       20,     44,     63,           79};
+  gsec_test_verify_crypter_on_test_vector(&vec, /*rekey=*/true);
+}
+
+static void gsec_test_do_vector_tests_nist() {
+  /**
+   * From:
+   * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/
+   * gcm-revised-spec.pdf
+   */
+
+  /* Test vector 1 */
+  gsec_aead_test_vector* test_vector_1;
+  const uint8_t test_vector_1_key[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00};
+  const uint8_t test_vector_1_nonce[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  const uint8_t test_vector_1_aad[1] = {};
+  const uint8_t test_vector_1_plaintext[1] = {};
+  const uint8_t test_vector_1_ciphertext_and_tag[] = {
+      0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61,
+      0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a};
+  gsec_aead_malloc_test_vector(
+      &test_vector_1, test_vector_1_key,
+      sizeof(test_vector_1_key) / sizeof(uint8_t), test_vector_1_nonce,
+      sizeof(test_vector_1_nonce) / sizeof(uint8_t), test_vector_1_aad, 0,
+      test_vector_1_plaintext, 0, test_vector_1_ciphertext_and_tag,
+      sizeof(test_vector_1_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_1);
+  gsec_aead_free_test_vector(test_vector_1);
+
+  /* Test vector 2 */
+  gsec_aead_test_vector* test_vector_2;
+  const uint8_t test_vector_2_key[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00};
+  const uint8_t test_vector_2_nonce[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  const uint8_t test_vector_2_aad[1] = {};
+  const uint8_t test_vector_2_plaintext[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                             0x00, 0x00, 0x00, 0x00};
+  const uint8_t test_vector_2_ciphertext_and_tag[] = {
+      0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2,
+      0xb9, 0x71, 0xb2, 0xfe, 0x78, 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec,
+      0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf};
+  gsec_aead_malloc_test_vector(
+      &test_vector_2, test_vector_2_key,
+      sizeof(test_vector_2_key) / sizeof(uint8_t), test_vector_2_nonce,
+      sizeof(test_vector_2_nonce) / sizeof(uint8_t), test_vector_2_aad, 0,
+      test_vector_2_plaintext,
+      sizeof(test_vector_2_plaintext) / sizeof(uint8_t),
+      test_vector_2_ciphertext_and_tag,
+      sizeof(test_vector_2_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_2);
+  gsec_aead_free_test_vector(test_vector_2);
+
+  /* Test vector 3 */
+  gsec_aead_test_vector* test_vector_3;
+  const uint8_t test_vector_3_key[] = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65,
+                                       0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94,
+                                       0x67, 0x30, 0x83, 0x08};
+  const uint8_t test_vector_3_nonce[] = {0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                                         0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88};
+  const uint8_t test_vector_3_aad[1] = {};
+  const uint8_t test_vector_3_plaintext[] = {
+      0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09,
+      0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34,
+      0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c,
+      0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24,
+      0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6,
+      0x57, 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55};
+  const uint8_t test_vector_3_ciphertext_and_tag[] = {
+      0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7,
+      0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0,
+      0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2,
+      0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05,
+      0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91,
+      0x47, 0x3f, 0x59, 0x85, 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6,
+      0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4};
+  gsec_aead_malloc_test_vector(
+      &test_vector_3, test_vector_3_key,
+      sizeof(test_vector_3_key) / sizeof(uint8_t), test_vector_3_nonce,
+      sizeof(test_vector_3_nonce) / sizeof(uint8_t), test_vector_3_aad, 0,
+      test_vector_3_plaintext,
+      sizeof(test_vector_3_plaintext) / sizeof(uint8_t),
+      test_vector_3_ciphertext_and_tag,
+      sizeof(test_vector_3_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_3);
+  gsec_aead_free_test_vector(test_vector_3);
+
+  /* Test vector 4 */
+  gsec_aead_test_vector* test_vector_4;
+  const uint8_t test_vector_4_key[] = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65,
+                                       0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94,
+                                       0x67, 0x30, 0x83, 0x08};
+  const uint8_t test_vector_4_nonce[] = {0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                                         0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88};
+  const uint8_t test_vector_4_aad[] = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe,
+                                       0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad,
+                                       0xbe, 0xef, 0xab, 0xad, 0xda, 0xd2};
+  const uint8_t test_vector_4_plaintext[] = {
+      0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5,
+      0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+      0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+      0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+      0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39};
+  const uint8_t test_vector_4_ciphertext_and_tag[] = {
+      0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21,
+      0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02,
+      0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21,
+      0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a,
+      0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac,
+      0x97, 0x3d, 0x58, 0xe0, 0x91, 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21,
+      0xa5, 0xdb, 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47};
+  gsec_aead_malloc_test_vector(
+      &test_vector_4, test_vector_4_key,
+      sizeof(test_vector_4_key) / sizeof(uint8_t), test_vector_4_nonce,
+      sizeof(test_vector_4_nonce) / sizeof(uint8_t), test_vector_4_aad,
+      sizeof(test_vector_4_aad) / sizeof(uint8_t), test_vector_4_plaintext,
+      sizeof(test_vector_4_plaintext) / sizeof(uint8_t),
+      test_vector_4_ciphertext_and_tag,
+      sizeof(test_vector_4_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_4);
+  gsec_aead_free_test_vector(test_vector_4);
+}
+
+static void gsec_test_do_vector_tests_ieee() {
+  /**
+   * From:
+   * http://www.ieee802.org/1/files/public/docs2011/
+   * bn-randall-test-vectors-0511-v1.pdf
+   */
+
+  /* 2.1.1 54-byte auth */
+  gsec_aead_test_vector* test_vector_5;
+  const uint8_t test_vector_5_key[] = {0xad, 0x7a, 0x2b, 0xd0, 0x3e, 0xac,
+                                       0x83, 0x5a, 0x6f, 0x62, 0x0f, 0xdc,
+                                       0xb5, 0x06, 0xb3, 0x45};
+  const uint8_t test_vector_5_nonce[] = {0x12, 0x15, 0x35, 0x24, 0xc0, 0x89,
+                                         0x5e, 0x81, 0xb2, 0xc2, 0x84, 0x65};
+  const uint8_t test_vector_5_aad[] = {
+      0xd6, 0x09, 0xb1, 0xf0, 0x56, 0x63, 0x7a, 0x0d, 0x46, 0xdf, 0x99, 0x8d,
+      0x88, 0xe5, 0x22, 0x2a, 0xb2, 0xc2, 0x84, 0x65, 0x12, 0x15, 0x35, 0x24,
+      0xc0, 0x89, 0x5e, 0x81, 0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+      0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+      0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x00, 0x01};
+  const uint8_t test_vector_5_plaintext[1] = {};
+  const uint8_t test_vector_5_ciphertext_and_tag[] = {
+      0xf0, 0x94, 0x78, 0xa9, 0xb0, 0x90, 0x07, 0xd0,
+      0x6f, 0x46, 0xe9, 0xb6, 0xa1, 0xda, 0x25, 0xdd};
+  gsec_aead_malloc_test_vector(
+      &test_vector_5, test_vector_5_key,
+      sizeof(test_vector_5_key) / sizeof(uint8_t), test_vector_5_nonce,
+      sizeof(test_vector_5_nonce) / sizeof(uint8_t), test_vector_5_aad,
+      sizeof(test_vector_5_aad) / sizeof(uint8_t), test_vector_5_plaintext, 0,
+      test_vector_5_ciphertext_and_tag,
+      sizeof(test_vector_5_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_5);
+  gsec_aead_free_test_vector(test_vector_5);
+
+  /* 2.1.2 54-byte auth */
+  gsec_aead_test_vector* test_vector_6;
+  const uint8_t test_vector_6_key[] = {
+      0xe3, 0xc0, 0x8a, 0x8f, 0x06, 0xc6, 0xe3, 0xad, 0x95, 0xa7, 0x05,
+      0x57, 0xb2, 0x3f, 0x75, 0x48, 0x3c, 0xe3, 0x30, 0x21, 0xa9, 0xc7,
+      0x2b, 0x70, 0x25, 0x66, 0x62, 0x04, 0xc6, 0x9c, 0x0b, 0x72};
+
+  const uint8_t test_vector_6_nonce[] = {0x12, 0x15, 0x35, 0x24, 0xc0, 0x89,
+                                         0x5e, 0x81, 0xb2, 0xc2, 0x84, 0x65};
+  const uint8_t test_vector_6_aad[] = {
+      0xd6, 0x09, 0xb1, 0xf0, 0x56, 0x63, 0x7a, 0x0d, 0x46, 0xdf, 0x99, 0x8d,
+      0x88, 0xe5, 0x22, 0x2a, 0xb2, 0xc2, 0x84, 0x65, 0x12, 0x15, 0x35, 0x24,
+      0xc0, 0x89, 0x5e, 0x81, 0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+      0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+      0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x00, 0x01};
+  const uint8_t test_vector_6_plaintext[1] = {};
+  const uint8_t test_vector_6_ciphertext_and_tag[] = {
+      0x2f, 0x0b, 0xc5, 0xaf, 0x40, 0x9e, 0x06, 0xd6,
+      0x09, 0xea, 0x8b, 0x7d, 0x0f, 0xa5, 0xea, 0x50};
+  gsec_aead_malloc_test_vector(
+      &test_vector_6, test_vector_6_key,
+      sizeof(test_vector_6_key) / sizeof(uint8_t), test_vector_6_nonce,
+      sizeof(test_vector_6_nonce) / sizeof(uint8_t), test_vector_6_aad,
+      sizeof(test_vector_6_aad) / sizeof(uint8_t), test_vector_6_plaintext, 0,
+      test_vector_6_ciphertext_and_tag,
+      sizeof(test_vector_6_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_6);
+  gsec_aead_free_test_vector(test_vector_6);
+
+  /* 2.2.1 60-byte crypt */
+  gsec_aead_test_vector* test_vector_7;
+  const uint8_t test_vector_7_key[] = {0xad, 0x7a, 0x2b, 0xd0, 0x3e, 0xac,
+                                       0x83, 0x5a, 0x6f, 0x62, 0x0f, 0xdc,
+                                       0xb5, 0x06, 0xb3, 0x45};
+
+  const uint8_t test_vector_7_nonce[] = {0x12, 0x15, 0x35, 0x24, 0xc0, 0x89,
+                                         0x5e, 0x81, 0xb2, 0xc2, 0x84, 0x65};
+  const uint8_t test_vector_7_aad[] = {
+      0xd6, 0x09, 0xb1, 0xf0, 0x56, 0x63, 0x7a, 0x0d, 0x46, 0xdf,
+      0x99, 0x8d, 0x88, 0xe5, 0x2e, 0x00, 0xb2, 0xc2, 0x84, 0x65,
+      0x12, 0x15, 0x35, 0x24, 0xc0, 0x89, 0x5e, 0x81};
+  const uint8_t test_vector_7_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+      0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+      0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x00, 0x02};
+  const uint8_t test_vector_7_ciphertext_and_tag[] = {
+      0x70, 0x1a, 0xfa, 0x1c, 0xc0, 0x39, 0xc0, 0xd7, 0x65, 0x12, 0x8a,
+      0x66, 0x5d, 0xab, 0x69, 0x24, 0x38, 0x99, 0xbf, 0x73, 0x18, 0xcc,
+      0xdc, 0x81, 0xc9, 0x93, 0x1d, 0xa1, 0x7f, 0xbe, 0x8e, 0xdd, 0x7d,
+      0x17, 0xcb, 0x8b, 0x4c, 0x26, 0xfc, 0x81, 0xe3, 0x28, 0x4f, 0x2b,
+      0x7f, 0xba, 0x71, 0x3d, 0x4f, 0x8d, 0x55, 0xe7, 0xd3, 0xf0, 0x6f,
+      0xd5, 0xa1, 0x3c, 0x0c, 0x29, 0xb9, 0xd5, 0xb8, 0x80};
+  gsec_aead_malloc_test_vector(
+      &test_vector_7, test_vector_7_key,
+      sizeof(test_vector_7_key) / sizeof(uint8_t), test_vector_7_nonce,
+      sizeof(test_vector_7_nonce) / sizeof(uint8_t), test_vector_7_aad,
+      sizeof(test_vector_7_aad) / sizeof(uint8_t), test_vector_7_plaintext,
+      sizeof(test_vector_7_plaintext) / sizeof(uint8_t),
+      test_vector_7_ciphertext_and_tag,
+      sizeof(test_vector_7_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_7);
+  gsec_aead_free_test_vector(test_vector_7);
+
+  /* 2.2.2 60-byte crypt */
+  gsec_aead_test_vector* test_vector_8;
+  const uint8_t test_vector_8_key[] = {
+      0xe3, 0xc0, 0x8a, 0x8f, 0x06, 0xc6, 0xe3, 0xad, 0x95, 0xa7, 0x05,
+      0x57, 0xb2, 0x3f, 0x75, 0x48, 0x3c, 0xe3, 0x30, 0x21, 0xa9, 0xc7,
+      0x2b, 0x70, 0x25, 0x66, 0x62, 0x04, 0xc6, 0x9c, 0x0b, 0x72};
+  const uint8_t test_vector_8_nonce[] = {0x12, 0x15, 0x35, 0x24, 0xc0, 0x89,
+                                         0x5e, 0x81, 0xb2, 0xc2, 0x84, 0x65};
+  const uint8_t test_vector_8_aad[] = {
+      0xd6, 0x09, 0xb1, 0xf0, 0x56, 0x63, 0x7a, 0x0d, 0x46, 0xdf,
+      0x99, 0x8d, 0x88, 0xe5, 0x2e, 0x00, 0xb2, 0xc2, 0x84, 0x65,
+      0x12, 0x15, 0x35, 0x24, 0xc0, 0x89, 0x5e, 0x81};
+  const uint8_t test_vector_8_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+      0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+      0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x00, 0x02};
+  const uint8_t test_vector_8_ciphertext_and_tag[] = {
+      0xe2, 0x00, 0x6e, 0xb4, 0x2f, 0x52, 0x77, 0x02, 0x2d, 0x9b, 0x19,
+      0x92, 0x5b, 0xc4, 0x19, 0xd7, 0xa5, 0x92, 0x66, 0x6c, 0x92, 0x5f,
+      0xe2, 0xef, 0x71, 0x8e, 0xb4, 0xe3, 0x08, 0xef, 0xea, 0xa7, 0xc5,
+      0x27, 0x3b, 0x39, 0x41, 0x18, 0x86, 0x0a, 0x5b, 0xe2, 0xa9, 0x7f,
+      0x56, 0xab, 0x78, 0x36, 0x5c, 0xa5, 0x97, 0xcd, 0xbb, 0x3e, 0xdb,
+      0x8d, 0x1a, 0x11, 0x51, 0xea, 0x0a, 0xf7, 0xb4, 0x36};
+  gsec_aead_malloc_test_vector(
+      &test_vector_8, test_vector_8_key,
+      sizeof(test_vector_8_key) / sizeof(uint8_t), test_vector_8_nonce,
+      sizeof(test_vector_8_nonce) / sizeof(uint8_t), test_vector_8_aad,
+      sizeof(test_vector_8_aad) / sizeof(uint8_t), test_vector_8_plaintext,
+      sizeof(test_vector_8_plaintext) / sizeof(uint8_t),
+      test_vector_8_ciphertext_and_tag,
+      sizeof(test_vector_8_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_8);
+  gsec_aead_free_test_vector(test_vector_8);
+
+  /* 2.3.1 60-byte auth */
+  gsec_aead_test_vector* test_vector_9;
+  const uint8_t test_vector_9_key[] = {0x07, 0x1b, 0x11, 0x3b, 0x0c, 0xa7,
+                                       0x43, 0xfe, 0xcc, 0xcf, 0x3d, 0x05,
+                                       0x1f, 0x73, 0x73, 0x82};
+  const uint8_t test_vector_9_nonce[] = {0xf0, 0x76, 0x1e, 0x8d, 0xcd, 0x3d,
+                                         0x00, 0x01, 0x76, 0xd4, 0x57, 0xed};
+  const uint8_t test_vector_9_aad[] = {
+      0xe2, 0x01, 0x06, 0xd7, 0xcd, 0x0d, 0xf0, 0x76, 0x1e, 0x8d, 0xcd, 0x3d,
+      0x88, 0xe5, 0x40, 0x00, 0x76, 0xd4, 0x57, 0xed, 0x08, 0x00, 0x0f, 0x10,
+      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+      0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+      0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x00, 0x03};
+  const uint8_t test_vector_9_plaintext[1] = {};
+  const uint8_t test_vector_9_ciphertext_and_tag[] = {
+      0x0c, 0x01, 0x7b, 0xc7, 0x3b, 0x22, 0x7d, 0xfc,
+      0xc9, 0xba, 0xfa, 0x1c, 0x41, 0xac, 0xc3, 0x53};
+  gsec_aead_malloc_test_vector(
+      &test_vector_9, test_vector_9_key,
+      sizeof(test_vector_9_key) / sizeof(uint8_t), test_vector_9_nonce,
+      sizeof(test_vector_9_nonce) / sizeof(uint8_t), test_vector_9_aad,
+      sizeof(test_vector_9_aad) / sizeof(uint8_t), test_vector_9_plaintext, 0,
+      test_vector_9_ciphertext_and_tag,
+      sizeof(test_vector_9_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_9);
+  gsec_aead_free_test_vector(test_vector_9);
+
+  /* 2.3.2 60-byte auth */
+  gsec_aead_test_vector* test_vector_10;
+  const uint8_t test_vector_10_key[] = {
+      0x69, 0x1d, 0x3e, 0xe9, 0x09, 0xd7, 0xf5, 0x41, 0x67, 0xfd, 0x1c,
+      0xa0, 0xb5, 0xd7, 0x69, 0x08, 0x1f, 0x2b, 0xde, 0x1a, 0xee, 0x65,
+      0x5f, 0xdb, 0xab, 0x80, 0xbd, 0x52, 0x95, 0xae, 0x6b, 0xe7};
+  const uint8_t test_vector_10_nonce[] = {0xf0, 0x76, 0x1e, 0x8d, 0xcd, 0x3d,
+                                          0x00, 0x01, 0x76, 0xd4, 0x57, 0xed};
+  const uint8_t test_vector_10_aad[] = {
+      0xe2, 0x01, 0x06, 0xd7, 0xcd, 0x0d, 0xf0, 0x76, 0x1e, 0x8d, 0xcd, 0x3d,
+      0x88, 0xe5, 0x40, 0x00, 0x76, 0xd4, 0x57, 0xed, 0x08, 0x00, 0x0f, 0x10,
+      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+      0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+      0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x00, 0x03};
+  const uint8_t test_vector_10_plaintext[1] = {};
+  const uint8_t test_vector_10_ciphertext_and_tag[] = {
+      0x35, 0x21, 0x7c, 0x77, 0x4b, 0xbc, 0x31, 0xb6,
+      0x31, 0x66, 0xbc, 0xf9, 0xd4, 0xab, 0xed, 0x07};
+  gsec_aead_malloc_test_vector(
+      &test_vector_10, test_vector_10_key,
+      sizeof(test_vector_10_key) / sizeof(uint8_t), test_vector_10_nonce,
+      sizeof(test_vector_10_nonce) / sizeof(uint8_t), test_vector_10_aad,
+      sizeof(test_vector_10_aad) / sizeof(uint8_t), test_vector_10_plaintext, 0,
+      test_vector_10_ciphertext_and_tag,
+      sizeof(test_vector_10_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_10);
+  gsec_aead_free_test_vector(test_vector_10);
+
+  /* 2.4.1 54-byte crypt */
+  gsec_aead_test_vector* test_vector_11;
+  const uint8_t test_vector_11_key[] = {0x07, 0x1b, 0x11, 0x3b, 0x0c, 0xa7,
+                                        0x43, 0xfe, 0xcc, 0xcf, 0x3d, 0x05,
+                                        0x1f, 0x73, 0x73, 0x82};
+  const uint8_t test_vector_11_nonce[] = {0xf0, 0x76, 0x1e, 0x8d, 0xcd, 0x3d,
+                                          0x00, 0x01, 0x76, 0xd4, 0x57, 0xed};
+  const uint8_t test_vector_11_aad[] = {
+      0xe2, 0x01, 0x06, 0xd7, 0xcd, 0x0d, 0xf0, 0x76, 0x1e, 0x8d,
+      0xcd, 0x3d, 0x88, 0xe5, 0x4c, 0x2a, 0x76, 0xd4, 0x57, 0xed};
+  const uint8_t test_vector_11_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+      0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x00, 0x04};
+  const uint8_t test_vector_11_ciphertext_and_tag[] = {
+      0x13, 0xb4, 0xc7, 0x2b, 0x38, 0x9d, 0xc5, 0x01, 0x8e, 0x72, 0xa1, 0x71,
+      0xdd, 0x85, 0xa5, 0xd3, 0x75, 0x22, 0x74, 0xd3, 0xa0, 0x19, 0xfb, 0xca,
+      0xed, 0x09, 0xa4, 0x25, 0xcd, 0x9b, 0x2e, 0x1c, 0x9b, 0x72, 0xee, 0xe7,
+      0xc9, 0xde, 0x7d, 0x52, 0xb3, 0xf3, 0xd6, 0xa5, 0x28, 0x4f, 0x4a, 0x6d,
+      0x3f, 0xe2, 0x2a, 0x5d, 0x6c, 0x2b, 0x96, 0x04, 0x94, 0xc3};
+  gsec_aead_malloc_test_vector(
+      &test_vector_11, test_vector_11_key,
+      sizeof(test_vector_11_key) / sizeof(uint8_t), test_vector_11_nonce,
+      sizeof(test_vector_11_nonce) / sizeof(uint8_t), test_vector_11_aad,
+      sizeof(test_vector_11_aad) / sizeof(uint8_t), test_vector_11_plaintext,
+      sizeof(test_vector_11_plaintext) / sizeof(uint8_t),
+      test_vector_11_ciphertext_and_tag,
+      sizeof(test_vector_11_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_11);
+  gsec_aead_free_test_vector(test_vector_11);
+
+  /* 2.4.2 54-byte crypt */
+  gsec_aead_test_vector* test_vector_12;
+  const uint8_t test_vector_12_key[] = {
+      0x69, 0x1d, 0x3e, 0xe9, 0x09, 0xd7, 0xf5, 0x41, 0x67, 0xfd, 0x1c,
+      0xa0, 0xb5, 0xd7, 0x69, 0x08, 0x1f, 0x2b, 0xde, 0x1a, 0xee, 0x65,
+      0x5f, 0xdb, 0xab, 0x80, 0xbd, 0x52, 0x95, 0xae, 0x6b, 0xe7};
+  const uint8_t test_vector_12_nonce[] = {0xf0, 0x76, 0x1e, 0x8d, 0xcd, 0x3d,
+                                          0x00, 0x01, 0x76, 0xd4, 0x57, 0xed};
+  const uint8_t test_vector_12_aad[] = {
+      0xe2, 0x01, 0x06, 0xd7, 0xcd, 0x0d, 0xf0, 0x76, 0x1e, 0x8d,
+      0xcd, 0x3d, 0x88, 0xe5, 0x4c, 0x2a, 0x76, 0xd4, 0x57, 0xed};
+  const uint8_t test_vector_12_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+      0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x00, 0x04};
+  const uint8_t test_vector_12_ciphertext_and_tag[] = {
+      0xc1, 0x62, 0x3f, 0x55, 0x73, 0x0c, 0x93, 0x53, 0x30, 0x97, 0xad, 0xda,
+      0xd2, 0x56, 0x64, 0x96, 0x61, 0x25, 0x35, 0x2b, 0x43, 0xad, 0xac, 0xbd,
+      0x61, 0xc5, 0xef, 0x3a, 0xc9, 0x0b, 0x5b, 0xee, 0x92, 0x9c, 0xe4, 0x63,
+      0x0e, 0xa7, 0x9f, 0x6c, 0xe5, 0x19, 0x12, 0xaf, 0x39, 0xc2, 0xd1, 0xfd,
+      0xc2, 0x05, 0x1f, 0x8b, 0x7b, 0x3c, 0x9d, 0x39, 0x7e, 0xf2};
+  gsec_aead_malloc_test_vector(
+      &test_vector_12, test_vector_12_key,
+      sizeof(test_vector_12_key) / sizeof(uint8_t), test_vector_12_nonce,
+      sizeof(test_vector_12_nonce) / sizeof(uint8_t), test_vector_12_aad,
+      sizeof(test_vector_12_aad) / sizeof(uint8_t), test_vector_12_plaintext,
+      sizeof(test_vector_12_plaintext) / sizeof(uint8_t),
+      test_vector_12_ciphertext_and_tag,
+      sizeof(test_vector_12_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_12);
+  gsec_aead_free_test_vector(test_vector_12);
+
+  /* 2.5.1 65-byte auth */
+  gsec_aead_test_vector* test_vector_13;
+  const uint8_t test_vector_13_key[] = {0x01, 0x3f, 0xe0, 0x0b, 0x5f, 0x11,
+                                        0xbe, 0x7f, 0x86, 0x6d, 0x0c, 0xbb,
+                                        0xc5, 0x5a, 0x7a, 0x90};
+  const uint8_t test_vector_13_nonce[] = {0x7c, 0xfd, 0xe9, 0xf9, 0xe3, 0x37,
+                                          0x24, 0xc6, 0x89, 0x32, 0xd6, 0x12};
+  const uint8_t test_vector_13_aad[] = {
+      0x84, 0xc5, 0xd5, 0x13, 0xd2, 0xaa, 0xf6, 0xe5, 0xbb, 0xd2, 0x72, 0x77,
+      0x88, 0xe5, 0x23, 0x00, 0x89, 0x32, 0xd6, 0x12, 0x7c, 0xfd, 0xe9, 0xf9,
+      0xe3, 0x37, 0x24, 0xc6, 0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+      0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+      0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x00, 0x05};
+  const uint8_t test_vector_13_plaintext[1] = {};
+  const uint8_t test_vector_13_ciphertext_and_tag[] = {
+      0x21, 0x78, 0x67, 0xe5, 0x0c, 0x2d, 0xad, 0x74,
+      0xc2, 0x8c, 0x3b, 0x50, 0xab, 0xdf, 0x69, 0x5a};
+  gsec_aead_malloc_test_vector(
+      &test_vector_13, test_vector_13_key,
+      sizeof(test_vector_13_key) / sizeof(uint8_t), test_vector_13_nonce,
+      sizeof(test_vector_13_nonce) / sizeof(uint8_t), test_vector_13_aad,
+      sizeof(test_vector_13_aad) / sizeof(uint8_t), test_vector_13_plaintext, 0,
+      test_vector_13_ciphertext_and_tag,
+      sizeof(test_vector_13_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_13);
+  gsec_aead_free_test_vector(test_vector_13);
+
+  /* 2.5.2 65-byte auth */
+  gsec_aead_test_vector* test_vector_14;
+  const uint8_t test_vector_14_key[] = {
+      0x83, 0xc0, 0x93, 0xb5, 0x8d, 0xe7, 0xff, 0xe1, 0xc0, 0xda, 0x92,
+      0x6a, 0xc4, 0x3f, 0xb3, 0x60, 0x9a, 0xc1, 0xc8, 0x0f, 0xee, 0x1b,
+      0x62, 0x44, 0x97, 0xef, 0x94, 0x2e, 0x2f, 0x79, 0xa8, 0x23};
+  const uint8_t test_vector_14_nonce[] = {0x7c, 0xfd, 0xe9, 0xf9, 0xe3, 0x37,
+                                          0x24, 0xc6, 0x89, 0x32, 0xd6, 0x12};
+  const uint8_t test_vector_14_aad[] = {
+      0x84, 0xc5, 0xd5, 0x13, 0xd2, 0xaa, 0xf6, 0xe5, 0xbb, 0xd2, 0x72, 0x77,
+      0x88, 0xe5, 0x23, 0x00, 0x89, 0x32, 0xd6, 0x12, 0x7c, 0xfd, 0xe9, 0xf9,
+      0xe3, 0x37, 0x24, 0xc6, 0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+      0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+      0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x00, 0x05};
+  const uint8_t test_vector_14_plaintext[1] = {};
+  const uint8_t test_vector_14_ciphertext_and_tag[] = {
+      0x6e, 0xe1, 0x60, 0xe8, 0xfa, 0xec, 0xa4, 0xb3,
+      0x6c, 0x86, 0xb2, 0x34, 0x92, 0x0c, 0xa9, 0x75};
+  gsec_aead_malloc_test_vector(
+      &test_vector_14, test_vector_14_key,
+      sizeof(test_vector_14_key) / sizeof(uint8_t), test_vector_14_nonce,
+      sizeof(test_vector_14_nonce) / sizeof(uint8_t), test_vector_14_aad,
+      sizeof(test_vector_14_aad) / sizeof(uint8_t), test_vector_14_plaintext, 0,
+      test_vector_14_ciphertext_and_tag,
+      sizeof(test_vector_14_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_14);
+  gsec_aead_free_test_vector(test_vector_14);
+
+  /* 2.6.1 61-byte crypt */
+  gsec_aead_test_vector* test_vector_15;
+  const uint8_t test_vector_15_key[] = {0x01, 0x3f, 0xe0, 0x0b, 0x5f, 0x11,
+                                        0xbe, 0x7f, 0x86, 0x6d, 0x0c, 0xbb,
+                                        0xc5, 0x5a, 0x7a, 0x90};
+  const uint8_t test_vector_15_nonce[] = {0x7c, 0xfd, 0xe9, 0xf9, 0xe3, 0x37,
+                                          0x24, 0xc6, 0x89, 0x32, 0xd6, 0x12};
+  const uint8_t test_vector_15_aad[] = {
+      0x84, 0xc5, 0xd5, 0x13, 0xd2, 0xaa, 0xf6, 0xe5, 0xbb, 0xd2,
+      0x72, 0x77, 0x88, 0xe5, 0x2f, 0x00, 0x89, 0x32, 0xd6, 0x12,
+      0x7c, 0xfd, 0xe9, 0xf9, 0xe3, 0x37, 0x24, 0xc6};
+  const uint8_t test_vector_15_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+      0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+      0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x00, 0x06};
+  const uint8_t test_vector_15_ciphertext_and_tag[] = {
+      0x3a, 0x4d, 0xe6, 0xfa, 0x32, 0x19, 0x10, 0x14, 0xdb, 0xb3, 0x03,
+      0xd9, 0x2e, 0xe3, 0xa9, 0xe8, 0xa1, 0xb5, 0x99, 0xc1, 0x4d, 0x22,
+      0xfb, 0x08, 0x00, 0x96, 0xe1, 0x38, 0x11, 0x81, 0x6a, 0x3c, 0x9c,
+      0x9b, 0xcf, 0x7c, 0x1b, 0x9b, 0x96, 0xda, 0x80, 0x92, 0x04, 0xe2,
+      0x9d, 0x0e, 0x2a, 0x76, 0x42, 0xbf, 0xd3, 0x10, 0xa4, 0x83, 0x7c,
+      0x81, 0x6c, 0xcf, 0xa5, 0xac, 0x23, 0xab, 0x00, 0x39, 0x88};
+  gsec_aead_malloc_test_vector(
+      &test_vector_15, test_vector_15_key,
+      sizeof(test_vector_15_key) / sizeof(uint8_t), test_vector_15_nonce,
+      sizeof(test_vector_15_nonce) / sizeof(uint8_t), test_vector_15_aad,
+      sizeof(test_vector_15_aad) / sizeof(uint8_t), test_vector_15_plaintext,
+      sizeof(test_vector_15_plaintext) / sizeof(uint8_t),
+      test_vector_15_ciphertext_and_tag,
+      sizeof(test_vector_15_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_15);
+  gsec_aead_free_test_vector(test_vector_15);
+
+  /* 2.6.2 61-byte crypt */
+  gsec_aead_test_vector* test_vector_16;
+  const uint8_t test_vector_16_key[] = {
+      0x83, 0xc0, 0x93, 0xb5, 0x8d, 0xe7, 0xff, 0xe1, 0xc0, 0xda, 0x92,
+      0x6a, 0xc4, 0x3f, 0xb3, 0x60, 0x9a, 0xc1, 0xc8, 0x0f, 0xee, 0x1b,
+      0x62, 0x44, 0x97, 0xef, 0x94, 0x2e, 0x2f, 0x79, 0xa8, 0x23};
+  const uint8_t test_vector_16_nonce[] = {0x7c, 0xfd, 0xe9, 0xf9, 0xe3, 0x37,
+                                          0x24, 0xc6, 0x89, 0x32, 0xd6, 0x12};
+  const uint8_t test_vector_16_aad[] = {
+      0x84, 0xc5, 0xd5, 0x13, 0xd2, 0xaa, 0xf6, 0xe5, 0xbb, 0xd2,
+      0x72, 0x77, 0x88, 0xe5, 0x2f, 0x00, 0x89, 0x32, 0xd6, 0x12,
+      0x7c, 0xfd, 0xe9, 0xf9, 0xe3, 0x37, 0x24, 0xc6};
+  const uint8_t test_vector_16_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+      0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+      0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+      0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x00, 0x06};
+  const uint8_t test_vector_16_ciphertext_and_tag[] = {
+      0x11, 0x02, 0x22, 0xff, 0x80, 0x50, 0xcb, 0xec, 0xe6, 0x6a, 0x81,
+      0x3a, 0xd0, 0x9a, 0x73, 0xed, 0x7a, 0x9a, 0x08, 0x9c, 0x10, 0x6b,
+      0x95, 0x93, 0x89, 0x16, 0x8e, 0xd6, 0xe8, 0x69, 0x8e, 0xa9, 0x02,
+      0xeb, 0x12, 0x77, 0xdb, 0xec, 0x2e, 0x68, 0xe4, 0x73, 0x15, 0x5a,
+      0x15, 0xa7, 0xda, 0xee, 0xd4, 0xa1, 0x0f, 0x4e, 0x05, 0x13, 0x9c,
+      0x23, 0xdf, 0x00, 0xb3, 0xaa, 0xdc, 0x71, 0xf0, 0x59, 0x6a};
+  gsec_aead_malloc_test_vector(
+      &test_vector_16, test_vector_16_key,
+      sizeof(test_vector_16_key) / sizeof(uint8_t), test_vector_16_nonce,
+      sizeof(test_vector_16_nonce) / sizeof(uint8_t), test_vector_16_aad,
+      sizeof(test_vector_16_aad) / sizeof(uint8_t), test_vector_16_plaintext,
+      sizeof(test_vector_16_plaintext) / sizeof(uint8_t),
+      test_vector_16_ciphertext_and_tag,
+      sizeof(test_vector_16_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_16);
+  gsec_aead_free_test_vector(test_vector_16);
+
+  /* 2.7.1 79-byte crypt */
+  gsec_aead_test_vector* test_vector_17;
+  const uint8_t test_vector_17_key[] = {0x88, 0xee, 0x08, 0x7f, 0xd9, 0x5d,
+                                        0xa9, 0xfb, 0xf6, 0x72, 0x5a, 0xa9,
+                                        0xd7, 0x57, 0xb0, 0xcd};
+  const uint8_t test_vector_17_nonce[] = {0x7a, 0xe8, 0xe2, 0xca, 0x4e, 0xc5,
+                                          0x00, 0x01, 0x2e, 0x58, 0x49, 0x5c};
+  const uint8_t test_vector_17_aad[] = {
+      0x68, 0xf2, 0xe7, 0x76, 0x96, 0xce, 0x7a, 0xe8, 0xe2, 0xca, 0x4e,
+      0xc5, 0x88, 0xe5, 0x41, 0x00, 0x2e, 0x58, 0x49, 0x5c, 0x08, 0x00,
+      0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+      0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+      0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+      0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x07};
+  const uint8_t test_vector_17_plaintext[1] = {};
+  const uint8_t test_vector_17_ciphertext_and_tag[] = {
+      0x07, 0x92, 0x2b, 0x8e, 0xbc, 0xf1, 0x0b, 0xb2,
+      0x29, 0x75, 0x88, 0xca, 0x4c, 0x61, 0x45, 0x23};
+  gsec_aead_malloc_test_vector(
+      &test_vector_17, test_vector_17_key,
+      sizeof(test_vector_17_key) / sizeof(uint8_t), test_vector_17_nonce,
+      sizeof(test_vector_17_nonce) / sizeof(uint8_t), test_vector_17_aad,
+      sizeof(test_vector_17_aad) / sizeof(uint8_t), test_vector_17_plaintext, 0,
+      test_vector_17_ciphertext_and_tag,
+      sizeof(test_vector_17_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_17);
+  gsec_aead_free_test_vector(test_vector_17);
+
+  /* 2.7.2 79-byte crypt */
+  gsec_aead_test_vector* test_vector_18;
+  const uint8_t test_vector_18_key[] = {
+      0x4c, 0x97, 0x3d, 0xbc, 0x73, 0x64, 0x62, 0x16, 0x74, 0xf8, 0xb5,
+      0xb8, 0x9e, 0x5c, 0x15, 0x51, 0x1f, 0xce, 0xd9, 0x21, 0x64, 0x90,
+      0xfb, 0x1c, 0x1a, 0x2c, 0xaa, 0x0f, 0xfe, 0x04, 0x07, 0xe5};
+  const uint8_t test_vector_18_nonce[] = {0x7a, 0xe8, 0xe2, 0xca, 0x4e, 0xc5,
+                                          0x00, 0x01, 0x2e, 0x58, 0x49, 0x5c};
+  const uint8_t test_vector_18_aad[] = {
+      0x68, 0xf2, 0xe7, 0x76, 0x96, 0xce, 0x7a, 0xe8, 0xe2, 0xca, 0x4e,
+      0xc5, 0x88, 0xe5, 0x41, 0x00, 0x2e, 0x58, 0x49, 0x5c, 0x08, 0x00,
+      0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+      0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+      0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+      0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+      0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x07};
+  const uint8_t test_vector_18_plaintext[1] = {};
+  const uint8_t test_vector_18_ciphertext_and_tag[] = {
+      0x00, 0xbd, 0xa1, 0xb7, 0xe8, 0x76, 0x08, 0xbc,
+      0xbf, 0x47, 0x0f, 0x12, 0x15, 0x7f, 0x4c, 0x07};
+  gsec_aead_malloc_test_vector(
+      &test_vector_18, test_vector_18_key,
+      sizeof(test_vector_18_key) / sizeof(uint8_t), test_vector_18_nonce,
+      sizeof(test_vector_18_nonce) / sizeof(uint8_t), test_vector_18_aad,
+      sizeof(test_vector_18_aad) / sizeof(uint8_t), test_vector_18_plaintext, 0,
+      test_vector_18_ciphertext_and_tag,
+      sizeof(test_vector_18_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_18);
+  gsec_aead_free_test_vector(test_vector_18);
+
+  /* 2.8.1 61-byte crypt */
+  gsec_aead_test_vector* test_vector_19;
+  const uint8_t test_vector_19_key[] = {0x88, 0xee, 0x08, 0x7f, 0xd9, 0x5d,
+                                        0xa9, 0xfb, 0xf6, 0x72, 0x5a, 0xa9,
+                                        0xd7, 0x57, 0xb0, 0xcd};
+  const uint8_t test_vector_19_nonce[] = {0x7a, 0xe8, 0xe2, 0xca, 0x4e, 0xc5,
+                                          0x00, 0x01, 0x2e, 0x58, 0x49, 0x5c};
+  const uint8_t test_vector_19_aad[] = {
+      0x68, 0xf2, 0xe7, 0x76, 0x96, 0xce, 0x7a, 0xe8, 0xe2, 0xca,
+      0x4e, 0xc5, 0x88, 0xe5, 0x4d, 0x00, 0x2e, 0x58, 0x49, 0x5c};
+  const uint8_t test_vector_19_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+      0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+      0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x08};
+  const uint8_t test_vector_19_ciphertext_and_tag[] = {
+      0xc3, 0x1f, 0x53, 0xd9, 0x9e, 0x56, 0x87, 0xf7, 0x36, 0x51, 0x19, 0xb8,
+      0x32, 0xd2, 0xaa, 0xe7, 0x07, 0x41, 0xd5, 0x93, 0xf1, 0xf9, 0xe2, 0xab,
+      0x34, 0x55, 0x77, 0x9b, 0x07, 0x8e, 0xb8, 0xfe, 0xac, 0xdf, 0xec, 0x1f,
+      0x8e, 0x3e, 0x52, 0x77, 0xf8, 0x18, 0x0b, 0x43, 0x36, 0x1f, 0x65, 0x12,
+      0xad, 0xb1, 0x6d, 0x2e, 0x38, 0x54, 0x8a, 0x2c, 0x71, 0x9d, 0xba, 0x72,
+      0x28, 0xd8, 0x40, 0x88, 0xf8, 0x75, 0x7a, 0xdb, 0x8a, 0xa7, 0x88, 0xd8,
+      0xf6, 0x5a, 0xd6, 0x68, 0xbe, 0x70, 0xe7};
+  gsec_aead_malloc_test_vector(
+      &test_vector_19, test_vector_19_key,
+      sizeof(test_vector_19_key) / sizeof(uint8_t), test_vector_19_nonce,
+      sizeof(test_vector_19_nonce) / sizeof(uint8_t), test_vector_19_aad,
+      sizeof(test_vector_19_aad) / sizeof(uint8_t), test_vector_19_plaintext,
+      sizeof(test_vector_19_plaintext) / sizeof(uint8_t),
+      test_vector_19_ciphertext_and_tag,
+      sizeof(test_vector_19_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_19);
+  gsec_aead_free_test_vector(test_vector_19);
+
+  /* 2.8.2 61-byte crypt */
+  gsec_aead_test_vector* test_vector_20;
+  const uint8_t test_vector_20_key[] = {
+      0x4c, 0x97, 0x3d, 0xbc, 0x73, 0x64, 0x62, 0x16, 0x74, 0xf8, 0xb5,
+      0xb8, 0x9e, 0x5c, 0x15, 0x51, 0x1f, 0xce, 0xd9, 0x21, 0x64, 0x90,
+      0xfb, 0x1c, 0x1a, 0x2c, 0xaa, 0x0f, 0xfe, 0x04, 0x07, 0xe5};
+  const uint8_t test_vector_20_nonce[] = {0x7a, 0xe8, 0xe2, 0xca, 0x4e, 0xc5,
+                                          0x00, 0x01, 0x2e, 0x58, 0x49, 0x5c};
+  const uint8_t test_vector_20_aad[] = {
+      0x68, 0xf2, 0xe7, 0x76, 0x96, 0xce, 0x7a, 0xe8, 0xe2, 0xca,
+      0x4e, 0xc5, 0x88, 0xe5, 0x4d, 0x00, 0x2e, 0x58, 0x49, 0x5c};
+  const uint8_t test_vector_20_plaintext[] = {
+      0x08, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+      0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+      0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+      0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+      0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x08};
+  const uint8_t test_vector_20_ciphertext_and_tag[] = {
+      0xba, 0x8a, 0xe3, 0x1b, 0xc5, 0x06, 0x48, 0x6d, 0x68, 0x73, 0xe4, 0xfc,
+      0xe4, 0x60, 0xe7, 0xdc, 0x57, 0x59, 0x1f, 0xf0, 0x06, 0x11, 0xf3, 0x1c,
+      0x38, 0x34, 0xfe, 0x1c, 0x04, 0xad, 0x80, 0xb6, 0x68, 0x03, 0xaf, 0xcf,
+      0x5b, 0x27, 0xe6, 0x33, 0x3f, 0xa6, 0x7c, 0x99, 0xda, 0x47, 0xc2, 0xf0,
+      0xce, 0xd6, 0x8d, 0x53, 0x1b, 0xd7, 0x41, 0xa9, 0x43, 0xcf, 0xf7, 0xa6,
+      0x71, 0x3b, 0xd0, 0x26, 0x11, 0xcd, 0x7d, 0xaa, 0x01, 0xd6, 0x1c, 0x5c,
+      0x88, 0x6d, 0xc1, 0xa8, 0x17, 0x01, 0x07};
+  gsec_aead_malloc_test_vector(
+      &test_vector_20, test_vector_20_key,
+      sizeof(test_vector_20_key) / sizeof(uint8_t), test_vector_20_nonce,
+      sizeof(test_vector_20_nonce) / sizeof(uint8_t), test_vector_20_aad,
+      sizeof(test_vector_20_aad) / sizeof(uint8_t), test_vector_20_plaintext,
+      sizeof(test_vector_20_plaintext) / sizeof(uint8_t),
+      test_vector_20_ciphertext_and_tag,
+      sizeof(test_vector_20_ciphertext_and_tag) / sizeof(uint8_t));
+  gsec_test_verify_crypter_on_test_vector(test_vector_20);
+  gsec_aead_free_test_vector(test_vector_20);
+}
+
+int main(int argc, char** argv) {
+  grpc_init();
+  gsec_test_do_generic_crypter_tests();
+  gsec_test_do_vector_tests_nist();
+  gsec_test_do_vector_tests_ieee();
+  gsec_test_do_vector_tests_rekey_nist();
+  gsec_test_do_vector_tests_rekey_ieee();
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/core/tsi/alts/crypt/gsec_test_util.cc b/test/core/tsi/alts/crypt/gsec_test_util.cc
new file mode 100644
index 0000000..992c91e
--- /dev/null
+++ b/test/core/tsi/alts/crypt/gsec_test_util.cc
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+#include <grpc/support/alloc.h>
+
+void gsec_test_random_bytes(uint8_t* bytes, size_t length) {
+  if (!RAND_bytes(bytes, static_cast<int>(length))) {
+    fprintf(stderr,
+            "Random bytes generation failed in gsec_test_random_bytes().");
+    abort();
+  }
+}
+
+void gsec_test_random_array(uint8_t** bytes, size_t length) {
+  if (bytes != nullptr) {
+    *bytes = static_cast<uint8_t*>(gpr_malloc(length));
+    gsec_test_random_bytes(*bytes, length);
+  } else {
+    fprintf(stderr, "bytes buffer is nullptr in gsec_test_random_array().");
+    abort();
+  }
+}
+
+uint32_t gsec_test_bias_random_uint32(uint32_t max_length) {
+  uint32_t value;
+  gsec_test_random_bytes((uint8_t*)(&value), sizeof(value));
+  return value % max_length;
+}
+
+void gsec_test_copy(const uint8_t* src, uint8_t** des, size_t source_len) {
+  if (src != nullptr && des != nullptr) {
+    *des = static_cast<uint8_t*>(gpr_malloc(source_len));
+    memcpy(*des, src, source_len);
+  } else {
+    fprintf(stderr, "Either src or des buffer is nullptr in gsec_test_copy().");
+    abort();
+  }
+}
+
+void gsec_test_copy_and_alter_random_byte(const uint8_t* src, uint8_t** des,
+                                          size_t source_len) {
+  if (src != nullptr && des != nullptr) {
+    *des = static_cast<uint8_t*>(gpr_malloc(source_len));
+    memcpy(*des, src, source_len);
+    uint32_t offset;
+    offset = gsec_test_bias_random_uint32(static_cast<uint32_t>(source_len));
+    (*(*des + offset))++;
+  } else {
+    fprintf(stderr,
+            "Either src or des is nullptr in "
+            "gsec_test_copy_and_alter_random_byte().");
+    abort();
+  }
+}
+
+int gsec_test_expect_compare_code_and_substr(grpc_status_code status1,
+                                             grpc_status_code status2,
+                                             const char* msg1,
+                                             const char* msg2) {
+  int failure = 1;
+  if (status1 != status2) {
+    fprintf(stderr, "Status %d does not equal %d.\n", status1, status2);
+    failure = 0;
+  }
+  if (strstr(msg1, msg2) == nullptr) {
+    fprintf(stderr, "Status message <%s> does not contain <%s>.\n", msg1, msg2);
+    failure = 0;
+  }
+  return failure;
+}
diff --git a/test/core/tsi/alts/crypt/gsec_test_util.h b/test/core/tsi/alts/crypt/gsec_test_util.h
new file mode 100644
index 0000000..206ae2f
--- /dev/null
+++ b/test/core/tsi/alts/crypt/gsec_test_util.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_TEST_CORE_TSI_ALTS_CRYPT_GSEC_TEST_UTIL_H_
+#define GRPC_TEST_CORE_TSI_ALTS_CRYPT_GSEC_TEST_UTIL_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/rand.h>
+
+#include <grpc/grpc.h>
+
+/**
+ * This method returns random bytes of certain length.
+ *
+ * - bytes: buffer to hold random bytes.
+ * - length: length of buffer to be populated.
+ */
+void gsec_test_random_bytes(uint8_t* bytes, size_t length);
+
+/**
+ * This method returns an array of random bytes.
+ *
+ * - bytes: array to hold random bytes.
+ * - length: length of array to be populated.
+ */
+void gsec_test_random_array(uint8_t** bytes, size_t length);
+
+/**
+ * This method returns a uint32 that's not quite uniformly random, but good
+ * enough for tests.
+ *
+ * - max_length: a max value the returned random number can choose.
+ */
+uint32_t gsec_test_bias_random_uint32(uint32_t max_length);
+
+/**
+ * This method copies data from a source to a destination buffer.
+ *
+ * - src: a source buffer.
+ * - des: a destination buffer.
+ * - length: the length of source buffer to be copied from its beginning.
+ */
+void gsec_test_copy(const uint8_t* src, uint8_t** des, size_t length);
+
+/**
+ * This method copies data from a source to a destination buffer, and flips one
+ * byte in the destination buffer randomly.
+ *
+ * - src: a source buffer.
+ * - des: a destination buffer.
+ * - length: the length of source buffer to be copied from its beginning.
+ */
+void gsec_test_copy_and_alter_random_byte(const uint8_t* src, uint8_t** des,
+                                          size_t source_len);
+
+/**
+ * This method compares two grpc_status_code values, and verifies if one string
+ * is a substring of the other.
+ *
+ * - status1: the first grpc_status_code to be compared.
+ * - status2: the second grpc_status_code to be compared.
+ * - msg1: a string to be scanned.
+ * - msg2: a small string to be searched within msg1.
+ *
+ * If both checks succeed, the method returns 1 and otherwise, it returns 0.
+ */
+int gsec_test_expect_compare_code_and_substr(grpc_status_code status1,
+                                             grpc_status_code status2,
+                                             const char* msg1,
+                                             const char* msg2);
+
+#endif  // GRPC_TEST_CORE_TSI_ALTS_CRYPT_GSEC_TEST_UTIL_H_ */
diff --git a/test/core/tsi/alts/frame_protector/BUILD b/test/core/tsi/alts/frame_protector/BUILD
new file mode 100644
index 0000000..8bbc8c6
--- /dev/null
+++ b/test/core/tsi/alts/frame_protector/BUILD
@@ -0,0 +1,60 @@
+# Copyright 2018 gRPC authors.
+# 
+# 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.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package")
+
+licenses(["notice"])  # Apache v2
+
+grpc_package(name = "frame_protector")
+
+grpc_cc_test(
+    name = "alts_counter_test",
+    srcs = ["alts_counter_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_crypter_test",
+    srcs = ["alts_crypter_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_frame_protector_test",
+    srcs = ["alts_frame_protector_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+        "//test/core/tsi:transport_security_test_lib",
+    ],
+)
+
+grpc_cc_test(
+    name = "frame_handler_test",
+    srcs = ["frame_handler_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+    ],
+)
diff --git a/test/core/tsi/alts/frame_protector/alts_counter_test.cc b/test/core/tsi/alts/frame_protector/alts_counter_test.cc
new file mode 100644
index 0000000..49ff821
--- /dev/null
+++ b/test/core/tsi/alts/frame_protector/alts_counter_test.cc
@@ -0,0 +1,180 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+const size_t kSmallCounterSize = 4;
+const size_t kSmallOverflowSize = 1;
+const size_t kGcmCounterSize = 12;
+const size_t kGcmOverflowSize = 5;
+
+static bool do_bytes_represent_client(alts_counter* ctr, unsigned char* counter,
+                                      size_t size) {
+  return (ctr->counter[size - 1] & 0x80) == 0x80;
+}
+
+static void alts_counter_test_input_sanity_check(size_t counter_size,
+                                                 size_t overflow_size) {
+  alts_counter* ctr = nullptr;
+  char* error_details = nullptr;
+
+  /* Input sanity check on alts_counter_create(). */
+  /* Invalid counter size. */
+  grpc_status_code status =
+      alts_counter_create(true, 0, overflow_size, &ctr, &error_details);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_details,
+      "counter_size is invalid."));
+  gpr_free(error_details);
+
+  /* Invalid overflow size. */
+  status = alts_counter_create(true, counter_size, 0, &ctr, &error_details);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_details,
+      "overflow_size is invalid."));
+  gpr_free(error_details);
+
+  /* alts_counter is nullptr. */
+  status = alts_counter_create(true, counter_size, overflow_size, nullptr,
+                               &error_details);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_details,
+      "crypter_counter is nullptr."));
+  gpr_free(error_details);
+
+  status = alts_counter_create(true, counter_size, overflow_size, &ctr,
+                               &error_details);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+
+  /* Input sanity check on alts_counter_increment(). */
+  /* crypter_counter is nullptr. */
+  bool is_overflow = false;
+  status = alts_counter_increment(nullptr, &is_overflow, &error_details);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_details,
+      "crypter_counter is nullptr."));
+  gpr_free(error_details);
+  /* is_overflow is nullptr. */
+  status = alts_counter_increment(ctr, nullptr, &error_details);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_details,
+      "is_overflow is nullptr."));
+  gpr_free(error_details);
+  alts_counter_destroy(ctr);
+}
+
+static void alts_counter_test_overflow_full_range(bool is_client,
+                                                  size_t counter_size,
+                                                  size_t overflow_size) {
+  alts_counter* ctr = nullptr;
+  char* error_details = nullptr;
+  grpc_status_code status = alts_counter_create(
+      is_client, counter_size, overflow_size, &ctr, &error_details);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  unsigned char* expected =
+      static_cast<unsigned char*>(gpr_zalloc(counter_size));
+  if (is_client) {
+    expected[counter_size - 1] = 0x80;
+  }
+  /* Do a single iteration to ensure the counter is initialized as expected. */
+  GPR_ASSERT(do_bytes_represent_client(ctr, alts_counter_get_counter(ctr),
+                                       counter_size) == is_client);
+  GPR_ASSERT(memcmp(alts_counter_get_counter(ctr), expected, counter_size) ==
+             0);
+  bool is_overflow = false;
+  GPR_ASSERT(alts_counter_increment(ctr, &is_overflow, &error_details) ==
+             GRPC_STATUS_OK);
+  GPR_ASSERT(!is_overflow);
+  /**
+   * The counter can return 2^{overflow_size * 8} counters. The
+   * high-order bit is fixed to the client/server. The last call will yield a
+   * useable counter, but overflow the counter object.
+   */
+  int iterations = 1 << (overflow_size * 8);
+  int ind = 1;
+  for (ind = 1; ind < iterations - 1; ind++) {
+    GPR_ASSERT(do_bytes_represent_client(ctr, alts_counter_get_counter(ctr),
+                                         counter_size) == is_client);
+    GPR_ASSERT(alts_counter_increment(ctr, &is_overflow, &error_details) ==
+               GRPC_STATUS_OK);
+    GPR_ASSERT(!is_overflow);
+  }
+  GPR_ASSERT(do_bytes_represent_client(ctr, alts_counter_get_counter(ctr),
+                                       counter_size) == is_client);
+  GPR_ASSERT(alts_counter_increment(ctr, &is_overflow, &error_details) ==
+             GRPC_STATUS_FAILED_PRECONDITION);
+  GPR_ASSERT(is_overflow);
+  gpr_free(expected);
+  alts_counter_destroy(ctr);
+}
+
+/* Set the counter manually and make sure it overflows as expected. */
+static void alts_counter_test_overflow_single_increment(bool is_client,
+                                                        size_t counter_size,
+                                                        size_t overflow_size) {
+  alts_counter* ctr = nullptr;
+  char* error_details = nullptr;
+  grpc_status_code status = alts_counter_create(
+      is_client, counter_size, overflow_size, &ctr, &error_details);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  unsigned char* expected =
+      static_cast<unsigned char*>(gpr_zalloc(counter_size));
+  memset(expected, 0xFF, overflow_size);
+  expected[0] = 0xFE;
+
+  if (is_client) {
+    expected[counter_size - 1] = 0x80;
+  }
+  memcpy(ctr->counter, expected, counter_size);
+  GPR_ASSERT(do_bytes_represent_client(ctr, alts_counter_get_counter(ctr),
+                                       counter_size) == is_client);
+  GPR_ASSERT(memcmp(expected, alts_counter_get_counter(ctr), counter_size) ==
+             0);
+  bool is_overflow = false;
+  GPR_ASSERT(alts_counter_increment(ctr, &is_overflow, &error_details) ==
+             GRPC_STATUS_OK);
+  GPR_ASSERT(!is_overflow);
+  GPR_ASSERT(do_bytes_represent_client(ctr, alts_counter_get_counter(ctr),
+                                       counter_size) == is_client);
+  expected[0] = static_cast<unsigned char>(expected[0] + 1);
+  GPR_ASSERT(memcmp(expected, alts_counter_get_counter(ctr), counter_size) ==
+             0);
+  GPR_ASSERT(alts_counter_increment(ctr, &is_overflow, &error_details) ==
+             GRPC_STATUS_FAILED_PRECONDITION);
+  GPR_ASSERT(is_overflow);
+  gpr_free(expected);
+  alts_counter_destroy(ctr);
+}
+
+int main(int argc, char** argv) {
+  alts_counter_test_input_sanity_check(kGcmCounterSize, kGcmOverflowSize);
+  alts_counter_test_overflow_full_range(true, kSmallCounterSize,
+                                        kSmallOverflowSize);
+  alts_counter_test_overflow_full_range(false, kSmallCounterSize,
+                                        kSmallOverflowSize);
+  alts_counter_test_overflow_single_increment(true, kGcmCounterSize,
+                                              kGcmOverflowSize);
+  alts_counter_test_overflow_single_increment(false, kGcmCounterSize,
+                                              kGcmOverflowSize);
+
+  return 0;
+}
diff --git a/test/core/tsi/alts/frame_protector/alts_crypter_test.cc b/test/core/tsi/alts/frame_protector/alts_crypter_test.cc
new file mode 100644
index 0000000..0ad616b
--- /dev/null
+++ b/test/core/tsi/alts/frame_protector/alts_crypter_test.cc
@@ -0,0 +1,493 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+static void alts_crypter_test_random_seal_unseal(alts_crypter* server_seal,
+                                                 alts_crypter* server_unseal,
+                                                 alts_crypter* client_seal,
+                                                 alts_crypter* client_unseal) {
+  size_t data_size = gsec_test_bias_random_uint32(1024) + 1;
+  size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server_seal);
+  size_t protected_data_size = data_size + num_overhead_bytes;
+  uint8_t* data_buffer = static_cast<uint8_t*>(gpr_malloc(protected_data_size));
+  gsec_test_random_bytes(data_buffer, data_size);
+  uint8_t* duplicate_buffer = nullptr;
+  gsec_test_copy(data_buffer, &duplicate_buffer, data_size);
+
+  /* Client seal and server unseal */
+  size_t size = data_size;
+  grpc_status_code status = alts_crypter_process_in_place(
+      client_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+  status = alts_crypter_process_in_place(
+      server_unseal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(data_buffer, duplicate_buffer, data_size) == 0);
+  GPR_ASSERT(size == data_size);
+  /* Server seal and client unseal */
+  status = alts_crypter_process_in_place(
+      server_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+  status = alts_crypter_process_in_place(
+      client_unseal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(data_buffer, duplicate_buffer, data_size) == 0);
+  GPR_ASSERT(size == data_size);
+  gpr_free(data_buffer);
+  gpr_free(duplicate_buffer);
+}
+
+static void alts_crypter_test_multiple_random_seal_unseal(
+    alts_crypter* server_seal, alts_crypter* server_unseal,
+    alts_crypter* client_seal, alts_crypter* client_unseal) {
+  size_t data_size = gsec_test_bias_random_uint32(1024) + 1;
+  size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server_seal);
+  size_t protected_data_size = data_size + num_overhead_bytes;
+
+  uint8_t* data_buffer1 =
+      static_cast<uint8_t*>(gpr_malloc(protected_data_size));
+  uint8_t* data_buffer2 =
+      static_cast<uint8_t*>(gpr_malloc(protected_data_size));
+  uint8_t* duplicate_buffer1 = nullptr;
+  uint8_t* duplicate_buffer2 = nullptr;
+  gsec_test_random_bytes(data_buffer1, data_size);
+  gsec_test_random_bytes(data_buffer2, data_size);
+  gsec_test_copy(data_buffer1, &duplicate_buffer1, data_size);
+  gsec_test_copy(data_buffer2, &duplicate_buffer2, data_size);
+
+  /* Client seal and server unseal */
+  size_t size1 = data_size, size2 = data_size;
+  grpc_status_code status = alts_crypter_process_in_place(
+      client_seal, data_buffer1, protected_data_size, size1, &size1, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size1 == protected_data_size);
+  status = alts_crypter_process_in_place(
+      client_seal, data_buffer2, protected_data_size, size2, &size2, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size2 == protected_data_size);
+  status = alts_crypter_process_in_place(
+      server_unseal, data_buffer1, protected_data_size, size1, &size1, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(data_buffer1, duplicate_buffer1, data_size) == 0);
+  GPR_ASSERT(size1 == data_size);
+  status = alts_crypter_process_in_place(
+      server_unseal, data_buffer2, protected_data_size, size2, &size2, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(data_buffer2, duplicate_buffer2, data_size) == 0);
+  GPR_ASSERT(size2 == data_size);
+
+  /* Server seal and client unseal */
+  status = alts_crypter_process_in_place(
+      server_seal, data_buffer1, protected_data_size, size1, &size1, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size1 == protected_data_size);
+  status = alts_crypter_process_in_place(
+      server_seal, data_buffer2, protected_data_size, size2, &size2, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size2 == protected_data_size);
+  status = alts_crypter_process_in_place(
+      client_unseal, data_buffer1, protected_data_size, size1, &size1, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(data_buffer1, duplicate_buffer1, data_size) == 0);
+  GPR_ASSERT(size1 == data_size);
+  status = alts_crypter_process_in_place(
+      client_unseal, data_buffer2, protected_data_size, size2, &size2, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(data_buffer2, duplicate_buffer2, data_size) == 0);
+  GPR_ASSERT(size2 == data_size);
+
+  gpr_free(data_buffer1);
+  gpr_free(data_buffer2);
+  gpr_free(duplicate_buffer1);
+  gpr_free(duplicate_buffer2);
+}
+
+static void alts_crypter_test_corrupted_unseal(alts_crypter* server_seal,
+                                               alts_crypter* server_unseal,
+                                               alts_crypter* client_seal,
+                                               alts_crypter* client_unseal) {
+  size_t data_size = gsec_test_bias_random_uint32(1024) + 1;
+  size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server_seal);
+  size_t protected_data_size = data_size + num_overhead_bytes;
+  auto* data_buffer = static_cast<uint8_t*>(gpr_malloc(protected_data_size));
+  auto* zero_buffer = static_cast<uint8_t*>(gpr_zalloc(data_size));
+
+  /* Corrupt a random byte in protected data. */
+  size_t size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  grpc_status_code status = alts_crypter_process_in_place(
+      client_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+  uint8_t* corrupted_data_buffer;
+  char* error_message = nullptr;
+  gsec_test_copy_and_alter_random_byte(data_buffer, &corrupted_data_buffer,
+                                       protected_data_size);
+  status = alts_crypter_process_in_place(server_unseal, corrupted_data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Checking tag failed"));
+  GPR_ASSERT(memcmp(corrupted_data_buffer, zero_buffer, data_size) == 0);
+  gpr_free(corrupted_data_buffer);
+  gpr_free(error_message);
+
+  /* Corrupt the beginning of protected data. */
+  size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  status = alts_crypter_process_in_place(
+      client_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+  gsec_test_copy(data_buffer, &corrupted_data_buffer, protected_data_size);
+  (*corrupted_data_buffer)++;
+  status = alts_crypter_process_in_place(server_unseal, corrupted_data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Checking tag failed"));
+  GPR_ASSERT(memcmp(corrupted_data_buffer, zero_buffer, data_size) == 0);
+  gpr_free(corrupted_data_buffer);
+  gpr_free(error_message);
+
+  /* Corrupt the end of protected data. */
+  size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  status = alts_crypter_process_in_place(
+      client_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+  gsec_test_copy(data_buffer, &corrupted_data_buffer, protected_data_size);
+  (*(corrupted_data_buffer + protected_data_size - 1))++;
+  status = alts_crypter_process_in_place(server_unseal, corrupted_data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Checking tag failed"));
+  GPR_ASSERT(memcmp(corrupted_data_buffer, zero_buffer, data_size) == 0);
+  gpr_free(corrupted_data_buffer);
+  gpr_free(error_message);
+
+  gpr_free(data_buffer);
+  gpr_free(zero_buffer);
+}
+
+static void alts_crypter_test_unsync_seal_unseal(alts_crypter* server_seal,
+                                                 alts_crypter* server_unseal,
+                                                 alts_crypter* client_seal,
+                                                 alts_crypter* client_unseal) {
+  size_t data_size = gsec_test_bias_random_uint32(1024) + 1;
+  size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server_seal);
+  size_t protected_data_size = data_size + num_overhead_bytes;
+  auto* data_buffer = static_cast<uint8_t*>(gpr_malloc(protected_data_size));
+  auto* zero_buffer = static_cast<uint8_t*>(gpr_zalloc(data_size));
+
+  /* Perform two seals at client, one unseal at server. */
+  size_t size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  grpc_status_code status = alts_crypter_process_in_place(
+      client_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+
+  size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  status = alts_crypter_process_in_place(
+      client_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+
+  char* error_message = nullptr;
+  status = alts_crypter_process_in_place(server_unseal, data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Checking tag failed"));
+  GPR_ASSERT(memcmp(data_buffer, zero_buffer, data_size) == 0);
+  gpr_free(error_message);
+
+  /* Perform two seals at server, one unseal at client. */
+  size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  status = alts_crypter_process_in_place(
+      server_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+
+  size = data_size;
+  gsec_test_random_bytes(data_buffer, data_size);
+  status = alts_crypter_process_in_place(
+      server_seal, data_buffer, protected_data_size, size, &size, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(size == protected_data_size);
+
+  status = alts_crypter_process_in_place(client_unseal, data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Checking tag failed"));
+  GPR_ASSERT(memcmp(data_buffer, zero_buffer, data_size) == 0);
+  gpr_free(error_message);
+  gpr_free(data_buffer);
+  gpr_free(zero_buffer);
+}
+
+static void alts_crypter_test_input_sanity_check(alts_crypter* crypter_seal,
+                                                 alts_crypter* crypter_unseal) {
+  size_t data_size = gsec_test_bias_random_uint32(1024) + 1;
+  size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(crypter_seal);
+  size_t protected_data_size = data_size + num_overhead_bytes;
+  auto* data_buffer = static_cast<uint8_t*>(gpr_malloc(protected_data_size));
+  gsec_test_random_bytes(data_buffer, data_size);
+  char* error_message = nullptr;
+  size_t size = data_size;
+
+  /* Crypter is nullptr. */
+  grpc_status_code status = alts_crypter_process_in_place(
+      nullptr, data_buffer, protected_data_size, size, &size, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "crypter or crypter->vtable has not been initialized properly."));
+  gpr_free(error_message);
+
+  /* Seal data is nullptr. */
+  size = data_size;
+  status = alts_crypter_process_in_place(
+      crypter_seal, nullptr, protected_data_size, size, &size, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message, "data is nullptr."));
+  gpr_free(error_message);
+
+  /* Seal data size is 0. */
+  size = 0;
+  status = alts_crypter_process_in_place(crypter_seal, data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "data_size is zero."));
+  gpr_free(error_message);
+
+  /* Seal data buffer has a size smaller than the required. */
+  size = data_size;
+  status = alts_crypter_process_in_place(crypter_seal, data_buffer,
+                                         protected_data_size - 1, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "data_allocated_size is smaller than sum of data_size and "
+      "num_overhead_bytes."));
+  gpr_free(error_message);
+
+  /* Unseal data is nullptr. */
+  size = data_size;
+  status = alts_crypter_process_in_place(crypter_unseal, nullptr,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message, "data is nullptr."));
+  gpr_free(error_message);
+
+  /* Unseal data size is 0. */
+  size = 0;
+  status = alts_crypter_process_in_place(crypter_unseal, data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "data_size is smaller than num_overhead_bytes."));
+  gpr_free(error_message);
+
+  /* Unseal data size is smaller than number of overhead bytes. */
+  size = num_overhead_bytes - 1;
+  status = alts_crypter_process_in_place(crypter_unseal, data_buffer,
+                                         protected_data_size, size, &size,
+                                         &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "data_size is smaller than num_overhead_bytes."));
+  gpr_free(error_message);
+  gpr_free(data_buffer);
+}
+
+static void create_random_alts_seal_crypter(
+    alts_crypter** server_seal, alts_crypter** server_unseal,
+    alts_crypter** client_seal, alts_crypter** client_unseal,
+    gsec_aead_crypter** server_crypter_seal,
+    gsec_aead_crypter** server_crypter_unseal,
+    gsec_aead_crypter** client_crypter_seal,
+    gsec_aead_crypter** client_crypter_unseal, bool rekey) {
+  size_t key_length = rekey ? kAes128GcmRekeyKeyLength : kAes128GcmKeyLength;
+  uint8_t* key;
+  gsec_test_random_array(&key, key_length);
+  gsec_aes_gcm_aead_crypter_create(key, key_length, kAesGcmNonceLength,
+                                   kAesGcmTagLength, rekey, server_crypter_seal,
+                                   nullptr);
+  gsec_aes_gcm_aead_crypter_create(key, key_length, kAesGcmNonceLength,
+                                   kAesGcmTagLength, rekey,
+                                   server_crypter_unseal, nullptr);
+  gsec_aes_gcm_aead_crypter_create(key, key_length, kAesGcmNonceLength,
+                                   kAesGcmTagLength, rekey, client_crypter_seal,
+                                   nullptr);
+  gsec_aes_gcm_aead_crypter_create(key, key_length, kAesGcmNonceLength,
+                                   kAesGcmTagLength, rekey,
+                                   client_crypter_unseal, nullptr);
+
+  size_t overflow_size = rekey ? 8 : 5;
+  alts_seal_crypter_create(*client_crypter_seal, /*is_client=*/true,
+                           overflow_size, client_seal, nullptr);
+  alts_unseal_crypter_create(*client_crypter_unseal, /*is_client=*/true,
+                             overflow_size, client_unseal, nullptr);
+  alts_seal_crypter_create(*server_crypter_seal, /*is_client=*/false,
+                           overflow_size, server_seal, nullptr);
+  alts_unseal_crypter_create(*server_crypter_unseal, /*is_client=*/false,
+                             overflow_size, server_unseal, nullptr);
+  gpr_free(key);
+}
+
+static void destroy_random_alts_seal_crypter(alts_crypter* server_seal,
+                                             alts_crypter* server_unseal,
+                                             alts_crypter* client_seal,
+                                             alts_crypter* client_unseal) {
+  alts_crypter_destroy(server_seal);
+  alts_crypter_destroy(server_unseal);
+  alts_crypter_destroy(client_seal);
+  alts_crypter_destroy(client_unseal);
+}
+
+static void alts_crypter_do_generic_tests() {
+  alts_crypter *server_seal = nullptr, *server_unseal = nullptr,
+               *client_seal = nullptr, *client_unseal = nullptr;
+  gsec_aead_crypter *server_crypter_seal = nullptr,
+                    *server_crypter_unseal = nullptr,
+                    *client_crypter_seal = nullptr,
+                    *client_crypter_unseal = nullptr;
+  /* Random seal and unseal tests */
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/false);
+  alts_crypter_test_random_seal_unseal(server_seal, server_unseal, client_seal,
+                                       client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/true);
+  alts_crypter_test_random_seal_unseal(server_seal, server_unseal, client_seal,
+                                       client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  /* Multiple random seal and unseal tests */
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/false);
+  alts_crypter_test_multiple_random_seal_unseal(server_seal, server_unseal,
+                                                client_seal, client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/true);
+  alts_crypter_test_multiple_random_seal_unseal(server_seal, server_unseal,
+                                                client_seal, client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  /* Corrupted unseal tests */
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/false);
+  alts_crypter_test_corrupted_unseal(server_seal, server_unseal, client_seal,
+                                     client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/true);
+  alts_crypter_test_corrupted_unseal(server_seal, server_unseal, client_seal,
+                                     client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  /* Unsync seal and unseal tests */
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/false);
+  alts_crypter_test_unsync_seal_unseal(server_seal, server_unseal, client_seal,
+                                       client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/true);
+  alts_crypter_test_unsync_seal_unseal(server_seal, server_unseal, client_seal,
+                                       client_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  /* Input sanity check tests */
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/false);
+  alts_crypter_test_input_sanity_check(server_seal, server_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+
+  create_random_alts_seal_crypter(&server_seal, &server_unseal, &client_seal,
+                                  &client_unseal, &server_crypter_seal,
+                                  &server_crypter_unseal, &client_crypter_seal,
+                                  &client_crypter_unseal, /*rekey=*/true);
+  alts_crypter_test_input_sanity_check(server_seal, server_unseal);
+  destroy_random_alts_seal_crypter(server_seal, server_unseal, client_seal,
+                                   client_unseal);
+}
+
+int main(int argc, char** argv) {
+  alts_crypter_do_generic_tests();
+  return 0;
+}
diff --git a/test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc b/test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc
new file mode 100644
index 0000000..2bd4958
--- /dev/null
+++ b/test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc
@@ -0,0 +1,394 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
+#include "src/core/tsi/transport_security_interface.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+#include "test/core/tsi/transport_security_test_lib.h"
+
+const size_t kChannelSize = 32768;
+
+static void alts_test_do_round_trip_check_frames(
+    tsi_test_frame_protector_fixture* fixture, const uint8_t* key,
+    const size_t key_size, bool rekey, const uint8_t* client_message,
+    const size_t client_message_size, const uint8_t* client_expected_frames,
+    const size_t client_frame_size, const uint8_t* server_message,
+    const size_t server_message_size, const uint8_t* server_expected_frames,
+    const size_t server_frame_size) {
+  GPR_ASSERT(fixture != nullptr);
+  GPR_ASSERT(fixture->config != nullptr);
+  tsi_frame_protector* client_frame_protector = nullptr;
+  tsi_frame_protector* server_frame_protector = nullptr;
+  tsi_test_frame_protector_config* config = fixture->config;
+  tsi_test_channel* channel = fixture->channel;
+  /* Create a client frame protector. */
+  size_t client_max_output_protected_frame_size =
+      config->client_max_output_protected_frame_size;
+  GPR_ASSERT(
+      alts_create_frame_protector(key, key_size, /*is_client=*/true, rekey,
+                                  client_max_output_protected_frame_size == 0
+                                      ? nullptr
+                                      : &client_max_output_protected_frame_size,
+                                  &client_frame_protector) == TSI_OK);
+  /* Create a server frame protector. */
+  size_t server_max_output_protected_frame_size =
+      config->server_max_output_protected_frame_size;
+  GPR_ASSERT(
+      alts_create_frame_protector(key, key_size, /*is_client=*/false, rekey,
+                                  server_max_output_protected_frame_size == 0
+                                      ? nullptr
+                                      : &server_max_output_protected_frame_size,
+                                  &server_frame_protector) == TSI_OK);
+  tsi_test_frame_protector_fixture_init(fixture, client_frame_protector,
+                                        server_frame_protector);
+  /* Client sends a message to server. */
+  uint8_t* saved_client_message = config->client_message;
+  config->client_message = const_cast<uint8_t*>(client_message);
+  config->client_message_size = client_message_size;
+  tsi_test_frame_protector_send_message_to_peer(config, channel,
+                                                client_frame_protector,
+                                                /*is_client=*/true);
+  /* Verify if the generated frame is the same as the expected. */
+  GPR_ASSERT(channel->bytes_written_to_server_channel == client_frame_size);
+  GPR_ASSERT(memcmp(client_expected_frames, channel->server_channel,
+                    client_frame_size) == 0);
+  unsigned char* server_received_message =
+      static_cast<unsigned char*>(gpr_malloc(kChannelSize));
+  size_t server_received_message_size = 0;
+  tsi_test_frame_protector_receive_message_from_peer(
+      config, channel, server_frame_protector, server_received_message,
+      &server_received_message_size, /*is_client=*/false);
+  GPR_ASSERT(config->client_message_size == server_received_message_size);
+  GPR_ASSERT(memcmp(config->client_message, server_received_message,
+                    server_received_message_size) == 0);
+  /* Server sends a message to client. */
+  uint8_t* saved_server_message = config->server_message;
+  config->server_message = const_cast<uint8_t*>(server_message);
+  config->server_message_size = server_message_size;
+  tsi_test_frame_protector_send_message_to_peer(config, channel,
+                                                server_frame_protector,
+                                                /*is_client=*/false);
+  /* Verify if the generated frame is the same as the expected. */
+  GPR_ASSERT(channel->bytes_written_to_client_channel == server_frame_size);
+  GPR_ASSERT(memcmp(server_expected_frames, channel->client_channel,
+                    server_frame_size) == 0);
+  unsigned char* client_received_message =
+      static_cast<unsigned char*>(gpr_malloc(kChannelSize));
+  size_t client_received_message_size = 0;
+  tsi_test_frame_protector_receive_message_from_peer(
+      config, channel, client_frame_protector, client_received_message,
+      &client_received_message_size,
+      /*is_client=*/true);
+  GPR_ASSERT(config->server_message_size == client_received_message_size);
+  GPR_ASSERT(memcmp(config->server_message, client_received_message,
+                    client_received_message_size) == 0);
+  config->client_message = saved_client_message;
+  config->server_message = saved_server_message;
+  /* Destroy server and client frame protectors. */
+  gpr_free(server_received_message);
+  gpr_free(client_received_message);
+}
+
+static void alts_test_do_round_trip_vector_tests() {
+  const uint8_t key[] = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+                         0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08};
+  const char small_message[] = {'C', 'h', 'a', 'p', 'i', ' ',
+                                'C', 'h', 'a', 'p', 'o'};
+  const uint8_t large_message[] = {
+      0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5,
+      0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+      0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+      0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+      0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+      0x1a, 0xaf, 0xd2, 0x55, 0xd6, 0x09, 0xb1, 0xf0, 0x56, 0x63, 0x7a, 0x0d,
+      0x46, 0xdf, 0x99, 0x8d, 0x88, 0xe5, 0x22, 0x2a, 0xb2, 0xc2, 0x84, 0x65,
+      0x12, 0x15, 0x35, 0x24, 0xc0, 0x89, 0x5e, 0x81, 0x08, 0x06, 0x0f, 0x10,
+      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+      0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+      0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30};
+  const size_t small_message_size = sizeof(small_message) / sizeof(uint8_t);
+  const size_t large_message_size = sizeof(large_message) / sizeof(uint8_t);
+  /* Test small client message and large server message. */
+  const uint8_t client_expected_frame1[] = {
+      0x1f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0xd8, 0xd5, 0x92,
+      0x4d, 0x50, 0x32, 0xb7, 0x1f, 0xb8, 0xf2, 0xbb, 0x43, 0xc7, 0xe2, 0x94,
+      0x3d, 0x3e, 0x9a, 0x78, 0x76, 0xaa, 0x0a, 0x6b, 0xfa, 0x98, 0x3a};
+  const uint8_t server_expected_frame1[] = {
+      0x94, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4b, 0xf8, 0xc8,
+      0xe7, 0x8f, 0x1a, 0x26, 0x37, 0x44, 0xa2, 0x5c, 0x55, 0x94, 0x30, 0x4e,
+      0x3e, 0x16, 0xe7, 0x9e, 0x96, 0xe8, 0x1b, 0xc0, 0xdd, 0x52, 0x30, 0x06,
+      0xc2, 0x72, 0x9a, 0xa1, 0x0b, 0xdb, 0xdc, 0x19, 0x8c, 0x93, 0x5e, 0x84,
+      0x1f, 0x4b, 0x97, 0x26, 0xf0, 0x73, 0x85, 0x59, 0x00, 0x95, 0xc1, 0xc5,
+      0x22, 0x2f, 0x70, 0x85, 0x68, 0x2c, 0x4f, 0xfe, 0x30, 0x26, 0x91, 0xde,
+      0x62, 0x55, 0x1d, 0x35, 0x01, 0x96, 0x1c, 0xe7, 0xa2, 0x8b, 0x14, 0x8a,
+      0x5e, 0x1b, 0x4a, 0x3b, 0x4f, 0x65, 0x0f, 0xca, 0x79, 0x10, 0xb4, 0xdd,
+      0xf7, 0xa4, 0x8b, 0x64, 0x2f, 0x00, 0x39, 0x60, 0x03, 0xfc, 0xe1, 0x8b,
+      0x5c, 0x19, 0xba, 0xcc, 0x46, 0xba, 0x88, 0xdd, 0x40, 0x42, 0x27, 0x4f,
+      0xe4, 0x1a, 0x6a, 0x31, 0x6c, 0x1c, 0xb0, 0xb6, 0x5c, 0x3e, 0xca, 0x84,
+      0x9b, 0x5f, 0x04, 0x84, 0x11, 0xa9, 0xf8, 0x39, 0xe7, 0xe7, 0xc5, 0xc4,
+      0x33, 0x9f, 0x63, 0x21, 0x9a, 0x7c, 0x9c, 0x64};
+  const size_t client_frame_size1 =
+      sizeof(client_expected_frame1) / sizeof(uint8_t);
+  const size_t server_frame_size1 =
+      sizeof(server_expected_frame1) / sizeof(uint8_t);
+  tsi_test_frame_protector_fixture* fixture =
+      tsi_test_frame_protector_fixture_create();
+  alts_test_do_round_trip_check_frames(
+      fixture, key, kAes128GcmKeyLength, /*rekey=*/false,
+      reinterpret_cast<const uint8_t*>(small_message), small_message_size,
+      client_expected_frame1, client_frame_size1, large_message,
+      large_message_size, server_expected_frame1, server_frame_size1);
+  tsi_test_frame_protector_fixture_destroy(fixture);
+  /**
+   * Test large client message, small server message, and small
+   * message_buffer_allocated_size.
+   */
+  const uint8_t client_expected_frame2[] = {
+      0x94, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x81, 0x86, 0xc7,
+      0xdc, 0xf4, 0x77, 0x3a, 0xdb, 0x91, 0x94, 0x61, 0xba, 0xed, 0xd5, 0x37,
+      0x47, 0x53, 0x0c, 0xe1, 0xbf, 0x59, 0x23, 0x20, 0xde, 0x8b, 0x25, 0x13,
+      0x72, 0xe7, 0x8a, 0x4f, 0x32, 0x61, 0xc6, 0xda, 0xc3, 0xe9, 0xff, 0x31,
+      0x33, 0x53, 0x4a, 0xf8, 0xc9, 0x98, 0xe4, 0x19, 0x71, 0x9c, 0x5e, 0x72,
+      0xc7, 0x35, 0x97, 0x78, 0x30, 0xf2, 0xc4, 0xd1, 0x53, 0xd5, 0x6e, 0x8f,
+      0x4f, 0xd9, 0x28, 0x5a, 0xfd, 0x22, 0x57, 0x7f, 0x95, 0xb4, 0x8a, 0x5e,
+      0x7c, 0x47, 0xa8, 0xcf, 0x64, 0x3d, 0x83, 0xa5, 0xcf, 0xc3, 0xfe, 0x54,
+      0xc2, 0x6a, 0x40, 0xc4, 0xfb, 0x8e, 0x07, 0x77, 0x70, 0x8f, 0x99, 0x94,
+      0xb1, 0xd5, 0xa7, 0xf9, 0x0d, 0xc7, 0x11, 0xc5, 0x6f, 0x4a, 0x4f, 0x56,
+      0xd5, 0xe2, 0x9c, 0xbb, 0x95, 0x7a, 0xd0, 0x9f, 0x30, 0x54, 0xca, 0x6d,
+      0x5c, 0x8e, 0x83, 0xa0, 0x04, 0x5e, 0xd0, 0x22, 0x8c, 0x2a, 0x7f, 0xdb,
+      0xfe, 0xb3, 0x2e, 0xae, 0x22, 0xe6, 0xf4, 0xb7};
+  const uint8_t server_expected_frame2[] = {
+      0x1f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x12, 0xab, 0x9d,
+      0x76, 0x2b, 0x5f, 0xab, 0xf3, 0x6d, 0xc4, 0xaa, 0xe5, 0x1e, 0x63, 0xc1,
+      0x7b, 0x7b, 0x10, 0xd5, 0x63, 0x0f, 0x29, 0xad, 0x17, 0x33, 0x73};
+  const size_t client_frame_size2 =
+      sizeof(client_expected_frame2) / sizeof(uint8_t);
+  const size_t server_frame_size2 =
+      sizeof(server_expected_frame2) / sizeof(uint8_t);
+  fixture = tsi_test_frame_protector_fixture_create();
+  alts_test_do_round_trip_check_frames(
+      fixture, key, kAes128GcmKeyLength, /*rekey=*/false, large_message,
+      large_message_size, client_expected_frame2, client_frame_size2,
+      reinterpret_cast<const uint8_t*>(small_message), small_message_size,
+      server_expected_frame2, server_frame_size2);
+  tsi_test_frame_protector_fixture_destroy(fixture);
+  /**
+   * Test large client message, small server message, and small
+   * protected_buffer_size.
+   */
+  const uint8_t client_expected_frame3[] = {
+      0x94, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x81, 0x86, 0xc7,
+      0xdc, 0xf4, 0x77, 0x3a, 0xdb, 0x91, 0x94, 0x61, 0xba, 0xed, 0xd5, 0x37,
+      0x47, 0x53, 0x0c, 0xe1, 0xbf, 0x59, 0x23, 0x20, 0xde, 0x8b, 0x25, 0x13,
+      0x72, 0xe7, 0x8a, 0x4f, 0x32, 0x61, 0xc6, 0xda, 0xc3, 0xe9, 0xff, 0x31,
+      0x33, 0x53, 0x4a, 0xf8, 0xc9, 0x98, 0xe4, 0x19, 0x71, 0x9c, 0x5e, 0x72,
+      0xc7, 0x35, 0x97, 0x78, 0x30, 0xf2, 0xc4, 0xd1, 0x53, 0xd5, 0x6e, 0x8f,
+      0x4f, 0xd9, 0x28, 0x5a, 0xfd, 0x22, 0x57, 0x7f, 0x95, 0xb4, 0x8a, 0x5e,
+      0x7c, 0x47, 0xa8, 0xcf, 0x64, 0x3d, 0x83, 0xa5, 0xcf, 0xc3, 0xfe, 0x54,
+      0xc2, 0x6a, 0x40, 0xc4, 0xfb, 0x8e, 0x07, 0x77, 0x70, 0x8f, 0x99, 0x94,
+      0xb1, 0xd5, 0xa7, 0xf9, 0x0d, 0xc7, 0x11, 0xc5, 0x6f, 0x4a, 0x4f, 0x56,
+      0xd5, 0xe2, 0x9c, 0xbb, 0x95, 0x7a, 0xd0, 0x9f, 0x30, 0x54, 0xca, 0x6d,
+      0x5c, 0x8e, 0x83, 0xa0, 0x04, 0x5e, 0xd0, 0x22, 0x8c, 0x2a, 0x7f, 0xdb,
+      0xfe, 0xb3, 0x2e, 0xae, 0x22, 0xe6, 0xf4, 0xb7};
+  const uint8_t server_expected_frame3[] = {
+      0x1f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x12, 0xab, 0x9d,
+      0x76, 0x2b, 0x5f, 0xab, 0xf3, 0x6d, 0xc4, 0xaa, 0xe5, 0x1e, 0x63, 0xc1,
+      0x7b, 0x7b, 0x10, 0xd5, 0x63, 0x0f, 0x29, 0xad, 0x17, 0x33, 0x73};
+  const size_t client_frame_size3 =
+      sizeof(client_expected_frame3) / sizeof(uint8_t);
+  const size_t server_frame_size3 =
+      sizeof(server_expected_frame3) / sizeof(uint8_t);
+  fixture = tsi_test_frame_protector_fixture_create();
+  alts_test_do_round_trip_check_frames(
+      fixture, key, kAes128GcmKeyLength, /*rekey=*/false, large_message,
+      large_message_size, client_expected_frame3, client_frame_size3,
+      reinterpret_cast<const uint8_t*>(small_message), small_message_size,
+      server_expected_frame3, server_frame_size3);
+  tsi_test_frame_protector_fixture_destroy(fixture);
+  /**
+   * Test large client message, small server message, and small
+   * read_buffer_allocated_size.
+   */
+  const uint8_t client_expected_frame4[] = {
+      0x94, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x81, 0x86, 0xc7,
+      0xdc, 0xf4, 0x77, 0x3a, 0xdb, 0x91, 0x94, 0x61, 0xba, 0xed, 0xd5, 0x37,
+      0x47, 0x53, 0x0c, 0xe1, 0xbf, 0x59, 0x23, 0x20, 0xde, 0x8b, 0x25, 0x13,
+      0x72, 0xe7, 0x8a, 0x4f, 0x32, 0x61, 0xc6, 0xda, 0xc3, 0xe9, 0xff, 0x31,
+      0x33, 0x53, 0x4a, 0xf8, 0xc9, 0x98, 0xe4, 0x19, 0x71, 0x9c, 0x5e, 0x72,
+      0xc7, 0x35, 0x97, 0x78, 0x30, 0xf2, 0xc4, 0xd1, 0x53, 0xd5, 0x6e, 0x8f,
+      0x4f, 0xd9, 0x28, 0x5a, 0xfd, 0x22, 0x57, 0x7f, 0x95, 0xb4, 0x8a, 0x5e,
+      0x7c, 0x47, 0xa8, 0xcf, 0x64, 0x3d, 0x83, 0xa5, 0xcf, 0xc3, 0xfe, 0x54,
+      0xc2, 0x6a, 0x40, 0xc4, 0xfb, 0x8e, 0x07, 0x77, 0x70, 0x8f, 0x99, 0x94,
+      0xb1, 0xd5, 0xa7, 0xf9, 0x0d, 0xc7, 0x11, 0xc5, 0x6f, 0x4a, 0x4f, 0x56,
+      0xd5, 0xe2, 0x9c, 0xbb, 0x95, 0x7a, 0xd0, 0x9f, 0x30, 0x54, 0xca, 0x6d,
+      0x5c, 0x8e, 0x83, 0xa0, 0x04, 0x5e, 0xd0, 0x22, 0x8c, 0x2a, 0x7f, 0xdb,
+      0xfe, 0xb3, 0x2e, 0xae, 0x22, 0xe6, 0xf4, 0xb7};
+  const uint8_t server_expected_frame4[] = {
+      0x1f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x12, 0xab, 0x9d,
+      0x76, 0x2b, 0x5f, 0xab, 0xf3, 0x6d, 0xc4, 0xaa, 0xe5, 0x1e, 0x63, 0xc1,
+      0x7b, 0x7b, 0x10, 0xd5, 0x63, 0x0f, 0x29, 0xad, 0x17, 0x33, 0x73};
+  const size_t client_frame_size4 =
+      sizeof(client_expected_frame4) / sizeof(uint8_t);
+  const size_t server_frame_size4 =
+      sizeof(server_expected_frame4) / sizeof(uint8_t);
+  fixture = tsi_test_frame_protector_fixture_create();
+  alts_test_do_round_trip_check_frames(
+      fixture, key, kAes128GcmKeyLength, /*rekey=*/false, large_message,
+      large_message_size, client_expected_frame4, client_frame_size4,
+      reinterpret_cast<const uint8_t*>(small_message), small_message_size,
+      server_expected_frame4, server_frame_size4);
+  tsi_test_frame_protector_fixture_destroy(fixture);
+  /**
+   * Test large client message, small server message, and small
+   * client_max_output_protected_frame_size.
+   */
+  const uint8_t client_expected_frame5[] = {
+      0x94, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x81, 0x86, 0xc7,
+      0xdc, 0xf4, 0x77, 0x3a, 0xdb, 0x91, 0x94, 0x61, 0xba, 0xed, 0xd5, 0x37,
+      0x47, 0x53, 0x0c, 0xe1, 0xbf, 0x59, 0x23, 0x20, 0xde, 0x8b, 0x25, 0x13,
+      0x72, 0xe7, 0x8a, 0x4f, 0x32, 0x61, 0xc6, 0xda, 0xc3, 0xe9, 0xff, 0x31,
+      0x33, 0x53, 0x4a, 0xf8, 0xc9, 0x98, 0xe4, 0x19, 0x71, 0x9c, 0x5e, 0x72,
+      0xc7, 0x35, 0x97, 0x78, 0x30, 0xf2, 0xc4, 0xd1, 0x53, 0xd5, 0x6e, 0x8f,
+      0x4f, 0xd9, 0x28, 0x5a, 0xfd, 0x22, 0x57, 0x7f, 0x95, 0xb4, 0x8a, 0x5e,
+      0x7c, 0x47, 0xa8, 0xcf, 0x64, 0x3d, 0x83, 0xa5, 0xcf, 0xc3, 0xfe, 0x54,
+      0xc2, 0x6a, 0x40, 0xc4, 0xfb, 0x8e, 0x07, 0x77, 0x70, 0x8f, 0x99, 0x94,
+      0xb1, 0xd5, 0xa7, 0xf9, 0x0d, 0xc7, 0x11, 0xc5, 0x6f, 0x4a, 0x4f, 0x56,
+      0xd5, 0xe2, 0x9c, 0xbb, 0x95, 0x7a, 0xd0, 0x9f, 0x30, 0x54, 0xca, 0x6d,
+      0x5c, 0x8e, 0x83, 0xa0, 0x04, 0x5e, 0xd0, 0x22, 0x8c, 0x2a, 0x7f, 0xdb,
+      0xfe, 0xb3, 0x2e, 0xae, 0x22, 0xe6, 0xf4, 0xb7};
+  const uint8_t server_expected_frame5[] = {
+      0x1f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x12, 0xab, 0x9d,
+      0x76, 0x2b, 0x5f, 0xab, 0xf3, 0x6d, 0xc4, 0xaa, 0xe5, 0x1e, 0x63, 0xc1,
+      0x7b, 0x7b, 0x10, 0xd5, 0x63, 0x0f, 0x29, 0xad, 0x17, 0x33, 0x73};
+  const size_t client_frame_size5 =
+      sizeof(client_expected_frame5) / sizeof(uint8_t);
+  const size_t server_frame_size5 =
+      sizeof(server_expected_frame5) / sizeof(uint8_t);
+  fixture = tsi_test_frame_protector_fixture_create();
+  alts_test_do_round_trip_check_frames(
+      fixture, key, kAes128GcmKeyLength, /*rekey=*/false, large_message,
+      large_message_size, client_expected_frame5, client_frame_size5,
+      reinterpret_cast<const uint8_t*>(small_message), small_message_size,
+      server_expected_frame5, server_frame_size5);
+  tsi_test_frame_protector_fixture_destroy(fixture);
+  /**
+   * Test small client message, large server message, and small
+   * server_max_output_protected_frame_size.
+   */
+  const uint8_t client_expected_frame6[] = {
+      0x1f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0xd8, 0xd5, 0x92,
+      0x4d, 0x50, 0x32, 0xb7, 0x1f, 0xb8, 0xf2, 0xbb, 0x43, 0xc7, 0xe2, 0x94,
+      0x3d, 0x3e, 0x9a, 0x78, 0x76, 0xaa, 0x0a, 0x6b, 0xfa, 0x98, 0x3a};
+  const uint8_t server_expected_frame6[] = {
+      0x94, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4b, 0xf8, 0xc8,
+      0xe7, 0x8f, 0x1a, 0x26, 0x37, 0x44, 0xa2, 0x5c, 0x55, 0x94, 0x30, 0x4e,
+      0x3e, 0x16, 0xe7, 0x9e, 0x96, 0xe8, 0x1b, 0xc0, 0xdd, 0x52, 0x30, 0x06,
+      0xc2, 0x72, 0x9a, 0xa1, 0x0b, 0xdb, 0xdc, 0x19, 0x8c, 0x93, 0x5e, 0x84,
+      0x1f, 0x4b, 0x97, 0x26, 0xf0, 0x73, 0x85, 0x59, 0x00, 0x95, 0xc1, 0xc5,
+      0x22, 0x2f, 0x70, 0x85, 0x68, 0x2c, 0x4f, 0xfe, 0x30, 0x26, 0x91, 0xde,
+      0x62, 0x55, 0x1d, 0x35, 0x01, 0x96, 0x1c, 0xe7, 0xa2, 0x8b, 0x14, 0x8a,
+      0x5e, 0x1b, 0x4a, 0x3b, 0x4f, 0x65, 0x0f, 0xca, 0x79, 0x10, 0xb4, 0xdd,
+      0xf7, 0xa4, 0x8b, 0x64, 0x2f, 0x00, 0x39, 0x60, 0x03, 0xfc, 0xe1, 0x8b,
+      0x5c, 0x19, 0xba, 0xcc, 0x46, 0xba, 0x88, 0xdd, 0x40, 0x42, 0x27, 0x4f,
+      0xe4, 0x1a, 0x6a, 0x31, 0x6c, 0x1c, 0xb0, 0xb6, 0x5c, 0x3e, 0xca, 0x84,
+      0x9b, 0x5f, 0x04, 0x84, 0x11, 0xa9, 0xf8, 0x39, 0xe7, 0xe7, 0xc5, 0xc4,
+      0x33, 0x9f, 0x63, 0x21, 0x9a, 0x7c, 0x9c, 0x64};
+  const size_t client_frame_size6 =
+      sizeof(client_expected_frame6) / sizeof(uint8_t);
+  const size_t server_frame_size6 =
+      sizeof(server_expected_frame6) / sizeof(uint8_t);
+  fixture = tsi_test_frame_protector_fixture_create();
+  alts_test_do_round_trip_check_frames(
+      fixture, key, kAes128GcmKeyLength, /*rekey=*/false,
+      reinterpret_cast<const uint8_t*>(small_message), small_message_size,
+      client_expected_frame6, client_frame_size6, large_message,
+      large_message_size, server_expected_frame6, server_frame_size6);
+  tsi_test_frame_protector_fixture_destroy(fixture);
+}
+
+static void alts_test_do_round_trip(tsi_test_frame_protector_fixture* fixture,
+                                    bool rekey) {
+  GPR_ASSERT(fixture != nullptr);
+  GPR_ASSERT(fixture->config != nullptr);
+  tsi_frame_protector* client_frame_protector = nullptr;
+  tsi_frame_protector* server_frame_protector = nullptr;
+  tsi_test_frame_protector_config* config = fixture->config;
+  /* Create a key to be used by both client and server. */
+  uint8_t* key = nullptr;
+  size_t key_length = rekey ? kAes128GcmRekeyKeyLength : kAes128GcmKeyLength;
+  gsec_test_random_array(&key, key_length);
+  /* Create a client frame protector. */
+  size_t client_max_output_protected_frame_size =
+      config->client_max_output_protected_frame_size;
+  GPR_ASSERT(
+      alts_create_frame_protector(key, key_length, /*is_client=*/true, rekey,
+                                  client_max_output_protected_frame_size == 0
+                                      ? nullptr
+                                      : &client_max_output_protected_frame_size,
+                                  &client_frame_protector) == TSI_OK);
+  /* Create a server frame protector. */
+  size_t server_max_output_protected_frame_size =
+      config->server_max_output_protected_frame_size;
+  GPR_ASSERT(
+      alts_create_frame_protector(key, key_length, /*is_client=*/false, rekey,
+                                  server_max_output_protected_frame_size == 0
+                                      ? nullptr
+                                      : &server_max_output_protected_frame_size,
+                                  &server_frame_protector) == TSI_OK);
+  tsi_test_frame_protector_fixture_init(fixture, client_frame_protector,
+                                        server_frame_protector);
+  tsi_test_frame_protector_do_round_trip_no_handshake(fixture);
+  gpr_free(key);
+}
+
+/* Run all combinations of different arguments of test config. */
+static void alts_test_do_round_trip_all(bool rekey) {
+  unsigned int* bit_array = static_cast<unsigned int*>(
+      gpr_malloc(sizeof(unsigned int) * TSI_TEST_NUM_OF_ARGUMENTS));
+  unsigned int mask = 1U << (TSI_TEST_NUM_OF_ARGUMENTS - 1);
+  unsigned int val = 0, ind = 0;
+  for (val = 0; val < TSI_TEST_NUM_OF_COMBINATIONS; val++) {
+    unsigned int v = val;
+    for (ind = 0; ind < TSI_TEST_NUM_OF_ARGUMENTS; ind++) {
+      bit_array[ind] = (v & mask) ? 1 : 0;
+      v <<= 1;
+    }
+    tsi_test_frame_protector_fixture* fixture =
+        tsi_test_frame_protector_fixture_create();
+    tsi_test_frame_protector_config_destroy(fixture->config);
+    fixture->config = tsi_test_frame_protector_config_create(
+        bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4],
+        bit_array[5], bit_array[6]);
+    alts_test_do_round_trip(fixture, rekey);
+    tsi_test_frame_protector_fixture_destroy(fixture);
+  }
+  gpr_free(bit_array);
+}
+
+int main(int argc, char** argv) {
+  alts_test_do_round_trip_vector_tests();
+  alts_test_do_round_trip_all(/*rekey=*/false);
+  alts_test_do_round_trip_all(/*rekey=*/true);
+  return 0;
+}
diff --git a/test/core/tsi/alts/frame_protector/frame_handler_test.cc b/test/core/tsi/alts/frame_protector/frame_handler_test.cc
new file mode 100644
index 0000000..6434ea1
--- /dev/null
+++ b/test/core/tsi/alts/frame_protector/frame_handler_test.cc
@@ -0,0 +1,244 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/tsi/alts/frame_protector/frame_handler.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+const size_t kFrameHandlerTestBufferSize = 1024;
+
+typedef struct frame_handler {
+  alts_frame_writer* writer;
+  alts_frame_reader* reader;
+  unsigned char* buffer;
+  size_t buffer_size;
+} frame_handler;
+
+static size_t frame_length(size_t payload_length) {
+  return payload_length + kFrameHeaderSize;
+}
+
+static frame_handler* create_frame_handler() {
+  frame_handler* handler =
+      static_cast<frame_handler*>(gpr_malloc(sizeof(frame_handler)));
+  handler->writer = alts_create_frame_writer();
+  handler->reader = alts_create_frame_reader();
+  handler->buffer = nullptr;
+  handler->buffer_size = 0;
+  return handler;
+}
+
+static void destroy_frame_handler(frame_handler* handler) {
+  if (handler != nullptr) {
+    alts_destroy_frame_reader(handler->reader);
+    alts_destroy_frame_writer(handler->writer);
+    if (handler->buffer != nullptr) gpr_free(handler->buffer);
+    gpr_free(handler);
+  }
+}
+
+static void frame(frame_handler* handler, unsigned char* payload,
+                  size_t payload_length, size_t write_length) {
+  handler->buffer_size = frame_length(payload_length);
+  handler->buffer =
+      static_cast<unsigned char*>(gpr_malloc(handler->buffer_size));
+  GPR_ASSERT(alts_reset_frame_writer(handler->writer, payload, payload_length));
+  size_t offset = 0;
+  while (offset < handler->buffer_size &&
+         !alts_is_frame_writer_done(handler->writer)) {
+    size_t bytes_written = GPR_MIN(write_length, handler->buffer_size - offset);
+    GPR_ASSERT(alts_write_frame_bytes(handler->writer, handler->buffer + offset,
+                                      &bytes_written));
+    offset += bytes_written;
+  }
+  GPR_ASSERT(alts_is_frame_writer_done(handler->writer));
+  GPR_ASSERT(handler->buffer_size == offset);
+}
+
+static size_t deframe(frame_handler* handler, unsigned char* bytes,
+                      size_t read_length) {
+  GPR_ASSERT(alts_reset_frame_reader(handler->reader, bytes));
+  size_t offset = 0;
+  while (offset < handler->buffer_size &&
+         !alts_is_frame_reader_done(handler->reader)) {
+    size_t bytes_read = GPR_MIN(read_length, handler->buffer_size - offset);
+    GPR_ASSERT(alts_read_frame_bytes(handler->reader, handler->buffer + offset,
+                                     &bytes_read));
+    offset += bytes_read;
+  }
+  GPR_ASSERT(alts_is_frame_reader_done(handler->reader));
+  GPR_ASSERT(handler->buffer_size == offset);
+  return offset - handler->reader->header_bytes_read;
+}
+
+static void frame_n_deframe(frame_handler* handler, unsigned char* payload,
+                            size_t payload_length, size_t write_length,
+                            size_t read_length) {
+  frame(handler, payload, payload_length, write_length);
+  unsigned char* bytes =
+      static_cast<unsigned char*>(gpr_malloc(kFrameHandlerTestBufferSize));
+  size_t deframed_payload_length = deframe(handler, bytes, read_length);
+  GPR_ASSERT(payload_length == deframed_payload_length);
+  GPR_ASSERT(memcmp(payload, bytes, payload_length) == 0);
+  gpr_free(bytes);
+}
+
+static void frame_handler_test_frame_deframe() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen((char*)payload) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame_n_deframe(handler, payload, payload_length,
+                  frame_length(payload_length), frame_length(payload_length));
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_small_buffer() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame_n_deframe(handler, payload, payload_length, 1, 1);
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_null_input_stream() {
+  frame_handler* handler = create_frame_handler();
+  GPR_ASSERT(!alts_reset_frame_writer(handler->writer, nullptr, 0));
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_bad_input_length() {
+  unsigned char payload[] = "hello world";
+  frame_handler* handler = create_frame_handler();
+  GPR_ASSERT(!alts_reset_frame_writer(handler->writer, payload, SIZE_MAX));
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_null_writer_byte_length() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  GPR_ASSERT(alts_reset_frame_writer(handler->writer, payload, payload_length));
+  GPR_ASSERT(
+      !alts_write_frame_bytes(handler->writer, handler->buffer, nullptr));
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_null_writer_bytes() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  GPR_ASSERT(alts_reset_frame_writer(handler->writer, payload, payload_length));
+  GPR_ASSERT(
+      !alts_write_frame_bytes(handler->writer, nullptr, &payload_length));
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_bad_frame_length() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame(handler, payload, payload_length, payload_length);
+  memset(handler->buffer, 0x00, kFrameLengthFieldSize);
+  unsigned char* bytes =
+      static_cast<unsigned char*>(gpr_malloc(kFrameHandlerTestBufferSize));
+  GPR_ASSERT(alts_reset_frame_reader(handler->reader, bytes));
+  size_t bytes_read = handler->buffer_size;
+  GPR_ASSERT(
+      !alts_read_frame_bytes(handler->reader, handler->buffer, &bytes_read));
+  GPR_ASSERT(alts_is_frame_reader_done(handler->reader));
+  GPR_ASSERT(bytes_read == 0);
+  gpr_free(bytes);
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_unsupported_message_type() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame(handler, payload, payload_length, payload_length);
+  memset(handler->buffer + kFrameLengthFieldSize, 0x00,
+         kFrameMessageTypeFieldSize);
+  unsigned char* bytes =
+      static_cast<unsigned char*>(gpr_malloc(kFrameHandlerTestBufferSize));
+  GPR_ASSERT(alts_reset_frame_reader(handler->reader, bytes));
+  size_t bytes_read = handler->buffer_size;
+  GPR_ASSERT(
+      !alts_read_frame_bytes(handler->reader, handler->buffer, &bytes_read));
+  GPR_ASSERT(alts_is_frame_reader_done(handler->reader));
+  GPR_ASSERT(bytes_read == 0);
+  gpr_free(bytes);
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_null_output_stream() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame(handler, payload, payload_length, payload_length);
+  GPR_ASSERT(!alts_reset_frame_reader(handler->reader, nullptr));
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_null_reader_byte_length() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame(handler, payload, payload_length, payload_length);
+  unsigned char* bytes =
+      static_cast<unsigned char*>(gpr_malloc(kFrameHandlerTestBufferSize));
+  GPR_ASSERT(alts_reset_frame_reader(handler->reader, bytes));
+  GPR_ASSERT(!alts_read_frame_bytes(handler->reader, handler->buffer, nullptr));
+  gpr_free(bytes);
+  destroy_frame_handler(handler);
+}
+
+static void frame_handler_test_null_reader_bytes() {
+  unsigned char payload[] = "hello world";
+  size_t payload_length = strlen(reinterpret_cast<char*>(payload)) + 1;
+  frame_handler* handler = create_frame_handler();
+  frame(handler, payload, payload_length, payload_length);
+  unsigned char* bytes =
+      static_cast<unsigned char*>(gpr_malloc(kFrameHandlerTestBufferSize));
+  GPR_ASSERT(alts_reset_frame_reader(handler->reader, bytes));
+  size_t bytes_read = handler->buffer_size;
+  GPR_ASSERT(!alts_read_frame_bytes(handler->reader, nullptr, &bytes_read));
+  gpr_free(bytes);
+  destroy_frame_handler(handler);
+}
+
+int main(int argc, char** argv) {
+  frame_handler_test_frame_deframe();
+  frame_handler_test_small_buffer();
+  frame_handler_test_null_input_stream();
+  frame_handler_test_bad_input_length();
+  frame_handler_test_null_writer_byte_length();
+  frame_handler_test_null_writer_bytes();
+  frame_handler_test_bad_frame_length();
+  frame_handler_test_unsupported_message_type();
+  frame_handler_test_null_output_stream();
+  frame_handler_test_null_reader_byte_length();
+  frame_handler_test_null_reader_bytes();
+  return 0;
+}
diff --git a/test/core/tsi/alts/handshaker/BUILD b/test/core/tsi/alts/handshaker/BUILD
new file mode 100644
index 0000000..db39732
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/BUILD
@@ -0,0 +1,78 @@
+# Copyright 2018 gRPC authors.
+# 
+# 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.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package")
+
+licenses(["notice"])  # Apache v2 
+         
+grpc_package(name = "handshaker")
+         
+grpc_cc_library(
+    name = "alts_handshaker_service_api_test_lib",
+    srcs = ["alts_handshaker_service_api_test_lib.cc"],
+    hdrs = ["alts_handshaker_service_api_test_lib.h"],
+    deps = [
+        "//:grpc",
+    ], 
+)
+
+grpc_cc_test(
+    name = "alts_handshaker_client_test",
+    srcs = ["alts_handshaker_client_test.cc"],
+    language = "C++",
+    deps = [
+        ":alts_handshaker_service_api_test_lib",
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_handshaker_service_api_test",
+    srcs = ["alts_handshaker_service_api_test.cc"],
+    language = "C++",
+    deps = [
+        ":alts_handshaker_service_api_test_lib",
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_tsi_handshaker_test",
+    srcs = ["alts_tsi_handshaker_test.cc"],
+    language = "C++",
+    deps = [
+        ":alts_handshaker_service_api_test_lib",
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_tsi_utils_test",
+    srcs = ["alts_tsi_utils_test.cc"],
+    language = "C++",
+    deps = [
+        ":alts_handshaker_service_api_test_lib",
+        "//:grpc",
+    ],
+)
+
+grpc_cc_test(
+    name = "transport_security_common_api_test",
+    srcs = ["transport_security_common_api_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+    ],
+)
+
diff --git a/test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc b/test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
new file mode 100644
index 0000000..7072be6
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc
@@ -0,0 +1,412 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/grpc.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+#include "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+
+#define ALTS_HANDSHAKER_CLIENT_TEST_OUT_FRAME "Hello Google"
+#define ALTS_HANDSHAKER_CLIENT_TEST_HANDSHAKER_SERVICE_URL "lame"
+#define ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME "bigtable.google.api.com"
+#define ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1 "A@google.com"
+#define ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2 "B@google.com"
+
+const size_t kHandshakerClientOpNum = 4;
+const size_t kMaxRpcVersionMajor = 3;
+const size_t kMaxRpcVersionMinor = 2;
+const size_t kMinRpcVersionMajor = 2;
+const size_t kMinRpcVersionMinor = 1;
+
+using grpc_core::internal::alts_handshaker_client_set_grpc_caller_for_testing;
+
+typedef struct alts_handshaker_client_test_config {
+  grpc_channel* channel;
+  grpc_completion_queue* cq;
+  alts_handshaker_client* client;
+  grpc_slice out_frame;
+} alts_handshaker_client_test_config;
+
+static alts_tsi_event* alts_tsi_event_create_for_testing(bool is_client) {
+  alts_tsi_event* e = static_cast<alts_tsi_event*>(gpr_zalloc(sizeof(*e)));
+  grpc_metadata_array_init(&e->initial_metadata);
+  grpc_metadata_array_init(&e->trailing_metadata);
+  e->options = is_client ? grpc_alts_credentials_client_options_create()
+                         : grpc_alts_credentials_server_options_create();
+  if (is_client) {
+    grpc_alts_credentials_client_options_add_target_service_account(
+        reinterpret_cast<grpc_alts_credentials_client_options*>(e->options),
+        ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1);
+    grpc_alts_credentials_client_options_add_target_service_account(
+        reinterpret_cast<grpc_alts_credentials_client_options*>(e->options),
+        ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2);
+  }
+  grpc_gcp_rpc_protocol_versions* versions = &e->options->rpc_versions;
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(
+      versions, kMaxRpcVersionMajor, kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(
+      versions, kMinRpcVersionMajor, kMinRpcVersionMinor));
+  e->target_name =
+      grpc_slice_from_static_string(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME);
+  return e;
+}
+
+static void validate_rpc_protocol_versions(
+    grpc_gcp_rpc_protocol_versions* versions) {
+  GPR_ASSERT(versions != nullptr);
+  GPR_ASSERT(versions->max_rpc_version.major == kMaxRpcVersionMajor);
+  GPR_ASSERT(versions->max_rpc_version.minor == kMaxRpcVersionMinor);
+  GPR_ASSERT(versions->min_rpc_version.major == kMinRpcVersionMajor);
+  GPR_ASSERT(versions->min_rpc_version.minor == kMinRpcVersionMinor);
+}
+
+static void validate_target_identities(
+    const repeated_field* target_identity_head) {
+  grpc_gcp_identity* target_identity1 = static_cast<grpc_gcp_identity*>(
+      const_cast<void*>(target_identity_head->next->data));
+  grpc_gcp_identity* target_identity2 = static_cast<grpc_gcp_identity*>(
+      const_cast<void*>(target_identity_head->data));
+  grpc_slice* service_account1 =
+      static_cast<grpc_slice*>(target_identity1->service_account.arg);
+  grpc_slice* service_account2 =
+      static_cast<grpc_slice*>(target_identity2->service_account.arg);
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*service_account1),
+                    ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1,
+                    GRPC_SLICE_LENGTH(*service_account1)) == 0);
+  GPR_ASSERT(strlen(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT1) ==
+             GRPC_SLICE_LENGTH(*service_account1));
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*service_account2),
+                    ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2,
+                    GRPC_SLICE_LENGTH(*service_account2)) == 0);
+  GPR_ASSERT(strlen(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_SERVICE_ACCOUNT2) ==
+             GRPC_SLICE_LENGTH(*service_account2));
+}
+
+/**
+ * Validate if grpc operation data is correctly populated with the fields of
+ * ALTS TSI event.
+ */
+static bool validate_op(alts_tsi_event* event, const grpc_op* op, size_t nops,
+                        bool is_start) {
+  GPR_ASSERT(event != nullptr && op != nullptr && nops != 0);
+  bool ok = true;
+  grpc_op* start_op = const_cast<grpc_op*>(op);
+  if (is_start) {
+    ok &= (op->op == GRPC_OP_SEND_INITIAL_METADATA);
+    ok &= (op->data.send_initial_metadata.count == 0);
+    op++;
+    GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
+
+    ok &= (op->op == GRPC_OP_RECV_INITIAL_METADATA);
+    ok &= (op->data.recv_initial_metadata.recv_initial_metadata ==
+           &event->initial_metadata);
+    op++;
+    GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
+  }
+  ok &= (op->op == GRPC_OP_SEND_MESSAGE);
+  ok &= (op->data.send_message.send_message == event->send_buffer);
+  op++;
+  GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
+
+  ok &= (op->op == GRPC_OP_RECV_MESSAGE);
+  ok &= (op->data.recv_message.recv_message == &event->recv_buffer);
+  op++;
+  GPR_ASSERT((size_t)(op - start_op) <= kHandshakerClientOpNum);
+
+  return ok;
+}
+
+static grpc_gcp_handshaker_req* deserialize_handshaker_req(
+    grpc_gcp_handshaker_req_type type, grpc_byte_buffer* buffer) {
+  GPR_ASSERT(buffer != nullptr);
+  grpc_gcp_handshaker_req* req = grpc_gcp_handshaker_decoded_req_create(type);
+  grpc_byte_buffer_reader bbr;
+  GPR_ASSERT(grpc_byte_buffer_reader_init(&bbr, buffer));
+  grpc_slice slice = grpc_byte_buffer_reader_readall(&bbr);
+  GPR_ASSERT(grpc_gcp_handshaker_req_decode(slice, req));
+  grpc_slice_unref(slice);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  return req;
+}
+
+/**
+ * A mock grpc_caller used to check if client_start, server_start, and next
+ * operations correctly handle invalid arguments. It should not be called.
+ */
+static grpc_call_error check_must_not_be_called(grpc_call* call,
+                                                const grpc_op* ops, size_t nops,
+                                                void* tag) {
+  GPR_ASSERT(0);
+}
+
+/**
+ * A mock grpc_caller used to check correct execution of client_start operation.
+ * It checks if the client_start handshaker request is populated with correct
+ * handshake_security_protocol, application_protocol, and record_protocol, and
+ * op is correctly populated.
+ */
+static grpc_call_error check_client_start_success(grpc_call* call,
+                                                  const grpc_op* op,
+                                                  size_t nops, void* tag) {
+  alts_tsi_event* event = static_cast<alts_tsi_event*>(tag);
+  grpc_gcp_handshaker_req* req =
+      deserialize_handshaker_req(CLIENT_START_REQ, event->send_buffer);
+  GPR_ASSERT(req->client_start.handshake_security_protocol ==
+             grpc_gcp_HandshakeProtocol_ALTS);
+  const void* data = (static_cast<repeated_field*>(
+                          req->client_start.application_protocols.arg))
+                         ->data;
+  GPR_ASSERT(data != nullptr);
+  grpc_slice* application_protocol = (grpc_slice*)data;
+  data = (static_cast<repeated_field*>(req->client_start.record_protocols.arg))
+             ->data;
+  grpc_slice* record_protocol = (grpc_slice*)data;
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*application_protocol),
+                    ALTS_APPLICATION_PROTOCOL,
+                    GRPC_SLICE_LENGTH(*application_protocol)) == 0);
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*record_protocol),
+                    ALTS_RECORD_PROTOCOL,
+                    GRPC_SLICE_LENGTH(*record_protocol)) == 0);
+  validate_rpc_protocol_versions(&req->client_start.rpc_versions);
+  validate_target_identities(
+      static_cast<repeated_field*>(req->client_start.target_identities.arg));
+  grpc_slice* target_name =
+      static_cast<grpc_slice*>(req->client_start.target_name.arg);
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*target_name),
+                    ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME,
+                    GRPC_SLICE_LENGTH(*target_name)) == 0);
+  GPR_ASSERT(GRPC_SLICE_LENGTH(*target_name) ==
+             strlen(ALTS_HANDSHAKER_CLIENT_TEST_TARGET_NAME));
+  GPR_ASSERT(validate_op(event, op, nops, true /* is_start */));
+  grpc_gcp_handshaker_req_destroy(req);
+  return GRPC_CALL_OK;
+}
+
+/**
+ * A mock grpc_caller used to check correct execution of server_start operation.
+ * It checks if the server_start handshaker request is populated with correct
+ * handshake_security_protocol, application_protocol, and record_protocol, and
+ * op is correctly populated.
+ */
+static grpc_call_error check_server_start_success(grpc_call* call,
+                                                  const grpc_op* op,
+                                                  size_t nops, void* tag) {
+  alts_tsi_event* event = static_cast<alts_tsi_event*>(tag);
+  grpc_gcp_handshaker_req* req =
+      deserialize_handshaker_req(SERVER_START_REQ, event->send_buffer);
+  const void* data = (static_cast<repeated_field*>(
+                          req->server_start.application_protocols.arg))
+                         ->data;
+  GPR_ASSERT(data != nullptr);
+  grpc_slice* application_protocol = (grpc_slice*)data;
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*application_protocol),
+                    ALTS_APPLICATION_PROTOCOL,
+                    GRPC_SLICE_LENGTH(*application_protocol)) == 0);
+  GPR_ASSERT(req->server_start.handshake_parameters_count == 1);
+  GPR_ASSERT(req->server_start.handshake_parameters[0].key ==
+             grpc_gcp_HandshakeProtocol_ALTS);
+  data = (static_cast<repeated_field*>(req->server_start.handshake_parameters[0]
+                                           .value.record_protocols.arg))
+             ->data;
+  GPR_ASSERT(data != nullptr);
+  grpc_slice* record_protocol = (grpc_slice*)data;
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*record_protocol),
+                    ALTS_RECORD_PROTOCOL,
+                    GRPC_SLICE_LENGTH(*record_protocol)) == 0);
+  validate_rpc_protocol_versions(&req->server_start.rpc_versions);
+  GPR_ASSERT(validate_op(event, op, nops, true /* is_start */));
+  grpc_gcp_handshaker_req_destroy(req);
+  return GRPC_CALL_OK;
+}
+
+/**
+ * A mock grpc_caller used to check correct execution of next operation. It
+ * checks if the next handshaker request is populated with correct information,
+ * and op is correctly populated.
+ */
+static grpc_call_error check_next_success(grpc_call* call, const grpc_op* op,
+                                          size_t nops, void* tag) {
+  alts_tsi_event* event = static_cast<alts_tsi_event*>(tag);
+  grpc_gcp_handshaker_req* req =
+      deserialize_handshaker_req(NEXT_REQ, event->send_buffer);
+  grpc_slice* in_bytes = static_cast<grpc_slice*>(req->next.in_bytes.arg);
+  GPR_ASSERT(in_bytes != nullptr);
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*in_bytes),
+                    ALTS_HANDSHAKER_CLIENT_TEST_OUT_FRAME,
+                    GRPC_SLICE_LENGTH(*in_bytes)) == 0);
+  GPR_ASSERT(validate_op(event, op, nops, false /* is_start */));
+  grpc_gcp_handshaker_req_destroy(req);
+  return GRPC_CALL_OK;
+}
+/**
+ * A mock grpc_caller used to check if client_start, server_start, and next
+ * operations correctly handle the situation when the grpc call made to the
+ * handshaker service fails.
+ */
+static grpc_call_error check_grpc_call_failure(grpc_call* call,
+                                               const grpc_op* op, size_t nops,
+                                               void* tag) {
+  return GRPC_CALL_ERROR;
+}
+
+static alts_handshaker_client_test_config* create_config() {
+  alts_handshaker_client_test_config* config =
+      static_cast<alts_handshaker_client_test_config*>(
+          gpr_zalloc(sizeof(*config)));
+  config->channel = grpc_insecure_channel_create(
+      ALTS_HANDSHAKER_CLIENT_TEST_HANDSHAKER_SERVICE_URL, nullptr, nullptr);
+  config->cq = grpc_completion_queue_create_for_next(nullptr);
+  config->client = alts_grpc_handshaker_client_create(
+      config->channel, config->cq,
+      ALTS_HANDSHAKER_CLIENT_TEST_HANDSHAKER_SERVICE_URL);
+  GPR_ASSERT(config->client != nullptr);
+  config->out_frame =
+      grpc_slice_from_static_string(ALTS_HANDSHAKER_CLIENT_TEST_OUT_FRAME);
+  return config;
+}
+
+static void destroy_config(alts_handshaker_client_test_config* config) {
+  if (config == nullptr) {
+    return;
+  }
+  grpc_completion_queue_destroy(config->cq);
+  grpc_channel_destroy(config->channel);
+  alts_handshaker_client_destroy(config->client);
+  grpc_slice_unref(config->out_frame);
+  gpr_free(config);
+}
+
+static void schedule_request_invalid_arg_test() {
+  /* Initialization. */
+  alts_handshaker_client_test_config* config = create_config();
+  alts_tsi_event* event = nullptr;
+
+  /* Tests. */
+  alts_handshaker_client_set_grpc_caller_for_testing(config->client,
+                                                     check_must_not_be_called);
+  event = alts_tsi_event_create_for_testing(true /* is_client */);
+  /* Check client_start. */
+  GPR_ASSERT(alts_handshaker_client_start_client(nullptr, event) ==
+             TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(alts_handshaker_client_start_client(config->client, nullptr) ==
+             TSI_INVALID_ARGUMENT);
+
+  /* Check server_start. */
+  GPR_ASSERT(alts_handshaker_client_start_server(
+                 config->client, event, nullptr) == TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(alts_handshaker_client_start_server(config->client, nullptr,
+                                                 &config->out_frame) ==
+             TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(alts_handshaker_client_start_server(
+                 nullptr, event, &config->out_frame) == TSI_INVALID_ARGUMENT);
+
+  /* Check next. */
+  GPR_ASSERT(alts_handshaker_client_next(config->client, event, nullptr) ==
+             TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(alts_handshaker_client_next(config->client, nullptr,
+                                         &config->out_frame) ==
+             TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(alts_handshaker_client_next(nullptr, event, &config->out_frame) ==
+             TSI_INVALID_ARGUMENT);
+
+  /* Cleanup. */
+  alts_tsi_event_destroy(event);
+  destroy_config(config);
+}
+
+static void schedule_request_success_test() {
+  /* Initialization. */
+  alts_handshaker_client_test_config* config = create_config();
+  alts_tsi_event* event = nullptr;
+
+  /* Check client_start success. */
+  alts_handshaker_client_set_grpc_caller_for_testing(
+      config->client, check_client_start_success);
+  event = alts_tsi_event_create_for_testing(true /* is_client. */);
+  GPR_ASSERT(alts_handshaker_client_start_client(config->client, event) ==
+             TSI_OK);
+  alts_tsi_event_destroy(event);
+
+  /* Check server_start success. */
+  alts_handshaker_client_set_grpc_caller_for_testing(
+      config->client, check_server_start_success);
+  event = alts_tsi_event_create_for_testing(false /* is_client. */);
+  GPR_ASSERT(alts_handshaker_client_start_server(config->client, event,
+                                                 &config->out_frame) == TSI_OK);
+  alts_tsi_event_destroy(event);
+
+  /* Check next success. */
+  alts_handshaker_client_set_grpc_caller_for_testing(config->client,
+                                                     check_next_success);
+  event = alts_tsi_event_create_for_testing(true /* is_client. */);
+  GPR_ASSERT(alts_handshaker_client_next(config->client, event,
+                                         &config->out_frame) == TSI_OK);
+  alts_tsi_event_destroy(event);
+
+  /* Cleanup. */
+  destroy_config(config);
+}
+
+static void schedule_request_grpc_call_failure_test() {
+  /* Initialization. */
+  alts_handshaker_client_test_config* config = create_config();
+  alts_tsi_event* event = nullptr;
+
+  /* Check client_start failure. */
+  alts_handshaker_client_set_grpc_caller_for_testing(config->client,
+                                                     check_grpc_call_failure);
+  event = alts_tsi_event_create_for_testing(true /* is_client. */);
+  GPR_ASSERT(alts_handshaker_client_start_client(config->client, event) ==
+             TSI_INTERNAL_ERROR);
+  alts_tsi_event_destroy(event);
+
+  /* Check server_start failure. */
+  event = alts_tsi_event_create_for_testing(false /* is_client. */);
+  GPR_ASSERT(alts_handshaker_client_start_server(config->client, event,
+                                                 &config->out_frame) ==
+             TSI_INTERNAL_ERROR);
+  alts_tsi_event_destroy(event);
+
+  /* Check next failure. */
+  event = alts_tsi_event_create_for_testing(true /* is_cleint. */);
+  GPR_ASSERT(
+      alts_handshaker_client_next(config->client, event, &config->out_frame) ==
+      TSI_INTERNAL_ERROR);
+  alts_tsi_event_destroy(event);
+
+  /* Cleanup. */
+  destroy_config(config);
+}
+
+int main(int argc, char** argv) {
+  /* Initialization. */
+  grpc_init();
+
+  /* Tests. */
+  schedule_request_invalid_arg_test();
+  schedule_request_success_test();
+  schedule_request_grpc_call_failure_test();
+
+  /* Cleanup. */
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc b/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc
new file mode 100644
index 0000000..3506264
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+
+int main(int argc, char** argv) {
+  const char in_bytes[] = "HELLO GOOGLE!";
+  const char out_frames[] = "HELLO WORLD!";
+  const char key_data[] = "THIS IS KEY DATA.";
+  const char details[] = "DETAILS NEED TO BE POPULATED";
+  const uint32_t max_rpc_version_major = 3;
+  const uint32_t max_rpc_version_minor = 2;
+  const uint32_t min_rpc_version_major = 2;
+  const uint32_t min_rpc_version_minor = 1;
+
+  /* handshaker_req_next. */
+  grpc_gcp_handshaker_req* req = grpc_gcp_handshaker_req_create(NEXT_REQ);
+  grpc_gcp_handshaker_req* decoded_req =
+      grpc_gcp_handshaker_decoded_req_create(NEXT_REQ);
+  GPR_ASSERT(
+      grpc_gcp_handshaker_req_set_in_bytes(req, in_bytes, strlen(in_bytes)));
+  grpc_slice encoded_req;
+  GPR_ASSERT(grpc_gcp_handshaker_req_encode(req, &encoded_req));
+  GPR_ASSERT(grpc_gcp_handshaker_req_decode(encoded_req, decoded_req));
+  GPR_ASSERT(grpc_gcp_handshaker_req_equals(req, decoded_req));
+  grpc_gcp_handshaker_req_destroy(req);
+  grpc_gcp_handshaker_req_destroy(decoded_req);
+  grpc_slice_unref(encoded_req);
+
+  /* handshaker_req_client_start. */
+  req = grpc_gcp_handshaker_req_create(CLIENT_START_REQ);
+  decoded_req = grpc_gcp_handshaker_decoded_req_create(CLIENT_START_REQ);
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_handshake_protocol(
+      req, grpc_gcp_HandshakeProtocol_TLS));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_local_identity_hostname(
+      req, "www.google.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_local_endpoint(
+      req, "2001:db8::8:800:200C:417a", 9876, grpc_gcp_NetworkProtocol_TCP));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_remote_endpoint(
+      req, "2001:db8::bac5::fed0:84a2", 1234, grpc_gcp_NetworkProtocol_TCP));
+  GPR_ASSERT(grpc_gcp_handshaker_req_add_application_protocol(req, "grpc"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_add_application_protocol(req, "http2"));
+  GPR_ASSERT(
+      grpc_gcp_handshaker_req_add_record_protocol(req, "ALTSRP_GCM_AES256"));
+  GPR_ASSERT(
+      grpc_gcp_handshaker_req_add_record_protocol(req, "ALTSRP_GCM_AES384"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_add_target_identity_service_account(
+      req, "foo@google.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_target_name(
+      req, "google.example.library.service"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_rpc_versions(
+      req, max_rpc_version_major, max_rpc_version_minor, min_rpc_version_major,
+      min_rpc_version_minor));
+  GPR_ASSERT(grpc_gcp_handshaker_req_encode(req, &encoded_req));
+  GPR_ASSERT(grpc_gcp_handshaker_req_decode(encoded_req, decoded_req));
+  GPR_ASSERT(grpc_gcp_handshaker_req_equals(req, decoded_req));
+  grpc_gcp_handshaker_req_destroy(req);
+  grpc_gcp_handshaker_req_destroy(decoded_req);
+  grpc_slice_unref(encoded_req);
+
+  /* handshaker_req_server_start. */
+  req = grpc_gcp_handshaker_req_create(SERVER_START_REQ);
+  decoded_req = grpc_gcp_handshaker_decoded_req_create(SERVER_START_REQ);
+  GPR_ASSERT(grpc_gcp_handshaker_req_add_application_protocol(req, "grpc"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_add_application_protocol(req, "http2"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_local_endpoint(
+      req, "2001:db8::8:800:200C:417a", 9876, grpc_gcp_NetworkProtocol_TCP));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_remote_endpoint(
+      req, "2001:db8::bac5::fed0:84a2", 1234, grpc_gcp_NetworkProtocol_UDP));
+  GPR_ASSERT(
+      grpc_gcp_handshaker_req_set_in_bytes(req, in_bytes, strlen(in_bytes)));
+  GPR_ASSERT(grpc_gcp_handshaker_req_param_add_record_protocol(
+      req, grpc_gcp_HandshakeProtocol_TLS, "ALTSRP_GCM_AES128"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_param_add_local_identity_service_account(
+      req, grpc_gcp_HandshakeProtocol_TLS, "foo@google.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_param_add_local_identity_hostname(
+      req, grpc_gcp_HandshakeProtocol_TLS, "yihuaz0.mtv.corp.google.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_param_add_record_protocol(
+      req, grpc_gcp_HandshakeProtocol_ALTS, "ALTSRP_GCM_AES128"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_param_add_local_identity_hostname(
+      req, grpc_gcp_HandshakeProtocol_ALTS, "www.amazon.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_req_set_rpc_versions(
+      req, max_rpc_version_major, max_rpc_version_minor, min_rpc_version_major,
+      min_rpc_version_minor));
+
+  GPR_ASSERT(grpc_gcp_handshaker_req_encode(req, &encoded_req));
+  GPR_ASSERT(grpc_gcp_handshaker_req_decode(encoded_req, decoded_req));
+  GPR_ASSERT(grpc_gcp_handshaker_req_equals(req, decoded_req));
+  grpc_gcp_handshaker_req_destroy(req);
+  grpc_gcp_handshaker_req_destroy(decoded_req);
+  grpc_slice_unref(encoded_req);
+
+  /* handshaker_resp. */
+  grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+  grpc_gcp_handshaker_resp* decoded_resp = grpc_gcp_handshaker_resp_create();
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_out_frames(resp, out_frames,
+                                                     strlen(out_frames)));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_bytes_consumed(resp, 1024));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_application_protocol(resp, "http"));
+  GPR_ASSERT(
+      grpc_gcp_handshaker_resp_set_record_protocol(resp, "ALTSRP_GCM_AES128"));
+  GPR_ASSERT(
+      grpc_gcp_handshaker_resp_set_key_data(resp, key_data, strlen(key_data)));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_local_identity_hostname(
+      resp, "www.faceboook.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_peer_identity_hostname(
+      resp, "www.amazon.com"));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_channel_open(
+      resp, false /* channel_open */));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_code(resp, 1023));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_details(resp, details));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_peer_rpc_versions(
+      resp, max_rpc_version_major, max_rpc_version_minor, min_rpc_version_major,
+      min_rpc_version_minor));
+  grpc_slice encoded_resp;
+  GPR_ASSERT(grpc_gcp_handshaker_resp_encode(resp, &encoded_resp));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_decode(encoded_resp, decoded_resp));
+  GPR_ASSERT(grpc_gcp_handshaker_resp_equals(resp, decoded_resp));
+  grpc_gcp_handshaker_resp_destroy(resp);
+  grpc_gcp_handshaker_resp_destroy(decoded_resp);
+  grpc_slice_unref(encoded_resp);
+  /* Test invalid arguments. */
+  GPR_ASSERT(!grpc_gcp_handshaker_req_set_in_bytes(nullptr, in_bytes,
+                                                   strlen(in_bytes)));
+  GPR_ASSERT(!grpc_gcp_handshaker_req_param_add_record_protocol(
+      req, grpc_gcp_HandshakeProtocol_TLS, nullptr));
+  GPR_ASSERT(!grpc_gcp_handshaker_req_param_add_local_identity_service_account(
+      nullptr, grpc_gcp_HandshakeProtocol_TLS, nullptr));
+  GPR_ASSERT(!grpc_gcp_handshaker_resp_set_record_protocol(nullptr, nullptr));
+}
diff --git a/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc b/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc
new file mode 100644
index 0000000..ecca04d
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc
@@ -0,0 +1,642 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+
+const size_t kHandshakeProtocolNum = 3;
+
+grpc_gcp_handshaker_req* grpc_gcp_handshaker_decoded_req_create(
+    grpc_gcp_handshaker_req_type type) {
+  grpc_gcp_handshaker_req* req =
+      static_cast<grpc_gcp_handshaker_req*>(gpr_zalloc(sizeof(*req)));
+  switch (type) {
+    case CLIENT_START_REQ:
+      req->has_client_start = true;
+      req->client_start.target_identities.funcs.decode =
+          decode_repeated_identity_cb;
+      req->client_start.application_protocols.funcs.decode =
+          decode_repeated_string_cb;
+      req->client_start.record_protocols.funcs.decode =
+          decode_repeated_string_cb;
+      req->client_start.local_identity.hostname.funcs.decode =
+          decode_string_or_bytes_cb;
+      req->client_start.local_identity.service_account.funcs.decode =
+          decode_string_or_bytes_cb;
+      req->client_start.local_endpoint.ip_address.funcs.decode =
+          decode_string_or_bytes_cb;
+      req->client_start.remote_endpoint.ip_address.funcs.decode =
+          decode_string_or_bytes_cb;
+      req->client_start.target_name.funcs.decode = decode_string_or_bytes_cb;
+      break;
+    case SERVER_START_REQ:
+      req->has_server_start = true;
+      req->server_start.application_protocols.funcs.decode =
+          &decode_repeated_string_cb;
+      for (size_t i = 0; i < kHandshakeProtocolNum; i++) {
+        req->server_start.handshake_parameters[i]
+            .value.local_identities.funcs.decode = &decode_repeated_identity_cb;
+        req->server_start.handshake_parameters[i]
+            .value.record_protocols.funcs.decode = &decode_repeated_string_cb;
+      }
+      req->server_start.in_bytes.funcs.decode = decode_string_or_bytes_cb;
+      req->server_start.local_endpoint.ip_address.funcs.decode =
+          decode_string_or_bytes_cb;
+      req->server_start.remote_endpoint.ip_address.funcs.decode =
+          decode_string_or_bytes_cb;
+      break;
+    case NEXT_REQ:
+      req->has_next = true;
+      break;
+  }
+  return req;
+}
+
+bool grpc_gcp_handshaker_resp_set_application_protocol(
+    grpc_gcp_handshaker_resp* resp, const char* application_protocol) {
+  if (resp == nullptr || application_protocol == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "handshaker_resp_set_application_protocol().");
+    return false;
+  }
+  resp->has_result = true;
+  grpc_slice* slice =
+      create_slice(application_protocol, strlen(application_protocol));
+  resp->result.application_protocol.arg = slice;
+  resp->result.application_protocol.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_record_protocol(
+    grpc_gcp_handshaker_resp* resp, const char* record_protocol) {
+  if (resp == nullptr || record_protocol == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "handshaker_resp_set_record_protocol().");
+    return false;
+  }
+  resp->has_result = true;
+  grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol));
+  resp->result.record_protocol.arg = slice;
+  resp->result.record_protocol.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_key_data(grpc_gcp_handshaker_resp* resp,
+                                           const char* key_data, size_t size) {
+  if (resp == nullptr || key_data == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to handshaker_resp_set_key_data().");
+    return false;
+  }
+  resp->has_result = true;
+  grpc_slice* slice = create_slice(key_data, size);
+  resp->result.key_data.arg = slice;
+  resp->result.key_data.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+static void set_identity_hostname(grpc_gcp_identity* identity,
+                                  const char* hostname) {
+  grpc_slice* slice = create_slice(hostname, strlen(hostname));
+  identity->hostname.arg = slice;
+  identity->hostname.funcs.encode = encode_string_or_bytes_cb;
+}
+
+static void set_identity_service_account(grpc_gcp_identity* identity,
+                                         const char* service_account) {
+  grpc_slice* slice = create_slice(service_account, strlen(service_account));
+  identity->service_account.arg = slice;
+  identity->service_account.funcs.encode = encode_string_or_bytes_cb;
+}
+
+bool grpc_gcp_handshaker_resp_set_local_identity_hostname(
+    grpc_gcp_handshaker_resp* resp, const char* hostname) {
+  if (resp == nullptr || hostname == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_resp_set_local_identity_hostname().");
+    return false;
+  }
+  resp->has_result = true;
+  resp->result.has_local_identity = true;
+  set_identity_hostname(&resp->result.local_identity, hostname);
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_local_identity_service_account(
+    grpc_gcp_handshaker_resp* resp, const char* service_account) {
+  if (resp == nullptr || service_account == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_resp_set_local_identity_service_account().");
+    return false;
+  }
+  resp->has_result = true;
+  resp->result.has_local_identity = true;
+  set_identity_service_account(&resp->result.local_identity, service_account);
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_peer_identity_hostname(
+    grpc_gcp_handshaker_resp* resp, const char* hostname) {
+  if (resp == nullptr || hostname == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_resp_set_peer_identity_hostname().");
+    return false;
+  }
+  resp->has_result = true;
+  resp->result.has_peer_identity = true;
+  set_identity_hostname(&resp->result.peer_identity, hostname);
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_peer_identity_service_account(
+    grpc_gcp_handshaker_resp* resp, const char* service_account) {
+  if (resp == nullptr || service_account == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_resp_set_peer_identity_service_account().");
+    return false;
+  }
+  resp->has_result = true;
+  resp->result.has_peer_identity = true;
+  set_identity_service_account(&resp->result.peer_identity, service_account);
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_channel_open(grpc_gcp_handshaker_resp* resp,
+                                               bool keep_channel_open) {
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to "
+            "grpc_gcp_handshaker_resp_set_channel_open().");
+    return false;
+  }
+  resp->has_result = true;
+  resp->result.has_keep_channel_open = true;
+  resp->result.keep_channel_open = keep_channel_open;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_code(grpc_gcp_handshaker_resp* resp,
+                                       uint32_t code) {
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to grpc_gcp_handshaker_resp_set_code().");
+    return false;
+  }
+  resp->has_status = true;
+  resp->status.has_code = true;
+  resp->status.code = code;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_details(grpc_gcp_handshaker_resp* resp,
+                                          const char* details) {
+  if (resp == nullptr || details == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to grpc_gcp_handshaker_resp_set_details().");
+    return false;
+  }
+  resp->has_status = true;
+  grpc_slice* slice = create_slice(details, strlen(details));
+  resp->status.details.arg = slice;
+  resp->status.details.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_out_frames(grpc_gcp_handshaker_resp* resp,
+                                             const char* out_frames,
+                                             size_t size) {
+  if (resp == nullptr || out_frames == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_resp_set_out_frames().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(out_frames, size);
+  resp->out_frames.arg = slice;
+  resp->out_frames.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_bytes_consumed(grpc_gcp_handshaker_resp* resp,
+                                                 int32_t bytes_consumed) {
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to "
+            "grpc_gcp_handshaker_resp_set_bytes_consumed().");
+    return false;
+  }
+  resp->has_bytes_consumed = true;
+  resp->bytes_consumed = bytes_consumed;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_set_peer_rpc_versions(
+    grpc_gcp_handshaker_resp* resp, uint32_t max_major, uint32_t max_minor,
+    uint32_t min_major, uint32_t min_minor) {
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to "
+            "grpc_gcp_handshaker_resp_set_peer_rpc_versions().");
+    return false;
+  }
+  resp->has_result = true;
+  resp->result.has_peer_rpc_versions = true;
+  grpc_gcp_rpc_protocol_versions* versions = &resp->result.peer_rpc_versions;
+  versions->has_max_rpc_version = true;
+  versions->has_min_rpc_version = true;
+  versions->max_rpc_version.has_major = true;
+  versions->max_rpc_version.has_minor = true;
+  versions->min_rpc_version.has_major = true;
+  versions->min_rpc_version.has_minor = true;
+  versions->max_rpc_version.major = max_major;
+  versions->max_rpc_version.minor = max_minor;
+  versions->min_rpc_version.major = min_major;
+  versions->min_rpc_version.minor = min_minor;
+  return true;
+}
+
+bool grpc_gcp_handshaker_resp_encode(grpc_gcp_handshaker_resp* resp,
+                                     grpc_slice* slice) {
+  if (resp == nullptr || slice == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to grpc_gcp_handshaker_resp_encode().");
+    return false;
+  }
+  pb_ostream_t size_stream;
+  memset(&size_stream, 0, sizeof(pb_ostream_t));
+  if (!pb_encode(&size_stream, grpc_gcp_HandshakerResp_fields, resp)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream));
+    return false;
+  }
+  size_t encoded_length = size_stream.bytes_written;
+  *slice = grpc_slice_malloc(encoded_length);
+  pb_ostream_t output_stream =
+      pb_ostream_from_buffer(GRPC_SLICE_START_PTR(*slice), encoded_length);
+  if (!pb_encode(&output_stream, grpc_gcp_HandshakerResp_fields, resp)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream));
+    return false;
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_decode(grpc_slice slice,
+                                    grpc_gcp_handshaker_req* req) {
+  if (req == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to grpc_gcp_handshaker_req_decode().");
+    return false;
+  }
+  pb_istream_t stream = pb_istream_from_buffer(GRPC_SLICE_START_PTR(slice),
+                                               GRPC_SLICE_LENGTH(slice));
+  req->next.in_bytes.funcs.decode = decode_string_or_bytes_cb;
+  if (!pb_decode(&stream, grpc_gcp_HandshakerReq_fields, req)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return false;
+  }
+  return true;
+}
+
+/* Check equality of a pair of grpc_slice fields. */
+static bool slice_equals(grpc_slice* l_slice, grpc_slice* r_slice) {
+  if (l_slice == nullptr && r_slice == nullptr) {
+    return true;
+  }
+  if (l_slice != nullptr && r_slice != nullptr) {
+    return grpc_slice_eq(*l_slice, *r_slice);
+  }
+  return false;
+}
+
+/* Check equality of a pair of grpc_gcp_identity fields. */
+static bool handshaker_identity_equals(const grpc_gcp_identity* l_id,
+                                       const grpc_gcp_identity* r_id) {
+  if (!((l_id->hostname.arg != nullptr) != (r_id->hostname.arg != nullptr))) {
+    if (l_id->hostname.arg != nullptr) {
+      return slice_equals(static_cast<grpc_slice*>(l_id->hostname.arg),
+                          static_cast<grpc_slice*>(r_id->hostname.arg));
+    }
+  } else {
+    return false;
+  }
+  if (!((l_id->service_account.arg != nullptr) !=
+        (r_id->service_account.arg != nullptr))) {
+    if (l_id->service_account.arg != nullptr) {
+      return slice_equals(static_cast<grpc_slice*>(l_id->service_account.arg),
+                          static_cast<grpc_slice*>(r_id->service_account.arg));
+    }
+  } else {
+    return false;
+  }
+  return true;
+}
+
+static bool handshaker_rpc_versions_equals(
+    const grpc_gcp_rpc_protocol_versions* l_version,
+    const grpc_gcp_rpc_protocol_versions* r_version) {
+  bool result = true;
+  result &=
+      (l_version->max_rpc_version.major == r_version->max_rpc_version.major);
+  result &=
+      (l_version->max_rpc_version.minor == r_version->max_rpc_version.minor);
+  result &=
+      (l_version->min_rpc_version.major == r_version->min_rpc_version.major);
+  result &=
+      (l_version->min_rpc_version.minor == r_version->min_rpc_version.minor);
+  return result;
+}
+
+/* Check equality of a pair of grpc_gcp_endpoint fields. */
+static bool handshaker_endpoint_equals(const grpc_gcp_endpoint* l_end,
+                                       const grpc_gcp_endpoint* r_end) {
+  bool result = true;
+  result &= (l_end->port == r_end->port);
+  result &= (l_end->protocol == r_end->protocol);
+  if (!((l_end->ip_address.arg != nullptr) !=
+        (r_end->ip_address.arg != nullptr))) {
+    if (l_end->ip_address.arg != nullptr) {
+      result &= slice_equals(static_cast<grpc_slice*>(l_end->ip_address.arg),
+                             static_cast<grpc_slice*>(r_end->ip_address.arg));
+    }
+  } else {
+    return false;
+  }
+  return result;
+}
+/**
+ * Check if a specific repeated field (i.e., target) is contained in a repeated
+ * field list (i.e., head).
+ */
+static bool repeated_field_list_contains_identity(
+    const repeated_field* head, const repeated_field* target) {
+  repeated_field* field = const_cast<repeated_field*>(head);
+  while (field != nullptr) {
+    if (handshaker_identity_equals(
+            static_cast<const grpc_gcp_identity*>(field->data),
+            static_cast<const grpc_gcp_identity*>(target->data))) {
+      return true;
+    }
+    field = field->next;
+  }
+  return false;
+}
+
+static bool repeated_field_list_contains_string(const repeated_field* head,
+                                                const repeated_field* target) {
+  repeated_field* field = const_cast<repeated_field*>(head);
+  while (field != nullptr) {
+    if (slice_equals((grpc_slice*)field->data, (grpc_slice*)target->data)) {
+      return true;
+    }
+    field = field->next;
+  }
+  return false;
+}
+
+/* Return a length of repeated field list. */
+static size_t repeated_field_list_get_length(const repeated_field* head) {
+  repeated_field* field = const_cast<repeated_field*>(head);
+  size_t len = 0;
+  while (field != nullptr) {
+    len++;
+    field = field->next;
+  }
+  return len;
+}
+
+/**
+ * Check if a pair of repeated field lists contain the same set of repeated
+ * fields.
+ */
+static bool repeated_field_list_equals_identity(const repeated_field* l_head,
+                                                const repeated_field* r_head) {
+  if (repeated_field_list_get_length(l_head) !=
+      repeated_field_list_get_length(r_head)) {
+    return false;
+  }
+  repeated_field* field = const_cast<repeated_field*>(l_head);
+  repeated_field* head = const_cast<repeated_field*>(r_head);
+  while (field != nullptr) {
+    if (!repeated_field_list_contains_identity(head, field)) {
+      return false;
+    }
+    field = field->next;
+  }
+  return true;
+}
+
+static bool repeated_field_list_equals_string(const repeated_field* l_head,
+                                              const repeated_field* r_head) {
+  if (repeated_field_list_get_length(l_head) !=
+      repeated_field_list_get_length(r_head)) {
+    return false;
+  }
+  repeated_field* field = const_cast<repeated_field*>(l_head);
+  repeated_field* head = const_cast<repeated_field*>(r_head);
+  while (field != nullptr) {
+    if (!repeated_field_list_contains_string(head, field)) {
+      return false;
+    }
+    field = field->next;
+  }
+  return true;
+}
+
+/* Check equality of a pair of ALTS client_start handshake requests. */
+bool grpc_gcp_handshaker_client_start_req_equals(
+    grpc_gcp_start_client_handshake_req* l_req,
+    grpc_gcp_start_client_handshake_req* r_req) {
+  bool result = true;
+  /* Compare handshake_security_protocol. */
+  result &=
+      l_req->handshake_security_protocol == r_req->handshake_security_protocol;
+  /* Compare application_protocols, record_protocols, and target_identities. */
+  result &= repeated_field_list_equals_string(
+      static_cast<const repeated_field*>(l_req->application_protocols.arg),
+      static_cast<const repeated_field*>(r_req->application_protocols.arg));
+  result &= repeated_field_list_equals_string(
+      static_cast<const repeated_field*>(l_req->record_protocols.arg),
+      static_cast<const repeated_field*>(r_req->record_protocols.arg));
+  result &= repeated_field_list_equals_identity(
+      static_cast<const repeated_field*>(l_req->target_identities.arg),
+      static_cast<const repeated_field*>(r_req->target_identities.arg));
+  if ((l_req->has_local_identity ^ r_req->has_local_identity) |
+      (l_req->has_local_endpoint ^ r_req->has_local_endpoint) |
+      ((l_req->has_remote_endpoint ^ r_req->has_remote_endpoint)) |
+      (l_req->has_rpc_versions ^ r_req->has_rpc_versions)) {
+    return false;
+  }
+  /* Compare local_identity, local_endpoint, and remote_endpoint. */
+  if (l_req->has_local_identity) {
+    result &= handshaker_identity_equals(&l_req->local_identity,
+                                         &r_req->local_identity);
+  }
+  if (l_req->has_local_endpoint) {
+    result &= handshaker_endpoint_equals(&l_req->local_endpoint,
+                                         &r_req->local_endpoint);
+  }
+  if (l_req->has_remote_endpoint) {
+    result &= handshaker_endpoint_equals(&l_req->remote_endpoint,
+                                         &r_req->remote_endpoint);
+  }
+  if (l_req->has_rpc_versions) {
+    result &= handshaker_rpc_versions_equals(&l_req->rpc_versions,
+                                             &r_req->rpc_versions);
+  }
+  return result;
+}
+
+/* Check equality of a pair of ALTS server_start handshake requests. */
+bool grpc_gcp_handshaker_server_start_req_equals(
+    grpc_gcp_start_server_handshake_req* l_req,
+    grpc_gcp_start_server_handshake_req* r_req) {
+  bool result = true;
+  /* Compare application_protocols. */
+  result &= repeated_field_list_equals_string(
+      static_cast<const repeated_field*>(l_req->application_protocols.arg),
+      static_cast<const repeated_field*>(r_req->application_protocols.arg));
+  /* Compare handshake_parameters. */
+  size_t i = 0, j = 0;
+  result &=
+      (l_req->handshake_parameters_count == r_req->handshake_parameters_count);
+  for (i = 0; i < l_req->handshake_parameters_count; i++) {
+    bool found = false;
+    for (j = 0; j < r_req->handshake_parameters_count; j++) {
+      if (l_req->handshake_parameters[i].key ==
+          r_req->handshake_parameters[j].key) {
+        found = true;
+        result &= repeated_field_list_equals_string(
+            static_cast<const repeated_field*>(
+                l_req->handshake_parameters[i].value.record_protocols.arg),
+            static_cast<const repeated_field*>(
+                r_req->handshake_parameters[j].value.record_protocols.arg));
+        result &= repeated_field_list_equals_identity(
+            static_cast<const repeated_field*>(
+                l_req->handshake_parameters[i].value.local_identities.arg),
+            static_cast<const repeated_field*>(
+                r_req->handshake_parameters[j].value.local_identities.arg));
+      }
+    }
+    if (!found) {
+      return false;
+    }
+  }
+  /* Compare in_bytes, local_endpoint, remote_endpoint. */
+  result &= slice_equals(static_cast<grpc_slice*>(l_req->in_bytes.arg),
+                         static_cast<grpc_slice*>(r_req->in_bytes.arg));
+  if ((l_req->has_local_endpoint ^ r_req->has_local_endpoint) |
+      (l_req->has_remote_endpoint ^ r_req->has_remote_endpoint) |
+      (l_req->has_rpc_versions ^ r_req->has_rpc_versions))
+    return false;
+  if (l_req->has_local_endpoint) {
+    result &= handshaker_endpoint_equals(&l_req->local_endpoint,
+                                         &r_req->local_endpoint);
+  }
+  if (l_req->has_remote_endpoint) {
+    result &= handshaker_endpoint_equals(&l_req->remote_endpoint,
+                                         &r_req->remote_endpoint);
+  }
+  if (l_req->has_rpc_versions) {
+    result &= handshaker_rpc_versions_equals(&l_req->rpc_versions,
+                                             &r_req->rpc_versions);
+  }
+  return result;
+}
+
+/* Check equality of a pair of ALTS handshake requests. */
+bool grpc_gcp_handshaker_req_equals(grpc_gcp_handshaker_req* l_req,
+                                    grpc_gcp_handshaker_req* r_req) {
+  if (l_req->has_next && r_req->has_next) {
+    return slice_equals(static_cast<grpc_slice*>(l_req->next.in_bytes.arg),
+                        static_cast<grpc_slice*>(r_req->next.in_bytes.arg));
+  } else if (l_req->has_client_start && r_req->has_client_start) {
+    return grpc_gcp_handshaker_client_start_req_equals(&l_req->client_start,
+                                                       &r_req->client_start);
+  } else if (l_req->has_server_start && r_req->has_server_start) {
+    return grpc_gcp_handshaker_server_start_req_equals(&l_req->server_start,
+                                                       &r_req->server_start);
+  }
+  return false;
+}
+
+/* Check equality of a pair of ALTS handshake results. */
+bool grpc_gcp_handshaker_resp_result_equals(
+    grpc_gcp_handshaker_result* l_result,
+    grpc_gcp_handshaker_result* r_result) {
+  bool result = true;
+  /* Compare application_protocol, record_protocol, and key_data. */
+  result &= slice_equals(
+      static_cast<grpc_slice*>(l_result->application_protocol.arg),
+      static_cast<grpc_slice*>(r_result->application_protocol.arg));
+  result &=
+      slice_equals(static_cast<grpc_slice*>(l_result->record_protocol.arg),
+                   static_cast<grpc_slice*>(r_result->record_protocol.arg));
+  result &= slice_equals(static_cast<grpc_slice*>(l_result->key_data.arg),
+                         static_cast<grpc_slice*>(r_result->key_data.arg));
+  /* Compare local_identity, peer_identity, and keep_channel_open. */
+  if ((l_result->has_local_identity ^ r_result->has_local_identity) |
+      (l_result->has_peer_identity ^ r_result->has_peer_identity) |
+      (l_result->has_peer_rpc_versions ^ r_result->has_peer_rpc_versions)) {
+    return false;
+  }
+  if (l_result->has_local_identity) {
+    result &= handshaker_identity_equals(&l_result->local_identity,
+                                         &r_result->local_identity);
+  }
+  if (l_result->has_peer_identity) {
+    result &= handshaker_identity_equals(&l_result->peer_identity,
+                                         &r_result->peer_identity);
+  }
+  if (l_result->has_peer_rpc_versions) {
+    result &= handshaker_rpc_versions_equals(&l_result->peer_rpc_versions,
+                                             &r_result->peer_rpc_versions);
+  }
+  result &= (l_result->keep_channel_open == r_result->keep_channel_open);
+  return result;
+}
+
+/* Check equality of a pair of ALTS handshake responses. */
+bool grpc_gcp_handshaker_resp_equals(grpc_gcp_handshaker_resp* l_resp,
+                                     grpc_gcp_handshaker_resp* r_resp) {
+  bool result = true;
+  /* Compare out_frames and bytes_consumed. */
+  result &= slice_equals(static_cast<grpc_slice*>(l_resp->out_frames.arg),
+                         static_cast<grpc_slice*>(r_resp->out_frames.arg));
+  result &= (l_resp->bytes_consumed == r_resp->bytes_consumed);
+  /* Compare result and status. */
+  if ((l_resp->has_result ^ r_resp->has_result) |
+      (l_resp->has_status ^ r_resp->has_status)) {
+    return false;
+  }
+  if (l_resp->has_result) {
+    result &= grpc_gcp_handshaker_resp_result_equals(&l_resp->result,
+                                                     &r_resp->result);
+  }
+  if (l_resp->has_status) {
+    result &= (l_resp->status.code == r_resp->status.code);
+    result &=
+        slice_equals(static_cast<grpc_slice*>(l_resp->status.details.arg),
+                     static_cast<grpc_slice*>(r_resp->status.details.arg));
+  }
+  return result;
+}
diff --git a/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h b/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h
new file mode 100644
index 0000000..2fcbb4e
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 GRPC_TEST_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_TEST_LIB_H
+#define GRPC_TEST_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_TEST_LIB_H
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+/**
+ * The first part of this file contains function signatures for de-serializing
+ * ALTS handshake requests and setting/serializing ALTS handshake responses,
+ * which simulate the behaviour of grpc server that runs ALTS handshaker
+ * service.
+ */
+
+/**
+ * This method creates a ALTS handshaker request that is used to hold
+ * de-serialized result.
+ */
+grpc_gcp_handshaker_req* grpc_gcp_handshaker_decoded_req_create(
+    grpc_gcp_handshaker_req_type type);
+
+/* This method de-serializes a ALTS handshaker request. */
+bool grpc_gcp_handshaker_req_decode(grpc_slice slice,
+                                    grpc_gcp_handshaker_req* req);
+
+/* This method serializes a ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_encode(grpc_gcp_handshaker_resp* resp,
+                                     grpc_slice* slice);
+
+/* This method sets application protocol of ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_application_protocol(
+    grpc_gcp_handshaker_resp* resp, const char* application_protocol);
+
+/* This method sets record protocol of ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_record_protocol(
+    grpc_gcp_handshaker_resp* resp, const char* record_protocol);
+
+/* This method sets key_data of ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_key_data(grpc_gcp_handshaker_resp* resp,
+                                           const char* key_data, size_t size);
+
+/* This method sets local identity's hostname for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_local_identity_hostname(
+    grpc_gcp_handshaker_resp* resp, const char* hostname);
+
+/**
+ * This method sets local identity's service account for ALTS handshaker
+ * response.
+ */
+bool grpc_gcp_handshaker_resp_set_local_identity_service_account(
+    grpc_gcp_handshaker_resp* resp, const char* service_account);
+
+/* This method sets peer identity's hostname for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_peer_identity_hostname(
+    grpc_gcp_handshaker_resp* resp, const char* hostname);
+
+/**
+ * This method sets peer identity's service account for ALTS handshaker
+ * response.
+ */
+bool grpc_gcp_handshaker_resp_set_peer_identity_service_account(
+    grpc_gcp_handshaker_resp* resp, const char* service_account);
+
+/* This method sets keep_channel_open for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_channel_open(grpc_gcp_handshaker_resp* resp,
+                                               bool keep_channel_open);
+
+/* This method sets code for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_code(grpc_gcp_handshaker_resp* resp,
+                                       uint32_t code);
+
+/* This method sets details for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_details(grpc_gcp_handshaker_resp* resp,
+                                          const char* details);
+
+/* This method sets out_frames for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_out_frames(grpc_gcp_handshaker_resp* resp,
+                                             const char* out_frames,
+                                             size_t size);
+
+/* This method sets peer_rpc_versions for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_peer_rpc_versions(
+    grpc_gcp_handshaker_resp* resp, uint32_t max_major, uint32_t max_minor,
+    uint32_t min_major, uint32_t min_minor);
+
+/* This method sets bytes_consumed for ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_set_bytes_consumed(grpc_gcp_handshaker_resp* resp,
+                                                 int32_t bytes_consumed);
+
+/* This method serializes ALTS handshaker response. */
+bool grpc_gcp_handshaker_resp_encode(grpc_gcp_handshaker_resp* resp,
+                                     grpc_slice* slice);
+
+/* This method de-serializes ALTS handshaker request. */
+bool grpc_gcp_handshaker_req_decode(grpc_slice slice,
+                                    grpc_gcp_handshaker_req* req);
+
+/**
+ * The second part contains function signatures for checking equality of a pair
+ * of ALTS handshake requests/responses.
+ */
+
+/* This method checks equality of two client_start handshaker requests. */
+bool grpc_gcp_handshaker_client_start_req_equals(
+    grpc_gcp_start_client_handshake_req* l_req,
+    grpc_gcp_start_client_handshake_req* r_req);
+
+/* This method checks equality of two server_start handshaker requests. */
+bool grpc_gcp_handshaker_server_start_req_equals(
+    grpc_gcp_start_server_handshake_req* l_req,
+    grpc_gcp_start_server_handshake_req* r_req);
+
+/* This method checks equality of two ALTS handshaker requests. */
+bool grpc_gcp_handshaker_req_equals(grpc_gcp_handshaker_req* l_req,
+                                    grpc_gcp_handshaker_req* r_req);
+
+/* This method checks equality of two handshaker response results. */
+bool grpc_gcp_handshaker_resp_result_equals(
+    grpc_gcp_handshaker_result* l_result, grpc_gcp_handshaker_result* r_result);
+
+/* This method checks equality of two ALTS handshaker responses. */
+bool grpc_gcp_handshaker_resp_equals(grpc_gcp_handshaker_resp* l_resp,
+                                     grpc_gcp_handshaker_resp* r_resp);
+
+#endif  // GRPC_TEST_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_TEST_LIB_H
diff --git a/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc b/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
new file mode 100644
index 0000000..95724f8
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc
@@ -0,0 +1,682 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_event.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h"
+#include "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+
+#define ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES "Hello World"
+#define ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME "Hello Google"
+#define ALTS_TSI_HANDSHAKER_TEST_CONSUMED_BYTES "Hello "
+#define ALTS_TSI_HANDSHAKER_TEST_REMAIN_BYTES "Google"
+#define ALTS_TSI_HANDSHAKER_TEST_PEER_IDENTITY "chapi@service.google.com"
+#define ALTS_TSI_HANDSHAKER_TEST_KEY_DATA \
+  "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKL"
+#define ALTS_TSI_HANDSHAKER_TEST_BUFFER_SIZE 100
+#define ALTS_TSI_HANDSHAKER_TEST_SLEEP_TIME_IN_SECONDS 2
+#define ALTS_TSI_HANDSHAKER_TEST_MAX_RPC_VERSION_MAJOR 3
+#define ALTS_TSI_HANDSHAKER_TEST_MAX_RPC_VERSION_MINOR 2
+#define ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MAJOR 2
+#define ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MINOR 1
+
+using grpc_core::internal::
+    alts_tsi_handshaker_get_has_sent_start_message_for_testing;
+using grpc_core::internal::alts_tsi_handshaker_get_is_client_for_testing;
+using grpc_core::internal::alts_tsi_handshaker_get_recv_bytes_for_testing;
+using grpc_core::internal::alts_tsi_handshaker_set_client_for_testing;
+using grpc_core::internal::alts_tsi_handshaker_set_recv_bytes_for_testing;
+
+/* ALTS mock notification. */
+typedef struct notification {
+  gpr_cv cv;
+  gpr_mu mu;
+  bool notified;
+} notification;
+
+/* ALTS mock handshaker client. */
+typedef struct alts_mock_handshaker_client {
+  alts_handshaker_client base;
+  bool used_for_success_test;
+} alts_mock_handshaker_client;
+
+/* Type of ALTS handshaker response. */
+typedef enum {
+  INVALID,
+  FAILED,
+  CLIENT_START,
+  SERVER_START,
+  CLIENT_NEXT,
+  SERVER_NEXT,
+} alts_handshaker_response_type;
+
+static alts_tsi_event* client_start_event;
+static alts_tsi_event* client_next_event;
+static alts_tsi_event* server_start_event;
+static alts_tsi_event* server_next_event;
+static notification caller_to_tsi_notification;
+static notification tsi_to_caller_notification;
+
+static void notification_init(notification* n) {
+  gpr_mu_init(&n->mu);
+  gpr_cv_init(&n->cv);
+  n->notified = false;
+}
+
+static void notification_destroy(notification* n) {
+  gpr_mu_destroy(&n->mu);
+  gpr_cv_destroy(&n->cv);
+}
+
+static void signal(notification* n) {
+  gpr_mu_lock(&n->mu);
+  n->notified = true;
+  gpr_cv_signal(&n->cv);
+  gpr_mu_unlock(&n->mu);
+}
+
+static void wait(notification* n) {
+  gpr_mu_lock(&n->mu);
+  while (!n->notified) {
+    gpr_cv_wait(&n->cv, &n->mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+  }
+  n->notified = false;
+  gpr_mu_unlock(&n->mu);
+}
+
+/**
+ * This method mocks ALTS handshaker service to generate handshaker response
+ * for a specific request.
+ */
+static grpc_byte_buffer* generate_handshaker_response(
+    alts_handshaker_response_type type) {
+  grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_code(resp, 0));
+  switch (type) {
+    case INVALID:
+      break;
+    case CLIENT_START:
+    case SERVER_START:
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_out_frames(
+          resp, ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME,
+          strlen(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME)));
+      break;
+    case CLIENT_NEXT:
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_out_frames(
+          resp, ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME,
+          strlen(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME)));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_peer_identity_service_account(
+          resp, ALTS_TSI_HANDSHAKER_TEST_PEER_IDENTITY));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_bytes_consumed(
+          resp, strlen(ALTS_TSI_HANDSHAKER_TEST_CONSUMED_BYTES)));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_key_data(
+          resp, ALTS_TSI_HANDSHAKER_TEST_KEY_DATA,
+          strlen(ALTS_TSI_HANDSHAKER_TEST_KEY_DATA)));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_peer_rpc_versions(
+          resp, ALTS_TSI_HANDSHAKER_TEST_MAX_RPC_VERSION_MAJOR,
+          ALTS_TSI_HANDSHAKER_TEST_MAX_RPC_VERSION_MINOR,
+          ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MAJOR,
+          ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MINOR));
+      break;
+    case SERVER_NEXT:
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_peer_identity_service_account(
+          resp, ALTS_TSI_HANDSHAKER_TEST_PEER_IDENTITY));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_bytes_consumed(
+          resp, strlen(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME)));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_key_data(
+          resp, ALTS_TSI_HANDSHAKER_TEST_KEY_DATA,
+          strlen(ALTS_TSI_HANDSHAKER_TEST_KEY_DATA)));
+      GPR_ASSERT(grpc_gcp_handshaker_resp_set_peer_rpc_versions(
+          resp, ALTS_TSI_HANDSHAKER_TEST_MAX_RPC_VERSION_MAJOR,
+          ALTS_TSI_HANDSHAKER_TEST_MAX_RPC_VERSION_MINOR,
+          ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MAJOR,
+          ALTS_TSI_HANDSHAKER_TEST_MIN_RPC_VERSION_MINOR));
+      break;
+    case FAILED:
+      GPR_ASSERT(
+          grpc_gcp_handshaker_resp_set_code(resp, 3 /* INVALID ARGUMENT */));
+      break;
+  }
+  grpc_slice slice;
+  GPR_ASSERT(grpc_gcp_handshaker_resp_encode(resp, &slice));
+  if (type == INVALID) {
+    grpc_slice bad_slice =
+        grpc_slice_split_head(&slice, GRPC_SLICE_LENGTH(slice) - 1);
+    grpc_slice_unref(slice);
+    slice = grpc_slice_ref(bad_slice);
+    grpc_slice_unref(bad_slice);
+  }
+  grpc_byte_buffer* buffer =
+      grpc_raw_byte_buffer_create(&slice, 1 /* number of slices */);
+  grpc_slice_unref(slice);
+  grpc_gcp_handshaker_resp_destroy(resp);
+  return buffer;
+}
+
+static void check_must_not_be_called(tsi_result status, void* user_data,
+                                     const unsigned char* bytes_to_send,
+                                     size_t bytes_to_send_size,
+                                     tsi_handshaker_result* result) {
+  GPR_ASSERT(0);
+}
+
+static void on_client_start_success_cb(tsi_result status, void* user_data,
+                                       const unsigned char* bytes_to_send,
+                                       size_t bytes_to_send_size,
+                                       tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_OK);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send_size == strlen(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME));
+  GPR_ASSERT(memcmp(bytes_to_send, ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME,
+                    bytes_to_send_size) == 0);
+  GPR_ASSERT(result == nullptr);
+  /* Validate peer identity. */
+  tsi_peer peer;
+  GPR_ASSERT(tsi_handshaker_result_extract_peer(result, &peer) ==
+             TSI_INVALID_ARGUMENT);
+  /* Validate frame protector. */
+  tsi_frame_protector* protector = nullptr;
+  GPR_ASSERT(tsi_handshaker_result_create_frame_protector(
+                 result, nullptr, &protector) == TSI_INVALID_ARGUMENT);
+  /* Validate unused bytes. */
+  const unsigned char* unused_bytes = nullptr;
+  size_t unused_bytes_size = 0;
+  GPR_ASSERT(tsi_handshaker_result_get_unused_bytes(result, &unused_bytes,
+                                                    &unused_bytes_size) ==
+             TSI_INVALID_ARGUMENT);
+  signal(&tsi_to_caller_notification);
+}
+
+static void on_server_start_success_cb(tsi_result status, void* user_data,
+                                       const unsigned char* bytes_to_send,
+                                       size_t bytes_to_send_size,
+                                       tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_OK);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send_size == strlen(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME));
+  GPR_ASSERT(memcmp(bytes_to_send, ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME,
+                    bytes_to_send_size) == 0);
+  GPR_ASSERT(result == nullptr);
+  /* Validate peer identity. */
+  tsi_peer peer;
+  GPR_ASSERT(tsi_handshaker_result_extract_peer(result, &peer) ==
+             TSI_INVALID_ARGUMENT);
+  /* Validate frame protector. */
+  tsi_frame_protector* protector = nullptr;
+  GPR_ASSERT(tsi_handshaker_result_create_frame_protector(
+                 result, nullptr, &protector) == TSI_INVALID_ARGUMENT);
+  /* Validate unused bytes. */
+  const unsigned char* unused_bytes = nullptr;
+  size_t unused_bytes_size = 0;
+  GPR_ASSERT(tsi_handshaker_result_get_unused_bytes(result, &unused_bytes,
+                                                    &unused_bytes_size) ==
+             TSI_INVALID_ARGUMENT);
+  signal(&tsi_to_caller_notification);
+}
+
+static void on_client_next_success_cb(tsi_result status, void* user_data,
+                                      const unsigned char* bytes_to_send,
+                                      size_t bytes_to_send_size,
+                                      tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_OK);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send_size == strlen(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME));
+  GPR_ASSERT(memcmp(bytes_to_send, ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME,
+                    bytes_to_send_size) == 0);
+  GPR_ASSERT(result != nullptr);
+  /* Validate peer identity. */
+  tsi_peer peer;
+  GPR_ASSERT(tsi_handshaker_result_extract_peer(result, &peer) == TSI_OK);
+  GPR_ASSERT(peer.property_count == kTsiAltsNumOfPeerProperties);
+  GPR_ASSERT(memcmp(TSI_ALTS_CERTIFICATE_TYPE, peer.properties[0].value.data,
+                    peer.properties[0].value.length) == 0);
+  GPR_ASSERT(memcmp(ALTS_TSI_HANDSHAKER_TEST_PEER_IDENTITY,
+                    peer.properties[1].value.data,
+                    peer.properties[1].value.length) == 0);
+  tsi_peer_destruct(&peer);
+  /* Validate unused bytes. */
+  const unsigned char* bytes = nullptr;
+  size_t bytes_size = 0;
+  GPR_ASSERT(tsi_handshaker_result_get_unused_bytes(result, &bytes,
+                                                    &bytes_size) == TSI_OK);
+  GPR_ASSERT(bytes_size == strlen(ALTS_TSI_HANDSHAKER_TEST_REMAIN_BYTES));
+  GPR_ASSERT(memcmp(bytes, ALTS_TSI_HANDSHAKER_TEST_REMAIN_BYTES, bytes_size) ==
+             0);
+  /* Validate frame protector. */
+  tsi_frame_protector* protector = nullptr;
+  GPR_ASSERT(tsi_handshaker_result_create_frame_protector(
+                 result, nullptr, &protector) == TSI_OK);
+  GPR_ASSERT(protector != nullptr);
+  tsi_frame_protector_destroy(protector);
+  tsi_handshaker_result_destroy(result);
+  signal(&tsi_to_caller_notification);
+}
+
+static void on_server_next_success_cb(tsi_result status, void* user_data,
+                                      const unsigned char* bytes_to_send,
+                                      size_t bytes_to_send_size,
+                                      tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_OK);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send_size == 0);
+  GPR_ASSERT(bytes_to_send == nullptr);
+  GPR_ASSERT(result != nullptr);
+  /* Validate peer identity. */
+  tsi_peer peer;
+  GPR_ASSERT(tsi_handshaker_result_extract_peer(result, &peer) == TSI_OK);
+  GPR_ASSERT(peer.property_count == kTsiAltsNumOfPeerProperties);
+  GPR_ASSERT(memcmp(TSI_ALTS_CERTIFICATE_TYPE, peer.properties[0].value.data,
+                    peer.properties[0].value.length) == 0);
+  GPR_ASSERT(memcmp(ALTS_TSI_HANDSHAKER_TEST_PEER_IDENTITY,
+                    peer.properties[1].value.data,
+                    peer.properties[1].value.length) == 0);
+  tsi_peer_destruct(&peer);
+  /* Validate unused bytes. */
+  const unsigned char* bytes = nullptr;
+  size_t bytes_size = 0;
+  GPR_ASSERT(tsi_handshaker_result_get_unused_bytes(result, &bytes,
+                                                    &bytes_size) == TSI_OK);
+  GPR_ASSERT(bytes_size == 0);
+  GPR_ASSERT(bytes == nullptr);
+  /* Validate frame protector. */
+  tsi_frame_protector* protector = nullptr;
+  GPR_ASSERT(tsi_handshaker_result_create_frame_protector(
+                 result, nullptr, &protector) == TSI_OK);
+  GPR_ASSERT(protector != nullptr);
+  tsi_frame_protector_destroy(protector);
+  tsi_handshaker_result_destroy(result);
+  signal(&tsi_to_caller_notification);
+}
+
+static tsi_result mock_client_start(alts_handshaker_client* self,
+                                    alts_tsi_event* event) {
+  alts_mock_handshaker_client* client =
+      reinterpret_cast<alts_mock_handshaker_client*>(self);
+  if (!client->used_for_success_test) {
+    alts_tsi_event_destroy(event);
+    return TSI_INTERNAL_ERROR;
+  }
+  GPR_ASSERT(event->cb == on_client_start_success_cb);
+  GPR_ASSERT(event->user_data == nullptr);
+  GPR_ASSERT(!alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+      event->handshaker));
+  /* Populate handshaker response for client_start request. */
+  event->recv_buffer = generate_handshaker_response(CLIENT_START);
+  client_start_event = event;
+  signal(&caller_to_tsi_notification);
+  return TSI_OK;
+}
+
+static tsi_result mock_server_start(alts_handshaker_client* self,
+                                    alts_tsi_event* event,
+                                    grpc_slice* bytes_received) {
+  alts_mock_handshaker_client* client =
+      reinterpret_cast<alts_mock_handshaker_client*>(self);
+  if (!client->used_for_success_test) {
+    alts_tsi_event_destroy(event);
+    return TSI_INTERNAL_ERROR;
+  }
+  GPR_ASSERT(event->cb == on_server_start_success_cb);
+  GPR_ASSERT(event->user_data == nullptr);
+  grpc_slice slice = grpc_empty_slice();
+  GPR_ASSERT(grpc_slice_cmp(*bytes_received, slice) == 0);
+  GPR_ASSERT(!alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+      event->handshaker));
+  /* Populate handshaker response for server_start request. */
+  event->recv_buffer = generate_handshaker_response(SERVER_START);
+  server_start_event = event;
+  grpc_slice_unref(slice);
+  signal(&caller_to_tsi_notification);
+  return TSI_OK;
+}
+
+static tsi_result mock_next(alts_handshaker_client* self, alts_tsi_event* event,
+                            grpc_slice* bytes_received) {
+  alts_mock_handshaker_client* client =
+      reinterpret_cast<alts_mock_handshaker_client*>(self);
+  if (!client->used_for_success_test) {
+    alts_tsi_event_destroy(event);
+    return TSI_INTERNAL_ERROR;
+  }
+  bool is_client =
+      alts_tsi_handshaker_get_is_client_for_testing(event->handshaker);
+  if (is_client) {
+    GPR_ASSERT(event->cb == on_client_next_success_cb);
+  } else {
+    GPR_ASSERT(event->cb == on_server_next_success_cb);
+  }
+  GPR_ASSERT(event->user_data == nullptr);
+  GPR_ASSERT(bytes_received != nullptr);
+  GPR_ASSERT(memcmp(GRPC_SLICE_START_PTR(*bytes_received),
+                    ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES,
+                    GRPC_SLICE_LENGTH(*bytes_received)) == 0);
+  GPR_ASSERT(grpc_slice_cmp(alts_tsi_handshaker_get_recv_bytes_for_testing(
+                                event->handshaker),
+                            *bytes_received) == 0);
+  GPR_ASSERT(alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+      event->handshaker));
+  /* Populate handshaker response for next request. */
+  grpc_slice out_frame =
+      grpc_slice_from_static_string(ALTS_TSI_HANDSHAKER_TEST_OUT_FRAME);
+  if (is_client) {
+    event->recv_buffer = generate_handshaker_response(CLIENT_NEXT);
+  } else {
+    event->recv_buffer = generate_handshaker_response(SERVER_NEXT);
+  }
+  alts_tsi_handshaker_set_recv_bytes_for_testing(event->handshaker, &out_frame);
+  if (is_client) {
+    client_next_event = event;
+  } else {
+    server_next_event = event;
+  }
+  signal(&caller_to_tsi_notification);
+  grpc_slice_unref(out_frame);
+  return TSI_OK;
+}
+
+static void mock_destruct(alts_handshaker_client* client) {}
+
+static const alts_handshaker_client_vtable vtable = {
+    mock_client_start, mock_server_start, mock_next, mock_destruct};
+
+static alts_handshaker_client* alts_mock_handshaker_client_create(
+    bool used_for_success_test) {
+  alts_mock_handshaker_client* client =
+      static_cast<alts_mock_handshaker_client*>(gpr_zalloc(sizeof(*client)));
+  client->base.vtable = &vtable;
+  client->used_for_success_test = used_for_success_test;
+  return &client->base;
+}
+
+static tsi_handshaker* create_test_handshaker(bool used_for_success_test,
+                                              bool is_client) {
+  tsi_handshaker* handshaker = nullptr;
+  alts_handshaker_client* client =
+      alts_mock_handshaker_client_create(used_for_success_test);
+  grpc_alts_credentials_options* options =
+      grpc_alts_credentials_client_options_create();
+  alts_tsi_handshaker_create(options, "target_name", "lame", is_client,
+                             &handshaker);
+  alts_tsi_handshaker* alts_handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+  alts_tsi_handshaker_set_client_for_testing(alts_handshaker, client);
+  grpc_alts_credentials_options_destroy(options);
+  return handshaker;
+}
+
+static void check_handshaker_next_invalid_input() {
+  /* Initialization. */
+  tsi_handshaker* handshaker = create_test_handshaker(true, true);
+  /* Check nullptr handshaker. */
+  GPR_ASSERT(tsi_handshaker_next(nullptr, nullptr, 0, nullptr, nullptr, nullptr,
+                                 check_must_not_be_called,
+                                 nullptr) == TSI_INVALID_ARGUMENT);
+  /* Check nullptr callback. */
+  GPR_ASSERT(tsi_handshaker_next(handshaker, nullptr, 0, nullptr, nullptr,
+                                 nullptr, nullptr,
+                                 nullptr) == TSI_INVALID_ARGUMENT);
+  /* Cleanup. */
+  tsi_handshaker_destroy(handshaker);
+}
+
+static void check_handshaker_next_success() {
+  /**
+   * Create handshakers for which internal mock client is going to do
+   * correctness check.
+   */
+  tsi_handshaker* client_handshaker = create_test_handshaker(
+      true /* used_for_success_test */, true /* is_client */);
+  tsi_handshaker* server_handshaker = create_test_handshaker(
+      true /* used_for_success_test */, false /* is_client */);
+  /* Client start. */
+  GPR_ASSERT(tsi_handshaker_next(client_handshaker, nullptr, 0, nullptr,
+                                 nullptr, nullptr, on_client_start_success_cb,
+                                 nullptr) == TSI_ASYNC);
+  wait(&tsi_to_caller_notification);
+  /* Client next. */
+  GPR_ASSERT(tsi_handshaker_next(
+                 client_handshaker,
+                 (const unsigned char*)ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES,
+                 strlen(ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES), nullptr, nullptr,
+                 nullptr, on_client_next_success_cb, nullptr) == TSI_ASYNC);
+  wait(&tsi_to_caller_notification);
+  /* Server start. */
+  GPR_ASSERT(tsi_handshaker_next(server_handshaker, nullptr, 0, nullptr,
+                                 nullptr, nullptr, on_server_start_success_cb,
+                                 nullptr) == TSI_ASYNC);
+  wait(&tsi_to_caller_notification);
+  /* Server next. */
+  GPR_ASSERT(tsi_handshaker_next(
+                 server_handshaker,
+                 (const unsigned char*)ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES,
+                 strlen(ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES), nullptr, nullptr,
+                 nullptr, on_server_next_success_cb, nullptr) == TSI_ASYNC);
+  wait(&tsi_to_caller_notification);
+  /* Cleanup. */
+  tsi_handshaker_destroy(server_handshaker);
+  tsi_handshaker_destroy(client_handshaker);
+}
+
+static void check_handshaker_next_failure() {
+  /**
+   * Create handshakers for which internal mock client is always going to fail.
+   */
+  tsi_handshaker* client_handshaker = create_test_handshaker(
+      false /* used_for_success_test */, true /* is_client */);
+  tsi_handshaker* server_handshaker = create_test_handshaker(
+      false /* used_for_success_test */, false /* is_client */);
+  /* Client start. */
+  GPR_ASSERT(tsi_handshaker_next(client_handshaker, nullptr, 0, nullptr,
+                                 nullptr, nullptr, check_must_not_be_called,
+                                 nullptr) == TSI_INTERNAL_ERROR);
+  /* Server start. */
+  GPR_ASSERT(tsi_handshaker_next(server_handshaker, nullptr, 0, nullptr,
+                                 nullptr, nullptr, check_must_not_be_called,
+                                 nullptr) == TSI_INTERNAL_ERROR);
+  /* Server next. */
+  GPR_ASSERT(tsi_handshaker_next(
+                 server_handshaker,
+                 (const unsigned char*)ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES,
+                 strlen(ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES), nullptr, nullptr,
+                 nullptr, check_must_not_be_called,
+                 nullptr) == TSI_INTERNAL_ERROR);
+  /* Client next. */
+  GPR_ASSERT(tsi_handshaker_next(
+                 client_handshaker,
+                 (const unsigned char*)ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES,
+                 strlen(ALTS_TSI_HANDSHAKER_TEST_RECV_BYTES), nullptr, nullptr,
+                 nullptr, check_must_not_be_called,
+                 nullptr) == TSI_INTERNAL_ERROR);
+  /* Cleanup. */
+  tsi_handshaker_destroy(server_handshaker);
+  tsi_handshaker_destroy(client_handshaker);
+}
+
+static void on_invalid_input_cb(tsi_result status, void* user_data,
+                                const unsigned char* bytes_to_send,
+                                size_t bytes_to_send_size,
+                                tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_INTERNAL_ERROR);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send == nullptr);
+  GPR_ASSERT(bytes_to_send_size == 0);
+  GPR_ASSERT(result == nullptr);
+}
+
+static void on_failed_grpc_call_cb(tsi_result status, void* user_data,
+                                   const unsigned char* bytes_to_send,
+                                   size_t bytes_to_send_size,
+                                   tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_INTERNAL_ERROR);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send == nullptr);
+  GPR_ASSERT(bytes_to_send_size == 0);
+  GPR_ASSERT(result == nullptr);
+}
+
+static void check_handle_response_invalid_input() {
+  /**
+   * Create a handshaker at the client side, for which internal mock client is
+   * always going to fail.
+   */
+  tsi_handshaker* handshaker = create_test_handshaker(
+      false /* used_for_success_test */, true /* is_client */);
+  alts_tsi_handshaker* alts_handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+  grpc_byte_buffer recv_buffer;
+  /* Check nullptr handshaker. */
+  alts_tsi_handshaker_handle_response(nullptr, &recv_buffer, GRPC_STATUS_OK,
+                                      nullptr, on_invalid_input_cb, nullptr,
+                                      true);
+  /* Check nullptr recv_bytes. */
+  alts_tsi_handshaker_handle_response(alts_handshaker, nullptr, GRPC_STATUS_OK,
+                                      nullptr, on_invalid_input_cb, nullptr,
+                                      true);
+  /* Check failed grpc call made to handshaker service. */
+  alts_tsi_handshaker_handle_response(alts_handshaker, &recv_buffer,
+                                      GRPC_STATUS_UNKNOWN, nullptr,
+                                      on_failed_grpc_call_cb, nullptr, true);
+
+  alts_tsi_handshaker_handle_response(alts_handshaker, &recv_buffer,
+                                      GRPC_STATUS_OK, nullptr,
+                                      on_failed_grpc_call_cb, nullptr, false);
+
+  /* Cleanup. */
+  tsi_handshaker_destroy(handshaker);
+}
+
+static void on_invalid_resp_cb(tsi_result status, void* user_data,
+                               const unsigned char* bytes_to_send,
+                               size_t bytes_to_send_size,
+                               tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_DATA_CORRUPTED);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send == nullptr);
+  GPR_ASSERT(bytes_to_send_size == 0);
+  GPR_ASSERT(result == nullptr);
+}
+
+static void check_handle_response_invalid_resp() {
+  /**
+   * Create a handshaker at the client side, for which internal mock client is
+   * always going to fail.
+   */
+  tsi_handshaker* handshaker = create_test_handshaker(
+      false /* used_for_success_test */, true /* is_client */);
+  alts_tsi_handshaker* alts_handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+  /* Tests. */
+  grpc_byte_buffer* recv_buffer = generate_handshaker_response(INVALID);
+  alts_tsi_handshaker_handle_response(alts_handshaker, recv_buffer,
+                                      GRPC_STATUS_OK, nullptr,
+                                      on_invalid_resp_cb, nullptr, true);
+  /* Cleanup. */
+  grpc_byte_buffer_destroy(recv_buffer);
+  tsi_handshaker_destroy(handshaker);
+}
+
+static void check_handle_response_success(void* unused) {
+  /* Client start. */
+  wait(&caller_to_tsi_notification);
+  alts_tsi_event_dispatch_to_handshaker(client_start_event, true /* is_ok */);
+  alts_tsi_event_destroy(client_start_event);
+  /* Client next. */
+  wait(&caller_to_tsi_notification);
+  alts_tsi_event_dispatch_to_handshaker(client_next_event, true /* is_ok */);
+  alts_tsi_event_destroy(client_next_event);
+  /* Server start. */
+  wait(&caller_to_tsi_notification);
+  alts_tsi_event_dispatch_to_handshaker(server_start_event, true /* is_ok */);
+  alts_tsi_event_destroy(server_start_event);
+  /* Server next. */
+  wait(&caller_to_tsi_notification);
+  alts_tsi_event_dispatch_to_handshaker(server_next_event, true /* is_ok */);
+  alts_tsi_event_destroy(server_next_event);
+}
+
+static void on_failed_resp_cb(tsi_result status, void* user_data,
+                              const unsigned char* bytes_to_send,
+                              size_t bytes_to_send_size,
+                              tsi_handshaker_result* result) {
+  GPR_ASSERT(status == TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(user_data == nullptr);
+  GPR_ASSERT(bytes_to_send == nullptr);
+  GPR_ASSERT(bytes_to_send_size == 0);
+  GPR_ASSERT(result == nullptr);
+}
+
+static void check_handle_response_failure() {
+  /**
+   * Create a handshaker at the client side, for which internal mock client is
+   * always going to fail.
+   */
+  tsi_handshaker* handshaker = create_test_handshaker(
+      false /* used_for_success_test */, true /* is_client */);
+  alts_tsi_handshaker* alts_handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(handshaker);
+  /* Tests. */
+  grpc_byte_buffer* recv_buffer = generate_handshaker_response(FAILED);
+  alts_tsi_handshaker_handle_response(alts_handshaker, recv_buffer,
+                                      GRPC_STATUS_OK, nullptr,
+                                      on_failed_resp_cb, nullptr, true);
+  grpc_byte_buffer_destroy(recv_buffer);
+  /* Cleanup. */
+  tsi_handshaker_destroy(handshaker);
+}
+
+void check_handshaker_success() {
+  /* Initialization. */
+  notification_init(&caller_to_tsi_notification);
+  notification_init(&tsi_to_caller_notification);
+  client_start_event = nullptr;
+  client_next_event = nullptr;
+  server_start_event = nullptr;
+  server_next_event = nullptr;
+  /* Tests. */
+  grpc_core::Thread thd("alts_tsi_handshaker_test",
+                        &check_handle_response_success, nullptr);
+  thd.Start();
+  check_handshaker_next_success();
+  thd.Join();
+  /* Cleanup. */
+  notification_destroy(&caller_to_tsi_notification);
+  notification_destroy(&tsi_to_caller_notification);
+}
+
+int main(int argc, char** argv) {
+  /* Initialization. */
+  grpc_init();
+  /* Tests. */
+  check_handshaker_success();
+  check_handshaker_next_invalid_input();
+  check_handshaker_next_failure();
+  check_handle_response_invalid_input();
+  check_handle_response_invalid_resp();
+  check_handle_response_failure();
+  /* Cleanup. */
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc b/test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc
new file mode 100644
index 0000000..98c5d23
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+#include "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+
+#define ALTS_TSI_UTILS_TEST_OUT_FRAME "Hello Google"
+
+static void convert_to_tsi_result_test() {
+  GPR_ASSERT(alts_tsi_utils_convert_to_tsi_result(GRPC_STATUS_OK) == TSI_OK);
+  GPR_ASSERT(alts_tsi_utils_convert_to_tsi_result(GRPC_STATUS_UNKNOWN) ==
+             TSI_UNKNOWN_ERROR);
+  GPR_ASSERT(alts_tsi_utils_convert_to_tsi_result(
+                 GRPC_STATUS_INVALID_ARGUMENT) == TSI_INVALID_ARGUMENT);
+  GPR_ASSERT(alts_tsi_utils_convert_to_tsi_result(GRPC_STATUS_OUT_OF_RANGE) ==
+             TSI_UNKNOWN_ERROR);
+  GPR_ASSERT(alts_tsi_utils_convert_to_tsi_result(GRPC_STATUS_INTERNAL) ==
+             TSI_INTERNAL_ERROR);
+  GPR_ASSERT(alts_tsi_utils_convert_to_tsi_result(GRPC_STATUS_NOT_FOUND) ==
+             TSI_NOT_FOUND);
+}
+
+static void deserialize_response_test() {
+  grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+  GPR_ASSERT(grpc_gcp_handshaker_resp_set_out_frames(
+      resp, ALTS_TSI_UTILS_TEST_OUT_FRAME,
+      strlen(ALTS_TSI_UTILS_TEST_OUT_FRAME)));
+  grpc_slice slice;
+  GPR_ASSERT(grpc_gcp_handshaker_resp_encode(resp, &slice));
+
+  /* Valid serialization. */
+  grpc_byte_buffer* buffer =
+      grpc_raw_byte_buffer_create(&slice, 1 /* number of slices */);
+  grpc_gcp_handshaker_resp* decoded_resp =
+      alts_tsi_utils_deserialize_response(buffer);
+  GPR_ASSERT(grpc_gcp_handshaker_resp_equals(resp, decoded_resp));
+  grpc_byte_buffer_destroy(buffer);
+
+  /* Invalid serializaiton. */
+  grpc_slice bad_slice =
+      grpc_slice_split_head(&slice, GRPC_SLICE_LENGTH(slice) - 1);
+  buffer = grpc_raw_byte_buffer_create(&bad_slice, 1 /* number of slices */);
+  GPR_ASSERT(alts_tsi_utils_deserialize_response(buffer) == nullptr);
+
+  /* Clean up. */
+  grpc_slice_unref(slice);
+  grpc_slice_unref(bad_slice);
+  grpc_byte_buffer_destroy(buffer);
+  grpc_gcp_handshaker_resp_destroy(resp);
+  grpc_gcp_handshaker_resp_destroy(decoded_resp);
+}
+
+int main(int argc, char** argv) {
+  /* Tests. */
+  deserialize_response_test();
+  convert_to_tsi_result_test();
+  return 0;
+}
diff --git a/test/core/tsi/alts/handshaker/transport_security_common_api_test.cc b/test/core/tsi/alts/handshaker/transport_security_common_api_test.cc
new file mode 100644
index 0000000..6ff1357
--- /dev/null
+++ b/test/core/tsi/alts/handshaker/transport_security_common_api_test.cc
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+const size_t kMaxRpcVersionMajor = 3;
+const size_t kMaxRpcVersionMinor = 2;
+const size_t kMinRpcVersionMajor = 2;
+const size_t kMinRpcVersionMinor = 1;
+
+static bool grpc_gcp_rpc_protocol_versions_equal(
+    grpc_gcp_rpc_protocol_versions* l_versions,
+    grpc_gcp_rpc_protocol_versions* r_versions) {
+  GPR_ASSERT(l_versions != nullptr && r_versions != nullptr);
+  if ((l_versions->has_max_rpc_version ^ r_versions->has_max_rpc_version) |
+      (l_versions->has_min_rpc_version ^ r_versions->has_min_rpc_version)) {
+    return false;
+  }
+  if (l_versions->has_max_rpc_version) {
+    if ((l_versions->max_rpc_version.major !=
+         r_versions->max_rpc_version.major) ||
+        (l_versions->max_rpc_version.minor !=
+         r_versions->max_rpc_version.minor)) {
+      return false;
+    }
+  }
+  if (l_versions->has_min_rpc_version) {
+    if ((l_versions->min_rpc_version.major !=
+         r_versions->min_rpc_version.major) ||
+        (l_versions->min_rpc_version.minor !=
+         r_versions->min_rpc_version.minor)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static void test_success() {
+  grpc_gcp_rpc_protocol_versions version;
+  grpc_gcp_rpc_protocol_versions decoded_version;
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(
+      &version, kMaxRpcVersionMajor, kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(
+      &version, kMinRpcVersionMajor, kMinRpcVersionMinor));
+  /* Serializes to raw bytes. */
+  size_t encoded_length =
+      grpc_gcp_rpc_protocol_versions_encode_length(&version);
+  uint8_t* encoded_bytes = static_cast<uint8_t*>(gpr_malloc(encoded_length));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+      &version, encoded_bytes, encoded_length));
+  grpc_slice encoded_slice;
+  /* Serializes to grpc slice. */
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_encode(&version, &encoded_slice));
+  /* Checks serialized raw bytes and serialized grpc slice have same content. */
+  GPR_ASSERT(encoded_length == GRPC_SLICE_LENGTH(encoded_slice));
+  GPR_ASSERT(memcmp(encoded_bytes, GRPC_SLICE_START_PTR(encoded_slice),
+                    encoded_length) == 0);
+  /* Deserializes and compares with the original version. */
+  GPR_ASSERT(
+      grpc_gcp_rpc_protocol_versions_decode(encoded_slice, &decoded_version));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_equal(&version, &decoded_version));
+  grpc_slice_unref(encoded_slice);
+  gpr_free(encoded_bytes);
+}
+
+static void test_failure() {
+  grpc_gcp_rpc_protocol_versions version, decoded_version;
+  grpc_slice encoded_slice;
+  /* Test for invalid arguments. */
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_set_max(
+      nullptr, kMaxRpcVersionMajor, kMaxRpcVersionMinor));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_set_min(
+      nullptr, kMinRpcVersionMajor, kMinRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_encode_length(nullptr) == 0);
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(
+      &version, kMaxRpcVersionMajor, kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(
+      &version, kMinRpcVersionMajor, kMinRpcVersionMinor));
+  size_t encoded_length =
+      grpc_gcp_rpc_protocol_versions_encode_length(&version);
+  uint8_t* encoded_bytes = static_cast<uint8_t*>(gpr_malloc(encoded_length));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+      nullptr, encoded_bytes, encoded_length));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+      &version, nullptr, encoded_length));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+      &version, encoded_bytes, 0));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_encode(nullptr, &encoded_slice));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_encode(&version, nullptr));
+  GPR_ASSERT(!grpc_gcp_rpc_protocol_versions_decode(encoded_slice, nullptr));
+  /* Test for nanopb decode. */
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_encode(&version, &encoded_slice));
+  grpc_slice bad_slice = grpc_slice_split_head(
+      &encoded_slice, GRPC_SLICE_LENGTH(encoded_slice) - 1);
+  grpc_slice_unref(encoded_slice);
+  GPR_ASSERT(
+      !grpc_gcp_rpc_protocol_versions_decode(bad_slice, &decoded_version));
+  grpc_slice_unref(bad_slice);
+  gpr_free(encoded_bytes);
+}
+
+static void test_copy() {
+  grpc_gcp_rpc_protocol_versions src;
+  grpc_gcp_rpc_protocol_versions des;
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&src, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&src, kMinRpcVersionMajor,
+                                                    kMinRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_copy(&src, &des));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_equal(&src, &des));
+}
+
+static void test_check_success() {
+  grpc_gcp_rpc_protocol_versions v1;
+  grpc_gcp_rpc_protocol_versions v2;
+  grpc_gcp_rpc_protocol_versions_version highest_common_version;
+  /* test equality. */
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&v1, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&v1, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&v2, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&v2, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_check(
+                 (const grpc_gcp_rpc_protocol_versions*)&v1,
+                 (const grpc_gcp_rpc_protocol_versions*)&v2,
+                 &highest_common_version) == 1);
+  GPR_ASSERT(grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+                 &highest_common_version, &v1.max_rpc_version) == 0);
+
+  /* test inequality. */
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&v1, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&v1, kMinRpcVersionMinor,
+                                                    kMinRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&v2, kMaxRpcVersionMajor,
+                                                    kMinRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&v2, kMinRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_check(
+                 (const grpc_gcp_rpc_protocol_versions*)&v1,
+                 (const grpc_gcp_rpc_protocol_versions*)&v2,
+                 &highest_common_version) == 1);
+  GPR_ASSERT(grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+                 &highest_common_version, &v2.max_rpc_version) == 0);
+}
+
+static void test_check_failure() {
+  grpc_gcp_rpc_protocol_versions v1;
+  grpc_gcp_rpc_protocol_versions v2;
+  grpc_gcp_rpc_protocol_versions_version highest_common_version;
+
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&v1, kMinRpcVersionMajor,
+                                                    kMinRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&v1, kMinRpcVersionMajor,
+                                                    kMinRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_max(&v2, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_set_min(&v2, kMaxRpcVersionMajor,
+                                                    kMaxRpcVersionMinor));
+  GPR_ASSERT(grpc_gcp_rpc_protocol_versions_check(
+                 (const grpc_gcp_rpc_protocol_versions*)&v1,
+                 (const grpc_gcp_rpc_protocol_versions*)&v2,
+                 &highest_common_version) == 0);
+}
+
+int main(int argc, char** argv) {
+  /* Run tests. */
+  test_success();
+  test_failure();
+  test_copy();
+  test_check_success();
+  test_check_failure();
+  return 0;
+}
diff --git a/test/core/tsi/alts/zero_copy_frame_protector/BUILD b/test/core/tsi/alts/zero_copy_frame_protector/BUILD
new file mode 100644
index 0000000..80f4572
--- /dev/null
+++ b/test/core/tsi/alts/zero_copy_frame_protector/BUILD
@@ -0,0 +1,49 @@
+# Copyright 2018 gRPC authors.
+# 
+# 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.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
+
+licenses(["notice"])  # Apache v2
+
+grpc_package(name = "zero_copy_frame_protector")
+
+grpc_cc_test(
+    name = "alts_grpc_record_protocol_test",
+    srcs = ["alts_grpc_record_protocol_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_iovec_record_protocol_test",
+    srcs = ["alts_iovec_record_protocol_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "alts_zero_copy_grpc_protector_test",
+    srcs = ["alts_zero_copy_grpc_protector_test.cc"],
+    language = "C++",
+    deps = [
+        "//:grpc",
+        "//test/core/tsi/alts/crypt:alts_crypt_test_util",
+    ],
+)
diff --git a/test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc b/test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc
new file mode 100644
index 0000000..fbbea71
--- /dev/null
+++ b/test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc
@@ -0,0 +1,449 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+constexpr size_t kMaxSliceLength = 256;
+constexpr size_t kMaxSlices = 10;
+constexpr size_t kSealRepeatTimes = 5;
+constexpr size_t kTagLength = 16;
+
+/* Test fixtures for each test cases.  */
+struct alts_grpc_record_protocol_test_fixture {
+  alts_grpc_record_protocol* client_protect;
+  alts_grpc_record_protocol* client_unprotect;
+  alts_grpc_record_protocol* server_protect;
+  alts_grpc_record_protocol* server_unprotect;
+};
+
+/* Test input variables for protect/unprotect operations.  */
+struct alts_grpc_record_protocol_test_var {
+  size_t header_length;
+  size_t tag_length;
+  grpc_slice_buffer original_sb;
+  grpc_slice_buffer duplicate_sb;
+  grpc_slice_buffer protected_sb;
+  grpc_slice_buffer unprotected_sb;
+};
+
+/* --- Test utility functions. --- */
+
+static void create_random_slice_buffer(grpc_slice_buffer* sb) {
+  GPR_ASSERT(sb != nullptr);
+  size_t slice_count = gsec_test_bias_random_uint32(kMaxSlices) + 1;
+  for (size_t i = 0; i < slice_count; i++) {
+    size_t slice_length = gsec_test_bias_random_uint32(kMaxSliceLength) + 1;
+    grpc_slice slice = GRPC_SLICE_MALLOC(slice_length);
+    gsec_test_random_bytes(GRPC_SLICE_START_PTR(slice), slice_length);
+    grpc_slice_buffer_add(sb, slice);
+  }
+}
+
+static uint8_t* pointer_to_nth_byte(grpc_slice_buffer* sb, size_t index) {
+  GPR_ASSERT(sb != nullptr);
+  GPR_ASSERT(index < sb->length);
+  for (size_t i = 0; i < sb->count; i++) {
+    if (index < GRPC_SLICE_LENGTH(sb->slices[i])) {
+      return GRPC_SLICE_START_PTR(sb->slices[i]) + index;
+    } else {
+      index -= GRPC_SLICE_LENGTH(sb->slices[i]);
+    }
+  }
+  return nullptr;
+}
+
+/* Checks if two slice buffer contents are the same. It is not super efficient,
+ * but OK for testing.  */
+static bool are_slice_buffers_equal(grpc_slice_buffer* first,
+                                    grpc_slice_buffer* second) {
+  GPR_ASSERT(first != nullptr);
+  GPR_ASSERT(second != nullptr);
+  if (first->length != second->length) {
+    return false;
+  }
+  for (size_t i = 0; i < first->length; i++) {
+    uint8_t* first_ptr = pointer_to_nth_byte(first, i);
+    uint8_t* second_ptr = pointer_to_nth_byte(second, i);
+    GPR_ASSERT(first_ptr != nullptr);
+    GPR_ASSERT(second_ptr != nullptr);
+    if ((*first_ptr) != (*second_ptr)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static void alter_random_byte(grpc_slice_buffer* sb) {
+  GPR_ASSERT(sb != nullptr);
+  if (sb->length == 0) {
+    return;
+  }
+  uint32_t offset =
+      gsec_test_bias_random_uint32(static_cast<uint32_t>(sb->length));
+  uint8_t* ptr = pointer_to_nth_byte(sb, offset);
+  (*ptr)++;
+}
+
+static alts_grpc_record_protocol_test_fixture*
+test_fixture_integrity_only_create(bool rekey) {
+  alts_grpc_record_protocol_test_fixture* fixture =
+      static_cast<alts_grpc_record_protocol_test_fixture*>(
+          gpr_zalloc(sizeof(alts_grpc_record_protocol_test_fixture)));
+  size_t key_length = rekey ? kAes128GcmRekeyKeyLength : kAes128GcmKeyLength;
+  uint8_t* key;
+  gsec_test_random_array(&key, key_length);
+  gsec_aead_crypter* crypter = nullptr;
+
+  /* Create client record protocol for protect. */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_integrity_only_record_protocol_create(
+                 crypter, 8, /*is_client=*/true, /*is_protect=*/true,
+                 &fixture->client_protect) == TSI_OK);
+  /* Create client record protocol for unprotect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_integrity_only_record_protocol_create(
+                 crypter, 8, /*is_client=*/true, /*is_protect=*/false,
+                 &fixture->client_unprotect) == TSI_OK);
+  /* Create server record protocol for protect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_integrity_only_record_protocol_create(
+                 crypter, 8, /*is_client=*/false, /*is_protect=*/true,
+                 &fixture->server_protect) == TSI_OK);
+  /* Create server record protocol for unprotect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_integrity_only_record_protocol_create(
+                 crypter, 8, /*is_client=*/false, /*is_protect=*/false,
+                 &fixture->server_unprotect) == TSI_OK);
+
+  gpr_free(key);
+  return fixture;
+}
+
+static alts_grpc_record_protocol_test_fixture*
+test_fixture_integrity_only_no_rekey_create() {
+  return test_fixture_integrity_only_create(false);
+}
+
+static alts_grpc_record_protocol_test_fixture*
+test_fixture_integrity_only_rekey_create() {
+  return test_fixture_integrity_only_create(true);
+}
+
+static alts_grpc_record_protocol_test_fixture*
+test_fixture_privacy_integrity_create(bool rekey) {
+  alts_grpc_record_protocol_test_fixture* fixture =
+      static_cast<alts_grpc_record_protocol_test_fixture*>(
+          gpr_zalloc(sizeof(alts_grpc_record_protocol_test_fixture)));
+  size_t key_length = rekey ? kAes128GcmRekeyKeyLength : kAes128GcmKeyLength;
+  uint8_t* key;
+  gsec_test_random_array(&key, key_length);
+  gsec_aead_crypter* crypter = nullptr;
+
+  /* Create client record protocol for protect. */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_privacy_integrity_record_protocol_create(
+                 crypter, 8, /*is_client=*/true, /*is_protect=*/true,
+                 &fixture->client_protect) == TSI_OK);
+  /* Create client record protocol for unprotect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_privacy_integrity_record_protocol_create(
+                 crypter, 8, /*is_client=*/true, /*is_protect=*/false,
+                 &fixture->client_unprotect) == TSI_OK);
+  /* Create server record protocol for protect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_privacy_integrity_record_protocol_create(
+                 crypter, 8, /*is_client=*/false, /*is_protect=*/true,
+                 &fixture->server_protect) == TSI_OK);
+  /* Create server record protocol for unprotect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_grpc_privacy_integrity_record_protocol_create(
+                 crypter, 8, /*is_client=*/false, /*is_protect=*/false,
+                 &fixture->server_unprotect) == TSI_OK);
+
+  gpr_free(key);
+  return fixture;
+}
+
+static alts_grpc_record_protocol_test_fixture*
+test_fixture_privacy_integrity_no_rekey_create() {
+  return test_fixture_privacy_integrity_create(false);
+}
+
+static alts_grpc_record_protocol_test_fixture*
+test_fixture_privacy_integrity_rekey_create() {
+  return test_fixture_privacy_integrity_create(true);
+}
+
+static void alts_grpc_record_protocol_test_fixture_destroy(
+    alts_grpc_record_protocol_test_fixture* fixture) {
+  if (fixture == nullptr) {
+    return;
+  }
+  grpc_core::ExecCtx exec_ctx;
+  alts_grpc_record_protocol_destroy(fixture->client_protect);
+  alts_grpc_record_protocol_destroy(fixture->client_unprotect);
+  alts_grpc_record_protocol_destroy(fixture->server_protect);
+  alts_grpc_record_protocol_destroy(fixture->server_unprotect);
+  grpc_core::ExecCtx::Get()->Flush();
+  gpr_free(fixture);
+}
+
+static alts_grpc_record_protocol_test_var*
+alts_grpc_record_protocol_test_var_create() {
+  alts_grpc_record_protocol_test_var* var =
+      static_cast<alts_grpc_record_protocol_test_var*>(
+          gpr_zalloc(sizeof(alts_grpc_record_protocol_test_var)));
+  var->header_length = alts_iovec_record_protocol_get_header_length();
+  var->tag_length = kTagLength;
+  /* Initialized slice buffers.  */
+  grpc_slice_buffer_init(&var->original_sb);
+  grpc_slice_buffer_init(&var->duplicate_sb);
+  grpc_slice_buffer_init(&var->protected_sb);
+  grpc_slice_buffer_init(&var->unprotected_sb);
+  /* Randomly sets content of original_sb, and copies into duplicate_sb.  */
+  create_random_slice_buffer(&var->original_sb);
+  for (size_t i = 0; i < var->original_sb.count; i++) {
+    grpc_slice_buffer_add(&var->duplicate_sb,
+                          grpc_slice_ref(var->original_sb.slices[i]));
+  }
+  return var;
+}
+
+static void alts_grpc_record_protocol_test_var_destroy(
+    alts_grpc_record_protocol_test_var* var) {
+  if (var == nullptr) {
+    return;
+  }
+  grpc_slice_buffer_destroy_internal(&var->original_sb);
+  grpc_slice_buffer_destroy_internal(&var->duplicate_sb);
+  grpc_slice_buffer_destroy_internal(&var->protected_sb);
+  grpc_slice_buffer_destroy_internal(&var->unprotected_sb);
+  gpr_free(var);
+}
+
+/* --- alts grpc record protocol tests. --- */
+
+static void random_seal_unseal(alts_grpc_record_protocol* sender,
+                               alts_grpc_record_protocol* receiver) {
+  grpc_core::ExecCtx exec_ctx;
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_grpc_record_protocol_test_var* var =
+        alts_grpc_record_protocol_test_var_create();
+    /* Seals and then unseals.  */
+    size_t data_length = var->original_sb.length;
+    tsi_result status = alts_grpc_record_protocol_protect(
+        sender, &var->original_sb, &var->protected_sb);
+    GPR_ASSERT(status == TSI_OK);
+    GPR_ASSERT(var->protected_sb.length ==
+               data_length + var->header_length + var->tag_length);
+    status = alts_grpc_record_protocol_unprotect(receiver, &var->protected_sb,
+                                                 &var->unprotected_sb);
+    GPR_ASSERT(status == TSI_OK);
+    GPR_ASSERT(
+        are_slice_buffers_equal(&var->unprotected_sb, &var->duplicate_sb));
+    alts_grpc_record_protocol_test_var_destroy(var);
+  }
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+static void empty_seal_unseal(alts_grpc_record_protocol* sender,
+                              alts_grpc_record_protocol* receiver) {
+  grpc_core::ExecCtx exec_ctx;
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_grpc_record_protocol_test_var* var =
+        alts_grpc_record_protocol_test_var_create();
+    /* Seals and then unseals empty payload.  */
+    grpc_slice_buffer_reset_and_unref_internal(&var->original_sb);
+    grpc_slice_buffer_reset_and_unref_internal(&var->duplicate_sb);
+    tsi_result status = alts_grpc_record_protocol_protect(
+        sender, &var->original_sb, &var->protected_sb);
+    GPR_ASSERT(status == TSI_OK);
+    GPR_ASSERT(var->protected_sb.length ==
+               var->header_length + var->tag_length);
+    status = alts_grpc_record_protocol_unprotect(receiver, &var->protected_sb,
+                                                 &var->unprotected_sb);
+    GPR_ASSERT(status == TSI_OK);
+    GPR_ASSERT(
+        are_slice_buffers_equal(&var->unprotected_sb, &var->duplicate_sb));
+    alts_grpc_record_protocol_test_var_destroy(var);
+  }
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+static void unsync_seal_unseal(alts_grpc_record_protocol* sender,
+                               alts_grpc_record_protocol* receiver) {
+  grpc_core::ExecCtx exec_ctx;
+  tsi_result status;
+  alts_grpc_record_protocol_test_var* var =
+      alts_grpc_record_protocol_test_var_create();
+  /* Seals once.  */
+  status = alts_grpc_record_protocol_protect(sender, &var->original_sb,
+                                             &var->protected_sb);
+  GPR_ASSERT(status == TSI_OK);
+  grpc_slice_buffer_reset_and_unref_internal(&var->protected_sb);
+  /* Seals again.  */
+  status = alts_grpc_record_protocol_protect(sender, &var->duplicate_sb,
+                                             &var->protected_sb);
+  GPR_ASSERT(status == TSI_OK);
+  /* Unseals the second frame.  */
+  status = alts_grpc_record_protocol_unprotect(receiver, &var->protected_sb,
+                                               &var->unprotected_sb);
+  GPR_ASSERT(status == TSI_INTERNAL_ERROR);
+  alts_grpc_record_protocol_test_var_destroy(var);
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+static void corrupted_data(alts_grpc_record_protocol* sender,
+                           alts_grpc_record_protocol* receiver) {
+  grpc_core::ExecCtx exec_ctx;
+  tsi_result status;
+  alts_grpc_record_protocol_test_var* var =
+      alts_grpc_record_protocol_test_var_create();
+  /* Seals once.  */
+  status = alts_grpc_record_protocol_protect(sender, &var->original_sb,
+                                             &var->protected_sb);
+  GPR_ASSERT(status == TSI_OK);
+  /* Corrupts one byte in protected_sb and tries to unprotect.  */
+  alter_random_byte(&var->protected_sb);
+  status = alts_grpc_record_protocol_unprotect(receiver, &var->protected_sb,
+                                               &var->unprotected_sb);
+  GPR_ASSERT(status == TSI_INTERNAL_ERROR);
+  alts_grpc_record_protocol_test_var_destroy(var);
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+static void input_check(alts_grpc_record_protocol* rp) {
+  grpc_core::ExecCtx exec_ctx;
+  tsi_result status;
+  alts_grpc_record_protocol_test_var* var =
+      alts_grpc_record_protocol_test_var_create();
+  /* Protects with nullptr input.  */
+  status = alts_grpc_record_protocol_protect(rp, nullptr, &var->protected_sb);
+  GPR_ASSERT(status == TSI_INVALID_ARGUMENT);
+  status = alts_grpc_record_protocol_protect(rp, &var->original_sb, nullptr);
+  GPR_ASSERT(status == TSI_INVALID_ARGUMENT);
+  /* Unprotects with nullptr input.  */
+  status = alts_grpc_record_protocol_protect(rp, &var->original_sb,
+                                             &var->protected_sb);
+  GPR_ASSERT(status == TSI_OK);
+  status =
+      alts_grpc_record_protocol_unprotect(rp, nullptr, &var->unprotected_sb);
+  GPR_ASSERT(status == TSI_INVALID_ARGUMENT);
+  status = alts_grpc_record_protocol_unprotect(rp, &var->protected_sb, nullptr);
+  GPR_ASSERT(status == TSI_INVALID_ARGUMENT);
+  /* Unprotects on a temporary slice buffer which length is smaller than header
+   * length plus tag length.  */
+  grpc_slice_buffer temp_sb;
+  grpc_slice_buffer_init(&temp_sb);
+  grpc_slice_buffer_move_first(
+      &var->protected_sb, var->header_length + var->tag_length - 1, &temp_sb);
+  status =
+      alts_grpc_record_protocol_unprotect(rp, &temp_sb, &var->unprotected_sb);
+  GPR_ASSERT(status == TSI_INVALID_ARGUMENT);
+  grpc_slice_buffer_destroy_internal(&temp_sb);
+  alts_grpc_record_protocol_test_var_destroy(var);
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+/* --- Test cases. --- */
+
+static void alts_grpc_record_protocol_random_seal_unseal_tests(
+    alts_grpc_record_protocol_test_fixture* fixture) {
+  random_seal_unseal(fixture->client_protect, fixture->server_unprotect);
+  random_seal_unseal(fixture->server_protect, fixture->client_unprotect);
+}
+
+static void alts_grpc_record_protocol_empty_seal_unseal_tests(
+    alts_grpc_record_protocol_test_fixture* fixture) {
+  empty_seal_unseal(fixture->client_protect, fixture->server_unprotect);
+  empty_seal_unseal(fixture->server_protect, fixture->client_unprotect);
+}
+
+static void alts_grpc_record_protocol_unsync_seal_unseal_tests(
+    alts_grpc_record_protocol_test_fixture* fixture) {
+  unsync_seal_unseal(fixture->client_protect, fixture->server_unprotect);
+  unsync_seal_unseal(fixture->server_protect, fixture->client_unprotect);
+}
+
+static void alts_grpc_record_protocol_corrupted_data_tests(
+    alts_grpc_record_protocol_test_fixture* fixture) {
+  corrupted_data(fixture->client_protect, fixture->server_unprotect);
+  corrupted_data(fixture->server_protect, fixture->client_unprotect);
+}
+
+static void alts_grpc_record_protocol_input_check_tests(
+    alts_grpc_record_protocol_test_fixture* fixture) {
+  input_check(fixture->client_protect);
+}
+
+static void alts_grpc_record_protocol_tests(
+    alts_grpc_record_protocol_test_fixture* (*fixture_create)()) {
+  auto* fixture_1 = fixture_create();
+  alts_grpc_record_protocol_random_seal_unseal_tests(fixture_1);
+  alts_grpc_record_protocol_test_fixture_destroy(fixture_1);
+
+  auto* fixture_2 = fixture_create();
+  alts_grpc_record_protocol_empty_seal_unseal_tests(fixture_2);
+  alts_grpc_record_protocol_test_fixture_destroy(fixture_2);
+
+  auto* fixture_3 = fixture_create();
+  alts_grpc_record_protocol_unsync_seal_unseal_tests(fixture_3);
+  alts_grpc_record_protocol_test_fixture_destroy(fixture_3);
+
+  auto* fixture_4 = fixture_create();
+  alts_grpc_record_protocol_corrupted_data_tests(fixture_4);
+  alts_grpc_record_protocol_test_fixture_destroy(fixture_4);
+
+  auto* fixture_5 = fixture_create();
+  alts_grpc_record_protocol_input_check_tests(fixture_5);
+  alts_grpc_record_protocol_test_fixture_destroy(fixture_5);
+}
+
+int main(int argc, char** argv) {
+  alts_grpc_record_protocol_tests(&test_fixture_integrity_only_no_rekey_create);
+  alts_grpc_record_protocol_tests(&test_fixture_integrity_only_rekey_create);
+  alts_grpc_record_protocol_tests(
+      &test_fixture_privacy_integrity_no_rekey_create);
+  alts_grpc_record_protocol_tests(&test_fixture_privacy_integrity_rekey_create);
+
+  return 0;
+}
diff --git a/test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc b/test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc
new file mode 100644
index 0000000..db1934b
--- /dev/null
+++ b/test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc
@@ -0,0 +1,928 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+constexpr size_t kMaxDataSize = 1024;
+constexpr size_t kMaxSlices = 10;
+constexpr size_t kSealRepeatTimes = 5;
+constexpr size_t kTagLength = 16;
+
+/* Test fixtures for each test cases.  */
+struct alts_iovec_record_protocol_test_fixture {
+  alts_iovec_record_protocol* client_protect;
+  alts_iovec_record_protocol* client_unprotect;
+  alts_iovec_record_protocol* server_protect;
+  alts_iovec_record_protocol* server_unprotect;
+};
+
+/* Test variables for protect/unprotect operations.  */
+struct alts_iovec_record_protocol_test_var {
+  uint8_t* header_buf;
+  size_t header_length;
+  iovec_t header_iovec;
+  uint8_t* tag_buf;
+  size_t tag_length;
+  iovec_t tag_iovec;
+  uint8_t* data_buf;
+  uint8_t* dup_buf;
+  size_t data_length;
+  iovec_t* data_iovec;
+  size_t data_iovec_length;
+  uint8_t* protected_buf;
+  iovec_t protected_iovec;
+  iovec_t unprotected_iovec;
+};
+
+/* --- Test utility functions. --- */
+
+static void randomly_slice(uint8_t* input, size_t input_length,
+                           iovec_t** output, size_t* output_length) {
+  if (input_length == 0) {
+    *output = nullptr;
+    *output_length = 0;
+    return;
+  }
+  *output_length = gsec_test_bias_random_uint32(kMaxSlices) + 1;
+  *output = static_cast<iovec_t*>(gpr_malloc(*output_length * sizeof(iovec_t)));
+  for (size_t i = 0; i < *output_length - 1; i++) {
+    size_t slice_length =
+        gsec_test_bias_random_uint32(static_cast<uint32_t>(input_length));
+    iovec_t slice = {input, slice_length};
+    (*output)[i] = slice;
+    input += slice_length;
+    input_length -= slice_length;
+  }
+  iovec_t slice = {input, input_length};
+  (*output)[*output_length - 1] = slice;
+}
+
+static size_t alter_random_byte(uint8_t* buf, size_t buf_length) {
+  GPR_ASSERT(buf != nullptr);
+  uint32_t offset =
+      gsec_test_bias_random_uint32(static_cast<uint32_t>(buf_length));
+  (*(buf + offset))++;
+  return offset;
+}
+
+static void revert_back_alter(uint8_t* buf, size_t offset) {
+  GPR_ASSERT(buf != nullptr);
+  (*(buf + offset))--;
+}
+
+static alts_iovec_record_protocol_test_fixture*
+alts_iovec_record_protocol_test_fixture_create(bool rekey,
+                                               bool integrity_only) {
+  alts_iovec_record_protocol_test_fixture* fixture =
+      static_cast<alts_iovec_record_protocol_test_fixture*>(
+          gpr_malloc(sizeof(alts_iovec_record_protocol_test_fixture)));
+  size_t overflow_size = 8;
+  size_t key_length = rekey ? kAes128GcmRekeyKeyLength : kAes128GcmKeyLength;
+  uint8_t* key;
+  gsec_test_random_array(&key, key_length);
+  gsec_aead_crypter* crypter = nullptr;
+  /* Create client record protocol for protect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_iovec_record_protocol_create(
+                 crypter, overflow_size, /*is_client=*/true, integrity_only,
+                 /*is_protect=*/true, &fixture->client_protect,
+                 nullptr) == GRPC_STATUS_OK);
+  /* Create client record protocol for unprotect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_iovec_record_protocol_create(
+                 crypter, overflow_size, /*is_client=*/true, integrity_only,
+                 /*is_protect=*/false, &fixture->client_unprotect,
+                 nullptr) == GRPC_STATUS_OK);
+  /* Create server record protocol for protect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_iovec_record_protocol_create(
+                 crypter, overflow_size, /*is_client=*/false, integrity_only,
+                 /*is_protect=*/true, &fixture->server_protect,
+                 nullptr) == GRPC_STATUS_OK);
+  /* Create server record protocol for unprotect.  */
+  GPR_ASSERT(gsec_aes_gcm_aead_crypter_create(
+                 key, key_length, kAesGcmNonceLength, kAesGcmTagLength, rekey,
+                 &crypter, nullptr) == GRPC_STATUS_OK);
+  GPR_ASSERT(alts_iovec_record_protocol_create(
+                 crypter, overflow_size, /*is_client=*/false, integrity_only,
+                 /*is_protect=*/false, &fixture->server_unprotect,
+                 nullptr) == GRPC_STATUS_OK);
+
+  gpr_free(key);
+  return fixture;
+}
+
+static void alts_iovec_record_protocol_test_fixture_destroy(
+    alts_iovec_record_protocol_test_fixture* fixture) {
+  if (fixture == nullptr) {
+    return;
+  }
+  alts_iovec_record_protocol_destroy(fixture->client_protect);
+  alts_iovec_record_protocol_destroy(fixture->client_unprotect);
+  alts_iovec_record_protocol_destroy(fixture->server_protect);
+  alts_iovec_record_protocol_destroy(fixture->server_unprotect);
+  gpr_free(fixture);
+}
+
+static alts_iovec_record_protocol_test_var*
+alts_iovec_record_protocol_test_var_create() {
+  auto* var = static_cast<alts_iovec_record_protocol_test_var*>(
+      gpr_zalloc(sizeof(alts_iovec_record_protocol_test_var)));
+  /* Sets header buffer.  */
+  var->header_length = alts_iovec_record_protocol_get_header_length();
+  var->header_buf = static_cast<uint8_t*>(gpr_malloc(var->header_length));
+  var->header_iovec.iov_base = var->header_buf;
+  var->header_iovec.iov_len = var->header_length;
+  /* Sets tag buffer.  */
+  var->tag_length = kTagLength;
+  var->tag_buf = static_cast<uint8_t*>(gpr_malloc(var->tag_length));
+  var->tag_iovec.iov_base = var->tag_buf;
+  var->tag_iovec.iov_len = var->tag_length;
+  /* Randomly sets data buffer and duplicates to dup_buf.  */
+  var->data_length = gsec_test_bias_random_uint32(kMaxDataSize) + 1;
+  var->data_buf = static_cast<uint8_t*>(gpr_malloc(var->data_length));
+  gsec_test_random_bytes(var->data_buf, var->data_length);
+  gsec_test_copy(var->data_buf, &var->dup_buf, var->data_length);
+  var->data_iovec = nullptr;
+  var->data_iovec_length = 0;
+  randomly_slice(var->data_buf, var->data_length, &var->data_iovec,
+                 &var->data_iovec_length);
+  /* Sets protected iovec.  */
+  size_t protected_buf_length =
+      var->header_length + var->data_length + var->tag_length;
+  var->protected_buf = static_cast<uint8_t*>(gpr_malloc(protected_buf_length));
+  var->protected_iovec.iov_base = var->protected_buf;
+  var->protected_iovec.iov_len = protected_buf_length;
+  /* Unprotected iovec points to data_buf.  */
+  var->unprotected_iovec.iov_base = var->data_buf;
+  var->unprotected_iovec.iov_len = var->data_length;
+  return var;
+}
+
+static void alts_iovec_record_protocol_test_var_destroy(
+    alts_iovec_record_protocol_test_var* var) {
+  if (var == nullptr) {
+    return;
+  }
+  gpr_free(var->header_buf);
+  gpr_free(var->tag_buf);
+  gpr_free(var->data_buf);
+  gpr_free(var->dup_buf);
+  gpr_free(var->data_iovec);
+  gpr_free(var->protected_buf);
+  gpr_free(var);
+}
+
+/* --- Integrity-only protect/unprotect tests. --- */
+
+static void integrity_only_random_seal_unseal(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_iovec_record_protocol_test_var* var =
+        alts_iovec_record_protocol_test_var_create();
+    /* Seals and then unseals.  */
+    grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+        sender, var->data_iovec, var->data_iovec_length, var->header_iovec,
+        var->tag_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    gpr_free(var->data_iovec);
+    /* Randomly slices data buffer again.  */
+    randomly_slice(var->data_buf, var->data_length, &var->data_iovec,
+                   &var->data_iovec_length);
+    status = alts_iovec_record_protocol_integrity_only_unprotect(
+        receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+        var->tag_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    /* Makes sure data buffer has not been modified during
+     * seal/unseal.  */
+    GPR_ASSERT(memcmp(var->data_buf, var->dup_buf, var->data_length) == 0);
+    alts_iovec_record_protocol_test_var_destroy(var);
+  }
+}
+
+static void integrity_only_empty_seal_unseal(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_iovec_record_protocol_test_var* var =
+        alts_iovec_record_protocol_test_var_create();
+    /* Seals and then unseals empty payload.  */
+    grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+        sender, nullptr, 0, var->header_iovec, var->tag_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    status = alts_iovec_record_protocol_integrity_only_unprotect(
+        receiver, nullptr, 0, var->header_iovec, var->tag_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    alts_iovec_record_protocol_test_var_destroy(var);
+  }
+}
+
+static void integrity_only_unsync_seal_unseal(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  /* Seals once.  */
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+      sender, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  alts_iovec_record_protocol_test_var_destroy(var);
+  /* Seals again.  */
+  var = alts_iovec_record_protocol_test_var_create();
+  status = alts_iovec_record_protocol_integrity_only_protect(
+      sender, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  /* Unseals the second frame.  */
+  char* error_message = nullptr;
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message,
+      "Frame tag verification failed."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void integrity_only_corrupted_data(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  /* Seals the data first.  */
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+      sender, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  /* Alter frame length field.  */
+  char* error_message = nullptr;
+  size_t offset =
+      alter_random_byte(var->header_buf, kZeroCopyFrameLengthFieldSize);
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message, "Bad frame length."));
+  gpr_free(error_message);
+  revert_back_alter(var->header_buf, offset);
+  /* Alter message type field.  */
+  offset = alter_random_byte(var->header_buf + kZeroCopyFrameLengthFieldSize,
+                             kZeroCopyFrameMessageTypeFieldSize);
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message,
+      "Unsupported message type."));
+  gpr_free(error_message);
+  revert_back_alter(var->header_buf + kZeroCopyFrameLengthFieldSize, offset);
+  /* Alter data.  */
+  offset = alter_random_byte(var->data_buf, var->data_length);
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message,
+      "Frame tag verification failed."));
+  gpr_free(error_message);
+  revert_back_alter(var->data_buf, offset);
+  /* Alter tag.  */
+  offset = alter_random_byte(var->tag_buf, var->tag_length);
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message,
+      "Frame tag verification failed."));
+  gpr_free(error_message);
+  revert_back_alter(var->tag_buf, offset);
+  /* Reverted protected data should be verified correctly.  */
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      receiver, var->data_iovec, var->data_iovec_length, var->header_iovec,
+      var->tag_iovec, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(var->data_buf, var->dup_buf, var->data_length) == 0);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void integrity_only_protect_input_check(alts_iovec_record_protocol* rp) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  char* error_message = nullptr;
+  /* Header buffer is nullptr.  */
+  iovec_t header_iovec = {nullptr, var->header_length};
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+      rp, var->data_iovec, var->data_iovec_length, header_iovec, var->tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Header is nullptr."));
+  gpr_free(error_message);
+  /* Header buffer length is 0.  */
+  header_iovec.iov_base = var->header_buf;
+  header_iovec.iov_len = 0;
+  status = alts_iovec_record_protocol_integrity_only_protect(
+      rp, var->data_iovec, var->data_iovec_length, header_iovec, var->tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Header length is incorrect."));
+  gpr_free(error_message);
+  /* Tag buffer is nullptr.  */
+  iovec_t tag_iovec = {nullptr, var->tag_length};
+  status = alts_iovec_record_protocol_integrity_only_protect(
+      rp, var->data_iovec, var->data_iovec_length, var->header_iovec, tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message, "Tag is nullptr."));
+  gpr_free(error_message);
+  /* Tag buffer length is 0.  */
+  tag_iovec.iov_base = var->tag_buf;
+  tag_iovec.iov_len = 0;
+  status = alts_iovec_record_protocol_integrity_only_protect(
+      rp, var->data_iovec, var->data_iovec_length, var->header_iovec, tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Tag length is incorrect."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void integrity_only_unprotect_input_check(
+    alts_iovec_record_protocol* rp) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  char* error_message = nullptr;
+  /* Header buffer is nullptr.  */
+  iovec_t header_iovec = {nullptr, var->header_length};
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect(
+      rp, var->data_iovec, var->data_iovec_length, header_iovec, var->tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Header is nullptr."));
+  gpr_free(error_message);
+  /* Header buffer length is 0.  */
+  header_iovec.iov_base = var->header_buf;
+  header_iovec.iov_len = 0;
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      rp, var->data_iovec, var->data_iovec_length, header_iovec, var->tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Header length is incorrect."));
+  gpr_free(error_message);
+  /* Tag buffer is nullptr.  */
+  iovec_t tag_iovec = {nullptr, var->tag_length};
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      rp, var->data_iovec, var->data_iovec_length, var->header_iovec, tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message, "Tag is nullptr."));
+  gpr_free(error_message);
+  /* Tag buffer length is 0.  */
+  tag_iovec.iov_base = var->tag_buf;
+  tag_iovec.iov_len = 0;
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      rp, var->data_iovec, var->data_iovec_length, var->header_iovec, tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Tag length is incorrect."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+/* --- Privacy-integrity protect/unprotect tests. --- */
+
+static void privacy_integrity_random_seal_unseal(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_iovec_record_protocol_test_var* var =
+        alts_iovec_record_protocol_test_var_create();
+    /* Seals and then unseals.  */
+    grpc_status_code status =
+        alts_iovec_record_protocol_privacy_integrity_protect(
+            sender, var->data_iovec, var->data_iovec_length,
+            var->protected_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    iovec_t header_iovec = {var->protected_buf, var->header_length};
+    gpr_free(var->data_iovec);
+    /* Randomly slices protected buffer, excluding the header.  */
+    randomly_slice(var->protected_buf + var->header_length,
+                   var->data_length + var->tag_length, &var->data_iovec,
+                   &var->data_iovec_length);
+    status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+        receiver, header_iovec, var->data_iovec, var->data_iovec_length,
+        var->unprotected_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    /* Makes sure unprotected data are the same as the original.  */
+    GPR_ASSERT(memcmp(var->data_buf, var->dup_buf, var->data_length) == 0);
+    alts_iovec_record_protocol_test_var_destroy(var);
+  }
+}
+
+static void privacy_integrity_empty_seal_unseal(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  size_t empty_payload_frame_size = var->header_length + var->tag_length;
+  auto* protected_buf =
+      static_cast<uint8_t*>(gpr_malloc(empty_payload_frame_size));
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    iovec_t protected_iovec = {protected_buf, empty_payload_frame_size};
+    iovec_t unprotected_iovec = {nullptr, 0};
+    iovec_t data_iovec = {protected_buf + var->header_length, var->tag_length};
+    /* Seals and then unseals empty payload.  */
+    grpc_status_code status =
+        alts_iovec_record_protocol_privacy_integrity_protect(
+            sender, nullptr, 0, protected_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+    iovec_t header_iovec = {protected_buf, var->header_length};
+    status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+        receiver, header_iovec, &data_iovec, 1, unprotected_iovec, nullptr);
+    GPR_ASSERT(status == GRPC_STATUS_OK);
+  }
+  gpr_free(protected_buf);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void privacy_integrity_unsync_seal_unseal(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  /* Seals once.  */
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_protect(
+          sender, var->data_iovec, var->data_iovec_length, var->protected_iovec,
+          nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  alts_iovec_record_protocol_test_var_destroy(var);
+  /* Seals again.  */
+  var = alts_iovec_record_protocol_test_var_create();
+  status = alts_iovec_record_protocol_privacy_integrity_protect(
+      sender, var->data_iovec, var->data_iovec_length, var->protected_iovec,
+      nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  /* Unseals the second frame.  */
+  char* error_message = nullptr;
+  iovec_t header_iovec = {var->protected_buf, var->header_length};
+  iovec_t protected_iovec = {var->protected_buf + var->header_length,
+                             var->data_length + var->tag_length};
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      receiver, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message, "Frame decryption failed."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void privacy_integrity_corrupted_data(
+    alts_iovec_record_protocol* sender, alts_iovec_record_protocol* receiver) {
+  /* Seals the data first.  */
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_protect(
+          sender, var->data_iovec, var->data_iovec_length, var->protected_iovec,
+          nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  char* error_message = nullptr;
+  uint8_t* header_buf = var->protected_buf;
+  size_t header_length = var->header_length;
+  iovec_t header_iovec = {header_buf, header_length};
+  /* The following protected_buf and protected_length excludes header.  */
+  uint8_t* protected_buf = var->protected_buf + var->header_length;
+  size_t protected_length = var->data_length + var->tag_length;
+  iovec_t protected_iovec = {protected_buf, protected_length};
+  /* Alter frame length field.  */
+  size_t offset = alter_random_byte(header_buf, kZeroCopyFrameLengthFieldSize);
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      receiver, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message, "Bad frame length."));
+  gpr_free(error_message);
+  revert_back_alter(header_buf, offset);
+  /* Alter message type field.  */
+  offset = alter_random_byte(header_buf + kZeroCopyFrameLengthFieldSize,
+                             kZeroCopyFrameMessageTypeFieldSize);
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      receiver, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message,
+      "Unsupported message type."));
+  gpr_free(error_message);
+  revert_back_alter(header_buf + kZeroCopyFrameLengthFieldSize, offset);
+  /* Alter protected data.  */
+  offset = alter_random_byte(protected_buf, protected_length);
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      receiver, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message, "Frame decryption failed."));
+  gpr_free(error_message);
+  revert_back_alter(protected_buf, offset);
+  /* Reverted protected data should be verified correctly.  */
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      receiver, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+      nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(memcmp(var->data_buf, var->dup_buf, var->data_length) == 0);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void privacy_integrity_protect_input_check(
+    alts_iovec_record_protocol* rp) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  char* error_message = nullptr;
+  /* Protected output buffer is nullptr.  */
+  iovec_t protected_iovec = {nullptr, var->protected_iovec.iov_len};
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_protect(
+          rp, var->data_iovec, var->data_iovec_length, protected_iovec,
+          &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Protected frame is nullptr."));
+  gpr_free(error_message);
+  /* Protected output buffer length incorrect.  */
+  protected_iovec.iov_base = var->protected_buf;
+  protected_iovec.iov_len = var->header_length + var->data_length;
+  status = alts_iovec_record_protocol_privacy_integrity_protect(
+      rp, var->data_iovec, var->data_iovec_length, protected_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Protected frame size is incorrect."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void privacy_integrity_unprotect_input_check(
+    alts_iovec_record_protocol* rp) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  char* error_message = nullptr;
+  /* Header buffer is nullptr.  */
+  iovec_t header_iovec = {var->protected_buf, var->header_length};
+  iovec_t protected_iovec = {var->protected_buf + var->header_length,
+                             var->data_length + var->tag_length};
+  header_iovec.iov_base = nullptr;
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_unprotect(
+          rp, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+          &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Header is nullptr."));
+  gpr_free(error_message);
+  header_iovec.iov_base = var->protected_buf;
+  /* Header buffer length is 0.  */
+  header_iovec.iov_len = 0;
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      rp, header_iovec, &protected_iovec, 1, var->unprotected_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Header length is incorrect."));
+  gpr_free(error_message);
+  header_iovec.iov_len = var->header_length;
+  /* Unprotected output buffer length is incorrect.  */
+  iovec_t unprotected_iovec = {var->data_buf, var->data_length - 1};
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      rp, header_iovec, &protected_iovec, 1, unprotected_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INVALID_ARGUMENT, error_message,
+      "Unprotected data size is incorrect."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+/* --- Integrity-only and privacy-integrity mixed. --- */
+
+static void record_protocol_wrong_mode(
+    alts_iovec_record_protocol* integrity_only_protect_rp,
+    alts_iovec_record_protocol* integrity_only_unprotect_rp,
+    alts_iovec_record_protocol* privacy_integrity_protect_rp,
+    alts_iovec_record_protocol* privacy_integrity_unprotect_rp) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status;
+  char* error_message = nullptr;
+  /* Call integrity-only protect on privacy-integrity record protocol.  */
+  status = alts_iovec_record_protocol_integrity_only_protect(
+      privacy_integrity_protect_rp, var->data_iovec, var->data_iovec_length,
+      var->header_iovec, var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Integrity-only operations are not allowed for this object."));
+  gpr_free(error_message);
+  /* Call integrity-only unprotect on privacy-integrity record protocol.  */
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      privacy_integrity_unprotect_rp, var->data_iovec, var->data_iovec_length,
+      var->header_iovec, var->tag_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Integrity-only operations are not allowed for this object."));
+  gpr_free(error_message);
+  /* Call privacy-integrity protect on integrity-only record protocol.  */
+  status = alts_iovec_record_protocol_privacy_integrity_protect(
+      integrity_only_protect_rp, var->data_iovec, var->data_iovec_length,
+      var->protected_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Privacy-integrity operations are not allowed for this object."));
+  gpr_free(error_message);
+  /* Call privacy-integrity unprotect on integrity-only record protocol.  */
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      integrity_only_unprotect_rp, var->header_iovec, var->data_iovec,
+      var->data_iovec_length, var->unprotected_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_FAILED_PRECONDITION, error_message,
+      "Privacy-integrity operations are not allowed for this object."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void integrity_seal_privacy_unseal(
+    alts_iovec_record_protocol* integrity_only_sender,
+    alts_iovec_record_protocol* privacy_integrity_receiver) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status;
+  char* error_message = nullptr;
+  /* Seals with integrity-only protect.  */
+  status = alts_iovec_record_protocol_integrity_only_protect(
+      integrity_only_sender, var->data_iovec, var->data_iovec_length,
+      var->header_iovec, var->tag_iovec, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  /* Unseal with privacy-integrity unprotect.  */
+  memcpy(var->protected_buf, var->data_buf, var->data_length);
+  memcpy(var->protected_buf + var->data_length, var->tag_buf, var->tag_length);
+  iovec_t protected_iovec = {var->protected_buf,
+                             var->data_length + var->tag_length};
+  status = alts_iovec_record_protocol_privacy_integrity_unprotect(
+      privacy_integrity_receiver, var->header_iovec, &protected_iovec, 1,
+      var->unprotected_iovec, &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message, "Frame decryption failed."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+static void privacy_seal_integrity_unseal(
+    alts_iovec_record_protocol* privacy_integrity_sender,
+    alts_iovec_record_protocol* integrity_only_receiver) {
+  alts_iovec_record_protocol_test_var* var =
+      alts_iovec_record_protocol_test_var_create();
+  grpc_status_code status;
+  char* error_message = nullptr;
+  /* Seals with privacy-integrity protect.  */
+  status = alts_iovec_record_protocol_privacy_integrity_protect(
+      privacy_integrity_sender, var->data_iovec, var->data_iovec_length,
+      var->protected_iovec, nullptr);
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  /* Unseal with integrity-only unprotect.  */
+  iovec_t header_iovec = {var->protected_buf, var->header_length};
+  iovec_t data_iovec = {var->protected_buf + var->header_length,
+                        var->data_length};
+  iovec_t tag_iovec = {
+      var->protected_buf + var->header_length + var->data_length,
+      var->tag_length};
+  status = alts_iovec_record_protocol_integrity_only_unprotect(
+      integrity_only_receiver, &data_iovec, 1, header_iovec, tag_iovec,
+      &error_message);
+  GPR_ASSERT(gsec_test_expect_compare_code_and_substr(
+      status, GRPC_STATUS_INTERNAL, error_message,
+      "Frame tag verification failed."));
+  gpr_free(error_message);
+  alts_iovec_record_protocol_test_var_destroy(var);
+}
+
+/* --- Test cases. --- */
+
+static void alts_iovec_record_protocol_random_seal_unseal_tests() {
+  alts_iovec_record_protocol_test_fixture* fixture =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  integrity_only_random_seal_unseal(fixture->client_protect,
+                                    fixture->server_unprotect);
+  integrity_only_random_seal_unseal(fixture->server_protect,
+                                    fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  integrity_only_random_seal_unseal(fixture->client_protect,
+                                    fixture->server_unprotect);
+  integrity_only_random_seal_unseal(fixture->server_protect,
+                                    fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  privacy_integrity_random_seal_unseal(fixture->client_protect,
+                                       fixture->server_unprotect);
+  privacy_integrity_random_seal_unseal(fixture->server_protect,
+                                       fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  privacy_integrity_random_seal_unseal(fixture->client_protect,
+                                       fixture->server_unprotect);
+  privacy_integrity_random_seal_unseal(fixture->server_protect,
+                                       fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+}
+
+static void alts_iovec_record_protocol_empty_seal_unseal_tests() {
+  alts_iovec_record_protocol_test_fixture* fixture =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  integrity_only_empty_seal_unseal(fixture->client_protect,
+                                   fixture->server_unprotect);
+  integrity_only_empty_seal_unseal(fixture->server_protect,
+                                   fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  integrity_only_empty_seal_unseal(fixture->client_protect,
+                                   fixture->server_unprotect);
+  integrity_only_empty_seal_unseal(fixture->server_protect,
+                                   fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  privacy_integrity_empty_seal_unseal(fixture->client_protect,
+                                      fixture->server_unprotect);
+  privacy_integrity_empty_seal_unseal(fixture->server_protect,
+                                      fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  privacy_integrity_empty_seal_unseal(fixture->client_protect,
+                                      fixture->server_unprotect);
+  privacy_integrity_empty_seal_unseal(fixture->server_protect,
+                                      fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+}
+
+static void alts_iovec_record_protocol_unsync_seal_unseal_tests() {
+  alts_iovec_record_protocol_test_fixture* fixture =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  integrity_only_unsync_seal_unseal(fixture->client_protect,
+                                    fixture->server_unprotect);
+  integrity_only_unsync_seal_unseal(fixture->server_protect,
+                                    fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  integrity_only_unsync_seal_unseal(fixture->client_protect,
+                                    fixture->server_unprotect);
+  integrity_only_unsync_seal_unseal(fixture->server_protect,
+                                    fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  privacy_integrity_unsync_seal_unseal(fixture->client_protect,
+                                       fixture->server_unprotect);
+  privacy_integrity_unsync_seal_unseal(fixture->server_protect,
+                                       fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  privacy_integrity_unsync_seal_unseal(fixture->client_protect,
+                                       fixture->server_unprotect);
+  privacy_integrity_unsync_seal_unseal(fixture->server_protect,
+                                       fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+}
+
+static void alts_iovec_record_protocol_corrupted_data_tests() {
+  alts_iovec_record_protocol_test_fixture* fixture =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  integrity_only_corrupted_data(fixture->client_protect,
+                                fixture->server_unprotect);
+  integrity_only_corrupted_data(fixture->server_protect,
+                                fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  integrity_only_corrupted_data(fixture->client_protect,
+                                fixture->server_unprotect);
+  integrity_only_corrupted_data(fixture->server_protect,
+                                fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  privacy_integrity_corrupted_data(fixture->client_protect,
+                                   fixture->server_unprotect);
+  privacy_integrity_corrupted_data(fixture->server_protect,
+                                   fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  privacy_integrity_corrupted_data(fixture->client_protect,
+                                   fixture->server_unprotect);
+  privacy_integrity_corrupted_data(fixture->server_protect,
+                                   fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+}
+
+static void alts_iovec_record_protocol_input_check_tests() {
+  alts_iovec_record_protocol_test_fixture* fixture =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  integrity_only_protect_input_check(fixture->client_protect);
+  integrity_only_unprotect_input_check(fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  integrity_only_protect_input_check(fixture->client_protect);
+  integrity_only_unprotect_input_check(fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  privacy_integrity_protect_input_check(fixture->client_protect);
+  privacy_integrity_unprotect_input_check(fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+
+  fixture = alts_iovec_record_protocol_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  privacy_integrity_protect_input_check(fixture->client_protect);
+  privacy_integrity_unprotect_input_check(fixture->client_unprotect);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture);
+}
+
+static void alts_iovec_record_protocol_mix_operations_tests() {
+  alts_iovec_record_protocol_test_fixture* fixture_1 =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  alts_iovec_record_protocol_test_fixture* fixture_2 =
+      alts_iovec_record_protocol_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/false);
+
+  record_protocol_wrong_mode(
+      fixture_1->client_protect, fixture_1->client_unprotect,
+      fixture_2->client_protect, fixture_2->client_unprotect);
+  integrity_seal_privacy_unseal(fixture_1->client_protect,
+                                fixture_2->server_unprotect);
+  privacy_seal_integrity_unseal(fixture_2->client_protect,
+                                fixture_1->server_unprotect);
+
+  alts_iovec_record_protocol_test_fixture_destroy(fixture_1);
+  alts_iovec_record_protocol_test_fixture_destroy(fixture_2);
+}
+
+int main(int argc, char** argv) {
+  alts_iovec_record_protocol_random_seal_unseal_tests();
+  alts_iovec_record_protocol_empty_seal_unseal_tests();
+  alts_iovec_record_protocol_unsync_seal_unseal_tests();
+  alts_iovec_record_protocol_corrupted_data_tests();
+  alts_iovec_record_protocol_input_check_tests();
+  alts_iovec_record_protocol_mix_operations_tests();
+  return 0;
+}
diff --git a/test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc b/test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc
new file mode 100644
index 0000000..2388be9
--- /dev/null
+++ b/test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc
@@ -0,0 +1,289 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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 <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+#include "src/core/tsi/transport_security_grpc.h"
+#include "test/core/tsi/alts/crypt/gsec_test_util.h"
+
+/* TODO: tests zero_copy_grpc_protector under TSI test library, which
+ * has more comprehensive tests.  */
+
+constexpr size_t kSealRepeatTimes = 50;
+constexpr size_t kSmallBufferSize = 16;
+constexpr size_t kLargeBufferSize = 16384;
+constexpr size_t kChannelMaxSize = 2048;
+constexpr size_t kChannelMinSize = 128;
+
+/* Test fixtures for each test cases.  */
+struct alts_zero_copy_grpc_protector_test_fixture {
+  tsi_zero_copy_grpc_protector* client;
+  tsi_zero_copy_grpc_protector* server;
+};
+
+/* Test input variables for protect/unprotect operations.  */
+struct alts_zero_copy_grpc_protector_test_var {
+  grpc_slice_buffer original_sb;
+  grpc_slice_buffer duplicate_sb;
+  grpc_slice_buffer staging_sb;
+  grpc_slice_buffer protected_sb;
+  grpc_slice_buffer unprotected_sb;
+};
+
+/* --- Test utility functions. --- */
+
+static void create_random_slice_buffer(grpc_slice_buffer* sb,
+                                       grpc_slice_buffer* dup_sb,
+                                       size_t length) {
+  GPR_ASSERT(sb != nullptr);
+  GPR_ASSERT(dup_sb != nullptr);
+  GPR_ASSERT(length > 0);
+  grpc_slice slice = GRPC_SLICE_MALLOC(length);
+  gsec_test_random_bytes(GRPC_SLICE_START_PTR(slice), length);
+  grpc_slice_buffer_add(sb, grpc_slice_ref(slice));
+  grpc_slice_buffer_add(dup_sb, slice);
+}
+
+static uint8_t* pointer_to_nth_byte(grpc_slice_buffer* sb, size_t index) {
+  GPR_ASSERT(sb != nullptr);
+  GPR_ASSERT(index < sb->length);
+  for (size_t i = 0; i < sb->count; i++) {
+    if (index < GRPC_SLICE_LENGTH(sb->slices[i])) {
+      return GRPC_SLICE_START_PTR(sb->slices[i]) + index;
+    } else {
+      index -= GRPC_SLICE_LENGTH(sb->slices[i]);
+    }
+  }
+  return nullptr;
+}
+
+/* Checks if two slice buffer contents are the same. It is not super efficient,
+ * but OK for testing.  */
+static bool are_slice_buffers_equal(grpc_slice_buffer* first,
+                                    grpc_slice_buffer* second) {
+  GPR_ASSERT(first != nullptr);
+  GPR_ASSERT(second != nullptr);
+  if (first->length != second->length) {
+    return false;
+  }
+  for (size_t i = 0; i < first->length; i++) {
+    uint8_t* first_ptr = pointer_to_nth_byte(first, i);
+    uint8_t* second_ptr = pointer_to_nth_byte(second, i);
+    GPR_ASSERT(first_ptr != nullptr && second_ptr != nullptr);
+    if ((*first_ptr) != (*second_ptr)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static alts_zero_copy_grpc_protector_test_fixture*
+alts_zero_copy_grpc_protector_test_fixture_create(bool rekey,
+                                                  bool integrity_only) {
+  alts_zero_copy_grpc_protector_test_fixture* fixture =
+      static_cast<alts_zero_copy_grpc_protector_test_fixture*>(
+          gpr_zalloc(sizeof(alts_zero_copy_grpc_protector_test_fixture)));
+  grpc_core::ExecCtx exec_ctx;
+  size_t key_length = rekey ? kAes128GcmRekeyKeyLength : kAes128GcmKeyLength;
+  uint8_t* key;
+  size_t max_protected_frame_size = 1024;
+  gsec_test_random_array(&key, key_length);
+  GPR_ASSERT(alts_zero_copy_grpc_protector_create(
+                 key, key_length, rekey, /*is_client=*/true, integrity_only,
+                 &max_protected_frame_size, &fixture->client) == TSI_OK);
+  GPR_ASSERT(alts_zero_copy_grpc_protector_create(
+                 key, key_length, rekey, /*is_client=*/false, integrity_only,
+                 &max_protected_frame_size, &fixture->server) == TSI_OK);
+  gpr_free(key);
+  grpc_core::ExecCtx::Get()->Flush();
+  return fixture;
+}
+
+static void alts_zero_copy_grpc_protector_test_fixture_destroy(
+    alts_zero_copy_grpc_protector_test_fixture* fixture) {
+  if (fixture == nullptr) {
+    return;
+  }
+  grpc_core::ExecCtx exec_ctx;
+  tsi_zero_copy_grpc_protector_destroy(fixture->client);
+  tsi_zero_copy_grpc_protector_destroy(fixture->server);
+  grpc_core::ExecCtx::Get()->Flush();
+  gpr_free(fixture);
+}
+
+static alts_zero_copy_grpc_protector_test_var*
+alts_zero_copy_grpc_protector_test_var_create() {
+  alts_zero_copy_grpc_protector_test_var* var =
+      static_cast<alts_zero_copy_grpc_protector_test_var*>(
+          gpr_zalloc(sizeof(alts_zero_copy_grpc_protector_test_var)));
+  grpc_slice_buffer_init(&var->original_sb);
+  grpc_slice_buffer_init(&var->duplicate_sb);
+  grpc_slice_buffer_init(&var->staging_sb);
+  grpc_slice_buffer_init(&var->protected_sb);
+  grpc_slice_buffer_init(&var->unprotected_sb);
+  return var;
+}
+
+static void alts_zero_copy_grpc_protector_test_var_destroy(
+    alts_zero_copy_grpc_protector_test_var* var) {
+  if (var == nullptr) {
+    return;
+  }
+  grpc_slice_buffer_destroy_internal(&var->original_sb);
+  grpc_slice_buffer_destroy_internal(&var->duplicate_sb);
+  grpc_slice_buffer_destroy_internal(&var->staging_sb);
+  grpc_slice_buffer_destroy_internal(&var->protected_sb);
+  grpc_slice_buffer_destroy_internal(&var->unprotected_sb);
+  gpr_free(var);
+}
+
+/* --- ALTS zero-copy protector tests. --- */
+
+static void seal_unseal_small_buffer(tsi_zero_copy_grpc_protector* sender,
+                                     tsi_zero_copy_grpc_protector* receiver) {
+  grpc_core::ExecCtx exec_ctx;
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_zero_copy_grpc_protector_test_var* var =
+        alts_zero_copy_grpc_protector_test_var_create();
+    /* Creates a random small slice buffer and calls protect().  */
+    create_random_slice_buffer(&var->original_sb, &var->duplicate_sb,
+                               kSmallBufferSize);
+    GPR_ASSERT(tsi_zero_copy_grpc_protector_protect(
+                   sender, &var->original_sb, &var->protected_sb) == TSI_OK);
+    /* Splits protected slice buffer into two: first one is staging_sb, and
+     * second one is is protected_sb.  */
+    uint32_t staging_sb_size =
+        gsec_test_bias_random_uint32(
+            static_cast<uint32_t>(var->protected_sb.length - 1)) +
+        1;
+    grpc_slice_buffer_move_first(&var->protected_sb, staging_sb_size,
+                                 &var->staging_sb);
+    /* Unprotects one by one.  */
+    GPR_ASSERT(tsi_zero_copy_grpc_protector_unprotect(
+                   receiver, &var->staging_sb, &var->unprotected_sb) == TSI_OK);
+    GPR_ASSERT(var->unprotected_sb.length == 0);
+    GPR_ASSERT(tsi_zero_copy_grpc_protector_unprotect(
+                   receiver, &var->protected_sb, &var->unprotected_sb) ==
+               TSI_OK);
+    GPR_ASSERT(
+        are_slice_buffers_equal(&var->unprotected_sb, &var->duplicate_sb));
+    alts_zero_copy_grpc_protector_test_var_destroy(var);
+  }
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+static void seal_unseal_large_buffer(tsi_zero_copy_grpc_protector* sender,
+                                     tsi_zero_copy_grpc_protector* receiver) {
+  grpc_core::ExecCtx exec_ctx;
+  for (size_t i = 0; i < kSealRepeatTimes; i++) {
+    alts_zero_copy_grpc_protector_test_var* var =
+        alts_zero_copy_grpc_protector_test_var_create();
+    /* Creates a random large slice buffer and calls protect().  */
+    create_random_slice_buffer(&var->original_sb, &var->duplicate_sb,
+                               kLargeBufferSize);
+    GPR_ASSERT(tsi_zero_copy_grpc_protector_protect(
+                   sender, &var->original_sb, &var->protected_sb) == TSI_OK);
+    /* Splits protected slice buffer into multiple pieces. Receiver unprotects
+     * each slice buffer one by one.  */
+    uint32_t channel_size = gsec_test_bias_random_uint32(static_cast<uint32_t>(
+                                kChannelMaxSize + 1 - kChannelMinSize)) +
+                            static_cast<uint32_t>(kChannelMinSize);
+    while (var->protected_sb.length > channel_size) {
+      grpc_slice_buffer_reset_and_unref_internal(&var->staging_sb);
+      grpc_slice_buffer_move_first(&var->protected_sb, channel_size,
+                                   &var->staging_sb);
+      GPR_ASSERT(tsi_zero_copy_grpc_protector_unprotect(
+                     receiver, &var->staging_sb, &var->unprotected_sb) ==
+                 TSI_OK);
+    }
+    GPR_ASSERT(tsi_zero_copy_grpc_protector_unprotect(
+                   receiver, &var->protected_sb, &var->unprotected_sb) ==
+               TSI_OK);
+    GPR_ASSERT(
+        are_slice_buffers_equal(&var->unprotected_sb, &var->duplicate_sb));
+    alts_zero_copy_grpc_protector_test_var_destroy(var);
+  }
+  grpc_core::ExecCtx::Get()->Flush();
+}
+
+/* --- Test cases. --- */
+
+static void alts_zero_copy_protector_seal_unseal_small_buffer_tests() {
+  alts_zero_copy_grpc_protector_test_fixture* fixture =
+      alts_zero_copy_grpc_protector_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  seal_unseal_small_buffer(fixture->client, fixture->server);
+  seal_unseal_small_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+
+  fixture = alts_zero_copy_grpc_protector_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  seal_unseal_small_buffer(fixture->client, fixture->server);
+  seal_unseal_small_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+
+  fixture = alts_zero_copy_grpc_protector_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  seal_unseal_small_buffer(fixture->client, fixture->server);
+  seal_unseal_small_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+
+  fixture = alts_zero_copy_grpc_protector_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  seal_unseal_small_buffer(fixture->client, fixture->server);
+  seal_unseal_small_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+}
+
+static void alts_zero_copy_protector_seal_unseal_large_buffer_tests() {
+  alts_zero_copy_grpc_protector_test_fixture* fixture =
+      alts_zero_copy_grpc_protector_test_fixture_create(
+          /*rekey=*/false, /*integrity_only=*/true);
+  seal_unseal_large_buffer(fixture->client, fixture->server);
+  seal_unseal_large_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+
+  fixture = alts_zero_copy_grpc_protector_test_fixture_create(
+      /*rekey=*/false, /*integrity_only=*/false);
+  seal_unseal_large_buffer(fixture->client, fixture->server);
+  seal_unseal_large_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+
+  fixture = alts_zero_copy_grpc_protector_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/true);
+  seal_unseal_large_buffer(fixture->client, fixture->server);
+  seal_unseal_large_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+
+  fixture = alts_zero_copy_grpc_protector_test_fixture_create(
+      /*rekey=*/true, /*integrity_only=*/false);
+  seal_unseal_large_buffer(fixture->client, fixture->server);
+  seal_unseal_large_buffer(fixture->server, fixture->client);
+  alts_zero_copy_grpc_protector_test_fixture_destroy(fixture);
+}
+
+int main(int argc, char** argv) {
+  alts_zero_copy_protector_seal_unseal_small_buffer_tests();
+  alts_zero_copy_protector_seal_unseal_large_buffer_tests();
+  return 0;
+}
diff --git a/test/core/tsi/fake_transport_security_test.cc b/test/core/tsi/fake_transport_security_test.cc
index ea392d3..5e66719 100644
--- a/test/core/tsi/fake_transport_security_test.cc
+++ b/test/core/tsi/fake_transport_security_test.cc
@@ -20,7 +20,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/tsi/fake_transport_security.h"
 #include "test/core/tsi/transport_security_test_lib.h"
 #include "test/core/util/test_config.h"
@@ -107,7 +107,7 @@
     tsi_test_frame_protector_config_destroy(fake_fixture->base.config);
     fake_fixture->base.config = tsi_test_frame_protector_config_create(
         bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4],
-        bit_array[5], bit_array[6], bit_array[7]);
+        bit_array[5], bit_array[6]);
     tsi_test_do_round_trip(&fake_fixture->base);
     tsi_test_fixture_destroy(fixture);
   }
diff --git a/test/core/tsi/ssl_transport_security_test.cc b/test/core/tsi/ssl_transport_security_test.cc
index d7a4d74..d9eb747 100644
--- a/test/core/tsi/ssl_transport_security_test.cc
+++ b/test/core/tsi/ssl_transport_security_test.cc
@@ -21,7 +21,7 @@
 #include <string.h>
 
 #include "src/core/lib/iomgr/load_file.h"
-#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
 #include "src/core/tsi/ssl_transport_security.h"
 #include "src/core/tsi/transport_security.h"
 #include "src/core/tsi/transport_security_adapter.h"
@@ -528,7 +528,7 @@
     tsi_test_frame_protector_config_destroy(ssl_fixture->base.config);
     ssl_fixture->base.config = tsi_test_frame_protector_config_create(
         bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4],
-        bit_array[5], bit_array[6], bit_array[7]);
+        bit_array[5], bit_array[6]);
     tsi_test_do_round_trip(&ssl_fixture->base);
     tsi_test_fixture_destroy(fixture);
   }
@@ -681,15 +681,12 @@
   ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain();
   ssl_tsi_test_do_handshake_with_bad_server_cert();
   ssl_tsi_test_do_handshake_with_bad_client_cert();
-  // TODO: BoringSSL and OpenSSL have different behaviors on handling mismatched
-  // ALPN. Re-enable this test if we can detect in the runtime which SSL library
-  // is used.
-  // ssl_tsi_test_do_handshake_alpn_client_no_server();
+#ifdef OPENSSL_IS_BORINGSSL
+  // BoringSSL and OpenSSL have different behaviors on mismatched ALPN.
+  ssl_tsi_test_do_handshake_alpn_client_no_server();
+  ssl_tsi_test_do_handshake_alpn_client_server_mismatch();
+#endif
   ssl_tsi_test_do_handshake_alpn_server_no_client();
-  // TODO: BoringSSL and OpenSSL have different behaviors on handling mismatched
-  // ALPN. Re-enable this test if we can detect in the runtime which SSL library
-  // is used.
-  // ssl_tsi_test_do_handshake_alpn_client_server_mismatch();
   ssl_tsi_test_do_handshake_alpn_client_server_ok();
   ssl_tsi_test_do_round_trip_for_all_configs();
   ssl_tsi_test_do_round_trip_odd_buffer_size();
diff --git a/test/core/tsi/transport_security_test_lib.cc b/test/core/tsi/transport_security_test_lib.cc
index 7af6431..26349db 100644
--- a/test/core/tsi/transport_security_test_lib.cc
+++ b/test/core/tsi/transport_security_test_lib.cc
@@ -24,7 +24,6 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/security/transport/tsi_error.h"
 #include "test/core/tsi/transport_security_test_lib.h"
 
@@ -111,27 +110,29 @@
   fixture->vtable->check_handshaker_peers(fixture);
   /* Check unused bytes. */
   if (fixture->test_unused_bytes) {
+    tsi_test_channel* channel = fixture->channel;
     if (fixture->server_result != nullptr &&
         fixture->client_result != nullptr) {
       check_unused_bytes(fixture);
     }
-    fixture->bytes_written_to_server_channel = 0;
-    fixture->bytes_written_to_client_channel = 0;
-    fixture->bytes_read_from_client_channel = 0;
-    fixture->bytes_read_from_server_channel = 0;
+    channel->bytes_written_to_server_channel = 0;
+    channel->bytes_written_to_client_channel = 0;
+    channel->bytes_read_from_client_channel = 0;
+    channel->bytes_read_from_server_channel = 0;
   }
 }
 
-static void send_bytes_to_peer(tsi_test_fixture* fixture,
+static void send_bytes_to_peer(tsi_test_channel* test_channel,
                                const unsigned char* buf, size_t buf_size,
                                bool is_client) {
-  GPR_ASSERT(fixture != nullptr);
+  GPR_ASSERT(test_channel != nullptr);
   GPR_ASSERT(buf != nullptr);
   uint8_t* channel =
-      is_client ? fixture->server_channel : fixture->client_channel;
+      is_client ? test_channel->server_channel : test_channel->client_channel;
   GPR_ASSERT(channel != nullptr);
-  size_t* bytes_written = is_client ? &fixture->bytes_written_to_server_channel
-                                    : &fixture->bytes_written_to_client_channel;
+  size_t* bytes_written = is_client
+                              ? &test_channel->bytes_written_to_server_channel
+                              : &test_channel->bytes_written_to_client_channel;
   GPR_ASSERT(bytes_written != nullptr);
   GPR_ASSERT(*bytes_written + buf_size <= TSI_TEST_DEFAULT_CHANNEL_SIZE);
   /* Write data to channel. */
@@ -146,7 +147,8 @@
   if (fixture->test_unused_bytes && !args->appended_unused_bytes) {
     args->appended_unused_bytes = true;
     send_bytes_to_peer(
-        fixture, reinterpret_cast<const unsigned char*>(TSI_TEST_UNUSED_BYTES),
+        fixture->channel,
+        reinterpret_cast<const unsigned char*>(TSI_TEST_UNUSED_BYTES),
         strlen(TSI_TEST_UNUSED_BYTES), args->is_client);
     if (fixture->client_result != nullptr &&
         fixture->server_result == nullptr) {
@@ -155,19 +157,21 @@
   }
 }
 
-static void receive_bytes_from_peer(tsi_test_fixture* fixture,
+static void receive_bytes_from_peer(tsi_test_channel* test_channel,
                                     unsigned char** buf, size_t* buf_size,
                                     bool is_client) {
-  GPR_ASSERT(fixture != nullptr);
+  GPR_ASSERT(test_channel != nullptr);
   GPR_ASSERT(*buf != nullptr);
   GPR_ASSERT(buf_size != nullptr);
   uint8_t* channel =
-      is_client ? fixture->client_channel : fixture->server_channel;
+      is_client ? test_channel->client_channel : test_channel->server_channel;
   GPR_ASSERT(channel != nullptr);
-  size_t* bytes_read = is_client ? &fixture->bytes_read_from_client_channel
-                                 : &fixture->bytes_read_from_server_channel;
-  size_t* bytes_written = is_client ? &fixture->bytes_written_to_client_channel
-                                    : &fixture->bytes_written_to_server_channel;
+  size_t* bytes_read = is_client
+                           ? &test_channel->bytes_read_from_client_channel
+                           : &test_channel->bytes_read_from_server_channel;
+  size_t* bytes_written = is_client
+                              ? &test_channel->bytes_written_to_client_channel
+                              : &test_channel->bytes_written_to_server_channel;
   GPR_ASSERT(bytes_read != nullptr);
   GPR_ASSERT(bytes_written != nullptr);
   size_t to_read = *buf_size < *bytes_written - *bytes_read
@@ -179,14 +183,13 @@
   *bytes_read += to_read;
 }
 
-static void send_message_to_peer(tsi_test_fixture* fixture,
-                                 tsi_frame_protector* protector,
-                                 bool is_client) {
+void tsi_test_frame_protector_send_message_to_peer(
+    tsi_test_frame_protector_config* config, tsi_test_channel* channel,
+    tsi_frame_protector* protector, bool is_client) {
   /* Initialization. */
-  GPR_ASSERT(fixture != nullptr);
-  GPR_ASSERT(fixture->config != nullptr);
+  GPR_ASSERT(config != nullptr);
+  GPR_ASSERT(channel != nullptr);
   GPR_ASSERT(protector != nullptr);
-  tsi_test_frame_protector_config* config = fixture->config;
   unsigned char* protected_buffer =
       static_cast<unsigned char*>(gpr_zalloc(config->protected_buffer_size));
   size_t message_size =
@@ -206,7 +209,7 @@
         &protected_buffer_size_to_send);
     GPR_ASSERT(result == TSI_OK);
     /* Send protected data to peer. */
-    send_bytes_to_peer(fixture, protected_buffer, protected_buffer_size_to_send,
+    send_bytes_to_peer(channel, protected_buffer, protected_buffer_size_to_send,
                        is_client);
     message_bytes += processed_message_size;
     message_size -= processed_message_size;
@@ -219,7 +222,7 @@
             protector, protected_buffer, &protected_buffer_size_to_send,
             &still_pending_size);
         GPR_ASSERT(result == TSI_OK);
-        send_bytes_to_peer(fixture, protected_buffer,
+        send_bytes_to_peer(channel, protected_buffer,
                            protected_buffer_size_to_send, is_client);
       } while (still_pending_size > 0 && result == TSI_OK);
       GPR_ASSERT(result == TSI_OK);
@@ -229,17 +232,16 @@
   gpr_free(protected_buffer);
 }
 
-static void receive_message_from_peer(tsi_test_fixture* fixture,
-                                      tsi_frame_protector* protector,
-                                      unsigned char* message,
-                                      size_t* bytes_received, bool is_client) {
+void tsi_test_frame_protector_receive_message_from_peer(
+    tsi_test_frame_protector_config* config, tsi_test_channel* channel,
+    tsi_frame_protector* protector, unsigned char* message,
+    size_t* bytes_received, bool is_client) {
   /* Initialization. */
-  GPR_ASSERT(fixture != nullptr);
+  GPR_ASSERT(config != nullptr);
+  GPR_ASSERT(channel != nullptr);
   GPR_ASSERT(protector != nullptr);
   GPR_ASSERT(message != nullptr);
   GPR_ASSERT(bytes_received != nullptr);
-  GPR_ASSERT(fixture->config != nullptr);
-  tsi_test_frame_protector_config* config = fixture->config;
   size_t read_offset = 0;
   size_t message_offset = 0;
   size_t read_from_peer_size = 0;
@@ -254,7 +256,7 @@
     /* Receive data from peer. */
     if (read_from_peer_size == 0) {
       read_from_peer_size = config->read_buffer_allocated_size;
-      receive_bytes_from_peer(fixture, &read_buffer, &read_from_peer_size,
+      receive_bytes_from_peer(channel, &read_buffer, &read_from_peer_size,
                               is_client);
       read_offset = 0;
     }
@@ -315,7 +317,7 @@
   }
   /* Send data to peer, if needed. */
   if (bytes_to_send_size > 0) {
-    send_bytes_to_peer(args->fixture, bytes_to_send, bytes_to_send_size,
+    send_bytes_to_peer(fixture->channel, bytes_to_send, bytes_to_send_size,
                        args->is_client);
     args->transferred_data = true;
   }
@@ -362,8 +364,8 @@
   /* Receive data from peer, if available. */
   do {
     size_t buf_size = args->handshake_buffer_size;
-    receive_bytes_from_peer(args->fixture, &args->handshake_buffer, &buf_size,
-                            args->is_client);
+    receive_bytes_from_peer(fixture->channel, &args->handshake_buffer,
+                            &buf_size, args->is_client);
     if (buf_size > 0) {
       args->transferred_data = true;
     }
@@ -412,6 +414,50 @@
   handshaker_args_destroy(server_args);
 }
 
+static void tsi_test_do_ping_pong(tsi_test_frame_protector_config* config,
+                                  tsi_test_channel* channel,
+                                  tsi_frame_protector* client_frame_protector,
+                                  tsi_frame_protector* server_frame_protector) {
+  GPR_ASSERT(config != nullptr);
+  GPR_ASSERT(channel != nullptr);
+  GPR_ASSERT(client_frame_protector != nullptr);
+  GPR_ASSERT(server_frame_protector != nullptr);
+  /* Client sends a message to server. */
+  tsi_test_frame_protector_send_message_to_peer(
+      config, channel, client_frame_protector, true /* is_client */);
+  unsigned char* server_received_message =
+      static_cast<unsigned char*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
+  size_t server_received_message_size = 0;
+  tsi_test_frame_protector_receive_message_from_peer(
+      config, channel, server_frame_protector, server_received_message,
+      &server_received_message_size, false /* is_client */);
+  GPR_ASSERT(config->client_message_size == server_received_message_size);
+  GPR_ASSERT(memcmp(config->client_message, server_received_message,
+                    server_received_message_size) == 0);
+  /* Server sends a message to client. */
+  tsi_test_frame_protector_send_message_to_peer(
+      config, channel, server_frame_protector, false /* is_client */);
+  unsigned char* client_received_message =
+      static_cast<unsigned char*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
+  size_t client_received_message_size = 0;
+  tsi_test_frame_protector_receive_message_from_peer(
+      config, channel, client_frame_protector, client_received_message,
+      &client_received_message_size, true /* is_client */);
+  GPR_ASSERT(config->server_message_size == client_received_message_size);
+  GPR_ASSERT(memcmp(config->server_message, client_received_message,
+                    client_received_message_size) == 0);
+  gpr_free(server_received_message);
+  gpr_free(client_received_message);
+}
+
+void tsi_test_frame_protector_do_round_trip_no_handshake(
+    tsi_test_frame_protector_fixture* fixture) {
+  GPR_ASSERT(fixture != nullptr);
+  tsi_test_do_ping_pong(fixture->config, fixture->channel,
+                        fixture->client_frame_protector,
+                        fixture->server_frame_protector);
+}
+
 void tsi_test_do_round_trip(tsi_test_fixture* fixture) {
   /* Initialization. */
   GPR_ASSERT(fixture != nullptr);
@@ -438,33 +484,11 @@
                      ? nullptr
                      : &server_max_output_protected_frame_size,
                  &server_frame_protector) == TSI_OK);
-  /* Client sends a message to server. */
-  send_message_to_peer(fixture, client_frame_protector, true /* is_client */);
-  unsigned char* server_received_message =
-      static_cast<unsigned char*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
-  size_t server_received_message_size = 0;
-  receive_message_from_peer(
-      fixture, server_frame_protector, server_received_message,
-      &server_received_message_size, false /* is_client */);
-  GPR_ASSERT(config->client_message_size == server_received_message_size);
-  GPR_ASSERT(memcmp(config->client_message, server_received_message,
-                    server_received_message_size) == 0);
-  /* Server sends a message to client. */
-  send_message_to_peer(fixture, server_frame_protector, false /* is_client */);
-  unsigned char* client_received_message =
-      static_cast<unsigned char*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
-  size_t client_received_message_size = 0;
-  receive_message_from_peer(
-      fixture, client_frame_protector, client_received_message,
-      &client_received_message_size, true /* is_client */);
-  GPR_ASSERT(config->server_message_size == client_received_message_size);
-  GPR_ASSERT(memcmp(config->server_message, client_received_message,
-                    client_received_message_size) == 0);
+  tsi_test_do_ping_pong(config, fixture->channel, client_frame_protector,
+                        server_frame_protector);
   /* Destroy server and client frame protectors. */
   tsi_frame_protector_destroy(client_frame_protector);
   tsi_frame_protector_destroy(server_frame_protector);
-  gpr_free(server_received_message);
-  gpr_free(client_received_message);
 }
 
 static unsigned char* generate_random_message(size_t size) {
@@ -484,8 +508,7 @@
     bool use_default_protected_buffer_size, bool use_default_client_message,
     bool use_default_server_message,
     bool use_default_client_max_output_protected_frame_size,
-    bool use_default_server_max_output_protected_frame_size,
-    bool use_default_handshake_buffer_size) {
+    bool use_default_server_max_output_protected_frame_size) {
   tsi_test_frame_protector_config* config =
       static_cast<tsi_test_frame_protector_config*>(
           gpr_zalloc(sizeof(*config)));
@@ -553,24 +576,42 @@
 
 void tsi_test_frame_protector_config_destroy(
     tsi_test_frame_protector_config* config) {
-  GPR_ASSERT(config != nullptr);
+  if (config == nullptr) {
+    return;
+  }
   gpr_free(config->client_message);
   gpr_free(config->server_message);
   gpr_free(config);
 }
 
+static tsi_test_channel* tsi_test_channel_create() {
+  tsi_test_channel* channel =
+      static_cast<tsi_test_channel*>(gpr_zalloc(sizeof(*channel)));
+  channel->client_channel =
+      static_cast<uint8_t*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
+  channel->server_channel =
+      static_cast<uint8_t*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
+  channel->bytes_written_to_client_channel = 0;
+  channel->bytes_written_to_server_channel = 0;
+  channel->bytes_read_from_client_channel = 0;
+  channel->bytes_read_from_server_channel = 0;
+  return channel;
+}
+
+static void tsi_test_channel_destroy(tsi_test_channel* channel) {
+  if (channel == nullptr) {
+    return;
+  }
+  gpr_free(channel->client_channel);
+  gpr_free(channel->server_channel);
+  gpr_free(channel);
+}
+
 void tsi_test_fixture_init(tsi_test_fixture* fixture) {
   fixture->config = tsi_test_frame_protector_config_create(
-      true, true, true, true, true, true, true, true);
+      true, true, true, true, true, true, true);
   fixture->handshake_buffer_size = TSI_TEST_DEFAULT_BUFFER_SIZE;
-  fixture->client_channel =
-      static_cast<uint8_t*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
-  fixture->server_channel =
-      static_cast<uint8_t*>(gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE));
-  fixture->bytes_written_to_client_channel = 0;
-  fixture->bytes_written_to_server_channel = 0;
-  fixture->bytes_read_from_client_channel = 0;
-  fixture->bytes_read_from_server_channel = 0;
+  fixture->channel = tsi_test_channel_create();
   fixture->test_unused_bytes = true;
   fixture->has_client_finished_first = false;
   gpr_mu_init(&fixture->mu);
@@ -579,14 +620,15 @@
 }
 
 void tsi_test_fixture_destroy(tsi_test_fixture* fixture) {
-  GPR_ASSERT(fixture != nullptr);
+  if (fixture == nullptr) {
+    return;
+  }
   tsi_test_frame_protector_config_destroy(fixture->config);
   tsi_handshaker_destroy(fixture->client_handshaker);
   tsi_handshaker_destroy(fixture->server_handshaker);
   tsi_handshaker_result_destroy(fixture->client_result);
   tsi_handshaker_result_destroy(fixture->server_result);
-  gpr_free(fixture->client_channel);
-  gpr_free(fixture->server_channel);
+  tsi_test_channel_destroy(fixture->channel);
   GPR_ASSERT(fixture->vtable != nullptr);
   GPR_ASSERT(fixture->vtable->destruct != nullptr);
   fixture->vtable->destruct(fixture);
@@ -594,3 +636,34 @@
   gpr_cv_destroy(&fixture->cv);
   gpr_free(fixture);
 }
+
+tsi_test_frame_protector_fixture* tsi_test_frame_protector_fixture_create() {
+  tsi_test_frame_protector_fixture* fixture =
+      static_cast<tsi_test_frame_protector_fixture*>(
+          gpr_zalloc(sizeof(*fixture)));
+  fixture->config = tsi_test_frame_protector_config_create(
+      true, true, true, true, true, true, true);
+  fixture->channel = tsi_test_channel_create();
+  return fixture;
+}
+
+void tsi_test_frame_protector_fixture_init(
+    tsi_test_frame_protector_fixture* fixture,
+    tsi_frame_protector* client_frame_protector,
+    tsi_frame_protector* server_frame_protector) {
+  GPR_ASSERT(fixture != nullptr);
+  fixture->client_frame_protector = client_frame_protector;
+  fixture->server_frame_protector = server_frame_protector;
+}
+
+void tsi_test_frame_protector_fixture_destroy(
+    tsi_test_frame_protector_fixture* fixture) {
+  if (fixture == nullptr) {
+    return;
+  }
+  tsi_test_frame_protector_config_destroy(fixture->config);
+  tsi_test_channel_destroy(fixture->channel);
+  tsi_frame_protector_destroy(fixture->client_frame_protector);
+  tsi_frame_protector_destroy(fixture->server_frame_protector);
+  gpr_free(fixture);
+}
diff --git a/test/core/tsi/transport_security_test_lib.h b/test/core/tsi/transport_security_test_lib.h
index 9b07448..b6a431f 100644
--- a/test/core/tsi/transport_security_test_lib.h
+++ b/test/core/tsi/transport_security_test_lib.h
@@ -35,8 +35,8 @@
 #define TSI_TEST_DEFAULT_CHANNEL_SIZE 32768
 #define TSI_TEST_BIG_MESSAGE_SIZE 17000
 #define TSI_TEST_SMALL_MESSAGE_SIZE 10
-#define TSI_TEST_NUM_OF_ARGUMENTS 8
-#define TSI_TEST_NUM_OF_COMBINATIONS 256
+#define TSI_TEST_NUM_OF_ARGUMENTS 7
+#define TSI_TEST_NUM_OF_COMBINATIONS 128
 #define TSI_TEST_UNUSED_BYTES "HELLO GOOGLE"
 
 /* ---  tsi_test_fixture object ---
@@ -46,12 +46,22 @@
   protect/unprotect operations with respect to TSI implementations. */
 typedef struct tsi_test_fixture tsi_test_fixture;
 
-/* ---  tsi_test_frame_protector_config object ---
+/* ---  tsi_test_frame_protector_fixture object ---
+  The object wraps all necessary information used to test correctness of TSI
+  frame protector implementations. */
+typedef struct tsi_test_frame_protector_fixture
+    tsi_test_frame_protector_fixture;
 
+/* ---  tsi_test_frame_protector_config object ---
   This object is used to configure different parameters of TSI frame protector
   APIs. */
 typedef struct tsi_test_frame_protector_config tsi_test_frame_protector_config;
 
+/* ---  tsi_test_channel object ---
+  This object represents simulated channels between the client and server
+  from/to which they could read/write the exchanged information. */
+typedef struct tsi_test_channel tsi_test_channel;
+
 /* V-table for tsi_test_fixture operations that are implemented differently in
    different TSI implementations. */
 typedef struct tsi_test_fixture_vtable {
@@ -73,17 +83,8 @@
   tsi_handshaker_result* server_result;
   /* size of buffer used to store data received from the peer. */
   size_t handshake_buffer_size;
-  /* simulated channels between client and server. If the server (client)
-     wants to send data to the client (server), he will write data to
-     client_channel (server_channel), which will be read by client (server). */
-  uint8_t* client_channel;
-  uint8_t* server_channel;
-  /* size of data written to the client/server channel. */
-  size_t bytes_written_to_client_channel;
-  size_t bytes_written_to_server_channel;
-  /* size of data read from the client/server channel */
-  size_t bytes_read_from_client_channel;
-  size_t bytes_read_from_server_channel;
+  /* tsi_test_channel instance. */
+  tsi_test_channel* channel;
   /* tsi_test_frame_protector_config instance */
   tsi_test_frame_protector_config* config;
   /* a flag indicating if client has finished TSI handshake first (i.e., before
@@ -106,6 +107,30 @@
   bool notified;
 };
 
+struct tsi_test_frame_protector_fixture {
+  /* client/server TSI frame protectors whose ownership are transferred. */
+  tsi_frame_protector* client_frame_protector;
+  tsi_frame_protector* server_frame_protector;
+  /* tsi_test_channel instance. */
+  tsi_test_channel* channel;
+  /* tsi_test_frame_protector_config instance */
+  tsi_test_frame_protector_config* config;
+};
+
+struct tsi_test_channel {
+  /* simulated channels between client and server. If the server (client)
+     wants to send data to the client (server), he will write data to
+     client_channel (server_channel), which will be read by client (server). */
+  uint8_t* client_channel;
+  uint8_t* server_channel;
+  /* size of data written to the client/server channel. */
+  size_t bytes_written_to_client_channel;
+  size_t bytes_written_to_server_channel;
+  /* size of data read from the client/server channel */
+  size_t bytes_read_from_client_channel;
+  size_t bytes_read_from_server_channel;
+};
+
 struct tsi_test_frame_protector_config {
   /* size of buffer used to store protected frames to be unprotected. */
   size_t read_buffer_allocated_size;
@@ -135,8 +160,7 @@
     bool use_default_protected_buffer_size, bool use_default_client_message,
     bool use_default_server_message,
     bool use_default_client_max_output_protected_frame_size,
-    bool use_default_server_max_output_protected_frame_size,
-    bool use_default_handshake_buffer_size);
+    bool use_default_server_max_output_protected_frame_size);
 
 /* This method sets different buffer and frame sizes of a
    tsi_test_frame_protector_config instance with user provided values. */
@@ -160,6 +184,35 @@
    this function. */
 void tsi_test_fixture_destroy(tsi_test_fixture* fixture);
 
+/* This method creates a tsi_test_frame_protector_fixture instance. */
+tsi_test_frame_protector_fixture* tsi_test_frame_protector_fixture_create();
+
+/* This method initializes members of tsi_test_frame_protector_fixture instance.
+   Note that the struct instance should be allocated before making
+   this call. */
+void tsi_test_frame_protector_fixture_init(
+    tsi_test_frame_protector_fixture* fixture,
+    tsi_frame_protector* client_frame_protector,
+    tsi_frame_protector* server_frame_protector);
+
+/* This method destroys a tsi_test_frame_protector_fixture instance. Note that
+   the fixture intance must be dynamically allocated and will be freed by this
+   function. */
+void tsi_test_frame_protector_fixture_destroy(
+    tsi_test_frame_protector_fixture* fixture);
+
+/* This method performs a protect opeation on raw data and sends the result to
+   peer. */
+void tsi_test_frame_protector_send_message_to_peer(
+    tsi_test_frame_protector_config* config, tsi_test_channel* channel,
+    tsi_frame_protector* protector, bool is_client);
+
+/* This method receives message from peer and unprotects it. */
+void tsi_test_frame_protector_receive_message_from_peer(
+    tsi_test_frame_protector_config* config, tsi_test_channel* channel,
+    tsi_frame_protector* protector, unsigned char* message,
+    size_t* bytes_received, bool is_client);
+
 /* This method performs a full TSI handshake between a client and a server.
    Note that the test library will implement the new TSI handshaker API to
    perform handshakes. */
@@ -171,4 +224,8 @@
    the client and server switching its role. */
 void tsi_test_do_round_trip(tsi_test_fixture* fixture);
 
+/* This method performs the above round trip test without doing handshakes. */
+void tsi_test_frame_protector_do_round_trip_no_handshake(
+    tsi_test_frame_protector_fixture* fixture);
+
 #endif  // GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_
diff --git a/test/core/util/mock_endpoint.cc b/test/core/util/mock_endpoint.cc
index adeff18..1156cd5 100644
--- a/test/core/util/mock_endpoint.cc
+++ b/test/core/util/mock_endpoint.cc
@@ -30,7 +30,7 @@
 #include <grpc/support/string_util.h>
 #include "src/core/lib/iomgr/sockaddr.h"
 
-typedef struct grpc_mock_endpoint {
+typedef struct mock_endpoint {
   grpc_endpoint base;
   gpr_mu mu;
   void (*on_write)(grpc_slice slice);
@@ -38,11 +38,11 @@
   grpc_slice_buffer* on_read_out;
   grpc_closure* on_read;
   grpc_resource_user* resource_user;
-} grpc_mock_endpoint;
+} mock_endpoint;
 
 static void me_read(grpc_endpoint* ep, grpc_slice_buffer* slices,
                     grpc_closure* cb) {
-  grpc_mock_endpoint* m = reinterpret_cast<grpc_mock_endpoint*>(ep);
+  mock_endpoint* m = reinterpret_cast<mock_endpoint*>(ep);
   gpr_mu_lock(&m->mu);
   if (m->read_buffer.count > 0) {
     grpc_slice_buffer_swap(&m->read_buffer, slices);
@@ -56,7 +56,7 @@
 
 static void me_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
                      grpc_closure* cb) {
-  grpc_mock_endpoint* m = reinterpret_cast<grpc_mock_endpoint*>(ep);
+  mock_endpoint* m = reinterpret_cast<mock_endpoint*>(ep);
   for (size_t i = 0; i < slices->count; i++) {
     m->on_write(slices->slices[i]);
   }
@@ -72,7 +72,7 @@
                                        grpc_pollset_set* pollset) {}
 
 static void me_shutdown(grpc_endpoint* ep, grpc_error* why) {
-  grpc_mock_endpoint* m = reinterpret_cast<grpc_mock_endpoint*>(ep);
+  mock_endpoint* m = reinterpret_cast<mock_endpoint*>(ep);
   gpr_mu_lock(&m->mu);
   if (m->on_read) {
     GRPC_CLOSURE_SCHED(m->on_read,
@@ -86,7 +86,7 @@
 }
 
 static void me_destroy(grpc_endpoint* ep) {
-  grpc_mock_endpoint* m = reinterpret_cast<grpc_mock_endpoint*>(ep);
+  mock_endpoint* m = reinterpret_cast<mock_endpoint*>(ep);
   grpc_slice_buffer_destroy(&m->read_buffer);
   grpc_resource_user_unref(m->resource_user);
   gpr_free(m);
@@ -97,7 +97,7 @@
 }
 
 static grpc_resource_user* me_get_resource_user(grpc_endpoint* ep) {
-  grpc_mock_endpoint* m = reinterpret_cast<grpc_mock_endpoint*>(ep);
+  mock_endpoint* m = reinterpret_cast<mock_endpoint*>(ep);
   return m->resource_user;
 }
 
@@ -118,8 +118,7 @@
 
 grpc_endpoint* grpc_mock_endpoint_create(void (*on_write)(grpc_slice slice),
                                          grpc_resource_quota* resource_quota) {
-  grpc_mock_endpoint* m =
-      static_cast<grpc_mock_endpoint*>(gpr_malloc(sizeof(*m)));
+  mock_endpoint* m = static_cast<mock_endpoint*>(gpr_malloc(sizeof(*m)));
   m->base.vtable = &vtable;
   char* name;
   gpr_asprintf(&name, "mock_endpoint_%" PRIxPTR, (intptr_t)m);
@@ -133,7 +132,7 @@
 }
 
 void grpc_mock_endpoint_put_read(grpc_endpoint* ep, grpc_slice slice) {
-  grpc_mock_endpoint* m = reinterpret_cast<grpc_mock_endpoint*>(ep);
+  mock_endpoint* m = reinterpret_cast<mock_endpoint*>(ep);
   gpr_mu_lock(&m->mu);
   if (m->on_read != nullptr) {
     grpc_slice_buffer_add(m->on_read_out, slice);
diff --git a/test/cpp/client/client_channel_stress_test.cc b/test/cpp/client/client_channel_stress_test.cc
index 1d2dcae..ee6958d 100644
--- a/test/cpp/client/client_channel_stress_test.cc
+++ b/test/cpp/client/client_channel_stress_test.cc
@@ -35,8 +35,8 @@
 #include <grpc/support/time.h>
 
 #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 
 #include "test/core/util/port.h"
diff --git a/test/cpp/cocoapods/generic/generic.mm b/test/cpp/cocoapods/generic/generic.mm
index 5ca34f1..26f09b0 100644
--- a/test/cpp/cocoapods/generic/generic.mm
+++ b/test/cpp/cocoapods/generic/generic.mm
@@ -32,7 +32,7 @@
 #include <grpc/grpc.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
+#include "src/core/lib/gprpp/thd.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden
index 026a941..ca7db5d 100644
--- a/test/cpp/codegen/compiler_test_golden
+++ b/test/cpp/codegen/compiler_test_golden
@@ -26,15 +26,15 @@
 
 #include "src/proto/grpc/testing/compiler_test.pb.h"
 
-#include <grpc++/impl/codegen/async_stream.h>
-#include <grpc++/impl/codegen/async_unary_call.h>
-#include <grpc++/impl/codegen/method_handler_impl.h>
-#include <grpc++/impl/codegen/proto_utils.h>
-#include <grpc++/impl/codegen/rpc_method.h>
-#include <grpc++/impl/codegen/service_type.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc++/impl/codegen/stub_options.h>
-#include <grpc++/impl/codegen/sync_stream.h>
+#include <grpcpp/impl/codegen/async_stream.h>
+#include <grpcpp/impl/codegen/async_unary_call.h>
+#include <grpcpp/impl/codegen/method_handler_impl.h>
+#include <grpcpp/impl/codegen/proto_utils.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/stub_options.h>
+#include <grpcpp/impl/codegen/sync_stream.h>
 
 namespace grpc {
 class CompletionQueue;
diff --git a/test/cpp/codegen/compiler_test_mock_golden b/test/cpp/codegen/compiler_test_mock_golden
index f97c2dd..65a7cb0 100644
--- a/test/cpp/codegen/compiler_test_mock_golden
+++ b/test/cpp/codegen/compiler_test_mock_golden
@@ -5,8 +5,8 @@
 #include "src/proto/grpc/testing/compiler_test.pb.h"
 #include "src/proto/grpc/testing/compiler_test.grpc.pb.h"
 
-#include <grpc++/impl/codegen/async_stream.h>
-#include <grpc++/impl/codegen/sync_stream.h>
+#include <grpcpp/impl/codegen/async_stream.h>
+#include <grpcpp/impl/codegen/sync_stream.h>
 #include <gmock/gmock.h>
 namespace grpc {
 namespace testing {
diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD
index afa054a..8ab0811 100644
--- a/test/cpp/end2end/BUILD
+++ b/test/cpp/end2end/BUILD
@@ -201,6 +201,27 @@
 )
 
 grpc_cc_test(
+    name = "health_service_end2end_test",
+    srcs = ["health_service_end2end_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        ":test_service_impl",
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//src/proto/grpc/health/v1:health_proto",
+        "//src/proto/grpc/testing:echo_messages_proto",
+        "//src/proto/grpc/testing:echo_proto",
+        "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
+
+grpc_cc_test(
     name = "hybrid_end2end_test",
     srcs = ["hybrid_end2end_test.cc"],
     external_deps = [
diff --git a/test/cpp/end2end/OWNERS b/test/cpp/end2end/OWNERS
deleted file mode 100644
index d87b328..0000000
--- a/test/cpp/end2end/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-set noparent
-@vjpai
-@yang-g
-@y-zeng
-
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index df706a2..d362058 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -33,7 +33,6 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/iomgr/port.h"
 #include "src/proto/grpc/health/v1/health.grpc.pb.h"
diff --git a/test/cpp/end2end/client_crash_test.cc b/test/cpp/end2end/client_crash_test.cc
index c28ffea..6bde664 100644
--- a/test/cpp/end2end/client_crash_test.cc
+++ b/test/cpp/end2end/client_crash_test.cc
@@ -26,7 +26,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
diff --git a/test/cpp/end2end/client_lb_end2end_test.cc b/test/cpp/end2end/client_lb_end2end_test.cc
index 16600d1..96a855c 100644
--- a/test/cpp/end2end/client_lb_end2end_test.cc
+++ b/test/cpp/end2end/client_lb_end2end_test.cc
@@ -38,7 +38,6 @@
 #include "src/core/ext/filters/client_channel/subchannel_index.h"
 #include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gprpp/debug_location.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 38fdde9..1e2cdb8 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -35,7 +35,6 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
index ae6c6c7..20804bc 100644
--- a/test/cpp/end2end/filter_end2end_test.cc
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -33,7 +33,6 @@
 #include <grpc/grpc.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/cpp/common/channel_filter.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc
index 86a41fc..42b15a0 100644
--- a/test/cpp/end2end/generic_end2end_test.cc
+++ b/test/cpp/end2end/generic_end2end_test.cc
@@ -31,7 +31,6 @@
 #include <grpc/grpc.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/cpp/end2end/grpclb_end2end_test.cc b/test/cpp/end2end/grpclb_end2end_test.cc
index da5d498..33f485b 100644
--- a/test/cpp/end2end/grpclb_end2end_test.cc
+++ b/test/cpp/end2end/grpclb_end2end_test.cc
@@ -34,9 +34,12 @@
 
 #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
 #include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/gprpp/ref_counted_ptr.h"
 #include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
+#include "src/cpp/server/secure_server_credentials.h"
+
+#include "src/cpp/client/secure_credentials.h"
 
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -58,6 +61,8 @@
 // - Test handling of creation of faulty RR instance by having the LB return a
 //   serverlist with non-existent backends after having initially returned a
 //   valid one.
+// - test using secure credentials and make sure we don't send call
+//   credentials to the balancer
 //
 // Findings from end to end testing to be covered here:
 // - Handling of LB servers restart, including reconnection after backing-off
@@ -187,11 +192,16 @@
   Status BalanceLoad(ServerContext* context, Stream* stream) override {
     gpr_log(GPR_INFO, "LB[%p]: BalanceLoad", this);
     LoadBalanceRequest request;
-    stream->Read(&request);
+    std::vector<ResponseDelayPair> responses_and_delays;
+
+    if (!stream->Read(&request)) {
+      goto done;
+    }
     IncreaseRequestCount();
-    gpr_log(GPR_INFO, "LB[%p]: recv msg '%s'", this,
+    gpr_log(GPR_INFO, "LB[%p]: received initial message '%s'", this,
             request.DebugString().c_str());
 
+    // TODO(juanlishen): Initial response should always be the first response.
     if (client_load_reporting_interval_seconds_ > 0) {
       LoadBalanceResponse initial_response;
       initial_response.mutable_initial_response()
@@ -200,7 +210,6 @@
       stream->Write(initial_response);
     }
 
-    std::vector<ResponseDelayPair> responses_and_delays;
     {
       std::unique_lock<std::mutex> lock(mu_);
       responses_and_delays = responses_and_delays_;
@@ -216,14 +225,13 @@
       std::unique_lock<std::mutex> lock(mu_);
       if (shutdown_) goto done;
       serverlist_cond_.wait(lock, [this] { return serverlist_ready_; });
-      serverlist_ready_ = false;
     }
 
     if (client_load_reporting_interval_seconds_ > 0) {
       request.Clear();
       if (stream->Read(&request)) {
-        gpr_log(GPR_INFO, "LB[%p]: recv client load report msg: '%s'", this,
-                request.DebugString().c_str());
+        gpr_log(GPR_INFO, "LB[%p]: received client load report message '%s'",
+                this, request.DebugString().c_str());
         GPR_ASSERT(request.has_client_stats());
         // We need to acquire the lock here in order to prevent the notify_one
         // below from firing before its corresponding wait is executed.
@@ -296,7 +304,7 @@
   void NotifyDoneWithServerlists() {
     std::lock_guard<std::mutex> lock(mu_);
     serverlist_ready_ = true;
-    serverlist_cond_.notify_one();
+    serverlist_cond_.notify_all();
   }
 
  private:
@@ -304,9 +312,7 @@
                     int delay_ms) {
     gpr_log(GPR_INFO, "LB[%p]: sleeping for %d ms...", this, delay_ms);
     if (delay_ms > 0) {
-      gpr_sleep_until(
-          gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                       gpr_time_from_millis(delay_ms, GPR_TIMESPAN)));
+      gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(delay_ms));
     }
     gpr_log(GPR_INFO, "LB[%p]: Woke up! Sending response '%s'", this,
             response.DebugString().c_str());
@@ -375,15 +381,22 @@
     SetNextResolution(addresses);
   }
 
-  void ResetStub(int fallback_timeout = 0) {
+  void ResetStub(int fallback_timeout = 0,
+                 const grpc::string& expected_targets = "") {
     ChannelArguments args;
     args.SetGrpclbFallbackTimeout(fallback_timeout);
     args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
                     response_generator_.get());
+    if (!expected_targets.empty()) {
+      args.SetString(GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS, expected_targets);
+    }
     std::ostringstream uri;
-    uri << "fake:///servername_not_used";
-    channel_ =
-        CreateCustomChannel(uri.str(), InsecureChannelCredentials(), args);
+    uri << "fake:///" << kApplicationTargetName_;
+    // TODO(dgq): templatize tests to run everything using both secure and
+    // insecure channel credentials.
+    std::shared_ptr<ChannelCredentials> creds(new SecureChannelCredentials(
+        grpc_fake_transport_security_credentials_create()));
+    channel_ = CreateCustomChannel(uri.str(), creds, args);
     stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
@@ -444,7 +457,7 @@
 
   void WaitForBackend(size_t backend_idx) {
     do {
-      CheckRpcSendOk();
+      (void)SendRpc();
     } while (backends_[backend_idx]->request_count() == 0);
     ResetBackendCounters();
   }
@@ -520,10 +533,10 @@
     return status;
   }
 
-  void CheckRpcSendOk(const size_t times = 1) {
+  void CheckRpcSendOk(const size_t times = 1, const int timeout_ms = 1000) {
     for (size_t i = 0; i < times; ++i) {
       EchoResponse response;
-      const Status status = SendRpc(&response);
+      const Status status = SendRpc(&response, timeout_ms);
       EXPECT_TRUE(status.ok()) << "code=" << status.error_code()
                                << " message=" << status.error_message();
       EXPECT_EQ(response.message(), kRequestMessage_);
@@ -561,8 +574,9 @@
       std::ostringstream server_address;
       server_address << server_host << ":" << port_;
       ServerBuilder builder;
-      builder.AddListeningPort(server_address.str(),
-                               InsecureServerCredentials());
+      std::shared_ptr<ServerCredentials> creds(new SecureServerCredentials(
+          grpc_fake_transport_security_server_credentials_create()));
+      builder.AddListeningPort(server_address.str(), creds);
       builder.RegisterService(service_);
       server_ = builder.BuildAndStart();
       cond->notify_one();
@@ -595,6 +609,7 @@
   grpc_core::RefCountedPtr<grpc_core::FakeResolverResponseGenerator>
       response_generator_;
   const grpc::string kRequestMessage_ = "Live long and prosper.";
+  const grpc::string kApplicationTargetName_ = "application_target_name";
 };
 
 class SingleBalancerTest : public GrpclbEnd2endTest {
@@ -630,11 +645,52 @@
   EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
 }
 
+TEST_F(SingleBalancerTest, SecureNaming) {
+  ResetStub(0, kApplicationTargetName_ + ";lb");
+  SetNextResolution({AddressData{balancer_servers_[0].port_, true, "lb"}});
+  const size_t kNumRpcsPerAddress = 100;
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts(), {}),
+      0);
+  // Make sure that trying to connect works without a call.
+  channel_->GetState(true /* try_to_connect */);
+  // We need to wait for all backends to come online.
+  WaitForAllBackends();
+  // Send kNumRpcsPerAddress RPCs per server.
+  CheckRpcSendOk(kNumRpcsPerAddress * num_backends_);
+
+  // Each backend should have gotten 100 requests.
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    EXPECT_EQ(kNumRpcsPerAddress,
+              backend_servers_[i].service_->request_count());
+  }
+  balancers_[0]->NotifyDoneWithServerlists();
+  // The balancer got a single request.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
+  // and sent a single response.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
+  // Check LB policy name for the channel.
+  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(SingleBalancerTest, SecureNamingDeathTest) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  // Make sure that we blow up (via abort() from the security connector) when
+  // the name from the balancer doesn't match expectations.
+  ASSERT_DEATH(
+      {
+        ResetStub(0, kApplicationTargetName_ + ";lb");
+        SetNextResolution(
+            {AddressData{balancer_servers_[0].port_, true, "woops"}});
+        channel_->WaitForConnected(grpc_timeout_seconds_to_deadline(1));
+      },
+      "");
+}
+
 TEST_F(SingleBalancerTest, InitiallyEmptyServerlist) {
   SetNextResolutionAllBalancers();
   const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
-  const int kCallDeadlineMs = 1000 * grpc_test_slowdown_factor();
-
+  const int kCallDeadlineMs = kServerlistDelayMs * 2;
   // First response is an empty serverlist, sent right away.
   ScheduleResponseForBalancer(0, LoadBalanceResponse(), 0);
   // Send non-empty serverlist only after kServerlistDelayMs
@@ -644,28 +700,20 @@
 
   const auto t0 = system_clock::now();
   // Client will block: LB will initially send empty serverlist.
-  CheckRpcSendOk(num_backends_);
+  CheckRpcSendOk(1, kCallDeadlineMs);
   const auto ellapsed_ms =
       std::chrono::duration_cast<std::chrono::milliseconds>(
           system_clock::now() - t0);
   // but eventually, the LB sends a serverlist update that allows the call to
   // proceed. The call delay must be larger than the delay in sending the
-  // populated serverlist but under the call's deadline.
+  // populated serverlist but under the call's deadline (which is enforced by
+  // the call's deadline).
   EXPECT_GT(ellapsed_ms.count(), kServerlistDelayMs);
-  EXPECT_LT(ellapsed_ms.count(), kCallDeadlineMs);
-
-  // Each backend should have gotten 1 request.
-  for (size_t i = 0; i < backends_.size(); ++i) {
-    EXPECT_EQ(1U, backend_servers_[i].service_->request_count());
-  }
   balancers_[0]->NotifyDoneWithServerlists();
   // The balancer got a single request.
   EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
   // and sent two responses.
   EXPECT_EQ(2U, balancer_servers_[0].service_->response_count());
-
-  // Check LB policy name for the channel.
-  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
 }
 
 TEST_F(SingleBalancerTest, Fallback) {
@@ -874,8 +922,6 @@
   // machinery to either update the LB responses "on the fly" or instruct
   // backends which ports to restart on.
   CheckRpcSendFailure();
-  // Check LB policy name for the channel.
-  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
 }
 
 class UpdatesTest : public GrpclbEnd2endTest {
@@ -892,7 +938,10 @@
   ScheduleResponseForBalancer(
       1, BalancerServiceImpl::BuildResponseForBackends(second_backend, {}), 0);
 
-  // Start servers and send 10 RPCs per server.
+  // Wait until the first backend is ready.
+  WaitForBackend(0);
+
+  // Send 10 requests.
   gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
   CheckRpcSendOk(10);
   gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
@@ -939,8 +988,6 @@
   EXPECT_EQ(1U, balancer_servers_[1].service_->response_count());
   EXPECT_EQ(0U, balancer_servers_[2].service_->request_count());
   EXPECT_EQ(0U, balancer_servers_[2].service_->response_count());
-  // Check LB policy name for the channel.
-  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
 }
 
 // Send an update with the same set of LBs as the one in SetUp() in order to
@@ -956,7 +1003,10 @@
   ScheduleResponseForBalancer(
       1, BalancerServiceImpl::BuildResponseForBackends(second_backend, {}), 0);
 
-  // Start servers and send 10 RPCs per server.
+  // Wait until the first backend is ready.
+  WaitForBackend(0);
+
+  // Send 10 requests.
   gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
   CheckRpcSendOk(10);
   gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
@@ -1012,9 +1062,6 @@
   // doesn't assign the second backend.
   EXPECT_EQ(0U, backend_servers_[1].service_->request_count());
   balancers_[0]->NotifyDoneWithServerlists();
-
-  // Check LB policy name for the channel.
-  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
 }
 
 TEST_F(UpdatesTest, UpdateBalancersDeadUpdate) {
@@ -1097,8 +1144,134 @@
   EXPECT_LE(balancer_servers_[1].service_->response_count(), 2U);
   EXPECT_EQ(0U, balancer_servers_[2].service_->request_count());
   EXPECT_EQ(0U, balancer_servers_[2].service_->response_count());
-  // Check LB policy name for the channel.
-  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(UpdatesTest, ReresolveDeadBackend) {
+  ResetStub(500);
+  // The first resolution contains the addresses of a balancer that never
+  // responds, and a fallback backend.
+  std::vector<AddressData> addresses;
+  addresses.emplace_back(AddressData{balancer_servers_[0].port_, true, ""});
+  addresses.emplace_back(AddressData{backend_servers_[0].port_, false, ""});
+  SetNextResolution(addresses);
+  // The re-resolution result will contain the addresses of the same balancer
+  // and a new fallback backend.
+  addresses.clear();
+  addresses.emplace_back(AddressData{balancer_servers_[0].port_, true, ""});
+  addresses.emplace_back(AddressData{backend_servers_[1].port_, false, ""});
+  SetNextReresolutionResponse(addresses);
+
+  // Start servers and send 10 RPCs per server.
+  gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
+  CheckRpcSendOk(10);
+  gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
+  // All 10 requests should have gone to the fallback backend.
+  EXPECT_EQ(10U, backend_servers_[0].service_->request_count());
+
+  // Kill backend 0.
+  gpr_log(GPR_INFO, "********** ABOUT TO KILL BACKEND 0 *************");
+  if (backends_[0]->Shutdown()) backend_servers_[0].Shutdown();
+  gpr_log(GPR_INFO, "********** KILLED BACKEND 0 *************");
+
+  // Wait until re-resolution has finished, as signaled by the second backend
+  // receiving a request.
+  WaitForBackend(1);
+
+  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
+  CheckRpcSendOk(10);
+  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
+  // All 10 requests should have gone to the second backend.
+  EXPECT_EQ(10U, backend_servers_[1].service_->request_count());
+
+  balancers_[0]->NotifyDoneWithServerlists();
+  balancers_[1]->NotifyDoneWithServerlists();
+  balancers_[2]->NotifyDoneWithServerlists();
+  EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
+  EXPECT_EQ(0U, balancer_servers_[0].service_->response_count());
+  EXPECT_EQ(0U, balancer_servers_[1].service_->request_count());
+  EXPECT_EQ(0U, balancer_servers_[1].service_->response_count());
+  EXPECT_EQ(0U, balancer_servers_[2].service_->request_count());
+  EXPECT_EQ(0U, balancer_servers_[2].service_->response_count());
+}
+
+// TODO(juanlishen): Should be removed when the first response is always the
+// initial response. Currently, if client load reporting is not enabled, the
+// balancer doesn't send initial response. When the backend shuts down, an
+// unexpected re-resolution will happen. This test configuration is a workaround
+// for test ReresolveDeadBalancer.
+class UpdatesWithClientLoadReportingTest : public GrpclbEnd2endTest {
+ public:
+  UpdatesWithClientLoadReportingTest() : GrpclbEnd2endTest(4, 3, 2) {}
+};
+
+TEST_F(UpdatesWithClientLoadReportingTest, ReresolveDeadBalancer) {
+  std::vector<AddressData> addresses;
+  addresses.emplace_back(AddressData{balancer_servers_[0].port_, true, ""});
+  SetNextResolution(addresses);
+  addresses.clear();
+  addresses.emplace_back(AddressData{balancer_servers_[1].port_, true, ""});
+  SetNextReresolutionResponse(addresses);
+  const std::vector<int> first_backend{GetBackendPorts()[0]};
+  const std::vector<int> second_backend{GetBackendPorts()[1]};
+
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(first_backend, {}), 0);
+  ScheduleResponseForBalancer(
+      1, BalancerServiceImpl::BuildResponseForBackends(second_backend, {}), 0);
+
+  // Start servers and send 10 RPCs per server.
+  gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
+  CheckRpcSendOk(10);
+  gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
+  // All 10 requests should have gone to the first backend.
+  EXPECT_EQ(10U, backend_servers_[0].service_->request_count());
+
+  // Kill backend 0.
+  gpr_log(GPR_INFO, "********** ABOUT TO KILL BACKEND 0 *************");
+  if (backends_[0]->Shutdown()) backend_servers_[0].Shutdown();
+  gpr_log(GPR_INFO, "********** KILLED BACKEND 0 *************");
+
+  CheckRpcSendFailure();
+
+  // Balancer 0 got a single request.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
+  // and sent a single response.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
+  EXPECT_EQ(0U, balancer_servers_[1].service_->request_count());
+  EXPECT_EQ(0U, balancer_servers_[1].service_->response_count());
+  EXPECT_EQ(0U, balancer_servers_[2].service_->request_count());
+  EXPECT_EQ(0U, balancer_servers_[2].service_->response_count());
+
+  // Kill balancer 0.
+  gpr_log(GPR_INFO, "********** ABOUT TO KILL BALANCER 0 *************");
+  if (balancers_[0]->Shutdown()) balancer_servers_[0].Shutdown();
+  gpr_log(GPR_INFO, "********** KILLED BALANCER 0 *************");
+
+  // Wait until re-resolution has finished, as signaled by the second backend
+  // receiving a request.
+  WaitForBackend(1);
+
+  // This is serviced by the new serverlist.
+  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
+  CheckRpcSendOk(10);
+  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
+  // All 10 requests should have gone to the second backend.
+  EXPECT_EQ(10U, backend_servers_[1].service_->request_count());
+
+  EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
+  EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
+  // After balancer 0 is killed, we restart an LB call immediately (because we
+  // disconnect to a previously connected balancer). Although we will cancel
+  // this call when the re-resolution update is done and another LB call restart
+  // is needed, this old call may still succeed reaching the LB server if
+  // re-resolution is slow. So balancer 1 may have received 2 requests and sent
+  // 2 responses.
+  EXPECT_GE(balancer_servers_[1].service_->request_count(), 1U);
+  EXPECT_GE(balancer_servers_[1].service_->response_count(), 1U);
+  EXPECT_LE(balancer_servers_[1].service_->request_count(), 2U);
+  EXPECT_LE(balancer_servers_[1].service_->response_count(), 2U);
+  EXPECT_EQ(0U, balancer_servers_[2].service_->request_count());
+  EXPECT_EQ(0U, balancer_servers_[2].service_->response_count());
 }
 
 TEST_F(SingleBalancerTest, Drop) {
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index fe53043..e3976a4 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -29,7 +29,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "src/proto/grpc/testing/echo_mock.grpc.pb.h"
diff --git a/test/cpp/end2end/server_crash_test.cc b/test/cpp/end2end/server_crash_test.cc
index 574d357..108dcd2 100644
--- a/test/cpp/end2end/server_crash_test.cc
+++ b/test/cpp/end2end/server_crash_test.cc
@@ -26,7 +26,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
diff --git a/test/cpp/end2end/server_early_return_test.cc b/test/cpp/end2end/server_early_return_test.cc
index 38f9b67..a0349fd 100644
--- a/test/cpp/end2end/server_early_return_test.cc
+++ b/test/cpp/end2end/server_early_return_test.cc
@@ -29,7 +29,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/cpp/end2end/streaming_throughput_test.cc b/test/cpp/end2end/streaming_throughput_test.cc
index 2c89281..4dc1d1f 100644
--- a/test/cpp/end2end/streaming_throughput_test.cc
+++ b/test/cpp/end2end/streaming_throughput_test.cc
@@ -33,7 +33,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
diff --git a/test/cpp/end2end/thread_stress_test.cc b/test/cpp/end2end/thread_stress_test.cc
index 0842d8e..828beef 100644
--- a/test/cpp/end2end/thread_stress_test.cc
+++ b/test/cpp/end2end/thread_stress_test.cc
@@ -28,7 +28,6 @@
 #include <grpc/grpc.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/thd.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
diff --git a/test/cpp/grpclb/BUILD b/test/cpp/grpclb/BUILD
new file mode 100644
index 0000000..8319eb5
--- /dev/null
+++ b/test/cpp/grpclb/BUILD
@@ -0,0 +1,39 @@
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+licenses(["notice"])  # Apache v2
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package", "grpc_cc_binary")
+
+grpc_package(
+    name = "test/cpp/grpclb",
+    visibility = "public",
+)  # Allows external users to implement grpclb tests.
+
+grpc_cc_test(
+    name = "grpclb_api_test",
+    srcs = ["grpclb_api_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//src/proto/grpc/lb/v1:load_balancer_proto",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
diff --git a/test/cpp/grpclb/grpclb_test.cc b/test/cpp/grpclb/grpclb_test.cc
deleted file mode 100644
index 9f6d9fa..0000000
--- a/test/cpp/grpclb/grpclb_test.cc
+++ /dev/null
@@ -1,799 +0,0 @@
-/*
- *
- * Copyright 2016 gRPC authors.
- *
- * 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 <cinttypes>
-#include <cstdarg>
-#include <cstdint>
-#include <cstring>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <grpc/grpc.h>
-#include <grpc/impl/codegen/byte_buffer_reader.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/time.h>
-
-#include <grpc++/impl/codegen/config.h>
-
-#include "src/core/ext/filters/client_channel/client_channel.h"
-#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/channel/channel_stack.h"
-#include "src/core/lib/gpr/env.h"
-#include "src/core/lib/gpr/host_port.h"
-#include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/thd.h"
-#include "src/core/lib/gpr/tmpfile.h"
-#include "src/core/lib/gprpp/ref_counted_ptr.h"
-#include "src/core/lib/iomgr/sockaddr.h"
-#include "src/core/lib/security/credentials/fake/fake_credentials.h"
-#include "src/core/lib/surface/channel.h"
-#include "src/core/lib/surface/server.h"
-#include "test/core/end2end/cq_verifier.h"
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
-
-#include "src/proto/grpc/lb/v1/load_balancer.pb.h"
-
-#define NUM_BACKENDS 4
-#define PAYLOAD "hello you"
-
-// TODO(dgq): Other scenarios in need of testing:
-// - Send an empty serverlist update and verify that the client request blocks
-//   until a new serverlist with actual contents is available.
-// - Send identical serverlist update
-// - Send a serverlist with faulty ip:port addresses (port > 2^16, etc).
-// - Test reception of invalid serverlist
-// - Test pinging
-// - Test against a non-LB server.
-// - Random LB server closing the stream unexpectedly.
-// - Test using DNS-resolvable names (localhost?)
-// - Test handling of creation of faulty RR instance by having the LB return a
-//   serverlist with non-existent backends after having initially returned a
-//   valid one.
-//
-// Findings from end to end testing to be covered here:
-// - Handling of LB servers restart, including reconnection after backing-off
-//   retries.
-// - Destruction of load balanced channel (and therefore of grpclb instance)
-//   while:
-//   1) the internal LB call is still active. This should work by virtue
-//   of the weak reference the LB call holds. The call should be terminated as
-//   part of the grpclb shutdown process.
-//   2) the retry timer is active. Again, the weak reference it holds should
-//   prevent a premature call to \a glb_destroy.
-// - Restart of backend servers with no changes to serverlist. This exercises
-//   the RR handover mechanism.
-
-namespace grpc {
-namespace {
-
-typedef struct client_fixture {
-  grpc_channel* client;
-  char* server_uri;
-  grpc_completion_queue* cq;
-} client_fixture;
-
-typedef struct server_fixture {
-  grpc_server* server;
-  grpc_call* server_call;
-  grpc_completion_queue* cq;
-  char* servers_hostport;
-  const char* balancer_name;
-  int port;
-  const char* lb_token_prefix;
-  gpr_thd_id tid;
-  int num_calls_serviced;
-} server_fixture;
-
-typedef struct test_fixture {
-  server_fixture lb_server;
-  server_fixture lb_backends[NUM_BACKENDS];
-  client_fixture client;
-  int lb_server_update_delay_ms;
-} test_fixture;
-
-static void* tag(intptr_t t) { return (void*)t; }
-
-static grpc_slice build_response_payload_slice(const char* host, int* ports,
-                                               size_t nports,
-                                               const char* token_prefix) {
-  // server_list {
-  //   servers {
-  //     ip_address: <in_addr/6 bytes of an IP>
-  //     port: <16 bit uint>
-  //     load_balance_token: "token..."
-  //   }
-  //   ...
-  // }
-  grpc::lb::v1::LoadBalanceResponse response;
-  auto* serverlist = response.mutable_server_list();
-
-  for (size_t i = 0; i < nports; i++) {
-    auto* server = serverlist->add_servers();
-    // TODO(dgq): test ipv6
-    struct in_addr ip4;
-    GPR_ASSERT(inet_pton(AF_INET, host, &ip4) == 1);
-    server->set_ip_address(
-        string(reinterpret_cast<const char*>(&ip4), sizeof(ip4)));
-    server->set_port(ports[i]);
-    // Missing tokens are acceptable. Test that path.
-    if (strlen(token_prefix) > 0) {
-      string token_data = token_prefix + std::to_string(ports[i]);
-      server->set_load_balance_token(token_data);
-    }
-  }
-  const string& enc_resp = response.SerializeAsString();
-  return grpc_slice_from_copied_buffer(enc_resp.data(), enc_resp.size());
-}
-
-static void drain_cq(grpc_completion_queue* cq) {
-  grpc_event ev;
-  do {
-    ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
-                                    nullptr);
-  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
-}
-
-static void sleep_ms(int delay_ms) {
-  gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                               gpr_time_from_millis(delay_ms, GPR_TIMESPAN)));
-}
-
-static void start_lb_server(server_fixture* sf, int* ports, size_t nports,
-                            int update_delay_ms) {
-  grpc_call* s;
-  cq_verifier* cqv = cq_verifier_create(sf->cq);
-  grpc_op ops[6];
-  grpc_op* op;
-  grpc_metadata_array request_metadata_recv;
-  grpc_call_details call_details;
-  grpc_call_error error;
-  int was_cancelled = 2;
-  grpc_byte_buffer* request_payload_recv;
-  grpc_byte_buffer* response_payload;
-
-  memset(ops, 0, sizeof(ops));
-  grpc_metadata_array_init(&request_metadata_recv);
-  grpc_call_details_init(&call_details);
-
-  error = grpc_server_request_call(sf->server, &s, &call_details,
-                                   &request_metadata_recv, sf->cq, sf->cq,
-                                   tag(200));
-  GPR_ASSERT(GRPC_CALL_OK == error);
-  gpr_log(GPR_INFO, "LB Server[%s](%s) up", sf->servers_hostport,
-          sf->balancer_name);
-  CQ_EXPECT_COMPLETION(cqv, tag(200), 1);
-  cq_verify(cqv);
-  gpr_log(GPR_INFO, "LB Server[%s](%s) after tag 200", sf->servers_hostport,
-          sf->balancer_name);
-
-  // make sure we've received the initial metadata from the grpclb request.
-  GPR_ASSERT(request_metadata_recv.count > 0);
-  GPR_ASSERT(request_metadata_recv.metadata != nullptr);
-
-  // receive request for backends
-  op = ops;
-  op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message.recv_message = &request_payload_recv;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(202),
-                                nullptr);
-  GPR_ASSERT(GRPC_CALL_OK == error);
-  CQ_EXPECT_COMPLETION(cqv, tag(202), 1);
-  cq_verify(cqv);
-  gpr_log(GPR_INFO, "LB Server[%s](%s) after RECV_MSG", sf->servers_hostport,
-          sf->balancer_name);
-
-  // validate initial request.
-  grpc_byte_buffer_reader bbr;
-  grpc_byte_buffer_reader_init(&bbr, request_payload_recv);
-  grpc_slice request_payload_slice = grpc_byte_buffer_reader_readall(&bbr);
-  grpc::lb::v1::LoadBalanceRequest request;
-  request.ParseFromArray(GRPC_SLICE_START_PTR(request_payload_slice),
-                         GRPC_SLICE_LENGTH(request_payload_slice));
-  GPR_ASSERT(request.has_initial_request());
-  GPR_ASSERT(request.initial_request().name() == sf->servers_hostport);
-  grpc_slice_unref(request_payload_slice);
-  grpc_byte_buffer_reader_destroy(&bbr);
-  grpc_byte_buffer_destroy(request_payload_recv);
-
-  grpc_slice response_payload_slice;
-  op = ops;
-  op->op = GRPC_OP_SEND_INITIAL_METADATA;
-  op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
-  op->data.recv_close_on_server.cancelled = &was_cancelled;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(201),
-                                nullptr);
-  GPR_ASSERT(GRPC_CALL_OK == error);
-  gpr_log(GPR_INFO, "LB Server[%s](%s) after tag 201", sf->servers_hostport,
-          sf->balancer_name);
-
-  for (int i = 0; i < 2; i++) {
-    if (i == 0) {
-      // First half of the ports.
-      response_payload_slice = build_response_payload_slice(
-          "127.0.0.1", ports, nports / 2, sf->lb_token_prefix);
-    } else {
-      // Second half of the ports.
-      sleep_ms(update_delay_ms);
-      response_payload_slice = build_response_payload_slice(
-          "127.0.0.1", ports + (nports / 2), (nports + 1) / 2 /* ceil */,
-          "" /* this half doesn't get to receive an LB token */);
-    }
-
-    response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1);
-    op = ops;
-    op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message.send_message = response_payload;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops),
-                                  tag(203), nullptr);
-    GPR_ASSERT(GRPC_CALL_OK == error);
-    CQ_EXPECT_COMPLETION(cqv, tag(203), 1);
-    cq_verify(cqv);
-    gpr_log(GPR_INFO, "LB Server[%s](%s) after SEND_MESSAGE, iter %d",
-            sf->servers_hostport, sf->balancer_name, i);
-
-    grpc_byte_buffer_destroy(response_payload);
-    grpc_slice_unref(response_payload_slice);
-  }
-  gpr_log(GPR_INFO, "LB Server[%s](%s) shutting down", sf->servers_hostport,
-          sf->balancer_name);
-
-  op = ops;
-  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
-  op->data.send_status_from_server.trailing_metadata_count = 0;
-  op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  grpc_slice status_details = grpc_slice_from_static_string("xyz");
-  op->data.send_status_from_server.status_details = &status_details;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(204),
-                                nullptr);
-  GPR_ASSERT(GRPC_CALL_OK == error);
-
-  CQ_EXPECT_COMPLETION(cqv, tag(201), 1);
-  CQ_EXPECT_COMPLETION(cqv, tag(204), 1);
-  cq_verify(cqv);
-  gpr_log(GPR_INFO, "LB Server[%s](%s) after tag 204. All done. LB server out",
-          sf->servers_hostport, sf->balancer_name);
-
-  grpc_call_unref(s);
-
-  cq_verifier_destroy(cqv);
-
-  grpc_metadata_array_destroy(&request_metadata_recv);
-  grpc_call_details_destroy(&call_details);
-}
-
-static void start_backend_server(server_fixture* sf) {
-  grpc_call* s;
-  cq_verifier* cqv;
-  grpc_op ops[6];
-  grpc_op* op;
-  grpc_metadata_array request_metadata_recv;
-  grpc_call_details call_details;
-  grpc_call_error error;
-  int was_cancelled;
-  grpc_byte_buffer* request_payload_recv;
-  grpc_byte_buffer* response_payload;
-  grpc_event ev;
-
-  while (true) {
-    memset(ops, 0, sizeof(ops));
-    cqv = cq_verifier_create(sf->cq);
-    was_cancelled = 2;
-    grpc_metadata_array_init(&request_metadata_recv);
-    grpc_call_details_init(&call_details);
-
-    error = grpc_server_request_call(sf->server, &s, &call_details,
-                                     &request_metadata_recv, sf->cq, sf->cq,
-                                     tag(100));
-    GPR_ASSERT(GRPC_CALL_OK == error);
-    gpr_log(GPR_INFO, "Server[%s] up", sf->servers_hostport);
-    ev = grpc_completion_queue_next(
-        sf->cq, grpc_timeout_seconds_to_deadline(60), nullptr);
-    if (!ev.success) {
-      gpr_log(GPR_INFO, "Server[%s] being torn down", sf->servers_hostport);
-      cq_verifier_destroy(cqv);
-      grpc_metadata_array_destroy(&request_metadata_recv);
-      grpc_call_details_destroy(&call_details);
-      return;
-    }
-    GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
-    const string expected_token =
-        strlen(sf->lb_token_prefix) == 0
-            ? ""
-            : sf->lb_token_prefix + std::to_string(sf->port);
-    GPR_ASSERT(contains_metadata(&request_metadata_recv, "lb-token",
-                                 expected_token.c_str()));
-
-    gpr_log(GPR_INFO, "Server[%s] after tag 100", sf->servers_hostport);
-
-    op = ops;
-    op->op = GRPC_OP_SEND_INITIAL_METADATA;
-    op->data.send_initial_metadata.count = 0;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
-    op->data.recv_close_on_server.cancelled = &was_cancelled;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops),
-                                  tag(101), nullptr);
-    GPR_ASSERT(GRPC_CALL_OK == error);
-    gpr_log(GPR_INFO, "Server[%s] after tag 101", sf->servers_hostport);
-
-    bool exit = false;
-    grpc_slice response_payload_slice = grpc_slice_from_copied_string(PAYLOAD);
-    while (!exit) {
-      op = ops;
-      op->op = GRPC_OP_RECV_MESSAGE;
-      op->data.recv_message.recv_message = &request_payload_recv;
-      op->flags = 0;
-      op->reserved = nullptr;
-      op++;
-      error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops),
-                                    tag(102), nullptr);
-      GPR_ASSERT(GRPC_CALL_OK == error);
-      ev = grpc_completion_queue_next(
-          sf->cq, grpc_timeout_seconds_to_deadline(3), nullptr);
-      if (ev.type == GRPC_OP_COMPLETE && ev.success) {
-        GPR_ASSERT(ev.tag = tag(102));
-        if (request_payload_recv == nullptr) {
-          exit = true;
-          gpr_log(GPR_INFO,
-                  "Server[%s] recv \"close\" from client, exiting. Call #%d",
-                  sf->servers_hostport, sf->num_calls_serviced);
-        }
-      } else {
-        gpr_log(GPR_INFO, "Server[%s] forced to shutdown. Call #%d",
-                sf->servers_hostport, sf->num_calls_serviced);
-        exit = true;
-      }
-      gpr_log(GPR_INFO, "Server[%s] after tag 102. Call #%d",
-              sf->servers_hostport, sf->num_calls_serviced);
-
-      if (!exit) {
-        response_payload =
-            grpc_raw_byte_buffer_create(&response_payload_slice, 1);
-        op = ops;
-        op->op = GRPC_OP_SEND_MESSAGE;
-        op->data.send_message.send_message = response_payload;
-        op->flags = 0;
-        op->reserved = nullptr;
-        op++;
-        error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops),
-                                      tag(103), nullptr);
-        GPR_ASSERT(GRPC_CALL_OK == error);
-        ev = grpc_completion_queue_next(
-            sf->cq, grpc_timeout_seconds_to_deadline(3), nullptr);
-        if (ev.type == GRPC_OP_COMPLETE && ev.success) {
-          GPR_ASSERT(ev.tag = tag(103));
-        } else {
-          gpr_log(GPR_INFO, "Server[%s] forced to shutdown. Call #%d",
-                  sf->servers_hostport, sf->num_calls_serviced);
-          exit = true;
-        }
-        gpr_log(GPR_INFO, "Server[%s] after tag 103. Call #%d",
-                sf->servers_hostport, sf->num_calls_serviced);
-        grpc_byte_buffer_destroy(response_payload);
-      }
-
-      grpc_byte_buffer_destroy(request_payload_recv);
-    }
-    ++sf->num_calls_serviced;
-
-    gpr_log(GPR_INFO, "Server[%s] OUT OF THE LOOP", sf->servers_hostport);
-    grpc_slice_unref(response_payload_slice);
-
-    op = ops;
-    op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
-    op->data.send_status_from_server.trailing_metadata_count = 0;
-    op->data.send_status_from_server.status = GRPC_STATUS_OK;
-    grpc_slice status_details =
-        grpc_slice_from_static_string("Backend server out a-ok");
-    op->data.send_status_from_server.status_details = &status_details;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops),
-                                  tag(104), nullptr);
-    GPR_ASSERT(GRPC_CALL_OK == error);
-
-    CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
-    CQ_EXPECT_COMPLETION(cqv, tag(104), 1);
-    cq_verify(cqv);
-    gpr_log(GPR_INFO, "Server[%s] DONE. After servicing %d calls",
-            sf->servers_hostport, sf->num_calls_serviced);
-
-    grpc_call_unref(s);
-    cq_verifier_destroy(cqv);
-    grpc_metadata_array_destroy(&request_metadata_recv);
-    grpc_call_details_destroy(&call_details);
-  }
-}
-
-static void perform_request(client_fixture* cf) {
-  grpc_call* c;
-  cq_verifier* cqv = cq_verifier_create(cf->cq);
-  grpc_op ops[6];
-  grpc_op* op;
-  grpc_metadata_array initial_metadata_recv;
-  grpc_metadata_array trailing_metadata_recv;
-  grpc_status_code status;
-  grpc_call_error error;
-  grpc_slice details;
-  grpc_byte_buffer* request_payload;
-  grpc_byte_buffer* response_payload_recv;
-  int i;
-
-  memset(ops, 0, sizeof(ops));
-  grpc_slice request_payload_slice =
-      grpc_slice_from_copied_string("hello world");
-
-  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr:1234");
-  c = grpc_channel_create_call(cf->client, nullptr, GRPC_PROPAGATE_DEFAULTS,
-                               cf->cq, grpc_slice_from_static_string("/foo"),
-                               &host, grpc_timeout_seconds_to_deadline(5),
-                               nullptr);
-  gpr_log(GPR_INFO, "Call 0x%" PRIxPTR " created", (intptr_t)c);
-  GPR_ASSERT(c);
-  char* peer;
-
-  grpc_metadata_array_init(&initial_metadata_recv);
-  grpc_metadata_array_init(&trailing_metadata_recv);
-
-  op = ops;
-  op->op = GRPC_OP_SEND_INITIAL_METADATA;
-  op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
-  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
-  op->data.recv_status_on_client.status = &status;
-  op->data.recv_status_on_client.status_details = &details;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1),
-                                nullptr);
-  GPR_ASSERT(GRPC_CALL_OK == error);
-
-  for (i = 0; i < 4; i++) {
-    request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1);
-
-    op = ops;
-    op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message.send_message = request_payload;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message.recv_message = &response_payload_recv;
-    op->flags = 0;
-    op->reserved = nullptr;
-    op++;
-    error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(2),
-                                  nullptr);
-    GPR_ASSERT(GRPC_CALL_OK == error);
-
-    CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
-    cq_verify(cqv);
-    gpr_log(GPR_INFO, "Client after sending msg %d / 4", i + 1);
-    GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, PAYLOAD));
-
-    grpc_byte_buffer_destroy(request_payload);
-    grpc_byte_buffer_destroy(response_payload_recv);
-  }
-
-  grpc_slice_unref(request_payload_slice);
-
-  op = ops;
-  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
-  op->flags = 0;
-  op->reserved = nullptr;
-  op++;
-  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(3),
-                                nullptr);
-  GPR_ASSERT(GRPC_CALL_OK == error);
-
-  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
-  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
-  cq_verify(cqv);
-  peer = grpc_call_get_peer(c);
-  gpr_log(GPR_INFO, "Client DONE WITH SERVER %s ", peer);
-
-  grpc_call_unref(c);
-
-  cq_verify_empty_timeout(cqv, 1 /* seconds */);
-  cq_verifier_destroy(cqv);
-
-  grpc_metadata_array_destroy(&initial_metadata_recv);
-  grpc_metadata_array_destroy(&trailing_metadata_recv);
-  grpc_slice_unref(details);
-  gpr_log(GPR_INFO, "Client call (peer %s) DESTROYED.", peer);
-  gpr_free(peer);
-}
-
-#define BALANCERS_NAME "lb.name"
-static void setup_client(const server_fixture* lb_server,
-                         const server_fixture* backends, client_fixture* cf) {
-  grpc_core::ExecCtx exec_ctx;
-
-  char* expected_target_names = nullptr;
-  const char* backends_name = lb_server->servers_hostport;
-  gpr_asprintf(&expected_target_names, "%s;%s", backends_name, BALANCERS_NAME);
-
-  auto response_generator =
-      grpc_core::MakeRefCounted<grpc_core::FakeResolverResponseGenerator>();
-
-  grpc_lb_addresses* addresses = grpc_lb_addresses_create(1, nullptr);
-  char* lb_uri_str;
-  gpr_asprintf(&lb_uri_str, "ipv4:%s", lb_server->servers_hostport);
-  grpc_uri* lb_uri = grpc_uri_parse(lb_uri_str, true);
-  GPR_ASSERT(lb_uri != nullptr);
-  grpc_lb_addresses_set_address_from_uri(addresses, 0, lb_uri, true,
-                                         lb_server->balancer_name, nullptr);
-  grpc_uri_destroy(lb_uri);
-  gpr_free(lb_uri_str);
-
-  gpr_asprintf(&cf->server_uri, "fake:///%s", lb_server->servers_hostport);
-  const grpc_arg fake_addresses =
-      grpc_lb_addresses_create_channel_arg(addresses);
-  grpc_channel_args* fake_result =
-      grpc_channel_args_copy_and_add(nullptr, &fake_addresses, 1);
-  grpc_lb_addresses_destroy(addresses);
-
-  grpc_arg new_args[] = {
-      grpc_fake_transport_expected_targets_arg(expected_target_names),
-      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
-          response_generator.get())};
-
-  grpc_channel_args args = {GPR_ARRAY_SIZE(new_args), new_args};
-
-  cf->cq = grpc_completion_queue_create_for_next(nullptr);
-  grpc_channel_credentials* fake_creds =
-      grpc_fake_transport_security_credentials_create();
-  cf->client =
-      grpc_secure_channel_create(fake_creds, cf->server_uri, &args, nullptr);
-  response_generator->SetResponse(fake_result);
-  grpc_channel_args_destroy(fake_result);
-  grpc_channel_credentials_unref(fake_creds);
-  gpr_free(expected_target_names);
-}
-
-static void teardown_client(client_fixture* cf) {
-  grpc_completion_queue_shutdown(cf->cq);
-  drain_cq(cf->cq);
-  grpc_completion_queue_destroy(cf->cq);
-  cf->cq = nullptr;
-  grpc_channel_destroy(cf->client);
-  cf->client = nullptr;
-  gpr_free(cf->server_uri);
-}
-
-static void setup_server(const char* host, server_fixture* sf) {
-  int assigned_port;
-
-  sf->cq = grpc_completion_queue_create_for_next(nullptr);
-  const char* colon_idx = strchr(host, ':');
-  if (colon_idx) {
-    const char* port_str = colon_idx + 1;
-    sf->port = atoi(port_str);
-    sf->servers_hostport = gpr_strdup(host);
-  } else {
-    sf->port = grpc_pick_unused_port_or_die();
-    gpr_join_host_port(&sf->servers_hostport, host, sf->port);
-  }
-
-  grpc_server_credentials* server_creds =
-      grpc_fake_transport_security_server_credentials_create();
-
-  sf->server = grpc_server_create(nullptr, nullptr);
-  grpc_server_register_completion_queue(sf->server, sf->cq, nullptr);
-  GPR_ASSERT((assigned_port = grpc_server_add_secure_http2_port(
-                  sf->server, sf->servers_hostport, server_creds)) > 0);
-  grpc_server_credentials_release(server_creds);
-  GPR_ASSERT(sf->port == assigned_port);
-  grpc_server_start(sf->server);
-}
-
-static void teardown_server(server_fixture* sf) {
-  if (!sf->server) return;
-
-  gpr_log(GPR_INFO, "Server[%s] shutting down", sf->servers_hostport);
-
-  grpc_completion_queue* shutdown_cq =
-      grpc_completion_queue_create_for_pluck(nullptr);
-  grpc_server_shutdown_and_notify(sf->server, shutdown_cq, tag(1000));
-  GPR_ASSERT(grpc_completion_queue_pluck(shutdown_cq, tag(1000),
-                                         grpc_timeout_seconds_to_deadline(5),
-                                         nullptr)
-                 .type == GRPC_OP_COMPLETE);
-  grpc_completion_queue_destroy(shutdown_cq);
-  grpc_server_destroy(sf->server);
-  gpr_thd_join(sf->tid);
-
-  sf->server = nullptr;
-  grpc_completion_queue_shutdown(sf->cq);
-  drain_cq(sf->cq);
-  grpc_completion_queue_destroy(sf->cq);
-
-  gpr_log(GPR_INFO, "Server[%s] bye bye", sf->servers_hostport);
-  gpr_free(sf->servers_hostport);
-}
-
-static void fork_backend_server(void* arg) {
-  server_fixture* sf = static_cast<server_fixture*>(arg);
-  start_backend_server(sf);
-}
-
-static void fork_lb_server(void* arg) {
-  test_fixture* tf = static_cast<test_fixture*>(arg);
-  int ports[NUM_BACKENDS];
-  for (int i = 0; i < NUM_BACKENDS; i++) {
-    ports[i] = tf->lb_backends[i].port;
-  }
-  start_lb_server(&tf->lb_server, ports, NUM_BACKENDS,
-                  tf->lb_server_update_delay_ms);
-}
-
-#define LB_TOKEN_PREFIX "token"
-static test_fixture setup_test_fixture(int lb_server_update_delay_ms) {
-  test_fixture tf;
-  memset(&tf, 0, sizeof(tf));
-  tf.lb_server_update_delay_ms = lb_server_update_delay_ms;
-
-  gpr_thd_options options = gpr_thd_options_default();
-  gpr_thd_options_set_joinable(&options);
-
-  for (int i = 0; i < NUM_BACKENDS; ++i) {
-    // Only the first half of the servers expect an LB token.
-    if (i < NUM_BACKENDS / 2) {
-      tf.lb_backends[i].lb_token_prefix = LB_TOKEN_PREFIX;
-    } else {
-      tf.lb_backends[i].lb_token_prefix = "";
-    }
-    setup_server("127.0.0.1", &tf.lb_backends[i]);
-    gpr_thd_new(&tf.lb_backends[i].tid, "grpclb_backend", fork_backend_server,
-                &tf.lb_backends[i], &options);
-  }
-
-  tf.lb_server.lb_token_prefix = LB_TOKEN_PREFIX;
-  tf.lb_server.balancer_name = BALANCERS_NAME;
-  setup_server("127.0.0.1", &tf.lb_server);
-  gpr_thd_new(&tf.lb_server.tid, "grpclb_server", fork_lb_server, &tf.lb_server,
-              &options);
-  setup_client(&tf.lb_server, tf.lb_backends, &tf.client);
-  return tf;
-}
-
-static void teardown_test_fixture(test_fixture* tf) {
-  teardown_client(&tf->client);
-  for (int i = 0; i < NUM_BACKENDS; ++i) {
-    teardown_server(&tf->lb_backends[i]);
-  }
-  teardown_server(&tf->lb_server);
-}
-
-// The LB server will send two updates: batch 1 and batch 2. Each batch contains
-// two addresses, both of a valid and running backend server. Batch 1 is readily
-// available and provided as soon as the client establishes the streaming call.
-// Batch 2 is sent after a delay of \a lb_server_update_delay_ms milliseconds.
-static test_fixture test_update(int lb_server_update_delay_ms) {
-  gpr_log(GPR_INFO, "start %s(%d)", __func__, lb_server_update_delay_ms);
-  test_fixture tf = setup_test_fixture(lb_server_update_delay_ms);
-  perform_request(
-      &tf.client);  // "consumes" 1st backend server of 1st serverlist
-  perform_request(
-      &tf.client);  // "consumes" 2nd backend server of 1st serverlist
-
-  perform_request(
-      &tf.client);  // "consumes" 1st backend server of 2nd serverlist
-  perform_request(
-      &tf.client);  // "consumes" 2nd backend server of 2nd serverlist
-
-  teardown_test_fixture(&tf);
-  gpr_log(GPR_INFO, "end %s(%d)", __func__, lb_server_update_delay_ms);
-  return tf;
-}
-
-TEST(GrpclbTest, Updates) {
-  grpc::test_fixture tf_result;
-  // Clients take at least one second to complete a call (the last part of the
-  // call sleeps for 1 second while verifying the client's completion queue is
-  // empty), more if the system is under load. Therefore:
-  //
-  // If the LB server waits 800ms before sending an update, it will arrive
-  // before the first client request finishes, skipping the second server from
-  // batch 1. All subsequent picks will come from the second half of the
-  // backends, those coming in the LB update.
-  tf_result = grpc::test_update(800);
-  GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced +
-                 tf_result.lb_backends[1].num_calls_serviced ==
-             1);
-  GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced +
-                 tf_result.lb_backends[3].num_calls_serviced >
-             0);
-  int num_serviced_calls = 0;
-  for (int i = 0; i < 4; i++) {
-    num_serviced_calls += tf_result.lb_backends[i].num_calls_serviced;
-  }
-  GPR_ASSERT(num_serviced_calls == 4);
-
-  // If the LB server waits 2500ms, the update arrives after two calls and three
-  // picks. The third pick will be the 1st server of the 1st update (RR policy
-  // going around). The fourth and final pick will come from the second LB
-  // update. In any case, the total number of serviced calls must again be equal
-  // to four across all the backends.
-  tf_result = grpc::test_update(2500);
-  GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced +
-                 tf_result.lb_backends[1].num_calls_serviced >=
-             2);
-  GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced +
-                 tf_result.lb_backends[3].num_calls_serviced >
-             0);
-  num_serviced_calls = 0;
-  for (int i = 0; i < 4; i++) {
-    num_serviced_calls += tf_result.lb_backends[i].num_calls_serviced;
-  }
-  GPR_ASSERT(num_serviced_calls == 4);
-}
-
-TEST(GrpclbTest, InvalidAddressInServerlist) {}
-
-}  // namespace
-}  // namespace grpc
-
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  grpc_test_init(argc, argv);
-  // Make the backup poller poll very frequently in order to pick up
-  // updates from all the subchannels's FDs.
-  gpr_setenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS", "1");
-  grpc_init();
-  const auto result = RUN_ALL_TESTS();
-  grpc_shutdown();
-  return result;
-}
diff --git a/test/cpp/naming/BUILD b/test/cpp/naming/BUILD
index 24c3d1a..a8fa0a0 100644
--- a/test/cpp/naming/BUILD
+++ b/test/cpp/naming/BUILD
@@ -37,10 +37,11 @@
 grpc_py_binary(
   name = "test_dns_server",
   srcs = ["test_dns_server.py"],
+  testonly = True,
   data = [
       "resolver_test_record_groups.yaml",
   ],
-  deps = [
+  external_deps = [
       "twisted",
       "yaml",
   ]
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index e3fba36..8215ecb 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -50,9 +50,9 @@
   // next state, return false if done. Collect stats when appropriate
   virtual bool RunNextState(bool, HistogramEntry* entry) = 0;
   virtual void StartNewClone(CompletionQueue* cq) = 0;
-  static void* tag(ClientRpcContext* c) { return reinterpret_cast<void*>(c); }
+  static void* tag(ClientRpcContext* c) { return static_cast<void*>(c); }
   static ClientRpcContext* detag(void* t) {
-    return reinterpret_cast<ClientRpcContext*>(t);
+    return static_cast<ClientRpcContext*>(t);
   }
 
   virtual void Start(CompletionQueue* cq, const ClientConfig& config) = 0;
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index b88b884..f1dfea2 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -240,11 +240,9 @@
    private:
     std::mutex mu_;
   };
-  static void* tag(ServerRpcContext* func) {
-    return reinterpret_cast<void*>(func);
-  }
+  static void* tag(ServerRpcContext* func) { return static_cast<void*>(func); }
   static ServerRpcContext* detag(void* tag) {
-    return reinterpret_cast<ServerRpcContext*>(tag);
+    return static_cast<ServerRpcContext*>(tag);
   }
 
   class ServerRpcContextUnaryImpl final : public ServerRpcContext {
diff --git a/test/cpp/test/BUILD b/test/cpp/test/BUILD
new file mode 100644
index 0000000..c549478
--- /dev/null
+++ b/test/cpp/test/BUILD
@@ -0,0 +1,39 @@
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+licenses(["notice"])  # Apache v2
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package", "grpc_cc_binary")
+
+grpc_package(
+    name = "test/cpp/test",
+    visibility = "public",
+)
+
+grpc_cc_test(
+    name = "server_context_test_spouse_test",
+    srcs = ["server_context_test_spouse_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//:grpc++_test",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
diff --git a/test/cpp/thread_manager/BUILD b/test/cpp/thread_manager/BUILD
new file mode 100644
index 0000000..093e51e
--- /dev/null
+++ b/test/cpp/thread_manager/BUILD
@@ -0,0 +1,40 @@
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+licenses(["notice"])  # Apache v2
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package", "grpc_cc_binary")
+
+grpc_package(
+    name = "test/cpp/thread_manager",
+    visibility = "public",
+)
+
+grpc_cc_test(
+    name = "thread_manager_test",
+    srcs = ["thread_manager_test.cc"],
+    external_deps = [
+        "gflags",
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_config",
+        "//test/cpp/util:test_util",
+    ],
+)
diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc
index 30c43b2..e9dd751 100644
--- a/test/cpp/util/grpc_tool.cc
+++ b/test/cpp/util/grpc_tool.cc
@@ -747,6 +747,8 @@
       }
     }
     Status status = call.Finish(&server_trailing_metadata);
+    PrintMetadata(server_trailing_metadata,
+                  "Received trailing metadata from server:");
     if (status.ok()) {
       fprintf(stderr, "Rpc succeeded with OK status\n");
       return true;
diff --git a/test/cpp/util/slice_test.cc b/test/cpp/util/slice_test.cc
index c2e55f3..5f0b9c1 100644
--- a/test/cpp/util/slice_test.cc
+++ b/test/cpp/util/slice_test.cc
@@ -67,7 +67,7 @@
 TEST_F(SliceTest, SliceNew) {
   char* x = new char[strlen(kContent) + 1];
   strcpy(x, kContent);
-  Slice spp(x, strlen(x), [](void* p) { delete[] reinterpret_cast<char*>(p); });
+  Slice spp(x, strlen(x), [](void* p) { delete[] static_cast<char*>(p); });
   CheckSlice(spp, kContent);
 }
 
@@ -86,7 +86,7 @@
   strcpy(t->x, kContent);
   Slice spp(t->x, strlen(t->x),
             [](void* p) {
-              auto* t = reinterpret_cast<stest*>(p);
+              auto* t = static_cast<stest*>(p);
               delete[] t->x;
               delete t;
             },
diff --git a/third_party/BUILD b/third_party/BUILD
index dea1229..f06c5e9 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -3,4 +3,9 @@
     "gtest.BUILD",
     "objective_c/Cronet/bidirectional_stream_c.h",
     "zlib.BUILD",
+    "twisted.BUILD",
+    "yaml.BUILD",
+    "incremental.BUILD",
+    "zope_interface.BUILD",
+    "constantly.BUILD",
 ])
diff --git a/third_party/constantly.BUILD b/third_party/constantly.BUILD
new file mode 100644
index 0000000..f1f93cb
--- /dev/null
+++ b/third_party/constantly.BUILD
@@ -0,0 +1,7 @@
+py_library(
+    name = "constantly",
+    srcs = glob(["constantly/*.py"]),
+    visibility = [
+        "//visibility:public",
+    ],
+)
diff --git a/third_party/incremental.BUILD b/third_party/incremental.BUILD
new file mode 100644
index 0000000..f2a06b4
--- /dev/null
+++ b/third_party/incremental.BUILD
@@ -0,0 +1,10 @@
+py_library(
+    name = "incremental",
+    srcs = glob(["src/incremental/*.py"]),
+    imports = [
+        "src",
+    ],
+    visibility = [
+        "//visibility:public",
+    ],
+)
diff --git a/third_party/nanopb/pb.h b/third_party/nanopb/pb.h
index 4576f79..62dca73 100644
--- a/third_party/nanopb/pb.h
+++ b/third_party/nanopb/pb.h
@@ -25,7 +25,7 @@
 /* #define PB_FIELD_16BIT 1 */
 
 /* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
-/* #define PB_FIELD_32BIT 1 */
+/* #define PB_FIELD_32BIT 1 */ 
 
 /* Disable support for error messages in order to save some code space. */
 /* #define PB_NO_ERRMSG 1 */
diff --git a/third_party/twisted.BUILD b/third_party/twisted.BUILD
new file mode 100644
index 0000000..5005c5a
--- /dev/null
+++ b/third_party/twisted.BUILD
@@ -0,0 +1,15 @@
+py_library(
+    name = "twisted",
+    srcs = glob(["src/twisted/**/*.py"]),
+    imports = [
+        "src",
+    ],
+    visibility = [
+        "//visibility:public",
+    ],
+    deps = [
+        "@com_github_twisted_incremental//:incremental",
+        "@com_github_twisted_constantly//:constantly",
+        "@com_github_zopefoundation_zope_interface//:zope_interface",
+    ],
+)
diff --git a/third_party/yaml.BUILD b/third_party/yaml.BUILD
new file mode 100644
index 0000000..f88854c
--- /dev/null
+++ b/third_party/yaml.BUILD
@@ -0,0 +1,10 @@
+py_library(
+    name = "yaml",
+    srcs = glob(["lib/yaml/*.py"]),
+    imports = [
+        "lib",
+    ],
+    visibility = [
+        "//visibility:public",
+    ],
+)
diff --git a/third_party/zope_interface.BUILD b/third_party/zope_interface.BUILD
new file mode 100644
index 0000000..b7b8d1e
--- /dev/null
+++ b/third_party/zope_interface.BUILD
@@ -0,0 +1,13 @@
+py_library(
+    name = "zope_interface",
+    srcs = glob([
+        "src/zope/interface/*.py",
+        "src/zope/interface/common/*.py",
+    ]),
+    imports = [
+        "src",
+    ],
+    visibility = [
+        "//visibility:public",
+    ],
+)
diff --git a/tools/codegen/core/gen_nano_proto.sh b/tools/codegen/core/gen_nano_proto.sh
index 4246840..6ce1517 100755
--- a/tools/codegen/core/gen_nano_proto.sh
+++ b/tools/codegen/core/gen_nano_proto.sh
@@ -43,9 +43,9 @@
   echo "Input proto file '$INPUT_PROTO' doesn't exist."
   exit 2
 fi
+
 if [[ ! -f "${EXPECTED_OPTIONS_FILE_PATH}" ]]; then
-  echo "Expected nanopb options file '${EXPECTED_OPTIONS_FILE_PATH}' missing"
-  exit 3
+  echo "Input proto file may need .options file to be correctly compiled."
 fi
 
 if [[ "${OUTPUT_DIR:0:1}" != '/' ]]; then
@@ -81,6 +81,11 @@
 sed -i "s:$PROTO_BASENAME.pb.h:${GRPC_OUTPUT_DIR}/$PROTO_BASENAME.pb.h:g" \
   "$OUTPUT_DIR/$PROTO_BASENAME.pb.c"
 
+if [ $PROTO_BASENAME == "handshaker" ] || [ $PROTO_BASENAME == "altscontext" ]; then
+  sed -i "s:transport_security_common.pb.h:${GRPC_OUTPUT_DIR}/transport_security_common.pb.h:g" \
+    "$OUTPUT_DIR/$PROTO_BASENAME.pb.h"
+fi
+
 # Fix up the include guards such that they pass the check_include_guards.py
 # test. Assumes that the generated files are being placed in gRPC src dir.
 readonly INCLUDE_GUARD_BASE=`echo $GRPC_OUTPUT_DIR | tr [a-z/] [A-Z_] | sed s:^.*SRC_::`
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index 066f284..25da3fd 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -45,6 +45,12 @@
     'grpc-server-stats-bin',
     'grpc-tags-bin',
     'grpc-trace-bin',
+    'grpc-previous-rpc-attempts',
+    'grpc-retry-pushback-ms',
+    '1',
+    '2',
+    '3',
+    '4',
     '',
     # channel arg keys
     'grpc.wait_for_ready',
@@ -163,6 +169,8 @@
     ('user-agent', True),
     ('host', True),
     ('lb-token', True),
+    ('grpc-previous-rpc-attempts', True),
+    ('grpc-retry-pushback-ms', True),
 ]
 
 COMPRESSION_ALGORITHMS = [
diff --git a/tools/distrib/check_copyright.py b/tools/distrib/check_copyright.py
index 55cec93..e7893a1 100755
--- a/tools/distrib/check_copyright.py
+++ b/tools/distrib/check_copyright.py
@@ -77,6 +77,12 @@
     'examples/python/route_guide/route_guide_pb2_grpc.py',
     'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
     'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
+    'src/core/tsi/alts/handshaker/altscontext.pb.h',
+    'src/core/tsi/alts/handshaker/altscontext.pb.c',
+    'src/core/tsi/alts/handshaker/handshaker.pb.h',
+    'src/core/tsi/alts/handshaker/handshaker.pb.c',
+    'src/core/tsi/alts/handshaker/transport_security_common.pb.h',
+    'src/core/tsi/alts/handshaker/transport_security_common.pb.c',
     'src/cpp/server/health/health.pb.h',
     'src/cpp/server/health/health.pb.c',
 
@@ -86,6 +92,9 @@
     'tools/grpcz/census.proto',
     # status.proto copied from googleapis
     'src/proto/grpc/status/status.proto',
+
+    # Gradle wrapper used to build for Android
+    'examples/android/helloworld/gradlew.bat',
 ))
 
 RE_YEAR = r'Copyright (?P<first_year>[0-9]+\-)?(?P<last_year>[0-9]+) gRPC authors.'
diff --git a/tools/distrib/check_include_guards.py b/tools/distrib/check_include_guards.py
index 6fc606f..b356a74 100755
--- a/tools/distrib/check_include_guards.py
+++ b/tools/distrib/check_include_guards.py
@@ -49,7 +49,7 @@
         self.failed = False
 
     def fail(self, fpath, regexp, fcontents, match_txt, correct, fix):
-        cpp_header = 'grpc++' in fpath
+        cpp_header = 'grpc++' in fpath or 'grpcpp' in fpath
         self.failed = True
         invalid_guards_msg_template = (
             '{0}: Missing preprocessor guards (RE {1}). '
@@ -78,7 +78,7 @@
         return fcontents
 
     def check(self, fpath, fix):
-        cpp_header = 'grpc++' in fpath
+        cpp_header = 'grpc++' in fpath or 'grpcpp' in fpath
         valid_guard = build_valid_guard(fpath)
 
         fcontents = load(fpath)
@@ -157,6 +157,9 @@
 
 KNOWN_BAD = set([
     'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
+    'src/core/tsi/alts/handshaker/altscontext.pb.h',
+    'src/core/tsi/alts/handshaker/handshaker.pb.h',
+    'src/core/tsi/alts/handshaker/transport_security_common.pb.h',
     'include/grpc++/ext/reflection.grpc.pb.h',
     'include/grpc++/ext/reflection.pb.h',
 ])
diff --git a/tools/distrib/check_nanopb_output.sh b/tools/distrib/check_nanopb_output.sh
index a30b73f..8b5823b 100755
--- a/tools/distrib/check_nanopb_output.sh
+++ b/tools/distrib/check_nanopb_output.sh
@@ -15,6 +15,7 @@
 
 set -ex
 
+readonly NANOPB_ALTS_TMP_OUTPUT="$(mktemp -d)"
 readonly NANOPB_TMP_OUTPUT="$(mktemp -d)"
 readonly PROTOBUF_INSTALL_PREFIX="$(mktemp -d)"
 
@@ -55,3 +56,29 @@
   echo "Outputs differ: $NANOPB_TMP_OUTPUT vs $LOAD_BALANCER_GRPC_OUTPUT_PATH"
   exit 2
 fi
+
+#
+# Checks for handshaker.proto and transport_security_common.proto
+#
+readonly HANDSHAKER_GRPC_OUTPUT_PATH='src/core/tsi/alts/handshaker'
+# nanopb-compile the proto to a temp location
+./tools/codegen/core/gen_nano_proto.sh \
+  src/core/tsi/alts/handshaker/proto/handshaker.proto \
+  "$NANOPB_ALTS_TMP_OUTPUT" \
+  "$HANDSHAKER_GRPC_OUTPUT_PATH"
+./tools/codegen/core/gen_nano_proto.sh \
+  src/core/tsi/alts/handshaker/proto/transport_security_common.proto \
+  "$NANOPB_ALTS_TMP_OUTPUT" \
+  "$HANDSHAKER_GRPC_OUTPUT_PATH"
+./tools/codegen/core/gen_nano_proto.sh \
+  src/core/tsi/alts/handshaker/proto/altscontext.proto \
+  "$NANOPB_ALTS_TMP_OUTPUT" \
+  "$HANDSHAKER_GRPC_OUTPUT_PATH"
+
+# compare outputs to checked compiled code
+for NANOPB_OUTPUT_FILE in $NANOPB_ALTS_TMP_OUTPUT/*.pb.*; do
+  if ! diff "$NANOPB_OUTPUT_FILE" "src/core/tsi/alts/handshaker/$(basename $NANOPB_OUTPUT_FILE)"; then
+    echo "Outputs differ: $NANOPB_ALTS_TMP_OUTPUT vs $HANDSHAKER_GRPC_OUTPUT_PATH"
+    exit 2
+  fi
+done
diff --git a/tools/distrib/python/grpcio_tools/grpc_version.py b/tools/distrib/python/grpcio_tools/grpc_version.py
index 5caa1d1..e8ca685 100644
--- a/tools/distrib/python/grpcio_tools/grpc_version.py
+++ b/tools/distrib/python/grpcio_tools/grpc_version.py
@@ -14,4 +14,4 @@
 
 # AUTO-GENERATED FROM `$REPO_ROOT/templates/tools/distrib/python/grpcio_tools/grpc_version.py.template`!!!
 
-VERSION = '1.10.0.dev0'
+VERSION = '1.11.0.dev0'
diff --git a/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile b/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
index 6f61e17..07604c7 100644
--- a/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_python_manylinux_x64/Dockerfile
@@ -18,19 +18,7 @@
 
 # Update the package manager
 RUN yum update -y
-
-#############################################################
-# Update Git to allow cloning submodules with --reference arg
-RUN yum remove -y git
 RUN yum install -y curl-devel expat-devel gettext-devel linux-headers openssl-devel zlib-devel gcc
-RUN cd /usr/src && \
-  curl -O -L https://kernel.org/pub/software/scm/git/git-2.0.5.tar.gz && \
-  tar xzf git-2.0.5.tar.gz
-RUN cd /usr/src/git-2.0.5 && \
-  make prefix=/usr/local/git all && \
-  make prefix=/usr/local/git install
-ENV PATH /usr/local/git/bin:$PATH
-RUN source /etc/bashrc
 
 ###################################
 # Install Python build requirements
diff --git a/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile b/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
index 5c3c351..96ab515 100644
--- a/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_python_manylinux_x86/Dockerfile
@@ -18,19 +18,7 @@
 
 # Update the package manager
 RUN yum update -y
-
-#############################################################
-# Update Git to allow cloning submodules with --reference arg
-RUN yum remove -y git
 RUN yum install -y curl-devel expat-devel gettext-devel linux-headers openssl-devel zlib-devel gcc
-RUN cd /usr/src && \
-  curl -O -L https://kernel.org/pub/software/scm/git/git-2.0.5.tar.gz && \
-  tar xzf git-2.0.5.tar.gz
-RUN cd /usr/src/git-2.0.5 && \
-  make prefix=/usr/local/git all && \
-  make prefix=/usr/local/git install
-ENV PATH /usr/local/git/bin:$PATH
-RUN source /etc/bashrc
 
 ###################################
 # Install Python build requirements
diff --git a/tools/dockerfile/interoptest/grpc_interop_dart/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_dart/Dockerfile
new file mode 100644
index 0000000..d754996
--- /dev/null
+++ b/tools/dockerfile/interoptest/grpc_interop_dart/Dockerfile
@@ -0,0 +1,18 @@
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+FROM google/dart:latest
+
+# Define the default command.
+CMD ["bash"]
diff --git a/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh
new file mode 100644
index 0000000..c4cb682
--- /dev/null
+++ b/tools/dockerfile/interoptest/grpc_interop_dart/build_interop.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright 2017 gRPC authors.
+#
+# 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.
+#
+# Builds Dart interop server and client in a base image.
+set -e
+
+mkdir -p /var/local/git
+git clone /var/local/jenkins/grpc-dart /var/local/git/grpc-dart
+
+# copy service account keys if available
+cp -r /var/local/jenkins/service_account $HOME || true
+
+cd /var/local/git/grpc-dart/interop
+/usr/lib/dart/bin/pub get
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 0741c79..eb6700d 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -40,7 +40,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.10.0-dev
+PROJECT_NUMBER         = 1.11.0-dev
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -921,7 +921,84 @@
 include/grpc/support/sync_windows.h \
 include/grpc/support/thd_id.h \
 include/grpc/support/time.h \
-include/grpc/support/workaround_list.h
+include/grpc/support/workaround_list.h \
+include/grpcpp/alarm.h \
+include/grpcpp/channel.h \
+include/grpcpp/client_context.h \
+include/grpcpp/completion_queue.h \
+include/grpcpp/create_channel.h \
+include/grpcpp/create_channel_posix.h \
+include/grpcpp/ext/health_check_service_server_builder_option.h \
+include/grpcpp/generic/async_generic_service.h \
+include/grpcpp/generic/generic_stub.h \
+include/grpcpp/grpcpp.h \
+include/grpcpp/health_check_service_interface.h \
+include/grpcpp/impl/call.h \
+include/grpcpp/impl/channel_argument_option.h \
+include/grpcpp/impl/client_unary_call.h \
+include/grpcpp/impl/codegen/async_stream.h \
+include/grpcpp/impl/codegen/async_unary_call.h \
+include/grpcpp/impl/codegen/byte_buffer.h \
+include/grpcpp/impl/codegen/call.h \
+include/grpcpp/impl/codegen/call_hook.h \
+include/grpcpp/impl/codegen/channel_interface.h \
+include/grpcpp/impl/codegen/client_context.h \
+include/grpcpp/impl/codegen/client_unary_call.h \
+include/grpcpp/impl/codegen/completion_queue.h \
+include/grpcpp/impl/codegen/completion_queue_tag.h \
+include/grpcpp/impl/codegen/config.h \
+include/grpcpp/impl/codegen/config_protobuf.h \
+include/grpcpp/impl/codegen/core_codegen.h \
+include/grpcpp/impl/codegen/core_codegen_interface.h \
+include/grpcpp/impl/codegen/create_auth_context.h \
+include/grpcpp/impl/codegen/grpc_library.h \
+include/grpcpp/impl/codegen/metadata_map.h \
+include/grpcpp/impl/codegen/method_handler_impl.h \
+include/grpcpp/impl/codegen/proto_utils.h \
+include/grpcpp/impl/codegen/rpc_method.h \
+include/grpcpp/impl/codegen/rpc_service_method.h \
+include/grpcpp/impl/codegen/security/auth_context.h \
+include/grpcpp/impl/codegen/serialization_traits.h \
+include/grpcpp/impl/codegen/server_context.h \
+include/grpcpp/impl/codegen/server_interface.h \
+include/grpcpp/impl/codegen/service_type.h \
+include/grpcpp/impl/codegen/slice.h \
+include/grpcpp/impl/codegen/status.h \
+include/grpcpp/impl/codegen/status_code_enum.h \
+include/grpcpp/impl/codegen/string_ref.h \
+include/grpcpp/impl/codegen/stub_options.h \
+include/grpcpp/impl/codegen/sync_stream.h \
+include/grpcpp/impl/codegen/time.h \
+include/grpcpp/impl/grpc_library.h \
+include/grpcpp/impl/method_handler_impl.h \
+include/grpcpp/impl/rpc_method.h \
+include/grpcpp/impl/rpc_service_method.h \
+include/grpcpp/impl/serialization_traits.h \
+include/grpcpp/impl/server_builder_option.h \
+include/grpcpp/impl/server_builder_plugin.h \
+include/grpcpp/impl/server_initializer.h \
+include/grpcpp/impl/service_type.h \
+include/grpcpp/resource_quota.h \
+include/grpcpp/security/auth_context.h \
+include/grpcpp/security/auth_metadata_processor.h \
+include/grpcpp/security/credentials.h \
+include/grpcpp/security/server_credentials.h \
+include/grpcpp/server.h \
+include/grpcpp/server_builder.h \
+include/grpcpp/server_context.h \
+include/grpcpp/server_posix.h \
+include/grpcpp/support/async_stream.h \
+include/grpcpp/support/async_unary_call.h \
+include/grpcpp/support/byte_buffer.h \
+include/grpcpp/support/channel_arguments.h \
+include/grpcpp/support/config.h \
+include/grpcpp/support/slice.h \
+include/grpcpp/support/status.h \
+include/grpcpp/support/status_code_enum.h \
+include/grpcpp/support/string_ref.h \
+include/grpcpp/support/stub_options.h \
+include/grpcpp/support/sync_stream.h \
+include/grpcpp/support/time.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index d4b2b2b..ff5abc6 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -40,7 +40,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 1.10.0-dev
+PROJECT_NUMBER         = 1.11.0-dev
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -923,6 +923,84 @@
 include/grpc/support/thd_id.h \
 include/grpc/support/time.h \
 include/grpc/support/workaround_list.h \
+include/grpcpp/alarm.h \
+include/grpcpp/channel.h \
+include/grpcpp/client_context.h \
+include/grpcpp/completion_queue.h \
+include/grpcpp/create_channel.h \
+include/grpcpp/create_channel_posix.h \
+include/grpcpp/ext/health_check_service_server_builder_option.h \
+include/grpcpp/generic/async_generic_service.h \
+include/grpcpp/generic/generic_stub.h \
+include/grpcpp/grpcpp.h \
+include/grpcpp/health_check_service_interface.h \
+include/grpcpp/impl/call.h \
+include/grpcpp/impl/channel_argument_option.h \
+include/grpcpp/impl/client_unary_call.h \
+include/grpcpp/impl/codegen/async_stream.h \
+include/grpcpp/impl/codegen/async_unary_call.h \
+include/grpcpp/impl/codegen/byte_buffer.h \
+include/grpcpp/impl/codegen/call.h \
+include/grpcpp/impl/codegen/call_hook.h \
+include/grpcpp/impl/codegen/channel_interface.h \
+include/grpcpp/impl/codegen/client_context.h \
+include/grpcpp/impl/codegen/client_unary_call.h \
+include/grpcpp/impl/codegen/completion_queue.h \
+include/grpcpp/impl/codegen/completion_queue_tag.h \
+include/grpcpp/impl/codegen/config.h \
+include/grpcpp/impl/codegen/config_protobuf.h \
+include/grpcpp/impl/codegen/core_codegen.h \
+include/grpcpp/impl/codegen/core_codegen.h \
+include/grpcpp/impl/codegen/core_codegen_interface.h \
+include/grpcpp/impl/codegen/create_auth_context.h \
+include/grpcpp/impl/codegen/grpc_library.h \
+include/grpcpp/impl/codegen/metadata_map.h \
+include/grpcpp/impl/codegen/method_handler_impl.h \
+include/grpcpp/impl/codegen/proto_utils.h \
+include/grpcpp/impl/codegen/rpc_method.h \
+include/grpcpp/impl/codegen/rpc_service_method.h \
+include/grpcpp/impl/codegen/security/auth_context.h \
+include/grpcpp/impl/codegen/serialization_traits.h \
+include/grpcpp/impl/codegen/server_context.h \
+include/grpcpp/impl/codegen/server_interface.h \
+include/grpcpp/impl/codegen/service_type.h \
+include/grpcpp/impl/codegen/slice.h \
+include/grpcpp/impl/codegen/status.h \
+include/grpcpp/impl/codegen/status_code_enum.h \
+include/grpcpp/impl/codegen/string_ref.h \
+include/grpcpp/impl/codegen/stub_options.h \
+include/grpcpp/impl/codegen/sync_stream.h \
+include/grpcpp/impl/codegen/time.h \
+include/grpcpp/impl/grpc_library.h \
+include/grpcpp/impl/method_handler_impl.h \
+include/grpcpp/impl/rpc_method.h \
+include/grpcpp/impl/rpc_service_method.h \
+include/grpcpp/impl/serialization_traits.h \
+include/grpcpp/impl/server_builder_option.h \
+include/grpcpp/impl/server_builder_plugin.h \
+include/grpcpp/impl/server_initializer.h \
+include/grpcpp/impl/service_type.h \
+include/grpcpp/resource_quota.h \
+include/grpcpp/security/auth_context.h \
+include/grpcpp/security/auth_metadata_processor.h \
+include/grpcpp/security/credentials.h \
+include/grpcpp/security/server_credentials.h \
+include/grpcpp/server.h \
+include/grpcpp/server_builder.h \
+include/grpcpp/server_context.h \
+include/grpcpp/server_posix.h \
+include/grpcpp/support/async_stream.h \
+include/grpcpp/support/async_unary_call.h \
+include/grpcpp/support/byte_buffer.h \
+include/grpcpp/support/channel_arguments.h \
+include/grpcpp/support/config.h \
+include/grpcpp/support/slice.h \
+include/grpcpp/support/status.h \
+include/grpcpp/support/status_code_enum.h \
+include/grpcpp/support/string_ref.h \
+include/grpcpp/support/stub_options.h \
+include/grpcpp/support/sync_stream.h \
+include/grpcpp/support/time.h \
 src/core/ext/transport/inproc/inproc_transport.h \
 src/core/lib/avl/avl.h \
 src/core/lib/backoff/backoff.h \
@@ -952,7 +1030,6 @@
 src/core/lib/gpr/spinlock.h \
 src/core/lib/gpr/string.h \
 src/core/lib/gpr/string_windows.h \
-src/core/lib/gpr/thd.h \
 src/core/lib/gpr/time_precise.h \
 src/core/lib/gpr/tls.h \
 src/core/lib/gpr/tls_gcc.h \
@@ -971,6 +1048,7 @@
 src/core/lib/gprpp/orphanable.h \
 src/core/lib/gprpp/ref_counted.h \
 src/core/lib/gprpp/ref_counted_ptr.h \
+src/core/lib/gprpp/thd.h \
 src/core/lib/http/format_request.h \
 src/core/lib/http/httpcli.h \
 src/core/lib/http/parser.h \
@@ -1047,6 +1125,7 @@
 src/core/lib/slice/slice_hash_table.h \
 src/core/lib/slice/slice_internal.h \
 src/core/lib/slice/slice_string_helpers.h \
+src/core/lib/slice/slice_weak_hash_table.h \
 src/core/lib/surface/api_trace.h \
 src/core/lib/surface/call.h \
 src/core/lib/surface/call_test_only.h \
@@ -1071,6 +1150,7 @@
 src/core/lib/transport/service_config.h \
 src/core/lib/transport/static_metadata.h \
 src/core/lib/transport/status_conversion.h \
+src/core/lib/transport/status_metadata.h \
 src/core/lib/transport/timeout_encoding.h \
 src/core/lib/transport/transport.h \
 src/core/lib/transport/transport_impl.h \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index c095b5b..e7e9e04 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -885,7 +885,6 @@
 src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \
 src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc \
-src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \
@@ -902,6 +901,8 @@
 src/core/ext/filters/client_channel/lb_policy_factory.h \
 src/core/ext/filters/client_channel/lb_policy_registry.cc \
 src/core/ext/filters/client_channel/lb_policy_registry.h \
+src/core/ext/filters/client_channel/method_params.cc \
+src/core/ext/filters/client_channel/method_params.h \
 src/core/ext/filters/client_channel/parse_address.cc \
 src/core/ext/filters/client_channel/parse_address.h \
 src/core/ext/filters/client_channel/proxy_mapper.cc \
@@ -928,6 +929,8 @@
 src/core/ext/filters/client_channel/resolver_registry.h \
 src/core/ext/filters/client_channel/retry_throttle.cc \
 src/core/ext/filters/client_channel/retry_throttle.h \
+src/core/ext/filters/client_channel/status_util.cc \
+src/core/ext/filters/client_channel/status_util.h \
 src/core/ext/filters/client_channel/subchannel.cc \
 src/core/ext/filters/client_channel/subchannel.h \
 src/core/ext/filters/client_channel/subchannel_index.cc \
@@ -1094,10 +1097,6 @@
 src/core/lib/gpr/sync.cc \
 src/core/lib/gpr/sync_posix.cc \
 src/core/lib/gpr/sync_windows.cc \
-src/core/lib/gpr/thd.cc \
-src/core/lib/gpr/thd.h \
-src/core/lib/gpr/thd_posix.cc \
-src/core/lib/gpr/thd_windows.cc \
 src/core/lib/gpr/time.cc \
 src/core/lib/gpr/time_posix.cc \
 src/core/lib/gpr/time_precise.cc \
@@ -1126,6 +1125,9 @@
 src/core/lib/gprpp/orphanable.h \
 src/core/lib/gprpp/ref_counted.h \
 src/core/lib/gprpp/ref_counted_ptr.h \
+src/core/lib/gprpp/thd.h \
+src/core/lib/gprpp/thd_posix.cc \
+src/core/lib/gprpp/thd_windows.cc \
 src/core/lib/http/format_request.cc \
 src/core/lib/http/format_request.h \
 src/core/lib/http/httpcli.cc \
@@ -1282,6 +1284,17 @@
 src/core/lib/profiling/timers.h \
 src/core/lib/security/context/security_context.cc \
 src/core/lib/security/context/security_context.h \
+src/core/lib/security/credentials/alts/alts_credentials.cc \
+src/core/lib/security/credentials/alts/alts_credentials.h \
+src/core/lib/security/credentials/alts/check_gcp_environment.cc \
+src/core/lib/security/credentials/alts/check_gcp_environment.h \
+src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc \
+src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc \
+src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc \
+src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc \
+src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc \
+src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h \
+src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc \
 src/core/lib/security/credentials/composite/composite_credentials.cc \
 src/core/lib/security/credentials/composite/composite_credentials.h \
 src/core/lib/security/credentials/credentials.cc \
@@ -1306,17 +1319,19 @@
 src/core/lib/security/credentials/plugin/plugin_credentials.h \
 src/core/lib/security/credentials/ssl/ssl_credentials.cc \
 src/core/lib/security/credentials/ssl/ssl_credentials.h \
+src/core/lib/security/security_connector/alts_security_connector.cc \
+src/core/lib/security/security_connector/alts_security_connector.h \
+src/core/lib/security/security_connector/security_connector.cc \
+src/core/lib/security/security_connector/security_connector.h \
 src/core/lib/security/transport/auth_filters.h \
 src/core/lib/security/transport/client_auth_filter.cc \
-src/core/lib/security/transport/lb_targets_info.cc \
-src/core/lib/security/transport/lb_targets_info.h \
 src/core/lib/security/transport/secure_endpoint.cc \
 src/core/lib/security/transport/secure_endpoint.h \
-src/core/lib/security/transport/security_connector.cc \
-src/core/lib/security/transport/security_connector.h \
 src/core/lib/security/transport/security_handshaker.cc \
 src/core/lib/security/transport/security_handshaker.h \
 src/core/lib/security/transport/server_auth_filter.cc \
+src/core/lib/security/transport/target_authority_table.cc \
+src/core/lib/security/transport/target_authority_table.h \
 src/core/lib/security/transport/tsi_error.cc \
 src/core/lib/security/transport/tsi_error.h \
 src/core/lib/security/util/json_util.cc \
@@ -1327,12 +1342,12 @@
 src/core/lib/slice/percent_encoding.h \
 src/core/lib/slice/slice.cc \
 src/core/lib/slice/slice_buffer.cc \
-src/core/lib/slice/slice_hash_table.cc \
 src/core/lib/slice/slice_hash_table.h \
 src/core/lib/slice/slice_intern.cc \
 src/core/lib/slice/slice_internal.h \
 src/core/lib/slice/slice_string_helpers.cc \
 src/core/lib/slice/slice_string_helpers.h \
+src/core/lib/slice/slice_weak_hash_table.h \
 src/core/lib/surface/README.md \
 src/core/lib/surface/api_trace.cc \
 src/core/lib/surface/api_trace.h \
@@ -1389,6 +1404,8 @@
 src/core/lib/transport/static_metadata.h \
 src/core/lib/transport/status_conversion.cc \
 src/core/lib/transport/status_conversion.h \
+src/core/lib/transport/status_metadata.cc \
+src/core/lib/transport/status_metadata.h \
 src/core/lib/transport/timeout_encoding.cc \
 src/core/lib/transport/timeout_encoding.h \
 src/core/lib/transport/transport.cc \
@@ -1397,6 +1414,53 @@
 src/core/lib/transport/transport_op_string.cc \
 src/core/plugin_registry/grpc_plugin_registry.cc \
 src/core/tsi/README.md \
+src/core/tsi/alts/crypt/aes_gcm.cc \
+src/core/tsi/alts/crypt/gsec.cc \
+src/core/tsi/alts/crypt/gsec.h \
+src/core/tsi/alts/frame_protector/alts_counter.cc \
+src/core/tsi/alts/frame_protector/alts_counter.h \
+src/core/tsi/alts/frame_protector/alts_crypter.cc \
+src/core/tsi/alts/frame_protector/alts_crypter.h \
+src/core/tsi/alts/frame_protector/alts_frame_protector.cc \
+src/core/tsi/alts/frame_protector/alts_frame_protector.h \
+src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc \
+src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h \
+src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc \
+src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc \
+src/core/tsi/alts/frame_protector/frame_handler.cc \
+src/core/tsi/alts/frame_protector/frame_handler.h \
+src/core/tsi/alts/handshaker/alts_handshaker_client.cc \
+src/core/tsi/alts/handshaker/alts_handshaker_client.h \
+src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc \
+src/core/tsi/alts/handshaker/alts_handshaker_service_api.h \
+src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc \
+src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h \
+src/core/tsi/alts/handshaker/alts_tsi_event.cc \
+src/core/tsi/alts/handshaker/alts_tsi_event.h \
+src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc \
+src/core/tsi/alts/handshaker/alts_tsi_handshaker.h \
+src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h \
+src/core/tsi/alts/handshaker/alts_tsi_utils.cc \
+src/core/tsi/alts/handshaker/alts_tsi_utils.h \
+src/core/tsi/alts/handshaker/altscontext.pb.c \
+src/core/tsi/alts/handshaker/altscontext.pb.h \
+src/core/tsi/alts/handshaker/handshaker.pb.c \
+src/core/tsi/alts/handshaker/handshaker.pb.h \
+src/core/tsi/alts/handshaker/transport_security_common.pb.c \
+src/core/tsi/alts/handshaker/transport_security_common.pb.h \
+src/core/tsi/alts/handshaker/transport_security_common_api.cc \
+src/core/tsi/alts/handshaker/transport_security_common_api.h \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc \
+src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h \
+src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc \
+src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h \
+src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc \
+src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h \
 src/core/tsi/alts_transport_security.cc \
 src/core/tsi/alts_transport_security.h \
 src/core/tsi/fake_transport_security.cc \
diff --git a/tools/internal_ci/helper_scripts/prepare_build_interop_rc b/tools/internal_ci/helper_scripts/prepare_build_interop_rc
index db978c8..fb0f4b8 100644
--- a/tools/internal_ci/helper_scripts/prepare_build_interop_rc
+++ b/tools/internal_ci/helper_scripts/prepare_build_interop_rc
@@ -27,6 +27,7 @@
 git clone --recursive https://github.com/grpc/grpc-go ./../grpc-go
 git clone --recursive https://github.com/grpc/grpc-java ./../grpc-java
 git clone --recursive https://github.com/grpc/grpc-node ./../grpc-node
+git clone --recursive https://github.com/grpc/grpc-dart ./../grpc-dart
 
 # Download json file.
 mkdir ~/service_account
diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc
index 6ecf51d..786cd45 100644
--- a/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc
+++ b/tools/internal_ci/helper_scripts/prepare_build_macos_interop_rc
@@ -32,6 +32,7 @@
 git clone --recursive https://github.com/grpc/grpc-go ./../grpc-go
 git clone --recursive https://github.com/grpc/grpc-java ./../grpc-java
 git clone --recursive https://github.com/grpc/grpc-node ./../grpc-node
+git clone --recursive https://github.com/grpc/grpc-dart ./../grpc-dart
 
 # Set up Docker for Mac
 docker-machine create -d virtualbox --virtualbox-share-folder "/Users/kbuilder/workspace:" default
diff --git a/tools/internal_ci/linux/grpc_asan_on_foundry.sh b/tools/internal_ci/linux/grpc_asan_on_foundry.sh
new file mode 100644
index 0000000..2f9c853
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_asan_on_foundry.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=address --linkopt=-fsanitize=address"
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
+
diff --git a/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh b/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh
new file mode 100755
index 0000000..098d588
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+set -ex
+
+# A temporary solution to give Kokoro credentials. 
+# The file name 4321_grpc-testing-service needs to match auth_credential in 
+# the build config.
+mkdir -p ${KOKORO_KEYSTORE_DIR}
+cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
+
+temp_dir=$(mktemp -d)
+ln -f "${KOKORO_GFILE_DIR}/bazel-canary" ${temp_dir}/bazel
+chmod 755 "${KOKORO_GFILE_DIR}/bazel-canary"
+export PATH="${temp_dir}:${PATH}"
+# This should show ${temp_dir}/bazel
+which bazel
+chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
+
+# change to grpc repo root
+cd $(dirname $0)/../../..
+
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
+
+# TODO(adelez): implement size for test targets and change test_timeout back
+"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
+  --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
+  test --jobs="50" \
+  --test_timeout="3600,3600,3600,3600" \
+  --test_output=errors  \
+  --verbose_failures=true  \
+  --keep_going  \
+  --remote_accept_cached=true  \
+  --spawn_strategy=remote  \
+  --remote_local_fallback=false  \
+  --remote_timeout=3600  \
+  --strategy=Javac=remote  \
+  --strategy=Closure=remote  \
+  --genrule_strategy=remote  \
+  --experimental_strict_action_env=true \
+  --experimental_remote_platform_override='properties:{name:"container-image" value:"docker://gcr.io/cloud-marketplace/google/rbe-debian8@sha256:b2d946c1ddc20af250fe85cf98bd648ac5519131659f7c36e64184b433175a33" }' \
+  --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/debian8_clang/0.3.0/bazel_0.10.0:toolchain \
+  --define GRPC_PORT_ISOLATED_RUNTIME=1 \
+  $1 \
+  -- //test/...
diff --git a/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh b/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh
index c190298..e5ffea6 100644
--- a/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh
+++ b/tools/internal_ci/linux/grpc_bazel_on_foundry_dbg.sh
@@ -13,45 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set -ex
+EXTRA_FLAGS="-c dbg"
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
 
-# A temporary solution to give Kokoro credentials. 
-# The file name 4321_grpc-testing-service needs to match auth_credential in 
-# the build config.
-mkdir -p ${KOKORO_KEYSTORE_DIR}
-cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
-
-mkdir -p /tmpfs/tmp/bazel-canary
-ln -f "${KOKORO_GFILE_DIR}/bazel-canary" /tmpfs/tmp/bazel-canary/bazel
-chmod 755 "${KOKORO_GFILE_DIR}/bazel-canary"
-export PATH="/tmpfs/tmp/bazel-canary:${PATH}"
-# This should show /tmpfs/tmp/bazel-canary/bazel
-which bazel
-chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-source tools/internal_ci/helper_scripts/prepare_build_linux_rc
-
-# TODO(adelez): implement size for test targets and change test_timeout back
-"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
-  --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
-  test --jobs="50" \
-  --test_timeout="1200,1200,1200,3600" \
-  --test_output=errors  \
-  --verbose_failures=true  \
-  --keep_going  \
-  --remote_accept_cached=true  \
-  --spawn_strategy=remote  \
-  --remote_local_fallback=false  \
-  --remote_timeout=3600  \
-  --strategy=Javac=remote  \
-  --strategy=Closure=remote  \
-  --genrule_strategy=remote  \
-  --experimental_strict_action_env=true \
-  --experimental_remote_platform_override='properties:{name:"container-image" value:"docker://gcr.io/asci-toolchain/nosla-debian8-clang-fl@sha256:496193842f61c9494be68bd624e47c74d706cabf19a693c4653ffe96a97e43e3" }' \
-  --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/debian8_clang/0.2.0/bazel_0.7.0:toolchain \
-  --define GRPC_PORT_ISOLATED_RUNTIME=1 \
-  -c dbg \
-  -- //test/...
diff --git a/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh b/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh
index a8c5db4..6a769b9 100644
--- a/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh
+++ b/tools/internal_ci/linux/grpc_bazel_on_foundry_opt.sh
@@ -13,45 +13,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set -ex
-
-# A temporary solution to give Kokoro credentials. 
-# The file name 4321_grpc-testing-service needs to match auth_credential in 
-# the build config.
-mkdir -p ${KOKORO_KEYSTORE_DIR}
-cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
-
-mkdir -p /tmpfs/tmp/bazel-canary
-ln -f "${KOKORO_GFILE_DIR}/bazel-canary" /tmpfs/tmp/bazel-canary/bazel
-chmod 755 "${KOKORO_GFILE_DIR}/bazel-canary"
-export PATH="/tmpfs/tmp/bazel-canary:${PATH}"
-# This should show /tmpfs/tmp/bazel-canary/bazel
-which bazel
-chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-source tools/internal_ci/helper_scripts/prepare_build_linux_rc
-
-# TODO(adelez): implement size for test targets and change test_timeout back
-"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
-  --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
-  test --jobs="50" \
-  --test_timeout="1200,1200,1200,3600" \
-  --test_output=errors  \
-  --verbose_failures=true  \
-  --keep_going  \
-  --remote_accept_cached=true  \
-  --spawn_strategy=remote  \
-  --remote_local_fallback=false  \
-  --remote_timeout=3600  \
-  --strategy=Javac=remote  \
-  --strategy=Closure=remote  \
-  --genrule_strategy=remote  \
-  --experimental_strict_action_env=true \
-  --experimental_remote_platform_override='properties:{name:"container-image" value:"docker://gcr.io/asci-toolchain/nosla-debian8-clang-fl@sha256:496193842f61c9494be68bd624e47c74d706cabf19a693c4653ffe96a97e43e3" }' \
-  --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/debian8_clang/0.2.0/bazel_0.7.0:toolchain \
-  --define GRPC_PORT_ISOLATED_RUNTIME=1 \
-  -c opt \
-  -- //test/...
+EXTRA_FLAGS="-c opt"
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
diff --git a/tools/internal_ci/linux/grpc_msan_on_foundry.sh b/tools/internal_ci/linux/grpc_msan_on_foundry.sh
new file mode 100644
index 0000000..16586a2
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_msan_on_foundry.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+set -ex
+
+# A temporary solution to give Kokoro credentials. 
+# The file name 4321_grpc-testing-service needs to match auth_credential in 
+# the build config.
+# TODO: Use keystore.
+mkdir -p ${KOKORO_KEYSTORE_DIR}
+cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
+
+temp_dir=$(mktemp -d)
+ln -f "${KOKORO_GFILE_DIR}/bazel-canary" ${temp_dir}/bazel
+chmod 755 "${KOKORO_GFILE_DIR}/bazel-canary"
+export PATH="${temp_dir}:${PATH}"
+# This should show ${temp_dir}/bazel
+which bazel
+chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
+
+# change to grpc repo root
+cd $(dirname $0)/../../..
+
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
+
+"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
+  --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
+  test --jobs="50" \
+  --test_timeout="3600,3600,3600,3600" \
+  --test_output=errors  \
+  --verbose_failures=true  \
+  --keep_going  \
+  --remote_accept_cached=true  \
+  --spawn_strategy=remote  \
+  --remote_local_fallback=false  \
+  --remote_timeout=3600  \
+  --strategy=Javac=remote  \
+  --strategy=Closure=remote  \
+  --genrule_strategy=remote  \
+  --experimental_strict_action_env=true \
+  --experimental_remote_platform_override='properties:{name:"container-image" value:"docker://gcr.io/asci-toolchain/nosla-debian8-clang-msan@sha256:8f381d55c0456fb65821c90ada902c2584977e03a1eaca8fba8ce77e644c775b" }' \
+  --define GRPC_PORT_ISOLATED_RUNTIME=1 \
+  --copt=-gmlt \
+  --strip=never \
+  --cxxopt=--stdlib=libc++ \
+  --copt=-fsanitize=memory \
+  --linkopt=-fsanitize=memory \
+  --copt=-fsanitize-memory-track-origins \
+  --action_env=LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH \
+  --host_crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/debian8_clang/0.3.0/bazel_0.10.0:toolchain \
+  --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/debian8_clang/0.3.0/bazel_0.10.0/msan:msan_experimental_toolchain \
+  -- //test/...
diff --git a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
index 7da537c..f8e665b 100644
--- a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
+++ b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh
@@ -13,49 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set -ex
+EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=thread --linkopt=-fsanitize=thread"
+github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}"
 
-# A temporary solution to give Kokoro credentials. 
-# The file name 4321_grpc-testing-service needs to match auth_credential in 
-# the build config.
-# TODO: Use keystore.
-mkdir -p ${KOKORO_KEYSTORE_DIR}
-cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
-
-mkdir -p /tmpfs/tmp/bazel-canary
-ln -f "${KOKORO_GFILE_DIR}/bazel-canary" /tmpfs/tmp/bazel-canary/bazel
-chmod 755 "${KOKORO_GFILE_DIR}/bazel-canary"
-export PATH="/tmpfs/tmp/bazel-canary:${PATH}"
-# This should show /tmpfs/tmp/bazel-canary/bazel
-which bazel
-chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-source tools/internal_ci/helper_scripts/prepare_build_linux_rc
-
-"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
-  --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
-  test --jobs="50" \
-  --test_timeout="1500,1500,1500,3600" \
-  --test_output=errors  \
-  --verbose_failures=true  \
-  --keep_going  \
-  --remote_accept_cached=true  \
-  --spawn_strategy=remote  \
-  --remote_local_fallback=false  \
-  --remote_timeout=3600  \
-  --strategy=Javac=remote  \
-  --strategy=Closure=remote  \
-  --genrule_strategy=remote  \
-  --experimental_strict_action_env=true \
-  --experimental_remote_platform_override='properties:{name:"container-image" value:"docker://gcr.io/asci-toolchain/nosla-debian8-clang-fl@sha256:496193842f61c9494be68bd624e47c74d706cabf19a693c4653ffe96a97e43e3" }' \
-  --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/debian8_clang/0.2.0/bazel_0.7.0:toolchain \
-  --define GRPC_PORT_ISOLATED_RUNTIME=1 \
-  --copt=-gmlt \
-  --strip=never \
-  --copt=-fsanitize=thread \
-  --linkopt=-fsanitize=thread \
-  --test_verbose_timeout_warnings \
-  -- //test/...
diff --git a/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh b/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
new file mode 100644
index 0000000..cd1a340
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_ubsan_on_foundry.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+set -ex
+
+# A temporary solution to give Kokoro credentials. 
+# The file name 4321_grpc-testing-service needs to match auth_credential in 
+# the build config.
+# TODO: Use keystore.
+mkdir -p ${KOKORO_KEYSTORE_DIR}
+cp ${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json ${KOKORO_KEYSTORE_DIR}/4321_grpc-testing-service
+
+temp_dir=$(mktemp -d)
+ln -f "${KOKORO_GFILE_DIR}/bazel-canary" ${temp_dir}/bazel
+chmod 755 "${KOKORO_GFILE_DIR}/bazel-canary"
+export PATH="${temp_dir}:${PATH}"
+# This should show ${temp_dir}/bazel
+which bazel
+chmod +x "${KOKORO_GFILE_DIR}/bazel_wrapper.py"
+
+# change to grpc repo root
+cd $(dirname $0)/../../..
+
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
+
+"${KOKORO_GFILE_DIR}/bazel_wrapper.py" \
+  --host_jvm_args=-Dbazel.DigestFunction=SHA256 \
+  test --jobs="50" \
+  --test_timeout="3600,3600,3600,3600" \
+  --test_output=errors  \
+  --verbose_failures=true  \
+  --keep_going  \
+  --remote_accept_cached=true  \
+  --spawn_strategy=remote  \
+  --remote_local_fallback=false  \
+  --remote_timeout=3600  \
+  --strategy=Javac=remote  \
+  --strategy=Closure=remote  \
+  --genrule_strategy=remote  \
+  --experimental_strict_action_env=true \
+  --experimental_remote_platform_override='properties:{name:"container-image" value:"docker://gcr.io/cloud-marketplace/google/rbe-debian8@sha256:b2d946c1ddc20af250fe85cf98bd648ac5519131659f7c36e64184b433175a33" }' \
+  --define GRPC_PORT_ISOLATED_RUNTIME=1 \
+  --copt=-gmlt \
+  --strip=never \
+  --copt=-fsanitize=undefined \
+  --linkopt=-fsanitize=undefined \
+  --crosstool_top=@com_github_bazelbuild_bazeltoolchains//configs/experimental/debian8_clang/0.3.0/bazel_0.10.0/ubsan:ubsan_experimental_toolchain \
+  -- //test/...
diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py
index 5a6eaa9..83d75ec 100644
--- a/tools/interop_matrix/client_matrix.py
+++ b/tools/interop_matrix/client_matrix.py
@@ -18,9 +18,10 @@
 
 def get_github_repo(lang):
     return {
-        'go': 'https://github.com:grpc/grpc-go.git',
-        'java': 'https://github.com:grpc/grpc-java.git',
-        'node': 'https://github.com:grpc/grpc-node.git',
+        'dart': 'https://github.com/grpc/grpc-dart.git',
+        'go': 'https://github.com/grpc/grpc-go.git',
+        'java': 'https://github.com/grpc/grpc-java.git',
+        'node': 'https://github.com/grpc/grpc-node.git',
         # all other languages use the grpc.git repo.
     }.get(lang, 'https://github.com/grpc/grpc.git')
 
@@ -112,6 +113,9 @@
         {
             'v1.9.2': None
         },
+        {
+            'v1.10.0': None
+        },
     ],
     'java': [
         {
@@ -144,6 +148,9 @@
         {
             'v1.9.1': None
         },
+        {
+            'v1.10.0': None
+        },
     ],
     'python': [
         {
diff --git a/tools/run_tests/artifacts/build_artifact_python.sh b/tools/run_tests/artifacts/build_artifact_python.sh
index 10d8211..9ea0f05 100755
--- a/tools/run_tests/artifacts/build_artifact_python.sh
+++ b/tools/run_tests/artifacts/build_artifact_python.sh
@@ -35,6 +35,34 @@
 # https://bitbucket.org/pypa/wheel/issues/99/cannot-exclude-directory
 ${SETARCH_CMD} "${PYTHON}" setup.py bdist_wheel
 
+GRPCIO_STRIP_TEMPDIR=$(mktemp -d)
+GRPCIO_TAR_GZ_LIST=( dist/grpcio-*.tar.gz )
+GRPCIO_TAR_GZ=${GRPCIO_TAR_GZ_LIST[0]}
+GRPCIO_STRIPPED_TAR_GZ=$(mktemp -t "XXXXXXXXXX.tar.gz")
+
+clean_non_source_files() {
+( cd "$1"
+  find . -type f \
+    | grep -v '\.c$' | grep -v '\.cc$' | grep -v '\.cpp$' \
+    | grep -v '\.h$' | grep -v '\.hh$' \
+    | grep -v '\.s$' | grep -v '\.py$' \
+    | while read -r file; do
+      rm -f "$file" || true
+    done
+  find . -type d -empty -delete
+)
+}
+
+tar xzf "${GRPCIO_TAR_GZ}" -C "${GRPCIO_STRIP_TEMPDIR}"
+( cd "${GRPCIO_STRIP_TEMPDIR}"
+  find . -type d -name .git -exec rm -fr {} \; || true
+  for dir in */third_party/*; do
+    clean_non_source_files "${dir}" || true
+  done
+  tar czf "${GRPCIO_STRIPPED_TAR_GZ}" -- *
+)
+mv "${GRPCIO_STRIPPED_TAR_GZ}" "${GRPCIO_TAR_GZ}"
+
 # Build gRPC tools package distribution
 "${PYTHON}" tools/distrib/python/make_grpcio_tools.py
 
diff --git a/tools/run_tests/dockerize/build_interop_image.sh b/tools/run_tests/dockerize/build_interop_image.sh
index 90605d9..2bef4a3 100755
--- a/tools/run_tests/dockerize/build_interop_image.sh
+++ b/tools/run_tests/dockerize/build_interop_image.sh
@@ -48,6 +48,14 @@
   echo "WARNING: grpc-go not found, it won't be mounted to the docker container."
 fi
 
+echo "GRPC_DART_ROOT: ${GRPC_DART_ROOT:=$(cd ../grpc-dart && pwd)}"
+if [ -n "$GRPC_DART_ROOT" ]
+then
+  MOUNT_ARGS+=" -v $GRPC_DART_ROOT:/var/local/jenkins/grpc-dart:ro"
+else
+  echo "WARNING: grpc-dart not found, it won't be mounted to the docker container."
+fi
+
 echo "GRPC_NODE_ROOT: ${GRPC_NODE_ROOT:=$(cd ../grpc-node && pwd)}"
 if [ -n "$GRPC_NODE_ROOT" ]
 then
diff --git a/tools/run_tests/dockerize/docker_run.sh b/tools/run_tests/dockerize/docker_run.sh
index ac0d09c..e525019 100755
--- a/tools/run_tests/dockerize/docker_run.sh
+++ b/tools/run_tests/dockerize/docker_run.sh
@@ -25,9 +25,8 @@
   # clone gRPC submodules, use data from locally cloned submodules where possible
   # TODO: figure out a way to eliminate this following shellcheck suppressions
   # shellcheck disable=SC2016,SC1004
-  (cd "${EXTERNAL_GIT_ROOT}" && git submodule foreach 'cd /var/local/git/grpc \
-  && git submodule update --init --reference ${EXTERNAL_GIT_ROOT}/${name} \
-  ${name}')
+  (cd "${EXTERNAL_GIT_ROOT}" && git submodule foreach 'git clone ${EXTERNAL_GIT_ROOT}/${name} /var/local/git/grpc/${name}')
+  (cd /var/local/git/grpc && git submodule init)
 else
   mkdir -p "/var/local/git/grpc/$RELATIVE_COPY_PATH"
   cp -r "$EXTERNAL_GIT_ROOT/$RELATIVE_COPY_PATH"/* "/var/local/git/grpc/$RELATIVE_COPY_PATH"
diff --git a/tools/run_tests/dockerize/docker_run_tests.sh b/tools/run_tests/dockerize/docker_run_tests.sh
index 89ee315..c41734c 100755
--- a/tools/run_tests/dockerize/docker_run_tests.sh
+++ b/tools/run_tests/dockerize/docker_run_tests.sh
@@ -23,13 +23,12 @@
 export PATH=$PATH:/usr/bin/llvm-symbolizer
 
 mkdir -p /var/local/git
-git clone  /var/local/jenkins/grpc /var/local/git/grpc
+git clone /var/local/jenkins/grpc /var/local/git/grpc
 # clone gRPC submodules, use data from locally cloned submodules where possible
 # TODO: figure out a way to eliminate this shellcheck suppression:
-# shellcheck disable=SC2016,SC1004
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
+# shellcheck disable=SC2016
+(cd /var/local/jenkins/grpc/ && git submodule foreach 'git clone /var/local/jenkins/grpc/${name} /var/local/git/grpc/${name}')
+(cd /var/local/git/grpc/ && git submodule init)
 
 mkdir -p reports
 
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 4613357..7249671 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -764,7 +764,7 @@
     "language": "c", 
     "name": "gpr_thd_test", 
     "src": [
-      "test/core/gpr/thd_test.cc"
+      "test/core/gprpp/thd_test.cc"
     ], 
     "third_party": false, 
     "type": "target"
@@ -1964,23 +1964,6 @@
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
-    "name": "slice_hash_table_test", 
-    "src": [
-      "test/core/slice/slice_hash_table_test.cc"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc_test_util"
-    ], 
-    "headers": [], 
-    "is_filegroup": false, 
-    "language": "c", 
     "name": "slice_string_helpers_test", 
     "src": [
       "test/core/slice/slice_string_helpers_test.cc"
@@ -2434,6 +2417,215 @@
   }, 
   {
     "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_counter_test", 
+    "src": [
+      "test/core/tsi/alts/frame_protector/alts_counter_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_crypt_test", 
+    "src": [
+      "test/core/tsi/alts/crypt/aes_gcm_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_crypter_test", 
+    "src": [
+      "test/core/tsi/alts/frame_protector/alts_crypter_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_frame_handler_test", 
+    "src": [
+      "test/core/tsi/alts/frame_protector/frame_handler_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc", 
+      "transport_security_test_lib"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_frame_protector_test", 
+    "src": [
+      "test/core/tsi/alts/frame_protector/alts_frame_protector_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_grpc_record_protocol_test", 
+    "src": [
+      "test/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_handshaker_client_test", 
+    "src": [
+      "test/core/tsi/alts/handshaker/alts_handshaker_client_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_handshaker_service_api_test", 
+    "src": [
+      "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_iovec_record_protocol_test", 
+    "src": [
+      "test/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_security_connector_test", 
+    "src": [
+      "test/core/security/alts_security_connector_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_tsi_handshaker_test", 
+    "src": [
+      "test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_tsi_utils_test", 
+    "src": [
+      "test/core/tsi/alts/handshaker/alts_tsi_utils_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "alts_zero_copy_grpc_protector_test", 
+    "src": [
+      "test/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "gpr_test_util", 
       "grpc", 
@@ -2845,6 +3037,36 @@
   {
     "deps": [
       "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "check_gcp_environment_linux_test", 
+    "src": [
+      "test/core/security/check_gcp_environment_linux_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "check_gcp_environment_windows_test", 
+    "src": [
+      "test/core/security/check_gcp_environment_windows_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
       "gpr_test_util", 
       "grpc", 
       "grpc_test_util"
@@ -3230,6 +3452,21 @@
   {
     "deps": [
       "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "grpc_alts_credentials_options_test", 
+    "src": [
+      "test/core/security/grpc_alts_credentials_options_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
       "grpc", 
       "grpc++", 
       "grpc++_proto_reflection_desc_db", 
@@ -3424,29 +3661,6 @@
       "gpr_test_util", 
       "grpc", 
       "grpc++", 
-      "grpc++_test_util", 
-      "grpc_test_util"
-    ], 
-    "headers": [
-      "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h", 
-      "src/proto/grpc/lb/v1/load_balancer.pb.h", 
-      "src/proto/grpc/lb/v1/load_balancer_mock.grpc.pb.h"
-    ], 
-    "is_filegroup": false, 
-    "language": "c++", 
-    "name": "grpclb_test", 
-    "src": [
-      "test/cpp/grpclb/grpclb_test.cc"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc++", 
       "grpc++_test", 
       "grpc_test_util"
     ], 
@@ -3690,13 +3904,15 @@
       "grpc_test_util"
     ], 
     "headers": [
-      "include/grpc++/test/mock_stream.h"
+      "include/grpc++/test/mock_stream.h", 
+      "include/grpcpp/test/mock_stream.h"
     ], 
     "is_filegroup": false, 
     "language": "c++", 
     "name": "mock_test", 
     "src": [
       "include/grpc++/test/mock_stream.h", 
+      "include/grpcpp/test/mock_stream.h", 
       "test/cpp/end2end/mock_test.cc"
     ], 
     "third_party": false, 
@@ -4196,6 +4412,40 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "slice_hash_table_test", 
+    "src": [
+      "test/core/slice/slice_hash_table_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "slice_weak_hash_table_test", 
+    "src": [
+      "test/core/slice/slice_weak_hash_table_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
       "grpc++_test_util", 
       "grpc_test_util"
     ], 
@@ -4211,6 +4461,20 @@
   }, 
   {
     "deps": [
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "status_metadata_test", 
+    "src": [
+      "test/core/transport/status_metadata_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "gpr_test_util", 
       "grpc", 
@@ -4229,6 +4493,20 @@
   }, 
   {
     "deps": [
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "status_util_test", 
+    "src": [
+      "test/core/client_channel/status_util_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "gpr_test_util", 
       "grpc", 
@@ -4349,6 +4627,22 @@
   }, 
   {
     "deps": [
+      "alts_test_util", 
+      "gpr", 
+      "grpc"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "transport_security_common_api_test", 
+    "src": [
+      "test/core/tsi/alts/handshaker/transport_security_common_api_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "gpr_test_util", 
       "grpc", 
@@ -6267,6 +6561,26 @@
   }, 
   {
     "deps": [
+      "grpc"
+    ], 
+    "headers": [
+      "test/core/tsi/alts/crypt/gsec_test_util.h", 
+      "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "alts_test_util", 
+    "src": [
+      "test/core/tsi/alts/crypt/gsec_test_util.cc", 
+      "test/core/tsi/alts/crypt/gsec_test_util.h", 
+      "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc", 
+      "test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.h"
+    ], 
+    "third_party": false, 
+    "type": "lib"
+  }, 
+  {
+    "deps": [
       "gpr_base"
     ], 
     "headers": [], 
@@ -6486,6 +6800,7 @@
     ], 
     "headers": [
       "include/grpc++/impl/codegen/core_codegen.h", 
+      "include/grpcpp/impl/codegen/core_codegen.h", 
       "src/cpp/client/secure_credentials.h", 
       "src/cpp/common/secure_auth_context.h", 
       "src/cpp/server/secure_server_credentials.h"
@@ -6495,6 +6810,7 @@
     "name": "grpc++", 
     "src": [
       "include/grpc++/impl/codegen/core_codegen.h", 
+      "include/grpcpp/impl/codegen/core_codegen.h", 
       "src/cpp/client/insecure_credentials.cc", 
       "src/cpp/client/secure_credentials.cc", 
       "src/cpp/client/secure_credentials.h", 
@@ -6561,6 +6877,7 @@
     ], 
     "headers": [
       "include/grpc++/support/error_details.h", 
+      "include/grpcpp/support/error_details.h", 
       "src/proto/grpc/status/status.grpc.pb.h", 
       "src/proto/grpc/status/status.pb.h", 
       "src/proto/grpc/status/status_mock.grpc.pb.h"
@@ -6570,6 +6887,7 @@
     "name": "grpc++_error_details", 
     "src": [
       "include/grpc++/support/error_details.h", 
+      "include/grpcpp/support/error_details.h", 
       "src/cpp/util/error_details.cc"
     ], 
     "third_party": false, 
@@ -6603,6 +6921,7 @@
     ], 
     "headers": [
       "include/grpc++/ext/proto_server_reflection_plugin.h", 
+      "include/grpcpp/ext/proto_server_reflection_plugin.h", 
       "src/cpp/ext/proto_server_reflection.h"
     ], 
     "is_filegroup": false, 
@@ -6610,6 +6929,7 @@
     "name": "grpc++_reflection", 
     "src": [
       "include/grpc++/ext/proto_server_reflection_plugin.h", 
+      "include/grpcpp/ext/proto_server_reflection_plugin.h", 
       "src/cpp/ext/proto_server_reflection.cc", 
       "src/cpp/ext/proto_server_reflection.h", 
       "src/cpp/ext/proto_server_reflection_plugin.cc"
@@ -7114,20 +7434,80 @@
       "third_party/boringssl/crypto/curve25519/internal.h", 
       "third_party/boringssl/crypto/err/internal.h", 
       "third_party/boringssl/crypto/evp/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/aes/aes.c", 
       "third_party/boringssl/crypto/fipsmodule/aes/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/aes/key_wrap.c", 
+      "third_party/boringssl/crypto/fipsmodule/aes/mode_wrappers.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/add.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/asm/x86_64-gcc.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/bn.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/bytes.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/cmp.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/ctx.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/div.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/exponentiation.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/gcd.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/generic.c", 
       "third_party/boringssl/crypto/fipsmodule/bn/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/bn/jacobi.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/montgomery.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/montgomery_inv.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/mul.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/prime.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/random.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/rsaz_exp.c", 
       "third_party/boringssl/crypto/fipsmodule/bn/rsaz_exp.h", 
+      "third_party/boringssl/crypto/fipsmodule/bn/shift.c", 
+      "third_party/boringssl/crypto/fipsmodule/bn/sqrt.c", 
+      "third_party/boringssl/crypto/fipsmodule/cipher/aead.c", 
+      "third_party/boringssl/crypto/fipsmodule/cipher/cipher.c", 
+      "third_party/boringssl/crypto/fipsmodule/cipher/e_aes.c", 
+      "third_party/boringssl/crypto/fipsmodule/cipher/e_des.c", 
       "third_party/boringssl/crypto/fipsmodule/cipher/internal.h", 
       "third_party/boringssl/crypto/fipsmodule/delocate.h", 
+      "third_party/boringssl/crypto/fipsmodule/des/des.c", 
       "third_party/boringssl/crypto/fipsmodule/des/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/digest/digest.c", 
+      "third_party/boringssl/crypto/fipsmodule/digest/digests.c", 
       "third_party/boringssl/crypto/fipsmodule/digest/internal.h", 
       "third_party/boringssl/crypto/fipsmodule/digest/md32_common.h", 
+      "third_party/boringssl/crypto/fipsmodule/ec/ec.c", 
+      "third_party/boringssl/crypto/fipsmodule/ec/ec_key.c", 
+      "third_party/boringssl/crypto/fipsmodule/ec/ec_montgomery.c", 
       "third_party/boringssl/crypto/fipsmodule/ec/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/ec/oct.c", 
+      "third_party/boringssl/crypto/fipsmodule/ec/p224-64.c", 
+      "third_party/boringssl/crypto/fipsmodule/ec/p256-64.c", 
       "third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64-table.h", 
+      "third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64.c", 
       "third_party/boringssl/crypto/fipsmodule/ec/p256-x86_64.h", 
+      "third_party/boringssl/crypto/fipsmodule/ec/simple.c", 
+      "third_party/boringssl/crypto/fipsmodule/ec/util-64.c", 
+      "third_party/boringssl/crypto/fipsmodule/ec/wnaf.c", 
+      "third_party/boringssl/crypto/fipsmodule/ecdsa/ecdsa.c", 
+      "third_party/boringssl/crypto/fipsmodule/hmac/hmac.c", 
+      "third_party/boringssl/crypto/fipsmodule/md4/md4.c", 
+      "third_party/boringssl/crypto/fipsmodule/md5/md5.c", 
+      "third_party/boringssl/crypto/fipsmodule/modes/cbc.c", 
+      "third_party/boringssl/crypto/fipsmodule/modes/cfb.c", 
+      "third_party/boringssl/crypto/fipsmodule/modes/ctr.c", 
+      "third_party/boringssl/crypto/fipsmodule/modes/gcm.c", 
       "third_party/boringssl/crypto/fipsmodule/modes/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/modes/ofb.c", 
+      "third_party/boringssl/crypto/fipsmodule/modes/polyval.c", 
+      "third_party/boringssl/crypto/fipsmodule/rand/ctrdrbg.c", 
       "third_party/boringssl/crypto/fipsmodule/rand/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/rand/rand.c", 
+      "third_party/boringssl/crypto/fipsmodule/rand/urandom.c", 
+      "third_party/boringssl/crypto/fipsmodule/rsa/blinding.c", 
       "third_party/boringssl/crypto/fipsmodule/rsa/internal.h", 
+      "third_party/boringssl/crypto/fipsmodule/rsa/padding.c", 
+      "third_party/boringssl/crypto/fipsmodule/rsa/rsa.c", 
+      "third_party/boringssl/crypto/fipsmodule/rsa/rsa_impl.c", 
+      "third_party/boringssl/crypto/fipsmodule/sha/sha1-altivec.c", 
+      "third_party/boringssl/crypto/fipsmodule/sha/sha1.c", 
+      "third_party/boringssl/crypto/fipsmodule/sha/sha256.c", 
+      "third_party/boringssl/crypto/fipsmodule/sha/sha512.c", 
       "third_party/boringssl/crypto/internal.h", 
       "third_party/boringssl/crypto/obj/obj_dat.h", 
       "third_party/boringssl/crypto/pkcs7/internal.h", 
@@ -8066,6 +8446,21 @@
       "test/core/end2end/tests/request_with_flags.cc", 
       "test/core/end2end/tests/request_with_payload.cc", 
       "test/core/end2end/tests/resource_quota_server.cc", 
+      "test/core/end2end/tests/retry.cc", 
+      "test/core/end2end/tests/retry_cancellation.cc", 
+      "test/core/end2end/tests/retry_disabled.cc", 
+      "test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc", 
+      "test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc", 
+      "test/core/end2end/tests/retry_non_retriable_status.cc", 
+      "test/core/end2end/tests/retry_recv_initial_metadata.cc", 
+      "test/core/end2end/tests/retry_recv_message.cc", 
+      "test/core/end2end/tests/retry_server_pushback_delay.cc", 
+      "test/core/end2end/tests/retry_server_pushback_disabled.cc", 
+      "test/core/end2end/tests/retry_streaming.cc", 
+      "test/core/end2end/tests/retry_streaming_after_commit.cc", 
+      "test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc", 
+      "test/core/end2end/tests/retry_throttled.cc", 
+      "test/core/end2end/tests/retry_too_many_attempts.cc", 
       "test/core/end2end/tests/server_finishes_request.cc", 
       "test/core/end2end/tests/shutdown_finishes_calls.cc", 
       "test/core/end2end/tests/shutdown_finishes_tags.cc", 
@@ -8148,6 +8543,21 @@
       "test/core/end2end/tests/request_with_flags.cc", 
       "test/core/end2end/tests/request_with_payload.cc", 
       "test/core/end2end/tests/resource_quota_server.cc", 
+      "test/core/end2end/tests/retry.cc", 
+      "test/core/end2end/tests/retry_cancellation.cc", 
+      "test/core/end2end/tests/retry_disabled.cc", 
+      "test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc", 
+      "test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc", 
+      "test/core/end2end/tests/retry_non_retriable_status.cc", 
+      "test/core/end2end/tests/retry_recv_initial_metadata.cc", 
+      "test/core/end2end/tests/retry_recv_message.cc", 
+      "test/core/end2end/tests/retry_server_pushback_delay.cc", 
+      "test/core/end2end/tests/retry_server_pushback_disabled.cc", 
+      "test/core/end2end/tests/retry_streaming.cc", 
+      "test/core/end2end/tests/retry_streaming_after_commit.cc", 
+      "test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc", 
+      "test/core/end2end/tests/retry_throttled.cc", 
+      "test/core/end2end/tests/retry_too_many_attempts.cc", 
       "test/core/end2end/tests/server_finishes_request.cc", 
       "test/core/end2end/tests/shutdown_finishes_calls.cc", 
       "test/core/end2end/tests/shutdown_finishes_tags.cc", 
@@ -8169,6 +8579,138 @@
   }, 
   {
     "deps": [
+      "nanopb"
+    ], 
+    "headers": [
+      "src/core/tsi/alts/handshaker/altscontext.pb.h", 
+      "src/core/tsi/alts/handshaker/handshaker.pb.h", 
+      "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "alts_proto", 
+    "src": [
+      "src/core/tsi/alts/handshaker/altscontext.pb.c", 
+      "src/core/tsi/alts/handshaker/altscontext.pb.h", 
+      "src/core/tsi/alts/handshaker/handshaker.pb.c", 
+      "src/core/tsi/alts/handshaker/handshaker.pb.h", 
+      "src/core/tsi/alts/handshaker/transport_security_common.pb.c", 
+      "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
+      "alts_util", 
+      "gpr", 
+      "grpc_base", 
+      "grpc_transport_chttp2_client_insecure", 
+      "tsi", 
+      "tsi_interface"
+    ], 
+    "headers": [
+      "src/core/tsi/alts/crypt/gsec.h", 
+      "src/core/tsi/alts/frame_protector/alts_counter.h", 
+      "src/core/tsi/alts/frame_protector/alts_crypter.h", 
+      "src/core/tsi/alts/frame_protector/alts_frame_protector.h", 
+      "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h", 
+      "src/core/tsi/alts/frame_protector/frame_handler.h", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_client.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_event.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "alts_tsi", 
+    "src": [
+      "src/core/tsi/alts/crypt/aes_gcm.cc", 
+      "src/core/tsi/alts/crypt/gsec.cc", 
+      "src/core/tsi/alts/crypt/gsec.h", 
+      "src/core/tsi/alts/frame_protector/alts_counter.cc", 
+      "src/core/tsi/alts/frame_protector/alts_counter.h", 
+      "src/core/tsi/alts/frame_protector/alts_crypter.cc", 
+      "src/core/tsi/alts/frame_protector/alts_crypter.h", 
+      "src/core/tsi/alts/frame_protector/alts_frame_protector.cc", 
+      "src/core/tsi/alts/frame_protector/alts_frame_protector.h", 
+      "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc", 
+      "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h", 
+      "src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc", 
+      "src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc", 
+      "src/core/tsi/alts/frame_protector/frame_handler.cc", 
+      "src/core/tsi/alts/frame_protector/frame_handler.h", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_client.cc", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_client.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_event.cc", 
+      "src/core/tsi/alts/handshaker/alts_tsi_event.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc", 
+      "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc", 
+      "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
+      "alts_proto", 
+      "gpr", 
+      "grpc_base", 
+      "nanopb", 
+      "tsi_interface"
+    ], 
+    "headers": [
+      "src/core/lib/security/credentials/alts/check_gcp_environment.h", 
+      "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_utils.h", 
+      "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "alts_util", 
+    "src": [
+      "src/core/lib/security/credentials/alts/check_gcp_environment.cc", 
+      "src/core/lib/security/credentials/alts/check_gcp_environment.h", 
+      "src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc", 
+      "src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc", 
+      "src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc", 
+      "src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc", 
+      "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc", 
+      "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h", 
+      "src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc", 
+      "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h", 
+      "src/core/tsi/alts/handshaker/alts_tsi_utils.cc", 
+      "src/core/tsi/alts/handshaker/alts_tsi_utils.h", 
+      "src/core/tsi/alts/handshaker/transport_security_common_api.cc", 
+      "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "grpc_base", 
       "nanopb"
@@ -8238,9 +8780,6 @@
       "src/core/lib/gpr/sync.cc", 
       "src/core/lib/gpr/sync_posix.cc", 
       "src/core/lib/gpr/sync_windows.cc", 
-      "src/core/lib/gpr/thd.cc", 
-      "src/core/lib/gpr/thd_posix.cc", 
-      "src/core/lib/gpr/thd_windows.cc", 
       "src/core/lib/gpr/time.cc", 
       "src/core/lib/gpr/time_posix.cc", 
       "src/core/lib/gpr/time_precise.cc", 
@@ -8250,6 +8789,8 @@
       "src/core/lib/gpr/tmpfile_posix.cc", 
       "src/core/lib/gpr/tmpfile_windows.cc", 
       "src/core/lib/gpr/wrap_memcpy.cc", 
+      "src/core/lib/gprpp/thd_posix.cc", 
+      "src/core/lib/gprpp/thd_windows.cc", 
       "src/core/lib/profiling/basic_timers.cc", 
       "src/core/lib/profiling/stap_timers.cc"
     ], 
@@ -8287,7 +8828,6 @@
       "src/core/lib/gpr/spinlock.h", 
       "src/core/lib/gpr/string.h", 
       "src/core/lib/gpr/string_windows.h", 
-      "src/core/lib/gpr/thd.h", 
       "src/core/lib/gpr/time_precise.h", 
       "src/core/lib/gpr/tls.h", 
       "src/core/lib/gpr/tls_gcc.h", 
@@ -8301,6 +8841,7 @@
       "src/core/lib/gprpp/atomic_with_std.h", 
       "src/core/lib/gprpp/manual_constructor.h", 
       "src/core/lib/gprpp/memory.h", 
+      "src/core/lib/gprpp/thd.h", 
       "src/core/lib/profiling/timers.h"
     ], 
     "is_filegroup": true, 
@@ -8333,7 +8874,6 @@
       "src/core/lib/gpr/spinlock.h", 
       "src/core/lib/gpr/string.h", 
       "src/core/lib/gpr/string_windows.h", 
-      "src/core/lib/gpr/thd.h", 
       "src/core/lib/gpr/time_precise.h", 
       "src/core/lib/gpr/tls.h", 
       "src/core/lib/gpr/tls_gcc.h", 
@@ -8347,6 +8887,7 @@
       "src/core/lib/gprpp/atomic_with_std.h", 
       "src/core/lib/gprpp/manual_constructor.h", 
       "src/core/lib/gprpp/memory.h", 
+      "src/core/lib/gprpp/thd.h", 
       "src/core/lib/profiling/timers.h"
     ], 
     "third_party": false, 
@@ -8532,7 +9073,6 @@
       "src/core/lib/slice/percent_encoding.cc", 
       "src/core/lib/slice/slice.cc", 
       "src/core/lib/slice/slice_buffer.cc", 
-      "src/core/lib/slice/slice_hash_table.cc", 
       "src/core/lib/slice/slice_intern.cc", 
       "src/core/lib/slice/slice_string_helpers.cc", 
       "src/core/lib/surface/api_trace.cc", 
@@ -8563,6 +9103,7 @@
       "src/core/lib/transport/service_config.cc", 
       "src/core/lib/transport/static_metadata.cc", 
       "src/core/lib/transport/status_conversion.cc", 
+      "src/core/lib/transport/status_metadata.cc", 
       "src/core/lib/transport/timeout_encoding.cc", 
       "src/core/lib/transport/transport.cc", 
       "src/core/lib/transport/transport_op_string.cc"
@@ -8687,6 +9228,7 @@
       "src/core/lib/slice/slice_hash_table.h", 
       "src/core/lib/slice/slice_internal.h", 
       "src/core/lib/slice/slice_string_helpers.h", 
+      "src/core/lib/slice/slice_weak_hash_table.h", 
       "src/core/lib/surface/api_trace.h", 
       "src/core/lib/surface/call.h", 
       "src/core/lib/surface/call_test_only.h", 
@@ -8711,6 +9253,7 @@
       "src/core/lib/transport/service_config.h", 
       "src/core/lib/transport/static_metadata.h", 
       "src/core/lib/transport/status_conversion.h", 
+      "src/core/lib/transport/status_metadata.h", 
       "src/core/lib/transport/timeout_encoding.h", 
       "src/core/lib/transport/transport.h", 
       "src/core/lib/transport/transport_impl.h"
@@ -8829,6 +9372,7 @@
       "src/core/lib/slice/slice_hash_table.h", 
       "src/core/lib/slice/slice_internal.h", 
       "src/core/lib/slice/slice_string_helpers.h", 
+      "src/core/lib/slice/slice_weak_hash_table.h", 
       "src/core/lib/surface/api_trace.h", 
       "src/core/lib/surface/call.h", 
       "src/core/lib/surface/call_test_only.h", 
@@ -8853,6 +9397,7 @@
       "src/core/lib/transport/service_config.h", 
       "src/core/lib/transport/static_metadata.h", 
       "src/core/lib/transport/status_conversion.h", 
+      "src/core/lib/transport/status_metadata.h", 
       "src/core/lib/transport/timeout_encoding.h", 
       "src/core/lib/transport/transport.h", 
       "src/core/lib/transport/transport_impl.h"
@@ -8876,6 +9421,7 @@
       "src/core/ext/filters/client_channel/lb_policy.h", 
       "src/core/ext/filters/client_channel/lb_policy_factory.h", 
       "src/core/ext/filters/client_channel/lb_policy_registry.h", 
+      "src/core/ext/filters/client_channel/method_params.h", 
       "src/core/ext/filters/client_channel/parse_address.h", 
       "src/core/ext/filters/client_channel/proxy_mapper.h", 
       "src/core/ext/filters/client_channel/proxy_mapper_registry.h", 
@@ -8883,6 +9429,7 @@
       "src/core/ext/filters/client_channel/resolver_factory.h", 
       "src/core/ext/filters/client_channel/resolver_registry.h", 
       "src/core/ext/filters/client_channel/retry_throttle.h", 
+      "src/core/ext/filters/client_channel/status_util.h", 
       "src/core/ext/filters/client_channel/subchannel.h", 
       "src/core/ext/filters/client_channel/subchannel_index.h", 
       "src/core/ext/filters/client_channel/uri_parser.h"
@@ -8911,6 +9458,8 @@
       "src/core/ext/filters/client_channel/lb_policy_factory.h", 
       "src/core/ext/filters/client_channel/lb_policy_registry.cc", 
       "src/core/ext/filters/client_channel/lb_policy_registry.h", 
+      "src/core/ext/filters/client_channel/method_params.cc", 
+      "src/core/ext/filters/client_channel/method_params.h", 
       "src/core/ext/filters/client_channel/parse_address.cc", 
       "src/core/ext/filters/client_channel/parse_address.h", 
       "src/core/ext/filters/client_channel/proxy_mapper.cc", 
@@ -8924,6 +9473,8 @@
       "src/core/ext/filters/client_channel/resolver_registry.h", 
       "src/core/ext/filters/client_channel/retry_throttle.cc", 
       "src/core/ext/filters/client_channel/retry_throttle.h", 
+      "src/core/ext/filters/client_channel/status_util.cc", 
+      "src/core/ext/filters/client_channel/status_util.h", 
       "src/core/ext/filters/client_channel/subchannel.cc", 
       "src/core/ext/filters/client_channel/subchannel.h", 
       "src/core/ext/filters/client_channel/subchannel_index.cc", 
@@ -9017,7 +9568,6 @@
     ], 
     "headers": [
       "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
-      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", 
@@ -9030,7 +9580,6 @@
       "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc", 
-      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc", 
@@ -9054,7 +9603,6 @@
     ], 
     "headers": [
       "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
-      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", 
@@ -9067,7 +9615,6 @@
       "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc", 
-      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc", 
@@ -9246,6 +9793,7 @@
   }, 
   {
     "deps": [
+      "alts_tsi", 
       "gpr", 
       "grpc_base", 
       "grpc_transport_chttp2_alpn", 
@@ -9254,6 +9802,7 @@
     "headers": [
       "include/grpc/grpc_security.h", 
       "src/core/lib/security/context/security_context.h", 
+      "src/core/lib/security/credentials/alts/alts_credentials.h", 
       "src/core/lib/security/credentials/composite/composite_credentials.h", 
       "src/core/lib/security/credentials/credentials.h", 
       "src/core/lib/security/credentials/fake/fake_credentials.h", 
@@ -9265,11 +9814,12 @@
       "src/core/lib/security/credentials/oauth2/oauth2_credentials.h", 
       "src/core/lib/security/credentials/plugin/plugin_credentials.h", 
       "src/core/lib/security/credentials/ssl/ssl_credentials.h", 
+      "src/core/lib/security/security_connector/alts_security_connector.h", 
+      "src/core/lib/security/security_connector/security_connector.h", 
       "src/core/lib/security/transport/auth_filters.h", 
-      "src/core/lib/security/transport/lb_targets_info.h", 
       "src/core/lib/security/transport/secure_endpoint.h", 
-      "src/core/lib/security/transport/security_connector.h", 
       "src/core/lib/security/transport/security_handshaker.h", 
+      "src/core/lib/security/transport/target_authority_table.h", 
       "src/core/lib/security/transport/tsi_error.h", 
       "src/core/lib/security/util/json_util.h"
     ], 
@@ -9281,6 +9831,8 @@
       "src/core/lib/http/httpcli_security_connector.cc", 
       "src/core/lib/security/context/security_context.cc", 
       "src/core/lib/security/context/security_context.h", 
+      "src/core/lib/security/credentials/alts/alts_credentials.cc", 
+      "src/core/lib/security/credentials/alts/alts_credentials.h", 
       "src/core/lib/security/credentials/composite/composite_credentials.cc", 
       "src/core/lib/security/credentials/composite/composite_credentials.h", 
       "src/core/lib/security/credentials/credentials.cc", 
@@ -9305,17 +9857,19 @@
       "src/core/lib/security/credentials/plugin/plugin_credentials.h", 
       "src/core/lib/security/credentials/ssl/ssl_credentials.cc", 
       "src/core/lib/security/credentials/ssl/ssl_credentials.h", 
+      "src/core/lib/security/security_connector/alts_security_connector.cc", 
+      "src/core/lib/security/security_connector/alts_security_connector.h", 
+      "src/core/lib/security/security_connector/security_connector.cc", 
+      "src/core/lib/security/security_connector/security_connector.h", 
       "src/core/lib/security/transport/auth_filters.h", 
       "src/core/lib/security/transport/client_auth_filter.cc", 
-      "src/core/lib/security/transport/lb_targets_info.cc", 
-      "src/core/lib/security/transport/lb_targets_info.h", 
       "src/core/lib/security/transport/secure_endpoint.cc", 
       "src/core/lib/security/transport/secure_endpoint.h", 
-      "src/core/lib/security/transport/security_connector.cc", 
-      "src/core/lib/security/transport/security_connector.h", 
       "src/core/lib/security/transport/security_handshaker.cc", 
       "src/core/lib/security/transport/security_handshaker.h", 
       "src/core/lib/security/transport/server_auth_filter.cc", 
+      "src/core/lib/security/transport/target_authority_table.cc", 
+      "src/core/lib/security/transport/target_authority_table.h", 
       "src/core/lib/security/transport/tsi_error.cc", 
       "src/core/lib/security/transport/tsi_error.h", 
       "src/core/lib/security/util/json_util.cc", 
@@ -9893,7 +10447,37 @@
       "include/grpc++/impl/codegen/string_ref.h", 
       "include/grpc++/impl/codegen/stub_options.h", 
       "include/grpc++/impl/codegen/sync_stream.h", 
-      "include/grpc++/impl/codegen/time.h"
+      "include/grpc++/impl/codegen/time.h", 
+      "include/grpcpp/impl/codegen/async_stream.h", 
+      "include/grpcpp/impl/codegen/async_unary_call.h", 
+      "include/grpcpp/impl/codegen/byte_buffer.h", 
+      "include/grpcpp/impl/codegen/call.h", 
+      "include/grpcpp/impl/codegen/call_hook.h", 
+      "include/grpcpp/impl/codegen/channel_interface.h", 
+      "include/grpcpp/impl/codegen/client_context.h", 
+      "include/grpcpp/impl/codegen/client_unary_call.h", 
+      "include/grpcpp/impl/codegen/completion_queue.h", 
+      "include/grpcpp/impl/codegen/completion_queue_tag.h", 
+      "include/grpcpp/impl/codegen/config.h", 
+      "include/grpcpp/impl/codegen/core_codegen_interface.h", 
+      "include/grpcpp/impl/codegen/create_auth_context.h", 
+      "include/grpcpp/impl/codegen/grpc_library.h", 
+      "include/grpcpp/impl/codegen/metadata_map.h", 
+      "include/grpcpp/impl/codegen/method_handler_impl.h", 
+      "include/grpcpp/impl/codegen/rpc_method.h", 
+      "include/grpcpp/impl/codegen/rpc_service_method.h", 
+      "include/grpcpp/impl/codegen/security/auth_context.h", 
+      "include/grpcpp/impl/codegen/serialization_traits.h", 
+      "include/grpcpp/impl/codegen/server_context.h", 
+      "include/grpcpp/impl/codegen/server_interface.h", 
+      "include/grpcpp/impl/codegen/service_type.h", 
+      "include/grpcpp/impl/codegen/slice.h", 
+      "include/grpcpp/impl/codegen/status.h", 
+      "include/grpcpp/impl/codegen/status_code_enum.h", 
+      "include/grpcpp/impl/codegen/string_ref.h", 
+      "include/grpcpp/impl/codegen/stub_options.h", 
+      "include/grpcpp/impl/codegen/sync_stream.h", 
+      "include/grpcpp/impl/codegen/time.h"
     ], 
     "is_filegroup": true, 
     "language": "c++", 
@@ -9928,7 +10512,37 @@
       "include/grpc++/impl/codegen/string_ref.h", 
       "include/grpc++/impl/codegen/stub_options.h", 
       "include/grpc++/impl/codegen/sync_stream.h", 
-      "include/grpc++/impl/codegen/time.h"
+      "include/grpc++/impl/codegen/time.h", 
+      "include/grpcpp/impl/codegen/async_stream.h", 
+      "include/grpcpp/impl/codegen/async_unary_call.h", 
+      "include/grpcpp/impl/codegen/byte_buffer.h", 
+      "include/grpcpp/impl/codegen/call.h", 
+      "include/grpcpp/impl/codegen/call_hook.h", 
+      "include/grpcpp/impl/codegen/channel_interface.h", 
+      "include/grpcpp/impl/codegen/client_context.h", 
+      "include/grpcpp/impl/codegen/client_unary_call.h", 
+      "include/grpcpp/impl/codegen/completion_queue.h", 
+      "include/grpcpp/impl/codegen/completion_queue_tag.h", 
+      "include/grpcpp/impl/codegen/config.h", 
+      "include/grpcpp/impl/codegen/core_codegen_interface.h", 
+      "include/grpcpp/impl/codegen/create_auth_context.h", 
+      "include/grpcpp/impl/codegen/grpc_library.h", 
+      "include/grpcpp/impl/codegen/metadata_map.h", 
+      "include/grpcpp/impl/codegen/method_handler_impl.h", 
+      "include/grpcpp/impl/codegen/rpc_method.h", 
+      "include/grpcpp/impl/codegen/rpc_service_method.h", 
+      "include/grpcpp/impl/codegen/security/auth_context.h", 
+      "include/grpcpp/impl/codegen/serialization_traits.h", 
+      "include/grpcpp/impl/codegen/server_context.h", 
+      "include/grpcpp/impl/codegen/server_interface.h", 
+      "include/grpcpp/impl/codegen/service_type.h", 
+      "include/grpcpp/impl/codegen/slice.h", 
+      "include/grpcpp/impl/codegen/status.h", 
+      "include/grpcpp/impl/codegen/status_code_enum.h", 
+      "include/grpcpp/impl/codegen/string_ref.h", 
+      "include/grpcpp/impl/codegen/stub_options.h", 
+      "include/grpcpp/impl/codegen/sync_stream.h", 
+      "include/grpcpp/impl/codegen/time.h"
     ], 
     "third_party": false, 
     "type": "filegroup"
@@ -9953,13 +10567,15 @@
       "grpc++_config_proto"
     ], 
     "headers": [
-      "include/grpc++/impl/codegen/proto_utils.h"
+      "include/grpc++/impl/codegen/proto_utils.h", 
+      "include/grpcpp/impl/codegen/proto_utils.h"
     ], 
     "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_codegen_proto", 
     "src": [
-      "include/grpc++/impl/codegen/proto_utils.h"
+      "include/grpc++/impl/codegen/proto_utils.h", 
+      "include/grpcpp/impl/codegen/proto_utils.h"
     ], 
     "third_party": false, 
     "type": "filegroup"
@@ -10019,6 +10635,51 @@
       "include/grpc++/support/stub_options.h", 
       "include/grpc++/support/sync_stream.h", 
       "include/grpc++/support/time.h", 
+      "include/grpcpp/alarm.h", 
+      "include/grpcpp/channel.h", 
+      "include/grpcpp/client_context.h", 
+      "include/grpcpp/completion_queue.h", 
+      "include/grpcpp/create_channel.h", 
+      "include/grpcpp/create_channel_posix.h", 
+      "include/grpcpp/ext/health_check_service_server_builder_option.h", 
+      "include/grpcpp/generic/async_generic_service.h", 
+      "include/grpcpp/generic/generic_stub.h", 
+      "include/grpcpp/grpcpp.h", 
+      "include/grpcpp/health_check_service_interface.h", 
+      "include/grpcpp/impl/call.h", 
+      "include/grpcpp/impl/channel_argument_option.h", 
+      "include/grpcpp/impl/client_unary_call.h", 
+      "include/grpcpp/impl/codegen/core_codegen.h", 
+      "include/grpcpp/impl/grpc_library.h", 
+      "include/grpcpp/impl/method_handler_impl.h", 
+      "include/grpcpp/impl/rpc_method.h", 
+      "include/grpcpp/impl/rpc_service_method.h", 
+      "include/grpcpp/impl/serialization_traits.h", 
+      "include/grpcpp/impl/server_builder_option.h", 
+      "include/grpcpp/impl/server_builder_plugin.h", 
+      "include/grpcpp/impl/server_initializer.h", 
+      "include/grpcpp/impl/service_type.h", 
+      "include/grpcpp/resource_quota.h", 
+      "include/grpcpp/security/auth_context.h", 
+      "include/grpcpp/security/auth_metadata_processor.h", 
+      "include/grpcpp/security/credentials.h", 
+      "include/grpcpp/security/server_credentials.h", 
+      "include/grpcpp/server.h", 
+      "include/grpcpp/server_builder.h", 
+      "include/grpcpp/server_context.h", 
+      "include/grpcpp/server_posix.h", 
+      "include/grpcpp/support/async_stream.h", 
+      "include/grpcpp/support/async_unary_call.h", 
+      "include/grpcpp/support/byte_buffer.h", 
+      "include/grpcpp/support/channel_arguments.h", 
+      "include/grpcpp/support/config.h", 
+      "include/grpcpp/support/slice.h", 
+      "include/grpcpp/support/status.h", 
+      "include/grpcpp/support/status_code_enum.h", 
+      "include/grpcpp/support/string_ref.h", 
+      "include/grpcpp/support/stub_options.h", 
+      "include/grpcpp/support/sync_stream.h", 
+      "include/grpcpp/support/time.h", 
       "src/cpp/client/create_channel_internal.h", 
       "src/cpp/common/channel_filter.h", 
       "src/cpp/server/dynamic_thread_pool.h", 
@@ -10076,6 +10737,51 @@
       "include/grpc++/support/stub_options.h", 
       "include/grpc++/support/sync_stream.h", 
       "include/grpc++/support/time.h", 
+      "include/grpcpp/alarm.h", 
+      "include/grpcpp/channel.h", 
+      "include/grpcpp/client_context.h", 
+      "include/grpcpp/completion_queue.h", 
+      "include/grpcpp/create_channel.h", 
+      "include/grpcpp/create_channel_posix.h", 
+      "include/grpcpp/ext/health_check_service_server_builder_option.h", 
+      "include/grpcpp/generic/async_generic_service.h", 
+      "include/grpcpp/generic/generic_stub.h", 
+      "include/grpcpp/grpcpp.h", 
+      "include/grpcpp/health_check_service_interface.h", 
+      "include/grpcpp/impl/call.h", 
+      "include/grpcpp/impl/channel_argument_option.h", 
+      "include/grpcpp/impl/client_unary_call.h", 
+      "include/grpcpp/impl/codegen/core_codegen.h", 
+      "include/grpcpp/impl/grpc_library.h", 
+      "include/grpcpp/impl/method_handler_impl.h", 
+      "include/grpcpp/impl/rpc_method.h", 
+      "include/grpcpp/impl/rpc_service_method.h", 
+      "include/grpcpp/impl/serialization_traits.h", 
+      "include/grpcpp/impl/server_builder_option.h", 
+      "include/grpcpp/impl/server_builder_plugin.h", 
+      "include/grpcpp/impl/server_initializer.h", 
+      "include/grpcpp/impl/service_type.h", 
+      "include/grpcpp/resource_quota.h", 
+      "include/grpcpp/security/auth_context.h", 
+      "include/grpcpp/security/auth_metadata_processor.h", 
+      "include/grpcpp/security/credentials.h", 
+      "include/grpcpp/security/server_credentials.h", 
+      "include/grpcpp/server.h", 
+      "include/grpcpp/server_builder.h", 
+      "include/grpcpp/server_context.h", 
+      "include/grpcpp/server_posix.h", 
+      "include/grpcpp/support/async_stream.h", 
+      "include/grpcpp/support/async_unary_call.h", 
+      "include/grpcpp/support/byte_buffer.h", 
+      "include/grpcpp/support/channel_arguments.h", 
+      "include/grpcpp/support/config.h", 
+      "include/grpcpp/support/slice.h", 
+      "include/grpcpp/support/status.h", 
+      "include/grpcpp/support/status_code_enum.h", 
+      "include/grpcpp/support/string_ref.h", 
+      "include/grpcpp/support/stub_options.h", 
+      "include/grpcpp/support/sync_stream.h", 
+      "include/grpcpp/support/time.h", 
       "src/cpp/client/channel_cc.cc", 
       "src/cpp/client/client_context.cc", 
       "src/cpp/client/create_channel.cc", 
@@ -10124,13 +10830,15 @@
   {
     "deps": [], 
     "headers": [
-      "include/grpc++/impl/codegen/config_protobuf.h"
+      "include/grpc++/impl/codegen/config_protobuf.h", 
+      "include/grpcpp/impl/codegen/config_protobuf.h"
     ], 
     "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_config_proto", 
     "src": [
-      "include/grpc++/impl/codegen/config_protobuf.h"
+      "include/grpc++/impl/codegen/config_protobuf.h", 
+      "include/grpcpp/impl/codegen/config_protobuf.h"
     ], 
     "third_party": false, 
     "type": "filegroup"
@@ -10156,14 +10864,18 @@
     ], 
     "headers": [
       "include/grpc++/test/mock_stream.h", 
-      "include/grpc++/test/server_context_test_spouse.h"
+      "include/grpc++/test/server_context_test_spouse.h", 
+      "include/grpcpp/test/mock_stream.h", 
+      "include/grpcpp/test/server_context_test_spouse.h"
     ], 
     "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_test", 
     "src": [
       "include/grpc++/test/mock_stream.h", 
-      "include/grpc++/test/server_context_test_spouse.h"
+      "include/grpc++/test/server_context_test_spouse.h", 
+      "include/grpcpp/test/mock_stream.h", 
+      "include/grpcpp/test/server_context_test_spouse.h"
     ], 
     "third_party": false, 
     "type": "filegroup"
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 2fa635c..b7fdb6f 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -2274,30 +2274,6 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "slice_hash_table_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
     "name": "slice_string_helpers_test", 
     "platforms": [
       "linux", 
@@ -2900,6 +2876,318 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_counter_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_crypt_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_crypter_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_frame_handler_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_frame_protector_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_grpc_record_protocol_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_handshaker_client_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_handshaker_service_api_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_iovec_record_protocol_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_security_connector_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_tsi_handshaker_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_tsi_utils_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "alts_zero_copy_grpc_protector_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
     "gtest": true, 
     "language": "c++", 
     "name": "async_end2end_test", 
@@ -3374,6 +3662,54 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "check_gcp_environment_linux_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "check_gcp_environment_windows_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
     "gtest": true, 
     "language": "c++", 
     "name": "chttp2_settings_timeout_test", 
@@ -3806,6 +4142,30 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "grpc_alts_credentials_options_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
     "gtest": true, 
     "language": "c++", 
     "name": "grpc_tool_test", 
@@ -3878,30 +4238,6 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
-    "gtest": false, 
-    "language": "c++", 
-    "name": "grpclb_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "uses_polling": true
-  }, 
-  {
-    "args": [], 
-    "benchmark": false, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
     "gtest": true, 
     "language": "c++", 
     "name": "h2_ssl_cert_test", 
@@ -4489,6 +4825,54 @@
       "windows"
     ], 
     "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "slice_hash_table_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "slice_weak_hash_table_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
     "exclude_configs": [
       "tsan"
     ], 
@@ -4519,6 +4903,30 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "status_metadata_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
     "gtest": false, 
     "language": "c++", 
     "name": "status_test", 
@@ -4536,6 +4944,30 @@
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "status_util_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 1.0, 
@@ -4630,6 +5062,30 @@
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "transport_security_common_api_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "uses_polling": true
+  }, 
+  {
+    "args": [], 
+    "benchmark": false, 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.5, 
@@ -7320,6 +7776,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -8658,6 +9459,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -9975,6 +11121,336 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -12517,6 +13993,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -13698,6 +15519,291 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -14953,6 +17059,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -16314,6 +18765,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -17739,6 +20535,366 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -19115,6 +22271,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -20516,6 +24017,366 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -26794,6 +30655,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -29280,6 +33486,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -31561,6 +36112,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -32876,6 +37772,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -35387,6 +40628,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -36549,6 +42135,291 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -37781,6 +43652,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -39119,6 +45335,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+workarounds_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -40520,6 +47081,366 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -41873,6 +48794,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
@@ -47988,6 +55254,351 @@
   }, 
   {
     "args": [
+      "retry"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_cancellation"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_disabled"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_initial_batch"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_exceeds_buffer_size_in_subsequent_batch"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_non_retriable_status"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_initial_metadata"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_recv_message"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_delay"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_server_pushback_disabled"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_after_commit"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_streaming_succeeds_before_replay_finished"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_throttled"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "retry_too_many_attempts"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "server_finishes_request"
     ], 
     "ci_platforms": [
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index 44a6ec2..4a9c282 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -167,6 +167,36 @@
         return 'csharpcoreclr'
 
 
+class DartLanguage:
+
+    def __init__(self):
+        self.client_cwd = '../grpc-dart/interop'
+        self.server_cwd = '../grpc-dart/interop'
+        self.http2_cwd = '../grpc-dart/interop'
+        self.safename = str(self)
+
+    def client_cmd(self, args):
+        return ['dart', 'bin/client.dart'] + args
+
+    def cloud_to_prod_env(self):
+        return {}
+
+    def server_cmd(self, args):
+        return ['dart', 'bin/server.dart'] + args
+
+    def global_env(self):
+        return {}
+
+    def unimplemented_test_cases(self):
+        return _SKIP_COMPRESSION
+
+    def unimplemented_test_cases_server(self):
+        return _SKIP_COMPRESSION
+
+    def __str__(self):
+        return 'dart'
+
+
 class JavaLanguage:
 
     def __init__(self):
@@ -524,6 +554,7 @@
     'c++': CXXLanguage(),
     'csharp': CSharpLanguage(),
     'csharpcoreclr': CSharpCoreCLRLanguage(),
+    'dart': DartLanguage(),
     'go': GoLanguage(),
     'java': JavaLanguage(),
     'javaokhttp': JavaOkHttpClient(),
@@ -537,7 +568,8 @@
 
 # languages supported as cloud_to_cloud servers
 _SERVERS = [
-    'c++', 'node', 'csharp', 'csharpcoreclr', 'java', 'go', 'ruby', 'python'
+    'c++', 'node', 'csharp', 'csharpcoreclr', 'java', 'go', 'ruby', 'python',
+    'dart'
 ]
 
 _TEST_CASES = [
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 433137a..1c99e79 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -37,6 +37,9 @@
 # C++ TSAN takes longer than other sanitizers
 _CPP_TSAN_RUNTESTS_TIMEOUT = 8 * 60 * 60
 
+# Set timeout high for ObjC for Cocoapods to install pods
+_OBJC_RUNTESTS_TIMEOUT = 90 * 60
+
 # Number of jobs assigned to each run_tests.py instance
 _DEFAULT_INNER_JOBS = 2
 
@@ -213,7 +216,8 @@
         platforms=['macos'],
         labels=['basictests', 'multilang'],
         extra_args=extra_args,
-        inner_jobs=inner_jobs)
+        inner_jobs=inner_jobs,
+        timeout_seconds=_OBJC_RUNTESTS_TIMEOUT)
 
     # sanitizers
     test_jobs += _generate_jobs(
diff --git a/tools/run_tests/sanity/check_bazel_workspace.py b/tools/run_tests/sanity/check_bazel_workspace.py
index 62a6229..555149c 100755
--- a/tools/run_tests/sanity/check_bazel_workspace.py
+++ b/tools/run_tests/sanity/check_bazel_workspace.py
@@ -35,6 +35,11 @@
 }
 
 _BAZEL_TOOLCHAINS_DEP_NAME = 'com_github_bazelbuild_bazeltoolchains'
+_TWISTED_TWISTED_DEP_NAME = 'com_github_twisted_twisted'
+_YAML_PYYAML_DEP_NAME = 'com_github_yaml_pyyaml'
+_TWISTED_INCREMENTAL_DEP_NAME = 'com_github_twisted_incremental'
+_ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME = 'com_github_zopefoundation_zope_interface'
+_TWISTED_CONSTANTLY_DEP_NAME = 'com_github_twisted_constantly'
 
 _GRPC_DEP_NAMES = [
     'boringssl',
@@ -46,6 +51,20 @@
     'com_github_cares_cares',
     'com_google_absl',
     _BAZEL_TOOLCHAINS_DEP_NAME,
+    _TWISTED_TWISTED_DEP_NAME,
+    _YAML_PYYAML_DEP_NAME,
+    _TWISTED_INCREMENTAL_DEP_NAME,
+    _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME,
+    _TWISTED_CONSTANTLY_DEP_NAME,
+]
+
+_GRPC_BAZEL_ONLY_DEPS = [
+    _BAZEL_TOOLCHAINS_DEP_NAME,
+    _TWISTED_TWISTED_DEP_NAME,
+    _YAML_PYYAML_DEP_NAME,
+    _TWISTED_INCREMENTAL_DEP_NAME,
+    _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME,
+    _TWISTED_CONSTANTLY_DEP_NAME,
 ]
 
 
@@ -70,7 +89,8 @@
         return []
 
     def archive(self, **args):
-        if args['name'] == _BAZEL_TOOLCHAINS_DEP_NAME:
+        assert self.names_and_urls.get(args['name']) is None
+        if args['name'] in _GRPC_BAZEL_ONLY_DEPS:
             self.names_and_urls[args['name']] = 'dont care'
             return
         self.names_and_urls[args['name']] = args['url']
@@ -82,8 +102,10 @@
     eval_state = BazelEvalState(names_and_urls)
     bazel_file = f.read()
 
-# grpc_deps.bzl only defines 'grpc_deps', add this to call it
+# grpc_deps.bzl only defines 'grpc_deps' and 'grpc_test_only_deps', add these
+# lines to call them.
 bazel_file += '\ngrpc_deps()\n'
+bazel_file += '\ngrpc_test_only_deps()\n'
 build_rules = {
     'native': eval_state,
 }
@@ -92,11 +114,12 @@
     assert name in names_and_urls.keys()
 assert len(_GRPC_DEP_NAMES) == len(names_and_urls.keys())
 
-# bazeltoolschains is an exception to this sanity check,
-# we don't require that there is a corresponding git module.
-names_without_bazeltoolchains = names_and_urls.keys()
-names_without_bazeltoolchains.remove(_BAZEL_TOOLCHAINS_DEP_NAME)
-archive_urls = [names_and_urls[name] for name in names_without_bazeltoolchains]
+# There are some "bazel-only" deps that are exceptions to this sanity check,
+# we don't require that there is a corresponding git module for these.
+names_without_bazel_only_deps = names_and_urls.keys()
+for dep_name in _GRPC_BAZEL_ONLY_DEPS:
+    names_without_bazel_only_deps.remove(dep_name)
+archive_urls = [names_and_urls[name] for name in names_without_bazel_only_deps]
 workspace_git_hashes = {
     re.search(git_hash_pattern, url).group()
     for url in archive_urls
diff --git a/tools/run_tests/sanity/check_deprecated_grpc++.py b/tools/run_tests/sanity/check_deprecated_grpc++.py
new file mode 100755
index 0000000..4ec49fa
--- /dev/null
+++ b/tools/run_tests/sanity/check_deprecated_grpc++.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+
+# Copyright 2018 gRPC authors.
+#
+# 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.
+
+from __future__ import print_function
+
+import os
+import sys
+
+os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
+
+expected_files = [
+    "include/grpc++/create_channel_posix.h", "include/grpc++/server_builder.h",
+    "include/grpc++/resource_quota.h", "include/grpc++/create_channel.h",
+    "include/grpc++/alarm.h", "include/grpc++/server.h",
+    "include/grpc++/server_context.h", "include/grpc++/client_context.h",
+    "include/grpc++/server_posix.h", "include/grpc++/grpc++.h",
+    "include/grpc++/health_check_service_interface.h",
+    "include/grpc++/completion_queue.h", "include/grpc++/channel.h",
+    "include/grpc++/support/sync_stream.h", "include/grpc++/support/status.h",
+    "include/grpc++/support/config.h",
+    "include/grpc++/support/status_code_enum.h",
+    "include/grpc++/support/byte_buffer.h",
+    "include/grpc++/support/error_details.h",
+    "include/grpc++/support/async_unary_call.h",
+    "include/grpc++/support/channel_arguments.h",
+    "include/grpc++/support/async_stream.h", "include/grpc++/support/slice.h",
+    "include/grpc++/support/stub_options.h",
+    "include/grpc++/support/string_ref.h", "include/grpc++/support/time.h",
+    "include/grpc++/security/auth_metadata_processor.h",
+    "include/grpc++/security/credentials.h",
+    "include/grpc++/security/server_credentials.h",
+    "include/grpc++/security/auth_context.h",
+    "include/grpc++/impl/rpc_method.h",
+    "include/grpc++/impl/server_builder_option.h", "include/grpc++/impl/call.h",
+    "include/grpc++/impl/service_type.h", "include/grpc++/impl/grpc_library.h",
+    "include/grpc++/impl/client_unary_call.h",
+    "include/grpc++/impl/channel_argument_option.h",
+    "include/grpc++/impl/rpc_service_method.h",
+    "include/grpc++/impl/method_handler_impl.h",
+    "include/grpc++/impl/server_builder_plugin.h",
+    "include/grpc++/impl/sync_cxx11.h",
+    "include/grpc++/impl/server_initializer.h",
+    "include/grpc++/impl/serialization_traits.h",
+    "include/grpc++/impl/sync_no_cxx11.h",
+    "include/grpc++/impl/codegen/sync_stream.h",
+    "include/grpc++/impl/codegen/channel_interface.h",
+    "include/grpc++/impl/codegen/config_protobuf.h",
+    "include/grpc++/impl/codegen/status.h",
+    "include/grpc++/impl/codegen/core_codegen.h",
+    "include/grpc++/impl/codegen/config.h",
+    "include/grpc++/impl/codegen/core_codegen_interface.h",
+    "include/grpc++/impl/codegen/status_code_enum.h",
+    "include/grpc++/impl/codegen/metadata_map.h",
+    "include/grpc++/impl/codegen/rpc_method.h",
+    "include/grpc++/impl/codegen/server_context.h",
+    "include/grpc++/impl/codegen/byte_buffer.h",
+    "include/grpc++/impl/codegen/async_unary_call.h",
+    "include/grpc++/impl/codegen/server_interface.h",
+    "include/grpc++/impl/codegen/call.h",
+    "include/grpc++/impl/codegen/client_context.h",
+    "include/grpc++/impl/codegen/service_type.h",
+    "include/grpc++/impl/codegen/grpc_library.h",
+    "include/grpc++/impl/codegen/async_stream.h",
+    "include/grpc++/impl/codegen/slice.h",
+    "include/grpc++/impl/codegen/client_unary_call.h",
+    "include/grpc++/impl/codegen/proto_utils.h",
+    "include/grpc++/impl/codegen/stub_options.h",
+    "include/grpc++/impl/codegen/rpc_service_method.h",
+    "include/grpc++/impl/codegen/method_handler_impl.h",
+    "include/grpc++/impl/codegen/string_ref.h",
+    "include/grpc++/impl/codegen/completion_queue_tag.h",
+    "include/grpc++/impl/codegen/call_hook.h",
+    "include/grpc++/impl/codegen/completion_queue.h",
+    "include/grpc++/impl/codegen/serialization_traits.h",
+    "include/grpc++/impl/codegen/create_auth_context.h",
+    "include/grpc++/impl/codegen/time.h",
+    "include/grpc++/impl/codegen/security/auth_context.h",
+    "include/grpc++/ext/health_check_service_server_builder_option.h",
+    "include/grpc++/ext/proto_server_reflection_plugin.h",
+    "include/grpc++/generic/async_generic_service.h",
+    "include/grpc++/generic/generic_stub.h",
+    "include/grpc++/test/mock_stream.h",
+    "include/grpc++/test/server_context_test_spouse.h"
+]
+
+file_template = '''/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_FILE_PATH_NAME_UPPER
+#define GRPCXX_FILE_PATH_NAME_UPPER
+
+#include <grpcpp/FILE_PATH_NAME_LOWER>
+
+#endif  // GRPCXX_FILE_PATH_NAME_UPPER
+'''
+
+errors = 0
+
+path_files = []
+for root, dirs, files in os.walk('include/grpc++'):
+    for filename in files:
+        path_file = os.path.join(root, filename)
+        path_files.append(path_file)
+
+if path_files.sort() != expected_files.sort():
+    diff_plus = [file for file in path_files if file not in expected_files]
+    diff_minus = [file for file in expected_files if file not in path_files]
+    for file in diff_minus:
+        print('- ', file)
+    for file in diff_plus:
+        print('+ ', file)
+    errors += 1
+
+if errors > 0:
+    sys.exit(errors)
+
+for path_file in expected_files:
+    relative_path_file = path_file.split('/', 2)[2]
+
+    replace_lower = relative_path_file.replace('+', 'p')
+
+    replace_upper = relative_path_file.replace('/', '_')
+    replace_upper = replace_upper.replace('.', '_')
+    replace_upper = replace_upper.upper().replace('+', 'X')
+
+    expected_content = file_template.replace('FILE_PATH_NAME_LOWER',
+                                             replace_lower)
+    expected_content = expected_content.replace('FILE_PATH_NAME_UPPER',
+                                                replace_upper)
+
+    path_file_expected = path_file + '.expected'
+    with open(path_file_expected, "w") as fo:
+        fo.write(expected_content)
+
+    if 0 != os.system('diff %s %s' % (path_file_expected, path_file)):
+        print('Difference found in file:', path_file)
+        errors += 1
+
+    os.remove(path_file_expected)
+
+check_extensions = [".h", ".cc", ".c", ".m"]
+
+for root, dirs, files in os.walk('src'):
+    for filename in files:
+        path_file = os.path.join(root, filename)
+        for ext in check_extensions:
+            if path_file.endswith(ext):
+                try:
+                    with open(path_file, "r") as fi:
+                        content = fi.read()
+                        if '#include <grpc++/' in content:
+                            print(
+                                'Failed: invalid include of deprecated headers in include/grpc++ in %s'
+                                % path_file)
+                            errors += 1
+                except IOError:
+                    pass
+
+sys.exit(errors)
diff --git a/tools/run_tests/sanity/check_port_platform.py b/tools/run_tests/sanity/check_port_platform.py
new file mode 100755
index 0000000..fff828e
--- /dev/null
+++ b/tools/run_tests/sanity/check_port_platform.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+import os
+import sys
+
+os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
+
+
+def check_port_platform_inclusion(directory_root):
+    bad_files = []
+    for root, dirs, files in os.walk(directory_root):
+        for filename in files:
+            path = os.path.join(root, filename)
+            if os.path.splitext(path)[1] not in ['.c', '.cc', '.h']: continue
+            if path in [
+                    os.path.join('include', 'grpc', 'support',
+                                 'port_platform.h'),
+                    os.path.join('include', 'grpc', 'impl', 'codegen',
+                                 'port_platform.h'),
+            ]:
+                continue
+            if filename.endswith('.pb.h') or filename.endswith('.pb.c'):
+                continue
+            with open(path) as f:
+                all_lines_in_file = f.readlines()
+                for index, l in enumerate(all_lines_in_file):
+                    if '#include' in l:
+                        if l not in [
+                                '#include <grpc/support/port_platform.h>\n',
+                                '#include <grpc/impl/codegen/port_platform.h>\n'
+                        ]:
+                            bad_files.append(path)
+                        elif all_lines_in_file[index + 1] != '\n':
+                            # Require a blank line after including port_platform.h in
+                            # order to prevent the formatter from reording it's
+                            # inclusion order upon future changes.
+                            bad_files.append(path)
+                        break
+    return bad_files
+
+
+all_bad_files = []
+all_bad_files += check_port_platform_inclusion(os.path.join('src', 'core'))
+all_bad_files += check_port_platform_inclusion(os.path.join('include', 'grpc'))
+
+if len(all_bad_files) > 0:
+    for f in all_bad_files:
+        print(('port_platform.h is not the first included header or there '
+               'is not a blank line following its inclusion in %s') % f)
+    sys.exit(1)
diff --git a/tools/run_tests/sanity/sanity_tests.yaml b/tools/run_tests/sanity/sanity_tests.yaml
index efdb4d8..a15473d 100644
--- a/tools/run_tests/sanity/sanity_tests.yaml
+++ b/tools/run_tests/sanity/sanity_tests.yaml
@@ -10,6 +10,8 @@
 - script: tools/run_tests/sanity/check_unsecure.sh
 - script: tools/run_tests/sanity/core_banned_functions.py
 - script: tools/run_tests/sanity/core_untyped_structs.sh
+- script: tools/run_tests/sanity/check_deprecated_grpc++.py
+- script: tools/run_tests/sanity/check_port_platform.py
 - script: tools/buildgen/generate_projects.sh -j 3
   cpu_cost: 3
 - script: tools/distrib/check_copyright.py