Merge remote-tracking branch 'upstream/master' into service_config_json
diff --git a/BUILD b/BUILD
index 894c7b2..441246e 100644
--- a/BUILD
+++ b/BUILD
@@ -247,8 +247,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",
@@ -436,8 +436,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",
@@ -681,8 +681,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",
@@ -855,8 +855,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",
@@ -1070,8 +1070,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",
@@ -1236,8 +1236,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",
@@ -2097,8 +2097,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",
@@ -2321,8 +2321,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",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 98d54f2..09536d5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -391,8 +391,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
@@ -670,8 +670,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
@@ -921,8 +921,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/Makefile b/Makefile
index 56c05f1..72b0257 100644
--- a/Makefile
+++ b/Makefile
@@ -2725,8 +2725,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 \
@@ -3022,8 +3022,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 \
@@ -3310,8 +3310,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 \
@@ -3527,8 +3527,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/binding.gyp b/binding.gyp
index ba90fca..2f3ac0c 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 41c0f9a..e2469d4 100644
--- a/build.yaml
+++ b/build.yaml
@@ -254,8 +254,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
@@ -367,8 +367,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/config.m4 b/config.m4
index 988cc5b..8833831 100644
--- a/config.m4
+++ b/config.m4
@@ -187,8 +187,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/gRPC-Core.podspec b/gRPC-Core.podspec
index 8869bec..19d5d6e 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -336,8 +336,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',
@@ -529,8 +529,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',
@@ -739,8 +739,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',
diff --git a/grpc.gemspec b/grpc.gemspec
index b071fcc..43b8caf 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -256,8 +256,8 @@
   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 @@
   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 c836028..ca269be 100644
--- a/include/grpc++/channel.h
+++ b/include/grpc++/channel.h
@@ -57,6 +57,13 @@
   /// \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 49a3e94..37de51e 100644
--- a/include/grpc++/support/channel_arguments.h
+++ b/include/grpc++/support/channel_arguments.h
@@ -90,6 +90,10 @@
   /// 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 1bf78e4..9cb89e9 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -473,6 +473,9 @@
   /* 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 348ad5f..1e737f2 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 1cce3bc..a92a220 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 @@
   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};
+    gpr_free, method_parameters_copy};
 
-static void *method_config_convert_value(
-    const grpc_method_config *method_config) {
+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 @@
   /** 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 @@
   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 @@
     // 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 @@
     // 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 @@
       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 @@
   }
   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 @@
                                 ? 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 @@
     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/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c
index 1331fe1..28ad587 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 @@
   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 @@
   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 5b583a1..48b1368 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 681df4b..7111db0 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 @@
  *
  * 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 @@
  * 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 dc243bf..f10a30f 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 @@
   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 13891d0..e933e2e 100644
--- a/src/core/lib/support/string.h
+++ b/src/core/lib/support/string.h
@@ -77,6 +77,9 @@
 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 8e914c4..3d01e56 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* 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 @@
   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 @@
     }
     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 @@
   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 bceb4df..8982ec3 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_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 @@
     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 57d9770..0000000
--- 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 58fedd9..0000000
--- 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 0000000..2e2b59e
--- /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 0000000..2ffe475
--- /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 5f1d00d..357d831 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() { 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 d86ba9d..305ea0f 100644
--- a/src/cpp/common/channel_arguments.cc
+++ b/src/cpp/common/channel_arguments.cc
@@ -125,6 +125,11 @@
   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 b004187..58efc25 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -181,8 +181,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/test/core/client_channel/lb_policies_test.c b/test/core/client_channel/lb_policies_test.c
index 198aafb..3aba906 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"
@@ -642,21 +643,43 @@
 }
 
 static void test_get_channel_info() {
-  grpc_channel *channel = grpc_insecure_channel_create(
-      "test:127.0.0.1:1234?lb_policy=round_robin", NULL, NULL);
+  grpc_channel *channel =
+      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 = "{\"lb_policy_name\": \"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 13414c0..728d6dc 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 @@
   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 428a2d1..e582c59 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 @@
 
   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 0ddd7be..c222d9d 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 @@
   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 @@
   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 76211cb..cc340b9 100644
--- a/test/cpp/end2end/round_robin_end2end_test.cc
+++ b/test/cpp/end2end/round_robin_end2end_test.cc
@@ -109,9 +109,9 @@
       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 @@
 
   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 @@
     }
   }
   EXPECT_TRUE(found);
+  // Check LB policy name for the channel.
+  EXPECT_EQ("pick_first", channel_->GetLoadBalancingPolicyName());
 }
 
 TEST_F(RoundRobinEnd2endTest, RoundRobin) {
@@ -198,6 +201,8 @@
   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 55b8d40..d71efc1 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -879,8 +879,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 \
@@ -1072,8 +1072,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/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 104eaa7..6ab1fde 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -6739,8 +6739,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", 
@@ -6956,10 +6956,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 c608cb8..3beff4a 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 c6ca85c..f658aad 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 764f461..f0f49ec 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 2b72043..8413567 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 ed68f03..5d971b1 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 e8c16e5..01e4a7a 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">