diff --git a/BUILD b/BUILD
index 9f682b8f86dede82729bf948f6b914ac99270455..f80e5f066468b0f6bd06e521d8b756d06f01d4b3 100644
--- a/BUILD
+++ b/BUILD
@@ -247,8 +247,8 @@ cc_library(
     "src/core/lib/transport/mdstr_hash_table.h",
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/method_config.h",
     "src/core/lib/transport/pid_controller.h",
+    "src/core/lib/transport/service_config.h",
     "src/core/lib/transport/static_metadata.h",
     "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
@@ -436,8 +436,8 @@ cc_library(
     "src/core/lib/transport/mdstr_hash_table.c",
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/method_config.c",
     "src/core/lib/transport/pid_controller.c",
+    "src/core/lib/transport/service_config.c",
     "src/core/lib/transport/static_metadata.c",
     "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
@@ -681,8 +681,8 @@ cc_library(
     "src/core/lib/transport/mdstr_hash_table.h",
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/method_config.h",
     "src/core/lib/transport/pid_controller.h",
+    "src/core/lib/transport/service_config.h",
     "src/core/lib/transport/static_metadata.h",
     "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
@@ -855,8 +855,8 @@ cc_library(
     "src/core/lib/transport/mdstr_hash_table.c",
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/method_config.c",
     "src/core/lib/transport/pid_controller.c",
+    "src/core/lib/transport/service_config.c",
     "src/core/lib/transport/static_metadata.c",
     "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
@@ -1070,8 +1070,8 @@ cc_library(
     "src/core/lib/transport/mdstr_hash_table.h",
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/method_config.h",
     "src/core/lib/transport/pid_controller.h",
+    "src/core/lib/transport/service_config.h",
     "src/core/lib/transport/static_metadata.h",
     "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
@@ -1236,8 +1236,8 @@ cc_library(
     "src/core/lib/transport/mdstr_hash_table.c",
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/method_config.c",
     "src/core/lib/transport/pid_controller.c",
+    "src/core/lib/transport/service_config.c",
     "src/core/lib/transport/static_metadata.c",
     "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
@@ -2097,8 +2097,8 @@ objc_library(
     "src/core/lib/transport/mdstr_hash_table.c",
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
-    "src/core/lib/transport/method_config.c",
     "src/core/lib/transport/pid_controller.c",
+    "src/core/lib/transport/service_config.c",
     "src/core/lib/transport/static_metadata.c",
     "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
@@ -2321,8 +2321,8 @@ objc_library(
     "src/core/lib/transport/mdstr_hash_table.h",
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
-    "src/core/lib/transport/method_config.h",
     "src/core/lib/transport/pid_controller.h",
+    "src/core/lib/transport/service_config.h",
     "src/core/lib/transport/static_metadata.h",
     "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ba46adaa2bef51af87b481d38da8ec14d80ca372..cc9170038eec1a8dc06906894b761669294f392e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -391,8 +391,8 @@ add_library(grpc
   src/core/lib/transport/mdstr_hash_table.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
-  src/core/lib/transport/method_config.c
   src/core/lib/transport/pid_controller.c
+  src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
@@ -670,8 +670,8 @@ add_library(grpc_cronet
   src/core/lib/transport/mdstr_hash_table.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
-  src/core/lib/transport/method_config.c
   src/core/lib/transport/pid_controller.c
+  src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
@@ -921,8 +921,8 @@ add_library(grpc_unsecure
   src/core/lib/transport/mdstr_hash_table.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
-  src/core/lib/transport/method_config.c
   src/core/lib/transport/pid_controller.c
+  src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
diff --git a/Makefile b/Makefile
index 9802780c1b1106552e50dea986fc73e5ebdcc760..1609d6951be0c9e6bcb8fc97b67e8f956c0469bf 100644
--- a/Makefile
+++ b/Makefile
@@ -2730,8 +2730,8 @@ LIBGRPC_SRC = \
     src/core/lib/transport/mdstr_hash_table.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
-    src/core/lib/transport/method_config.c \
     src/core/lib/transport/pid_controller.c \
+    src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
@@ -3027,8 +3027,8 @@ LIBGRPC_CRONET_SRC = \
     src/core/lib/transport/mdstr_hash_table.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
-    src/core/lib/transport/method_config.c \
     src/core/lib/transport/pid_controller.c \
+    src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
@@ -3315,8 +3315,8 @@ LIBGRPC_TEST_UTIL_SRC = \
     src/core/lib/transport/mdstr_hash_table.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
-    src/core/lib/transport/method_config.c \
     src/core/lib/transport/pid_controller.c \
+    src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
@@ -3532,8 +3532,8 @@ LIBGRPC_UNSECURE_SRC = \
     src/core/lib/transport/mdstr_hash_table.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
-    src/core/lib/transport/method_config.c \
     src/core/lib/transport/pid_controller.c \
+    src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
diff --git a/binding.gyp b/binding.gyp
index a68a728edd8a2f6cacc1364ff282fe184017ddbc..28d7c6b9a5d7f9b08d10c3161091f39e81d046c8 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -671,8 +671,8 @@
         'src/core/lib/transport/mdstr_hash_table.c',
         'src/core/lib/transport/metadata.c',
         'src/core/lib/transport/metadata_batch.c',
-        'src/core/lib/transport/method_config.c',
         'src/core/lib/transport/pid_controller.c',
+        'src/core/lib/transport/service_config.c',
         'src/core/lib/transport/static_metadata.c',
         'src/core/lib/transport/timeout_encoding.c',
         'src/core/lib/transport/transport.c',
diff --git a/build.yaml b/build.yaml
index bdc282482f780db093f95aff9489db404f4a2de4..407b50ca7b91c66d242b225dbc78560f0c7b16c1 100644
--- a/build.yaml
+++ b/build.yaml
@@ -254,8 +254,8 @@ filegroups:
   - src/core/lib/transport/mdstr_hash_table.h
   - src/core/lib/transport/metadata.h
   - src/core/lib/transport/metadata_batch.h
-  - src/core/lib/transport/method_config.h
   - src/core/lib/transport/pid_controller.h
+  - src/core/lib/transport/service_config.h
   - src/core/lib/transport/static_metadata.h
   - src/core/lib/transport/timeout_encoding.h
   - src/core/lib/transport/transport.h
@@ -367,8 +367,8 @@ filegroups:
   - src/core/lib/transport/mdstr_hash_table.c
   - src/core/lib/transport/metadata.c
   - src/core/lib/transport/metadata_batch.c
-  - src/core/lib/transport/method_config.c
   - src/core/lib/transport/pid_controller.c
+  - src/core/lib/transport/service_config.c
   - src/core/lib/transport/static_metadata.c
   - src/core/lib/transport/timeout_encoding.c
   - src/core/lib/transport/transport.c
diff --git a/config.m4 b/config.m4
index 46e0109981a27faf2eed14f0464c2c7b9e0b2122..7f65b7c0d821ad49ebf06916c951938e8846baa2 100644
--- a/config.m4
+++ b/config.m4
@@ -187,8 +187,8 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/transport/mdstr_hash_table.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
-    src/core/lib/transport/method_config.c \
     src/core/lib/transport/pid_controller.c \
+    src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index bff342cc6615aa9a32e5a9e644ce110069e27955..c4ec01da931931de00cdf157fc9491aeba8d224a 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -336,8 +336,8 @@ Pod::Spec.new do |s|
                       'src/core/lib/transport/mdstr_hash_table.h',
                       'src/core/lib/transport/metadata.h',
                       'src/core/lib/transport/metadata_batch.h',
-                      'src/core/lib/transport/method_config.h',
                       'src/core/lib/transport/pid_controller.h',
+                      'src/core/lib/transport/service_config.h',
                       'src/core/lib/transport/static_metadata.h',
                       'src/core/lib/transport/timeout_encoding.h',
                       'src/core/lib/transport/transport.h',
@@ -529,8 +529,8 @@ Pod::Spec.new do |s|
                       'src/core/lib/transport/mdstr_hash_table.c',
                       'src/core/lib/transport/metadata.c',
                       'src/core/lib/transport/metadata_batch.c',
-                      'src/core/lib/transport/method_config.c',
                       'src/core/lib/transport/pid_controller.c',
+                      'src/core/lib/transport/service_config.c',
                       'src/core/lib/transport/static_metadata.c',
                       'src/core/lib/transport/timeout_encoding.c',
                       'src/core/lib/transport/transport.c',
@@ -739,8 +739,8 @@ Pod::Spec.new do |s|
                               'src/core/lib/transport/mdstr_hash_table.h',
                               'src/core/lib/transport/metadata.h',
                               'src/core/lib/transport/metadata_batch.h',
-                              'src/core/lib/transport/method_config.h',
                               'src/core/lib/transport/pid_controller.h',
+                              'src/core/lib/transport/service_config.h',
                               'src/core/lib/transport/static_metadata.h',
                               'src/core/lib/transport/timeout_encoding.h',
                               'src/core/lib/transport/transport.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 70708711783984108531452b02b7e1c575b1176d..e2a2ffc21a9460e10cd0f25ccf218193ea10743e 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -256,8 +256,8 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/transport/mdstr_hash_table.h )
   s.files += %w( src/core/lib/transport/metadata.h )
   s.files += %w( src/core/lib/transport/metadata_batch.h )
-  s.files += %w( src/core/lib/transport/method_config.h )
   s.files += %w( src/core/lib/transport/pid_controller.h )
+  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/timeout_encoding.h )
   s.files += %w( src/core/lib/transport/transport.h )
@@ -449,8 +449,8 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/transport/mdstr_hash_table.c )
   s.files += %w( src/core/lib/transport/metadata.c )
   s.files += %w( src/core/lib/transport/metadata_batch.c )
-  s.files += %w( src/core/lib/transport/method_config.c )
   s.files += %w( src/core/lib/transport/pid_controller.c )
+  s.files += %w( src/core/lib/transport/service_config.c )
   s.files += %w( src/core/lib/transport/static_metadata.c )
   s.files += %w( src/core/lib/transport/timeout_encoding.c )
   s.files += %w( src/core/lib/transport/transport.c )
diff --git a/include/grpc++/channel.h b/include/grpc++/channel.h
index c8360282e7b03c7979b3652f5b0329c85f0c0674..ca269be218fba5ed66d360310a9474607e723ea4 100644
--- a/include/grpc++/channel.h
+++ b/include/grpc++/channel.h
@@ -57,6 +57,13 @@ class Channel final : public ChannelInterface,
   /// \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 Status BlockingUnaryCall(ChannelInterface* channel,
diff --git a/include/grpc++/support/channel_arguments.h b/include/grpc++/support/channel_arguments.h
index 0c05f5478a93c8d12ab92c8e506ec3807d159a42..571ab5d53054489084c3c620b5c2bb40ec5ebb87 100644
--- a/include/grpc++/support/channel_arguments.h
+++ b/include/grpc++/support/channel_arguments.h
@@ -93,6 +93,10 @@ class ChannelArguments {
   /// 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);
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index 8c5215faee8899668722d87e5c9ceea204de25bb..9a862247952b8ced00638a38e0fd7b1ef4c5f40d 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -478,6 +478,9 @@ typedef struct {
   /* If non-NULL, will be set to point to a string indicating the LB
    * policy name.  Caller takes ownership. */
   char **lb_policy_name;
+  /* If non-NULL, will be set to point to a string containing the
+   * service config used by the channel in JSON form. */
+  char **service_config_json;
 } grpc_channel_info;
 
 typedef struct grpc_resource_quota grpc_resource_quota;
diff --git a/package.xml b/package.xml
index fdb449370c387ecd4b885afc5f7b983d036baf67..2822ac624d7b4f8f63d95460f844602c25a1fcd4 100644
--- a/package.xml
+++ b/package.xml
@@ -263,8 +263,8 @@
     <file baseinstalldir="/" name="src/core/lib/transport/mdstr_hash_table.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata_batch.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/transport/method_config.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/pid_controller.h" role="src" />
+    <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/timeout_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.h" role="src" />
@@ -456,8 +456,8 @@
     <file baseinstalldir="/" name="src/core/lib/transport/mdstr_hash_table.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata_batch.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/transport/method_config.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/pid_controller.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/service_config.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.c" role="src" />
diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c
index b66fed4b88a3ae49063fd8e113fb4492d61759fe..1fcff4388a55b582fea7549ae92f20cf90c6d6ec 100644
--- a/src/core/ext/client_channel/client_channel.c
+++ b/src/core/ext/client_channel/client_channel.c
@@ -56,7 +56,7 @@
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/metadata_batch.h"
-#include "src/core/lib/transport/method_config.h"
+#include "src/core/lib/transport/service_config.h"
 #include "src/core/lib/transport/static_metadata.h"
 
 /* Client channel implementation */
@@ -82,30 +82,61 @@ static void *method_parameters_copy(void *value) {
   return new_value;
 }
 
-static int method_parameters_cmp(void *value1, void *value2) {
-  const method_parameters *v1 = value1;
-  const method_parameters *v2 = value2;
-  const int retval = gpr_time_cmp(v1->timeout, v2->timeout);
-  if (retval != 0) return retval;
-  if (v1->wait_for_ready > v2->wait_for_ready) return 1;
-  if (v1->wait_for_ready < v2->wait_for_ready) return -1;
-  return 0;
-}
-
 static const grpc_mdstr_hash_table_vtable method_parameters_vtable = {
-    gpr_free, method_parameters_copy, method_parameters_cmp};
-
-static void *method_config_convert_value(
-    const grpc_method_config *method_config) {
+    gpr_free, method_parameters_copy};
+
+static void *method_parameters_create_from_json(const grpc_json *json) {
+  wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET;
+  gpr_timespec timeout = {0, 0, GPR_TIMESPAN};
+  for (grpc_json *field = json->child; field != NULL; field = field->next) {
+    if (field->key == NULL) continue;
+    if (strcmp(field->key, "waitForReady") == 0) {
+      if (wait_for_ready != WAIT_FOR_READY_UNSET) return NULL;  // Duplicate.
+      if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
+        return NULL;
+      }
+      wait_for_ready = field->type == GRPC_JSON_TRUE ? WAIT_FOR_READY_TRUE
+                                                     : WAIT_FOR_READY_FALSE;
+    } else if (strcmp(field->key, "timeout") == 0) {
+      if (timeout.tv_sec > 0 || timeout.tv_nsec > 0) return NULL;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING) return NULL;
+      size_t len = strlen(field->value);
+      if (field->value[len - 1] != 's') return NULL;
+      char *buf = gpr_strdup(field->value);
+      buf[len - 1] = '\0';  // Remove trailing 's'.
+      char *decimal_point = strchr(buf, '.');
+      if (decimal_point != NULL) {
+        *decimal_point = '\0';
+        timeout.tv_nsec = gpr_parse_nonnegative_int(decimal_point + 1);
+        if (timeout.tv_nsec == -1) {
+          gpr_free(buf);
+          return NULL;
+        }
+        // There should always be exactly 3, 6, or 9 fractional digits.
+        int multiplier = 1;
+        switch (strlen(decimal_point + 1)) {
+          case 9:
+            break;
+          case 6:
+            multiplier *= 1000;
+            break;
+          case 3:
+            multiplier *= 1000000;
+            break;
+          default:  // Unsupported number of digits.
+            gpr_free(buf);
+            return NULL;
+        }
+        timeout.tv_nsec *= multiplier;
+      }
+      timeout.tv_sec = gpr_parse_nonnegative_int(buf);
+      if (timeout.tv_sec == -1) return NULL;
+      gpr_free(buf);
+    }
+  }
   method_parameters *value = gpr_malloc(sizeof(method_parameters));
-  const gpr_timespec *timeout = grpc_method_config_get_timeout(method_config);
-  value->timeout = timeout != NULL ? *timeout : gpr_time_0(GPR_TIMESPAN);
-  const bool *wait_for_ready =
-      grpc_method_config_get_wait_for_ready(method_config);
-  value->wait_for_ready =
-      wait_for_ready == NULL
-          ? WAIT_FOR_READY_UNSET
-          : (wait_for_ready ? WAIT_FOR_READY_TRUE : WAIT_FOR_READY_FALSE);
+  value->timeout = timeout;
+  value->wait_for_ready = wait_for_ready;
   return value;
 }
 
@@ -126,6 +157,8 @@ typedef struct client_channel_channel_data {
   /** currently active load balancer */
   char *lb_policy_name;
   grpc_lb_policy *lb_policy;
+  /** service config in JSON form */
+  char *service_config_json;
   /** maps method names to method_parameters structs */
   grpc_mdstr_hash_table *method_params_table;
   /** incoming resolver result - set by resolver.next() */
@@ -232,15 +265,12 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
   grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE;
   bool exit_idle = false;
   grpc_error *state_error = GRPC_ERROR_CREATE("No load balancing policy");
+  char *service_config_json = NULL;
 
   if (chand->resolver_result != NULL) {
-    grpc_lb_policy_args lb_policy_args;
-    lb_policy_args.args = chand->resolver_result;
-    lb_policy_args.client_channel_factory = chand->client_channel_factory;
-
     // Find LB policy name.
     const grpc_arg *channel_arg =
-        grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_POLICY_NAME);
+        grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_POLICY_NAME);
     if (channel_arg != NULL) {
       GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
       lb_policy_name = channel_arg->value.string;
@@ -249,7 +279,7 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
     // assume that we should use the grpclb policy, regardless of what the
     // resolver actually specified.
     channel_arg =
-        grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_ADDRESSES);
+        grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_ADDRESSES);
     if (channel_arg != NULL) {
       GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER);
       grpc_lb_addresses *addresses = channel_arg->value.pointer.p;
@@ -274,7 +304,10 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
     // Use pick_first if nothing was specified and we didn't select grpclb
     // above.
     if (lb_policy_name == NULL) lb_policy_name = "pick_first";
-
+    // Instantiate LB policy.
+    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 =
         grpc_lb_policy_create(exec_ctx, lb_policy_name, &lb_policy_args);
     if (lb_policy != NULL) {
@@ -283,13 +316,20 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
       state =
           grpc_lb_policy_check_connectivity(exec_ctx, lb_policy, &state_error);
     }
+    // Find service config.
     channel_arg =
-        grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_SERVICE_CONFIG);
+        grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVICE_CONFIG);
     if (channel_arg != NULL) {
-      GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER);
-      method_params_table = grpc_method_config_table_convert(
-          (grpc_method_config_table *)channel_arg->value.pointer.p,
-          method_config_convert_value, &method_parameters_vtable);
+      GPR_ASSERT(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 != NULL) {
+        method_params_table = grpc_service_config_create_method_config_table(
+            service_config, method_parameters_create_from_json,
+            &method_parameters_vtable);
+        grpc_service_config_destroy(service_config);
+      }
     }
     // Before we clean up, save a copy of lb_policy_name, since it might
     // be pointing to data inside chand->resolver_result.
@@ -311,6 +351,10 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
   }
   old_lb_policy = chand->lb_policy;
   chand->lb_policy = lb_policy;
+  if (service_config_json != NULL) {
+    gpr_free(chand->service_config_json);
+    chand->service_config_json = service_config_json;
+  }
   if (chand->method_params_table != NULL) {
     grpc_mdstr_hash_table_unref(chand->method_params_table);
   }
@@ -446,6 +490,11 @@ static void cc_get_channel_info(grpc_exec_ctx *exec_ctx,
                                 ? NULL
                                 : gpr_strdup(chand->lb_policy_name);
   }
+  if (info->service_config_json != NULL) {
+    *info->service_config_json = chand->service_config_json == NULL
+                                     ? NULL
+                                     : gpr_strdup(chand->service_config_json);
+  }
   gpr_mu_unlock(&chand->mu);
 }
 
@@ -489,6 +538,7 @@ static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx,
     GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
   }
   gpr_free(chand->lb_policy_name);
+  gpr_free(chand->service_config_json);
   if (chand->method_params_table != NULL) {
     grpc_mdstr_hash_table_unref(chand->method_params_table);
   }
diff --git a/src/core/ext/client_channel/lb_policy_registry.c b/src/core/ext/client_channel/lb_policy_registry.c
index f46a721f9d4eda22ac9bde7105d9b9c8f14e2c7e..90c149d947b2e06c22ad00431c906d0b88f6db0c 100644
--- a/src/core/ext/client_channel/lb_policy_registry.c
+++ b/src/core/ext/client_channel/lb_policy_registry.c
@@ -35,6 +35,8 @@
 
 #include <string.h>
 
+#include "src/core/lib/support/string.h"
+
 #define MAX_POLICIES 10
 
 static grpc_lb_policy_factory *g_all_of_the_lb_policies[MAX_POLICIES];
@@ -52,8 +54,8 @@ void grpc_lb_policy_registry_shutdown(void) {
 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 != strcmp(factory->vtable->name,
-                           g_all_of_the_lb_policies[i]->vtable->name));
+    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);
@@ -66,7 +68,7 @@ static grpc_lb_policy_factory *lookup_factory(const char *name) {
   if (name == NULL) return NULL;
 
   for (i = 0; i < g_number_of_lb_policies; i++) {
-    if (0 == strcmp(name, g_all_of_the_lb_policies[i]->vtable->name)) {
+    if (0 == gpr_stricmp(name, g_all_of_the_lb_policies[i]->vtable->name)) {
       return g_all_of_the_lb_policies[i];
     }
   }
diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c
index 1331fe1c6592c2d567b7d7532805c7622485deb8..28ad587c0e48b57f47b32f4a15d5132c8c10cca3 100644
--- a/src/core/lib/channel/message_size_filter.c
+++ b/src/core/lib/channel/message_size_filter.c
@@ -39,7 +39,8 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/transport/method_config.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/service_config.h"
 
 #define DEFAULT_MAX_SEND_MESSAGE_LENGTH -1  // Unlimited.
 // The protobuf library will (by default) start warning at 100 megs.
@@ -56,30 +57,29 @@ static void* message_size_limits_copy(void* value) {
   return new_value;
 }
 
-static int message_size_limits_cmp(void* value1, void* value2) {
-  const message_size_limits* v1 = value1;
-  const message_size_limits* v2 = value2;
-  if (v1->max_send_size > v2->max_send_size) return 1;
-  if (v1->max_send_size < v2->max_send_size) return -1;
-  if (v1->max_recv_size > v2->max_recv_size) return 1;
-  if (v1->max_recv_size < v2->max_recv_size) return -1;
-  return 0;
-}
-
 static const grpc_mdstr_hash_table_vtable message_size_limits_vtable = {
-    gpr_free, message_size_limits_copy, message_size_limits_cmp};
+    gpr_free, message_size_limits_copy};
 
-static void* method_config_convert_value(
-    const grpc_method_config* method_config) {
+static void* message_size_limits_create_from_json(const grpc_json* json) {
+  int max_request_message_bytes = -1;
+  int max_response_message_bytes = -1;
+  for (grpc_json* field = json->child; field != NULL; field = field->next) {
+    if (field->key == NULL) continue;
+    if (strcmp(field->key, "maxRequestMessageBytes") == 0) {
+      if (max_request_message_bytes >= 0) return NULL;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING) return NULL;
+      max_request_message_bytes = gpr_parse_nonnegative_int(field->value);
+      if (max_request_message_bytes == -1) return NULL;
+    } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) {
+      if (max_response_message_bytes >= 0) return NULL;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING) return NULL;
+      max_response_message_bytes = gpr_parse_nonnegative_int(field->value);
+      if (max_response_message_bytes == -1) return NULL;
+    }
+  }
   message_size_limits* value = gpr_malloc(sizeof(message_size_limits));
-  const int32_t* max_request_message_bytes =
-      grpc_method_config_get_max_request_message_bytes(method_config);
-  value->max_send_size =
-      max_request_message_bytes != NULL ? *max_request_message_bytes : -1;
-  const int32_t* max_response_message_bytes =
-      grpc_method_config_get_max_response_message_bytes(method_config);
-  value->max_recv_size =
-      max_response_message_bytes != NULL ? *max_response_message_bytes : -1;
+  value->max_send_size = max_request_message_bytes;
+  value->max_recv_size = max_response_message_bytes;
   return value;
 }
 
@@ -223,10 +223,16 @@ static void init_channel_elem(grpc_exec_ctx* exec_ctx,
   const grpc_arg* channel_arg =
       grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG);
   if (channel_arg != NULL) {
-    GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER);
-    chand->method_limit_table = grpc_method_config_table_convert(
-        (grpc_method_config_table*)channel_arg->value.pointer.p,
-        method_config_convert_value, &message_size_limits_vtable);
+    GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING);
+    grpc_service_config* service_config =
+        grpc_service_config_create(channel_arg->value.string);
+    if (service_config != NULL) {
+      chand->method_limit_table =
+          grpc_service_config_create_method_config_table(
+              service_config, message_size_limits_create_from_json,
+              &message_size_limits_vtable);
+      grpc_service_config_destroy(service_config);
+    }
   }
 }
 
diff --git a/src/core/lib/json/json.c b/src/core/lib/json/json.c
index 5b583a1f2e0f235294d200e2c37fb50ca389cba0..48b13686d7b24fe0f183f9391ad33e64cb3e2c0c 100644
--- a/src/core/lib/json/json.c
+++ b/src/core/lib/json/json.c
@@ -37,15 +37,15 @@
 
 #include "src/core/lib/json/json.h"
 
-grpc_json *grpc_json_create(grpc_json_type type) {
-  grpc_json *json = gpr_malloc(sizeof(*json));
+grpc_json* grpc_json_create(grpc_json_type type) {
+  grpc_json* json = gpr_malloc(sizeof(*json));
   memset(json, 0, sizeof(*json));
   json->type = type;
 
   return json;
 }
 
-void grpc_json_destroy(grpc_json *json) {
+void grpc_json_destroy(grpc_json* json) {
   while (json->child) {
     grpc_json_destroy(json->child);
   }
diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h
index 681df4bb77da579245b8a2e47eccef2a2dfadb61..7111db0b52bd813fd1e344366b2f1355adf1be94 100644
--- a/src/core/lib/json/json.h
+++ b/src/core/lib/json/json.h
@@ -42,14 +42,14 @@
  * are not owned by it.
  */
 typedef struct grpc_json {
-  struct grpc_json *next;
-  struct grpc_json *prev;
-  struct grpc_json *child;
-  struct grpc_json *parent;
+  struct grpc_json* next;
+  struct grpc_json* prev;
+  struct grpc_json* child;
+  struct grpc_json* parent;
 
   grpc_json_type type;
-  const char *key;
-  const char *value;
+  const char* key;
+  const char* value;
 } grpc_json;
 
 /* The next two functions are going to parse the input string, and
@@ -65,8 +65,8 @@ typedef struct grpc_json {
  *
  * Delete the allocated tree afterward using grpc_json_destroy().
  */
-grpc_json *grpc_json_parse_string_with_len(char *input, size_t size);
-grpc_json *grpc_json_parse_string(char *input);
+grpc_json* grpc_json_parse_string_with_len(char* input, size_t size);
+grpc_json* grpc_json_parse_string(char* input);
 
 /* This function will create a new string using gpr_realloc, and will
  * deserialize the grpc_json tree into it. It'll be zero-terminated,
@@ -76,13 +76,13 @@ grpc_json *grpc_json_parse_string(char *input);
  * If indent is 0, then newlines will be suppressed as well, and the
  * output will be condensed at its maximum.
  */
-char *grpc_json_dump_to_string(grpc_json *json, int indent);
+char* grpc_json_dump_to_string(grpc_json* json, int indent);
 
 /* Use these to create or delete a grpc_json object.
  * Deletion is recursive. We will not attempt to free any of the strings
  * in any of the objects of that tree.
  */
-grpc_json *grpc_json_create(grpc_json_type type);
-void grpc_json_destroy(grpc_json *json);
+grpc_json* grpc_json_create(grpc_json_type type);
+void grpc_json_destroy(grpc_json* json);
 
 #endif /* GRPC_CORE_LIB_JSON_JSON_H */
diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c
index dc243bf0bf0f56e94c9550f1306c6cfb0155a0d8..f10a30f0fd5f7dd14d86e52011cf976ecb017a94 100644
--- a/src/core/lib/support/string.c
+++ b/src/core/lib/support/string.c
@@ -34,7 +34,9 @@
 #include "src/core/lib/support/string.h"
 
 #include <ctype.h>
+#include <limits.h>
 #include <stddef.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -189,6 +191,13 @@ int int64_ttoa(int64_t value, char *string) {
   return i;
 }
 
+int gpr_parse_nonnegative_int(const char *value) {
+  char *end;
+  long result = strtol(value, &end, 0);
+  if (*end != '\0' || result < 0 || result > INT_MAX) return -1;
+  return (int)result;
+}
+
 char *gpr_leftpad(const char *str, char flag, size_t length) {
   const size_t str_length = strlen(str);
   const size_t out_length = str_length > length ? str_length : length;
diff --git a/src/core/lib/support/string.h b/src/core/lib/support/string.h
index 13891d0b9e586571bca042de027eebce4cf36320..e933e2eb468c6b9818ceaf0d1b9901bd5cbe4f37 100644
--- a/src/core/lib/support/string.h
+++ b/src/core/lib/support/string.h
@@ -77,6 +77,9 @@ NOTE: This function ensures sufficient bit width even on Win x64,
 where long is 32bit is size.*/
 int int64_ttoa(int64_t value, char *output);
 
+// Parses a non-negative number from a value string.  Returns -1 on error.
+int gpr_parse_nonnegative_int(const char *value);
+
 /* Reverse a run of bytes */
 void gpr_reverse_bytes(char *str, int len);
 
diff --git a/src/core/lib/transport/mdstr_hash_table.c b/src/core/lib/transport/mdstr_hash_table.c
index 8e914c420b13a198b641a0065baaa38a9b14986f..3d01e56df7e7883cfc6d54d5573225ea54a483b9 100644
--- a/src/core/lib/transport/mdstr_hash_table.c
+++ b/src/core/lib/transport/mdstr_hash_table.c
@@ -41,7 +41,6 @@
 
 struct grpc_mdstr_hash_table {
   gpr_refcount refs;
-  size_t num_entries;
   size_t size;
   grpc_mdstr_hash_table_entry* entries;
 };
@@ -77,7 +76,6 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_create(
   grpc_mdstr_hash_table* table = gpr_malloc(sizeof(*table));
   memset(table, 0, sizeof(*table));
   gpr_ref_init(&table->refs, 1);
-  table->num_entries = num_entries;
   // Quadratic probing gets best performance when the table is no more
   // than half full.
   table->size = num_entries * 2;
@@ -96,7 +94,7 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table) {
   return table;
 }
 
-int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) {
+void grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) {
   if (table != NULL && gpr_unref(&table->refs)) {
     for (size_t i = 0; i < table->size; ++i) {
       grpc_mdstr_hash_table_entry* entry = &table->entries[i];
@@ -107,13 +105,7 @@ int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) {
     }
     gpr_free(table->entries);
     gpr_free(table);
-    return 1;
   }
-  return 0;
-}
-
-size_t grpc_mdstr_hash_table_num_entries(const grpc_mdstr_hash_table* table) {
-  return table->num_entries;
 }
 
 void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table,
@@ -123,35 +115,3 @@ void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table,
   if (idx == table->size) return NULL;  // Not found.
   return table->entries[idx].value;
 }
-
-int grpc_mdstr_hash_table_cmp(const grpc_mdstr_hash_table* table1,
-                              const grpc_mdstr_hash_table* table2) {
-  // Compare by num_entries.
-  if (table1->num_entries < table2->num_entries) return -1;
-  if (table1->num_entries > table2->num_entries) return 1;
-  for (size_t i = 0; i < table1->num_entries; ++i) {
-    grpc_mdstr_hash_table_entry* e1 = &table1->entries[i];
-    grpc_mdstr_hash_table_entry* e2 = &table2->entries[i];
-    // Compare keys by hash value.
-    if (e1->key->hash < e2->key->hash) return -1;
-    if (e1->key->hash > e2->key->hash) return 1;
-    // Compare by vtable (pointer equality).
-    if (e1->vtable < e2->vtable) return -1;
-    if (e1->vtable > e2->vtable) return 1;
-    // Compare values via vtable.
-    const int value_result = e1->vtable->compare_value(e1->value, e2->value);
-    if (value_result != 0) return value_result;
-  }
-  return 0;
-}
-
-void grpc_mdstr_hash_table_iterate(
-    const grpc_mdstr_hash_table* table,
-    void (*func)(const grpc_mdstr_hash_table_entry* entry, void* user_data),
-    void* user_data) {
-  for (size_t i = 0; i < table->size; ++i) {
-    if (table->entries[i].key != NULL) {
-      func(&table->entries[i], user_data);
-    }
-  }
-}
diff --git a/src/core/lib/transport/mdstr_hash_table.h b/src/core/lib/transport/mdstr_hash_table.h
index bceb4df93de7a1d050b2a4a77aed1ba9cbfb2e90..8982ec3a8dc01f305bc3849eff736f2cf47b56f6 100644
--- a/src/core/lib/transport/mdstr_hash_table.h
+++ b/src/core/lib/transport/mdstr_hash_table.h
@@ -51,7 +51,6 @@ typedef struct grpc_mdstr_hash_table grpc_mdstr_hash_table;
 typedef struct grpc_mdstr_hash_table_vtable {
   void (*destroy_value)(void* value);
   void* (*copy_value)(void* value);
-  int (*compare_value)(void* value1, void* value2);
 } grpc_mdstr_hash_table_vtable;
 
 typedef struct grpc_mdstr_hash_table_entry {
@@ -67,26 +66,11 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_create(
     size_t num_entries, grpc_mdstr_hash_table_entry* entries);
 
 grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table);
-/** Returns 1 when \a table is destroyed. */
-int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table);
-
-/** Returns the number of entries in \a table. */
-size_t grpc_mdstr_hash_table_num_entries(const grpc_mdstr_hash_table* table);
+void grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table);
 
 /** Returns the value from \a table associated with \a key.
     Returns NULL if \a key is not found. */
 void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table,
                                 const grpc_mdstr* key);
 
-/** Compares two hash tables.
-    The sort order is stable but undefined. */
-int grpc_mdstr_hash_table_cmp(const grpc_mdstr_hash_table* table1,
-                              const grpc_mdstr_hash_table* table2);
-
-/** Iterates over the entries in \a table, calling \a func for each entry. */
-void grpc_mdstr_hash_table_iterate(
-    const grpc_mdstr_hash_table* table,
-    void (*func)(const grpc_mdstr_hash_table_entry* entry, void* user_data),
-    void* user_data);
-
 #endif /* GRPC_CORE_LIB_TRANSPORT_MDSTR_HASH_TABLE_H */
diff --git a/src/core/lib/transport/method_config.c b/src/core/lib/transport/method_config.c
deleted file mode 100644
index 57d97700bfe4f1f7c07b5f9ee2d13d707833594e..0000000000000000000000000000000000000000
--- a/src/core/lib/transport/method_config.c
+++ /dev/null
@@ -1,340 +0,0 @@
-//
-// Copyright 2015, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-
-#include "src/core/lib/transport/method_config.h"
-
-#include <string.h>
-
-#include <grpc/impl/codegen/grpc_types.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/transport/mdstr_hash_table.h"
-#include "src/core/lib/transport/metadata.h"
-
-//
-// grpc_method_config
-//
-
-// bool vtable
-
-static void* bool_copy(void* valuep) {
-  bool value = *(bool*)valuep;
-  bool* new_value = gpr_malloc(sizeof(bool));
-  *new_value = value;
-  return new_value;
-}
-
-static int bool_cmp(void* v1, void* v2) {
-  bool b1 = *(bool*)v1;
-  bool b2 = *(bool*)v2;
-  if (!b1 && b2) return -1;
-  if (b1 && !b2) return 1;
-  return 0;
-}
-
-static grpc_mdstr_hash_table_vtable bool_vtable = {gpr_free, bool_copy,
-                                                   bool_cmp};
-
-// timespec vtable
-
-static void* timespec_copy(void* valuep) {
-  gpr_timespec value = *(gpr_timespec*)valuep;
-  gpr_timespec* new_value = gpr_malloc(sizeof(gpr_timespec));
-  *new_value = value;
-  return new_value;
-}
-
-static int timespec_cmp(void* v1, void* v2) {
-  return gpr_time_cmp(*(gpr_timespec*)v1, *(gpr_timespec*)v2);
-}
-
-static grpc_mdstr_hash_table_vtable timespec_vtable = {gpr_free, timespec_copy,
-                                                       timespec_cmp};
-
-// int32 vtable
-
-static void* int32_copy(void* valuep) {
-  int32_t value = *(int32_t*)valuep;
-  int32_t* new_value = gpr_malloc(sizeof(int32_t));
-  *new_value = value;
-  return new_value;
-}
-
-static int int32_cmp(void* v1, void* v2) {
-  int32_t i1 = *(int32_t*)v1;
-  int32_t i2 = *(int32_t*)v2;
-  if (i1 < i2) return -1;
-  if (i1 > i2) return 1;
-  return 0;
-}
-
-static grpc_mdstr_hash_table_vtable int32_vtable = {gpr_free, int32_copy,
-                                                    int32_cmp};
-
-// Hash table keys.
-#define GRPC_METHOD_CONFIG_WAIT_FOR_READY "grpc.wait_for_ready"  // bool
-#define GRPC_METHOD_CONFIG_TIMEOUT "grpc.timeout"                // gpr_timespec
-#define GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES \
-  "grpc.max_request_message_bytes"  // int32
-#define GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES \
-  "grpc.max_response_message_bytes"  // int32
-
-struct grpc_method_config {
-  grpc_mdstr_hash_table* table;
-  grpc_mdstr* wait_for_ready_key;
-  grpc_mdstr* timeout_key;
-  grpc_mdstr* max_request_message_bytes_key;
-  grpc_mdstr* max_response_message_bytes_key;
-};
-
-grpc_method_config* grpc_method_config_create(
-    bool* wait_for_ready, gpr_timespec* timeout,
-    int32_t* max_request_message_bytes, int32_t* max_response_message_bytes) {
-  grpc_method_config* method_config = gpr_malloc(sizeof(grpc_method_config));
-  memset(method_config, 0, sizeof(grpc_method_config));
-  method_config->wait_for_ready_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_WAIT_FOR_READY);
-  method_config->timeout_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_TIMEOUT);
-  method_config->max_request_message_bytes_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES);
-  method_config->max_response_message_bytes_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES);
-  grpc_mdstr_hash_table_entry entries[4];
-  size_t num_entries = 0;
-  if (wait_for_ready != NULL) {
-    entries[num_entries].key = method_config->wait_for_ready_key;
-    entries[num_entries].value = wait_for_ready;
-    entries[num_entries].vtable = &bool_vtable;
-    ++num_entries;
-  }
-  if (timeout != NULL) {
-    entries[num_entries].key = method_config->timeout_key;
-    entries[num_entries].value = timeout;
-    entries[num_entries].vtable = &timespec_vtable;
-    ++num_entries;
-  }
-  if (max_request_message_bytes != NULL) {
-    entries[num_entries].key = method_config->max_request_message_bytes_key;
-    entries[num_entries].value = max_request_message_bytes;
-    entries[num_entries].vtable = &int32_vtable;
-    ++num_entries;
-  }
-  if (max_response_message_bytes != NULL) {
-    entries[num_entries].key = method_config->max_response_message_bytes_key;
-    entries[num_entries].value = max_response_message_bytes;
-    entries[num_entries].vtable = &int32_vtable;
-    ++num_entries;
-  }
-  method_config->table = grpc_mdstr_hash_table_create(num_entries, entries);
-  return method_config;
-}
-
-grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) {
-  grpc_mdstr_hash_table_ref(method_config->table);
-  return method_config;
-}
-
-void grpc_method_config_unref(grpc_method_config* method_config) {
-  if (grpc_mdstr_hash_table_unref(method_config->table)) {
-    GRPC_MDSTR_UNREF(method_config->wait_for_ready_key);
-    GRPC_MDSTR_UNREF(method_config->timeout_key);
-    GRPC_MDSTR_UNREF(method_config->max_request_message_bytes_key);
-    GRPC_MDSTR_UNREF(method_config->max_response_message_bytes_key);
-    gpr_free(method_config);
-  }
-}
-
-int grpc_method_config_cmp(const grpc_method_config* method_config1,
-                           const grpc_method_config* method_config2) {
-  return grpc_mdstr_hash_table_cmp(method_config1->table,
-                                   method_config2->table);
-}
-
-const bool* grpc_method_config_get_wait_for_ready(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(method_config->table,
-                                   method_config->wait_for_ready_key);
-}
-
-const gpr_timespec* grpc_method_config_get_timeout(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(method_config->table,
-                                   method_config->timeout_key);
-}
-
-const int32_t* grpc_method_config_get_max_request_message_bytes(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(
-      method_config->table, method_config->max_request_message_bytes_key);
-}
-
-const int32_t* grpc_method_config_get_max_response_message_bytes(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(
-      method_config->table, method_config->max_response_message_bytes_key);
-}
-
-//
-// grpc_method_config_table
-//
-
-static void method_config_unref(void* valuep) {
-  grpc_method_config_unref(valuep);
-}
-
-static void* method_config_ref(void* valuep) {
-  return grpc_method_config_ref(valuep);
-}
-
-static int method_config_cmp(void* valuep1, void* valuep2) {
-  return grpc_method_config_cmp(valuep1, valuep2);
-}
-
-static const grpc_mdstr_hash_table_vtable method_config_table_vtable = {
-    method_config_unref, method_config_ref, method_config_cmp};
-
-grpc_method_config_table* grpc_method_config_table_create(
-    size_t num_entries, grpc_method_config_table_entry* entries) {
-  grpc_mdstr_hash_table_entry* hash_table_entries =
-      gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) * num_entries);
-  for (size_t i = 0; i < num_entries; ++i) {
-    hash_table_entries[i].key = entries[i].method_name;
-    hash_table_entries[i].value = entries[i].method_config;
-    hash_table_entries[i].vtable = &method_config_table_vtable;
-  }
-  grpc_method_config_table* method_config_table =
-      grpc_mdstr_hash_table_create(num_entries, hash_table_entries);
-  gpr_free(hash_table_entries);
-  return method_config_table;
-}
-
-grpc_method_config_table* grpc_method_config_table_ref(
-    grpc_method_config_table* table) {
-  return grpc_mdstr_hash_table_ref(table);
-}
-
-void grpc_method_config_table_unref(grpc_method_config_table* table) {
-  grpc_mdstr_hash_table_unref(table);
-}
-
-int grpc_method_config_table_cmp(const grpc_method_config_table* table1,
-                                 const grpc_method_config_table* table2) {
-  return grpc_mdstr_hash_table_cmp(table1, table2);
-}
-
-void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table,
-                                   const grpc_mdstr* path) {
-  void* value = grpc_mdstr_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 == NULL) {
-    const char* path_str = grpc_mdstr_as_c_string(path);
-    const char* sep = strrchr(path_str, '/') + 1;
-    const size_t len = (size_t)(sep - path_str);
-    char* buf = gpr_malloc(len + 2);  // '*' and NUL
-    memcpy(buf, path_str, len);
-    buf[len] = '*';
-    buf[len + 1] = '\0';
-    grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
-    gpr_free(buf);
-    value = grpc_mdstr_hash_table_get(table, wildcard_path);
-    GRPC_MDSTR_UNREF(wildcard_path);
-  }
-  return value;
-}
-
-static void* copy_arg(void* p) { return grpc_method_config_table_ref(p); }
-
-static void destroy_arg(void* p) { grpc_method_config_table_unref(p); }
-
-static int cmp_arg(void* p1, void* p2) {
-  return grpc_method_config_table_cmp(p1, p2);
-}
-
-static grpc_arg_pointer_vtable arg_vtable = {copy_arg, destroy_arg, cmp_arg};
-
-grpc_arg grpc_method_config_table_create_channel_arg(
-    grpc_method_config_table* table) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_SERVICE_CONFIG;
-  arg.value.pointer.p = table;
-  arg.value.pointer.vtable = &arg_vtable;
-  return arg;
-}
-
-// State used by convert_entry() below.
-typedef struct conversion_state {
-  void* (*convert_value)(const grpc_method_config* method_config);
-  const grpc_mdstr_hash_table_vtable* vtable;
-  size_t num_entries;
-  grpc_mdstr_hash_table_entry* entries;
-} conversion_state;
-
-// A function to be passed to grpc_mdstr_hash_table_iterate() to create
-// a copy of the entries.
-static void convert_entry(const grpc_mdstr_hash_table_entry* entry,
-                          void* user_data) {
-  conversion_state* state = user_data;
-  state->entries[state->num_entries].key = GRPC_MDSTR_REF(entry->key);
-  state->entries[state->num_entries].value = state->convert_value(entry->value);
-  state->entries[state->num_entries].vtable = state->vtable;
-  ++state->num_entries;
-}
-
-grpc_mdstr_hash_table* grpc_method_config_table_convert(
-    const grpc_method_config_table* table,
-    void* (*convert_value)(const grpc_method_config* method_config),
-    const grpc_mdstr_hash_table_vtable* vtable) {
-  // Create an array of the entries in the table with converted values.
-  conversion_state state;
-  state.convert_value = convert_value;
-  state.vtable = vtable;
-  state.num_entries = 0;
-  state.entries = gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) *
-                             grpc_mdstr_hash_table_num_entries(table));
-  grpc_mdstr_hash_table_iterate(table, convert_entry, &state);
-  // Create a new table based on the array we just constructed.
-  grpc_mdstr_hash_table* new_table =
-      grpc_mdstr_hash_table_create(state.num_entries, state.entries);
-  // Clean up the array.
-  for (size_t i = 0; i < state.num_entries; ++i) {
-    GRPC_MDSTR_UNREF(state.entries[i].key);
-    vtable->destroy_value(state.entries[i].value);
-  }
-  gpr_free(state.entries);
-  // Return the new table.
-  return new_table;
-}
diff --git a/src/core/lib/transport/method_config.h b/src/core/lib/transport/method_config.h
deleted file mode 100644
index 58fedd94366feeac2cde0b23af3fb23e7dfe6be3..0000000000000000000000000000000000000000
--- a/src/core/lib/transport/method_config.h
+++ /dev/null
@@ -1,136 +0,0 @@
-//
-// Copyright 2016, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-
-#ifndef GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H
-#define GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H
-
-#include <stdbool.h>
-
-#include <grpc/impl/codegen/gpr_types.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-#include "src/core/lib/transport/mdstr_hash_table.h"
-#include "src/core/lib/transport/metadata.h"
-
-/// Per-method configuration.
-typedef struct grpc_method_config grpc_method_config;
-
-/// Creates a grpc_method_config with the specified parameters.
-/// Any parameter may be NULL to indicate that the value is unset.
-///
-/// \a wait_for_ready indicates whether the client should wait until the
-/// request deadline for the channel to become ready, even if there is a
-/// temporary failure before the deadline while attempting to connect.
-///
-/// \a timeout indicates the timeout for calls.
-///
-/// \a max_request_message_bytes and \a max_response_message_bytes
-/// indicate the maximum sizes of the request (checked when sending) and
-/// response (checked when receiving) messages.
-grpc_method_config* grpc_method_config_create(
-    bool* wait_for_ready, gpr_timespec* timeout,
-    int32_t* max_request_message_bytes, int32_t* max_response_message_bytes);
-
-grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config);
-void grpc_method_config_unref(grpc_method_config* method_config);
-
-/// Compares two grpc_method_configs.
-/// The sort order is stable but undefined.
-int grpc_method_config_cmp(const grpc_method_config* method_config1,
-                           const grpc_method_config* method_config2);
-
-/// These methods return NULL if the requested field is unset.
-/// The caller does NOT take ownership of the result.
-const bool* grpc_method_config_get_wait_for_ready(
-    const grpc_method_config* method_config);
-const gpr_timespec* grpc_method_config_get_timeout(
-    const grpc_method_config* method_config);
-const int32_t* grpc_method_config_get_max_request_message_bytes(
-    const grpc_method_config* method_config);
-const int32_t* grpc_method_config_get_max_response_message_bytes(
-    const grpc_method_config* method_config);
-
-/// A table of method configs.
-typedef grpc_mdstr_hash_table grpc_method_config_table;
-
-typedef struct grpc_method_config_table_entry {
-  /// The name is of one of the following forms:
-  ///   service/method -- specifies exact service and method name
-  ///   service/*      -- matches all methods for the specified service
-  grpc_mdstr* method_name;
-  grpc_method_config* method_config;
-} grpc_method_config_table_entry;
-
-/// Takes new references to all keys and values in \a entries.
-grpc_method_config_table* grpc_method_config_table_create(
-    size_t num_entries, grpc_method_config_table_entry* entries);
-
-grpc_method_config_table* grpc_method_config_table_ref(
-    grpc_method_config_table* table);
-void grpc_method_config_table_unref(grpc_method_config_table* table);
-
-/// Compares two grpc_method_config_tables.
-/// The sort order is stable but undefined.
-int grpc_method_config_table_cmp(const grpc_method_config_table* table1,
-                                 const grpc_method_config_table* table2);
-
-/// 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.
-///
-/// Note: This returns a void* instead of a grpc_method_config* so that
-/// it can also be used for tables constructed via
-/// grpc_method_config_table_convert().
-void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table,
-                                   const grpc_mdstr* path);
-
-/// Returns a channel arg containing \a table.
-grpc_arg grpc_method_config_table_create_channel_arg(
-    grpc_method_config_table* table);
-
-/// Generates a new table from \a table whose values are converted to a
-/// new form via the \a convert_value function.  The new table will use
-/// \a vtable for its values.
-///
-/// This is generally used to convert the table's value type from
-/// grpc_method_config to a simple struct containing only the parameters
-/// relevant to a particular filter, thus avoiding the need for a hash
-/// table lookup on the fast path.  In that scenario, \a convert_value
-/// will return a new instance of the struct containing the values from
-/// the grpc_method_config, and \a vtable provides the methods for
-/// operating on the struct type.
-grpc_mdstr_hash_table* grpc_method_config_table_convert(
-    const grpc_method_config_table* table,
-    void* (*convert_value)(const grpc_method_config* method_config),
-    const grpc_mdstr_hash_table_vtable* vtable);
-
-#endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */
diff --git a/src/core/lib/transport/service_config.c b/src/core/lib/transport/service_config.c
new file mode 100644
index 0000000000000000000000000000000000000000..2e2b59e3f760c7b2fdd5103653bb945734248cb7
--- /dev/null
+++ b/src/core/lib/transport/service_config.c
@@ -0,0 +1,249 @@
+//
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "src/core/lib/transport/service_config.h"
+
+#include <string.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/mdstr_hash_table.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",
+//     }
+//   ]
+// }
+
+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 = 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 == NULL) {
+    gpr_log(GPR_INFO, "failed to parse JSON for service config");
+    gpr_free(service_config->json_string);
+    gpr_free(service_config);
+    return NULL;
+  }
+  return service_config;
+}
+
+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);
+}
+
+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 != NULL) return NULL;
+  const char* lb_policy_name = NULL;
+  for (grpc_json* field = json->child; field != NULL; field = field->next) {
+    if (field->key == NULL) return NULL;
+    if (strcmp(field->key, "loadBalancingPolicy") == 0) {
+      if (lb_policy_name != NULL) return NULL;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING) return NULL;
+      lb_policy_name = field->value;
+    }
+  }
+  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 num_names = 0;
+  for (grpc_json* field = json->child; field != NULL; field = field->next) {
+    if (field->key != NULL && strcmp(field->key, "name") == 0) ++num_names;
+  }
+  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) {
+  if (json->type != GRPC_JSON_OBJECT) return NULL;
+  const char* service_name = NULL;
+  const char* method_name = NULL;
+  for (grpc_json* child = json->child; child != NULL; child = child->next) {
+    if (child->key == NULL) return NULL;
+    if (child->type != GRPC_JSON_STRING) return NULL;
+    if (strcmp(child->key, "service") == 0) {
+      if (service_name != NULL) return NULL;  // Duplicate.
+      if (child->value == NULL) return NULL;
+      service_name = child->value;
+    } else if (strcmp(child->key, "method") == 0) {
+      if (method_name != NULL) return NULL;  // Duplicate.
+      if (child->value == NULL) return NULL;
+      method_name = child->value;
+    }
+  }
+  if (service_name == NULL) return NULL;  // Required field.
+  char* path;
+  gpr_asprintf(&path, "/%s/%s", service_name,
+               method_name == NULL ? "*" : method_name);
+  return 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),
+    const grpc_mdstr_hash_table_vtable* vtable,
+    grpc_mdstr_hash_table_entry* entries, size_t* idx) {
+  // Construct value.
+  void* method_config = create_value(json);
+  if (method_config == NULL) return false;
+  // Construct list of paths.
+  bool success = false;
+  gpr_strvec paths;
+  gpr_strvec_init(&paths);
+  for (grpc_json* child = json->child; child != NULL; child = child->next) {
+    if (child->key == NULL) continue;
+    if (strcmp(child->key, "name") == 0) {
+      if (child->type != GRPC_JSON_ARRAY) goto done;
+      for (grpc_json* name = child->child; name != NULL; name = name->next) {
+        char* path = parse_json_method_name(name);
+        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_mdstr_from_string(paths.strs[i]);
+    entries[*idx].value = vtable->copy_value(method_config);
+    entries[*idx].vtable = vtable;
+    ++*idx;
+  }
+  success = true;
+done:
+  vtable->destroy_value(method_config);
+  gpr_strvec_destroy(&paths);
+  return success;
+}
+
+grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
+    const grpc_service_config* service_config,
+    void* (*create_value)(const grpc_json* method_config_json),
+    const grpc_mdstr_hash_table_vtable* vtable) {
+  const grpc_json* json = service_config->json_tree;
+  // Traverse parsed JSON tree.
+  if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL;
+  size_t num_entries = 0;
+  grpc_mdstr_hash_table_entry* entries = NULL;
+  for (grpc_json* field = json->child; field != NULL; field = field->next) {
+    if (field->key == NULL) return NULL;
+    if (strcmp(field->key, "methodConfig") == 0) {
+      if (entries != NULL) return NULL;  // Duplicate.
+      if (field->type != GRPC_JSON_ARRAY) return NULL;
+      // Find number of entries.
+      for (grpc_json* method = field->child; method != NULL;
+           method = method->next) {
+        num_entries += count_names_in_method_config_json(method);
+      }
+      // Populate method config table entries.
+      entries = gpr_malloc(num_entries * sizeof(grpc_mdstr_hash_table_entry));
+      size_t idx = 0;
+      for (grpc_json* method = field->child; method != NULL;
+           method = method->next) {
+        if (!parse_json_method_config(method, create_value, vtable, entries,
+                                      &idx)) {
+          return NULL;
+        }
+      }
+      GPR_ASSERT(idx == num_entries);
+    }
+  }
+  // Instantiate method config table.
+  grpc_mdstr_hash_table* method_config_table = NULL;
+  if (entries != NULL) {
+    method_config_table = grpc_mdstr_hash_table_create(num_entries, entries);
+    // Clean up.
+    for (size_t i = 0; i < num_entries; ++i) {
+      GRPC_MDSTR_UNREF(entries[i].key);
+      vtable->destroy_value(entries[i].value);
+    }
+    gpr_free(entries);
+  }
+  return method_config_table;
+}
+
+void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table,
+                                   const grpc_mdstr* path) {
+  void* value = grpc_mdstr_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 == NULL) {
+    const char* path_str = grpc_mdstr_as_c_string(path);
+    const char* sep = strrchr(path_str, '/') + 1;
+    const size_t len = (size_t)(sep - path_str);
+    char* buf = gpr_malloc(len + 2);  // '*' and NUL
+    memcpy(buf, path_str, len);
+    buf[len] = '*';
+    buf[len + 1] = '\0';
+    grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
+    gpr_free(buf);
+    value = grpc_mdstr_hash_table_get(table, wildcard_path);
+    GRPC_MDSTR_UNREF(wildcard_path);
+  }
+  return value;
+}
diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ffe4751938f7e14ba9843e5b38269388d100973
--- /dev/null
+++ b/src/core/lib/transport/service_config.h
@@ -0,0 +1,70 @@
+//
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
+#define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/transport/mdstr_hash_table.h"
+
+typedef struct grpc_service_config grpc_service_config;
+
+grpc_service_config* grpc_service_config_create(const char* json_string);
+void grpc_service_config_destroy(grpc_service_config* service_config);
+
+/// 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);
+
+/// 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 vtable provides methods used to manage the values.
+/// Returns NULL on error.
+grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
+    const grpc_service_config* service_config,
+    void* (*create_value)(const grpc_json* method_config_json),
+    const grpc_mdstr_hash_table_vtable* vtable);
+
+/// 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_mdstr_hash_table* table,
+                                   const grpc_mdstr* path);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */
diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc
index 5f1d00d2b49708fc2161ccaa85198c89e8d045b8..357d8317adfc8eed1744d7e9221396f918778c9f 100644
--- a/src/cpp/client/channel_cc.cc
+++ b/src/cpp/client/channel_cc.cc
@@ -48,6 +48,7 @@
 #include <grpc++/support/time.h>
 #include <grpc/grpc.h>
 #include <grpc/slice.h>
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include "src/core/lib/profiling/timers.h"
 
@@ -61,6 +62,35 @@ Channel::Channel(const grpc::string& host, grpc_channel* channel)
 
 Channel::~Channel() { grpc_channel_destroy(c_channel_); }
 
+namespace {
+
+grpc::string GetChannelInfoField(grpc_channel* channel,
+                                 grpc_channel_info* channel_info,
+                                 char*** channel_info_field) {
+  char* value = NULL;
+  memset(channel_info, 0, sizeof(*channel_info));
+  *channel_info_field = &value;
+  grpc_channel_get_info(channel, channel_info);
+  if (value == NULL) return "";
+  grpc::string result = value;
+  gpr_free(value);
+  return result;
+}
+
+}  // namespace
+
+grpc::string Channel::GetLoadBalancingPolicyName() const {
+  grpc_channel_info channel_info;
+  return GetChannelInfoField(c_channel_, &channel_info,
+                             &channel_info.lb_policy_name);
+}
+
+grpc::string Channel::GetServiceConfigJSON() const {
+  grpc_channel_info channel_info;
+  return GetChannelInfoField(c_channel_, &channel_info,
+                             &channel_info.service_config_json);
+}
+
 Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
                          CompletionQueue* cq) {
   const bool kRegistered = method.channel_tag() && context->authority().empty();
diff --git a/src/cpp/common/channel_arguments.cc b/src/cpp/common/channel_arguments.cc
index 0067d6c5e1e6708985c50c4a3d2886b478e8dbe3..0301b5b8ec813f8baa4d47b0d626a6f5955ceb7f 100644
--- a/src/cpp/common/channel_arguments.cc
+++ b/src/cpp/common/channel_arguments.cc
@@ -143,6 +143,11 @@ void ChannelArguments::SetLoadBalancingPolicyName(
   SetString(GRPC_ARG_LB_POLICY_NAME, lb_policy_name);
 }
 
+void ChannelArguments::SetServiceConfigJSON(
+    const grpc::string& service_config_json) {
+  SetString(GRPC_ARG_SERVICE_CONFIG, service_config_json);
+}
+
 void ChannelArguments::SetInt(const grpc::string& key, int value) {
   grpc_arg arg;
   arg.type = GRPC_ARG_INTEGER;
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 5affff10f539ef5c35f0d2af87f2a2669e2b8c1c..bfd706a78bc978d3e65d977a6b477871dbaa2b04 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -181,8 +181,8 @@ CORE_SOURCE_FILES = [
   'src/core/lib/transport/mdstr_hash_table.c',
   'src/core/lib/transport/metadata.c',
   'src/core/lib/transport/metadata_batch.c',
-  'src/core/lib/transport/method_config.c',
   'src/core/lib/transport/pid_controller.c',
+  'src/core/lib/transport/service_config.c',
   'src/core/lib/transport/static_metadata.c',
   'src/core/lib/transport/timeout_encoding.c',
   'src/core/lib/transport/transport.c',
diff --git a/test/core/client_channel/lb_policies_test.c b/test/core/client_channel/lb_policies_test.c
index c373505dd769b77637c8cc8d3f98b5d443fb5346..95595d7c51c160540c20f5dec7d4c042e4c9f4e6 100644
--- a/test/core/client_channel/lb_policies_test.c
+++ b/test/core/client_channel/lb_policies_test.c
@@ -43,6 +43,7 @@
 
 #include "src/core/ext/client_channel/client_channel.h"
 #include "src/core/ext/client_channel/lb_policy_registry.h"
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/channel.h"
@@ -522,7 +523,7 @@ static grpc_channel *create_client(const servers_fixture *f) {
   arg_array[0].value.integer = RETRY_TIMEOUT;
   arg_array[1].type = GRPC_ARG_STRING;
   arg_array[1].key = GRPC_ARG_LB_POLICY_NAME;
-  arg_array[1].value.string = "round_robin";
+  arg_array[1].value.string = "ROUND_ROBIN";
   args.num_args = 2;
   args.args = arg_array;
 
@@ -611,29 +612,43 @@ static void test_pending_calls(size_t concurrent_calls) {
 }
 
 static void test_get_channel_info() {
-  grpc_channel_args args;
-  grpc_arg arg_array[1];
-  arg_array[0].type = GRPC_ARG_STRING;
-  arg_array[0].key = GRPC_ARG_LB_POLICY_NAME;
-  arg_array[0].value.string = "round_robin";
-  args.num_args = 1;
-  args.args = arg_array;
-
   grpc_channel *channel =
-      grpc_insecure_channel_create("ipv4:127.0.0.1:1234", &args, NULL);
+      grpc_insecure_channel_create("ipv4:127.0.0.1:1234", NULL, NULL);
   // Ensures that resolver returns.
   grpc_channel_check_connectivity_state(channel, true /* try_to_connect */);
-  // Use grpc_channel_get_info() to get LB policy name.
-  char *lb_policy_name = NULL;
+  // First, request no fields.  This is a no-op.
   grpc_channel_info channel_info;
+  memset(&channel_info, 0, sizeof(channel_info));
+  grpc_channel_get_info(channel, &channel_info);
+  // Request LB policy name.
+  char *lb_policy_name = NULL;
   channel_info.lb_policy_name = &lb_policy_name;
   grpc_channel_get_info(channel, &channel_info);
   GPR_ASSERT(lb_policy_name != NULL);
-  GPR_ASSERT(strcmp(lb_policy_name, "round_robin") == 0);
+  GPR_ASSERT(strcmp(lb_policy_name, "pick_first") == 0);
   gpr_free(lb_policy_name);
-  // Try again without requesting anything.  This is a no-op.
-  channel_info.lb_policy_name = NULL;
+  // Request service config, which does not exist, so we'll get nothing back.
+  memset(&channel_info, 0, sizeof(channel_info));
+  char *service_config_json = "dummy_string";
+  channel_info.service_config_json = &service_config_json;
+  grpc_channel_get_info(channel, &channel_info);
+  GPR_ASSERT(service_config_json == NULL);
+  // Recreate the channel such that it has a service config.
+  grpc_channel_destroy(channel);
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = GRPC_ARG_SERVICE_CONFIG;
+  arg.value.string = "{\"loadBalancingPolicy\": \"ROUND_ROBIN\"}";
+  grpc_channel_args *args = grpc_channel_args_copy_and_add(NULL, &arg, 1);
+  channel = grpc_insecure_channel_create("ipv4:127.0.0.1:1234", args, NULL);
+  grpc_channel_args_destroy(args);
+  // Ensures that resolver returns.
+  grpc_channel_check_connectivity_state(channel, true /* try_to_connect */);
+  // Now request the service config again.
   grpc_channel_get_info(channel, &channel_info);
+  GPR_ASSERT(service_config_json != NULL);
+  GPR_ASSERT(strcmp(service_config_json, arg.value.string) == 0);
+  gpr_free(service_config_json);
   // Clean up.
   grpc_channel_destroy(channel);
 }
diff --git a/test/core/end2end/connection_refused_test.c b/test/core/end2end/connection_refused_test.c
index 13414c0378bb731c5f1d40d620e36b035b4831db..728d6dca5900bf4fb098e3f401731479416b83db 100644
--- a/test/core/end2end/connection_refused_test.c
+++ b/test/core/end2end/connection_refused_test.c
@@ -41,7 +41,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/transport/metadata.h"
-#include "src/core/lib/transport/method_config.h"
+#include "src/core/lib/transport/service_config.h"
 
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/util/port.h"
@@ -76,18 +76,19 @@ static void run_test(bool wait_for_ready, bool use_service_config) {
   grpc_channel_args *args = NULL;
   if (use_service_config) {
     GPR_ASSERT(wait_for_ready);
-    grpc_method_config_table_entry entry = {
-        grpc_mdstr_from_string("/service/method"),
-        grpc_method_config_create(&wait_for_ready, NULL, NULL, NULL),
-    };
-    grpc_method_config_table *method_config_table =
-        grpc_method_config_table_create(1, &entry);
-    GRPC_MDSTR_UNREF(entry.method_name);
-    grpc_method_config_unref(entry.method_config);
-    grpc_arg arg =
-        grpc_method_config_table_create_channel_arg(method_config_table);
+    grpc_arg arg;
+    arg.type = GRPC_ARG_STRING;
+    arg.key = GRPC_ARG_SERVICE_CONFIG;
+    arg.value.string =
+        "{\n"
+        "  \"methodConfig\": [ {\n"
+        "    \"name\": [\n"
+        "      { \"service\": \"service\", \"method\": \"method\" }\n"
+        "    ],\n"
+        "    \"waitForReady\": true\n"
+        "  } ]\n"
+        "}";
     args = grpc_channel_args_copy_and_add(args, &arg, 1);
-    grpc_method_config_table_unref(method_config_table);
   }
 
   /* create a call, channel to a port which will refuse connection */
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
index 428a2d1c3ca05527a59d219783116617198e9bdf..e582c59f2d37de76a1db72b79804befff7ca7a2f 100644
--- a/test/core/end2end/tests/cancel_after_accept.c
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -44,7 +44,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/transport/metadata.h"
-#include "src/core/lib/transport/method_config.h"
+#include "src/core/lib/transport/service_config.h"
 
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/tests/cancel_test_helpers.h"
@@ -134,19 +134,19 @@ static void test_cancel_after_accept(grpc_end2end_test_config config,
 
   grpc_channel_args *args = NULL;
   if (use_service_config) {
-    gpr_timespec timeout = {5, 0, GPR_TIMESPAN};
-    grpc_method_config_table_entry entry = {
-        grpc_mdstr_from_string("/service/method"),
-        grpc_method_config_create(NULL, &timeout, NULL, NULL),
-    };
-    grpc_method_config_table *method_config_table =
-        grpc_method_config_table_create(1, &entry);
-    GRPC_MDSTR_UNREF(entry.method_name);
-    grpc_method_config_unref(entry.method_config);
-    grpc_arg arg =
-        grpc_method_config_table_create_channel_arg(method_config_table);
+    grpc_arg arg;
+    arg.type = GRPC_ARG_STRING;
+    arg.key = GRPC_ARG_SERVICE_CONFIG;
+    arg.value.string =
+        "{\n"
+        "  \"methodConfig\": [ {\n"
+        "    \"name\": [\n"
+        "      { \"service\": \"service\", \"method\": \"method\" }\n"
+        "    ],\n"
+        "    \"timeout\": \"5s\"\n"
+        "  } ]\n"
+        "}";
     args = grpc_channel_args_copy_and_add(args, &arg, 1);
-    grpc_method_config_table_unref(method_config_table);
   }
 
   grpc_end2end_test_fixture f =
diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c
index 0ddd7bed15be6bc0ed5af5b773c19665ebf1cd27..c222d9dfae7053ae58b3fa32b5edcca614ab6fd7 100644
--- a/test/core/end2end/tests/max_message_length.c
+++ b/test/core/end2end/tests/max_message_length.c
@@ -44,7 +44,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/transport/metadata.h"
-#include "src/core/lib/transport/method_config.h"
+#include "src/core/lib/transport/service_config.h"
 
 #include "test/core/end2end/cq_verifier.h"
 
@@ -138,19 +138,19 @@ static void test_max_message_length_on_request(grpc_end2end_test_config config,
   if (use_service_config) {
     // We don't currently support service configs on the server side.
     GPR_ASSERT(send_limit);
-    int32_t max_request_message_bytes = 5;
-    grpc_method_config_table_entry entry = {
-        grpc_mdstr_from_string("/service/method"),
-        grpc_method_config_create(NULL, NULL, &max_request_message_bytes, NULL),
-    };
-    grpc_method_config_table *method_config_table =
-        grpc_method_config_table_create(1, &entry);
-    GRPC_MDSTR_UNREF(entry.method_name);
-    grpc_method_config_unref(entry.method_config);
-    grpc_arg arg =
-        grpc_method_config_table_create_channel_arg(method_config_table);
+    grpc_arg arg;
+    arg.type = GRPC_ARG_STRING;
+    arg.key = GRPC_ARG_SERVICE_CONFIG;
+    arg.value.string =
+        "{\n"
+        "  \"methodConfig\": [ {\n"
+        "    \"name\": [\n"
+        "      { \"service\": \"service\", \"method\": \"method\" }\n"
+        "    ],\n"
+        "    \"maxRequestMessageBytes\": \"5\"\n"
+        "  } ]\n"
+        "}";
     client_args = grpc_channel_args_copy_and_add(NULL, &arg, 1);
-    grpc_method_config_table_unref(method_config_table);
   } else {
     // Set limit via channel args.
     grpc_arg arg;
@@ -312,20 +312,19 @@ static void test_max_message_length_on_response(grpc_end2end_test_config config,
   if (use_service_config) {
     // We don't currently support service configs on the server side.
     GPR_ASSERT(!send_limit);
-    int32_t max_response_message_bytes = 5;
-    grpc_method_config_table_entry entry = {
-        grpc_mdstr_from_string("/service/method"),
-        grpc_method_config_create(NULL, NULL, NULL,
-                                  &max_response_message_bytes),
-    };
-    grpc_method_config_table *method_config_table =
-        grpc_method_config_table_create(1, &entry);
-    GRPC_MDSTR_UNREF(entry.method_name);
-    grpc_method_config_unref(entry.method_config);
-    grpc_arg arg =
-        grpc_method_config_table_create_channel_arg(method_config_table);
+    grpc_arg arg;
+    arg.type = GRPC_ARG_STRING;
+    arg.key = GRPC_ARG_SERVICE_CONFIG;
+    arg.value.string =
+        "{\n"
+        "  \"methodConfig\": [ {\n"
+        "    \"name\": [\n"
+        "      { \"service\": \"service\", \"method\": \"method\" }\n"
+        "    ],\n"
+        "    \"maxResponseMessageBytes\": \"5\"\n"
+        "  } ]\n"
+        "}";
     client_args = grpc_channel_args_copy_and_add(NULL, &arg, 1);
-    grpc_method_config_table_unref(method_config_table);
   } else {
     // Set limit via channel args.
     grpc_arg arg;
diff --git a/test/cpp/end2end/round_robin_end2end_test.cc b/test/cpp/end2end/round_robin_end2end_test.cc
index 76211cbdd3e742cd827f772f4ed6b324cffa2066..cc340b96b34668ade39a675e3e71bc692f80c2e5 100644
--- a/test/cpp/end2end/round_robin_end2end_test.cc
+++ b/test/cpp/end2end/round_robin_end2end_test.cc
@@ -109,9 +109,9 @@ class RoundRobinEnd2endTest : public ::testing::Test {
       uri << "127.0.0.1:" << servers_[i]->port_ << ",";
     }
     uri << "127.0.0.1:" << servers_[servers_.size() - 1]->port_;
-    std::shared_ptr<Channel> channel =
+    channel_ =
         CreateCustomChannel(uri.str(), InsecureChannelCredentials(), args);
-    stub_ = grpc::testing::EchoTestService::NewStub(channel);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
   }
 
   void SendRpc(int num_rpcs) {
@@ -165,6 +165,7 @@ class RoundRobinEnd2endTest : public ::testing::Test {
 
   const grpc::string server_host_;
   CompletionQueue cli_cq_;
+  std::shared_ptr<Channel> channel_;
   std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::vector<std::unique_ptr<ServerData>> servers_;
 };
@@ -186,6 +187,8 @@ TEST_F(RoundRobinEnd2endTest, PickFirst) {
     }
   }
   EXPECT_TRUE(found);
+  // Check LB policy name for the channel.
+  EXPECT_EQ("pick_first", channel_->GetLoadBalancingPolicyName());
 }
 
 TEST_F(RoundRobinEnd2endTest, RoundRobin) {
@@ -198,6 +201,8 @@ TEST_F(RoundRobinEnd2endTest, RoundRobin) {
   for (size_t i = 0; i < servers_.size(); ++i) {
     EXPECT_EQ(1, servers_[i]->service_.request_count());
   }
+  // Check LB policy name for the channel.
+  EXPECT_EQ("round_robin", channel_->GetLoadBalancingPolicyName());
 }
 
 }  // namespace
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 2dd50b073c52859cf863f72049c57d9e46601c57..a629bdc542123c7da3b93e4f17287f0f2511d1bf 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -879,8 +879,8 @@ src/core/lib/transport/connectivity_state.h \
 src/core/lib/transport/mdstr_hash_table.h \
 src/core/lib/transport/metadata.h \
 src/core/lib/transport/metadata_batch.h \
-src/core/lib/transport/method_config.h \
 src/core/lib/transport/pid_controller.h \
+src/core/lib/transport/service_config.h \
 src/core/lib/transport/static_metadata.h \
 src/core/lib/transport/timeout_encoding.h \
 src/core/lib/transport/transport.h \
@@ -1072,8 +1072,8 @@ src/core/lib/transport/connectivity_state.c \
 src/core/lib/transport/mdstr_hash_table.c \
 src/core/lib/transport/metadata.c \
 src/core/lib/transport/metadata_batch.c \
-src/core/lib/transport/method_config.c \
 src/core/lib/transport/pid_controller.c \
+src/core/lib/transport/service_config.c \
 src/core/lib/transport/static_metadata.c \
 src/core/lib/transport/timeout_encoding.c \
 src/core/lib/transport/transport.c \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 1815b4b691881b7d21d630fead573ab62aec3ed5..9be07941d75278fcdf5aa0f1fb474690f092dd1f 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -6761,8 +6761,8 @@
       "src/core/lib/transport/mdstr_hash_table.h", 
       "src/core/lib/transport/metadata.h", 
       "src/core/lib/transport/metadata_batch.h", 
-      "src/core/lib/transport/method_config.h", 
       "src/core/lib/transport/pid_controller.h", 
+      "src/core/lib/transport/service_config.h", 
       "src/core/lib/transport/static_metadata.h", 
       "src/core/lib/transport/timeout_encoding.h", 
       "src/core/lib/transport/transport.h", 
@@ -6978,10 +6978,10 @@
       "src/core/lib/transport/metadata.h", 
       "src/core/lib/transport/metadata_batch.c", 
       "src/core/lib/transport/metadata_batch.h", 
-      "src/core/lib/transport/method_config.c", 
-      "src/core/lib/transport/method_config.h", 
       "src/core/lib/transport/pid_controller.c", 
       "src/core/lib/transport/pid_controller.h", 
+      "src/core/lib/transport/service_config.c", 
+      "src/core/lib/transport/service_config.h", 
       "src/core/lib/transport/static_metadata.c", 
       "src/core/lib/transport/static_metadata.h", 
       "src/core/lib/transport/timeout_encoding.c", 
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 2aa00df8bdc0a6d3acffb758839e9f384d9d3384..81b43f7a87ee35ae9fb4c5ae7e5ce428127bfadd 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -388,8 +388,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\method_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
@@ -690,10 +690,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\method_config.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\service_config.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 4e51d7ef97e1ed7a952f45704fd9f31d85eccef2..2f4a659aaa0e1045a9ecf4d2e6a4999e6f1633cf 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -322,10 +322,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\method_config.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\service_config.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
@@ -1001,10 +1001,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\method_config.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
index 71953a98f7512730bd3864f96bf833d603b931b5..2acdd32cf3ec3ddf92d211a3f1180e484b6ae50e 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
@@ -281,8 +281,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\method_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
@@ -541,10 +541,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\method_config.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\service_config.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
index 139973edbc34f6f39716a757a57614af12b855fe..6c918f125463d4dbc6df7fe589ecb7a34aba938e 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
@@ -379,10 +379,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\method_config.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\service_config.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
@@ -797,10 +797,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\method_config.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index e2e79333dfff07514038d290e62d2b6a3a759036..c96d48db58890caaa71b6bdb56ba42c348d6a10c 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -378,8 +378,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\method_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
@@ -658,10 +658,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\method_config.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\service_config.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 5ba2a072228e5907e105fcc7c4aaadb9913d8194..4b75288b2b673d1714a7c3c3ab46256116be6779 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -325,10 +325,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\method_config.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\service_config.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
@@ -914,10 +914,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\method_config.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">