alts: Expose ChannelCredentials for the various negotiators

AltsChannelBuilder could be improved a bit more by removing the call to
InternalNettyChannelBuilder.setProtocolNegotiatorFactory. However, to do
that cleanest would require reworking how port is plumbed in
NettyChannelBuilder and potentially AbstractManagedChannelImplBuilder to
move getDefaultPort() to ProtocolNegotiator from ClientFactory. Saving
that for another day.
diff --git a/alts/src/main/java/io/grpc/alts/AltsChannelBuilder.java b/alts/src/main/java/io/grpc/alts/AltsChannelBuilder.java
index 5a774da..969cef9 100644
--- a/alts/src/main/java/io/grpc/alts/AltsChannelBuilder.java
+++ b/alts/src/main/java/io/grpc/alts/AltsChannelBuilder.java
@@ -17,26 +17,14 @@
 package io.grpc.alts;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import io.grpc.CallOptions;
-import io.grpc.Channel;
-import io.grpc.ClientCall;
-import io.grpc.ClientInterceptor;
 import io.grpc.ExperimentalApi;
 import io.grpc.ForwardingChannelBuilder;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
-import io.grpc.MethodDescriptor;
-import io.grpc.Status;
-import io.grpc.alts.internal.AltsProtocolNegotiator.ClientAltsProtocolNegotiatorFactory;
 import io.grpc.internal.GrpcUtil;
-import io.grpc.internal.ObjectPool;
-import io.grpc.internal.SharedResourcePool;
 import io.grpc.netty.InternalNettyChannelBuilder;
 import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
 import io.grpc.netty.NettyChannelBuilder;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import javax.annotation.Nullable;
 
 /**
@@ -45,14 +33,9 @@
  */
 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4151")
 public final class AltsChannelBuilder extends ForwardingChannelBuilder<AltsChannelBuilder> {
-
-  private static final Logger logger = Logger.getLogger(AltsChannelBuilder.class.getName());
   private final NettyChannelBuilder delegate;
-  private final ImmutableList.Builder<String> targetServiceAccountsBuilder =
-      ImmutableList.builder();
-  private ObjectPool<Channel> handshakerChannelPool =
-      SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL);
-  private boolean enableUntrustedAlts;
+  private final AltsChannelCredentials.Builder credentialsBuilder =
+      new AltsChannelCredentials.Builder();
 
   /** "Overrides" the static method in {@link ManagedChannelBuilder}. */
   public static final AltsChannelBuilder forTarget(String target) {
@@ -73,7 +56,7 @@
    * service account in the handshaker result. Otherwise, the handshake fails.
    */
   public AltsChannelBuilder addTargetServiceAccount(String targetServiceAccount) {
-    targetServiceAccountsBuilder.add(targetServiceAccount);
+    credentialsBuilder.addTargetServiceAccount(targetServiceAccount);
     return this;
   }
 
@@ -82,17 +65,13 @@
    * is running on Google Cloud Platform.
    */
   public AltsChannelBuilder enableUntrustedAltsForTesting() {
-    enableUntrustedAlts = true;
+    credentialsBuilder.enableUntrustedAltsForTesting();
     return this;
   }
 
   /** Sets a new handshaker service address for testing. */
   public AltsChannelBuilder setHandshakerAddressForTesting(String handshakerAddress) {
-    // Instead of using the default shared channel to the handshaker service, create a separate
-    // resource to the test address.
-    handshakerChannelPool =
-        SharedResourcePool.forResource(
-            HandshakerServiceChannel.getHandshakerChannelForTesting(handshakerAddress));
+    credentialsBuilder.setHandshakerAddressForTesting(handshakerAddress);
     return this;
   }
 
@@ -103,22 +82,9 @@
 
   @Override
   public ManagedChannel build() {
-    if (!CheckGcpEnvironment.isOnGcp()) {
-      if (enableUntrustedAlts) {
-        logger.log(
-            Level.WARNING,
-            "Untrusted ALTS mode is enabled and we cannot guarantee the trustworthiness of the "
-                + "ALTS handshaker service");
-      } else {
-        Status status =
-            Status.INTERNAL.withDescription("ALTS is only allowed to run on Google Cloud Platform");
-        delegate().intercept(new FailingClientInterceptor(status));
-      }
-    }
     InternalNettyChannelBuilder.setProtocolNegotiatorFactory(
         delegate(),
-        new ClientAltsProtocolNegotiatorFactory(
-            targetServiceAccountsBuilder.build(), handshakerChannelPool));
+        credentialsBuilder.buildProtocolNegotiatorFactory());
 
     return delegate().build();
   }
@@ -126,24 +92,6 @@
   @VisibleForTesting
   @Nullable
   ProtocolNegotiator getProtocolNegotiatorForTest() {
-    return new ClientAltsProtocolNegotiatorFactory(
-        targetServiceAccountsBuilder.build(), handshakerChannelPool)
-            .buildProtocolNegotiator();
-  }
-
-  /** An implementation of {@link ClientInterceptor} that fails each call. */
-  static final class FailingClientInterceptor implements ClientInterceptor {
-
-    private final Status status;
-
-    public FailingClientInterceptor(Status status) {
-      this.status = status;
-    }
-
-    @Override
-    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
-        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
-      return new FailingClientCall<>(status);
-    }
+    return credentialsBuilder.buildProtocolNegotiatorFactory().newNegotiator();
   }
 }
diff --git a/alts/src/main/java/io/grpc/alts/AltsChannelCredentials.java b/alts/src/main/java/io/grpc/alts/AltsChannelCredentials.java
new file mode 100644
index 0000000..ca304c9
--- /dev/null
+++ b/alts/src/main/java/io/grpc/alts/AltsChannelCredentials.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.alts;
+
+import com.google.common.collect.ImmutableList;
+import io.grpc.Channel;
+import io.grpc.ChannelCredentials;
+import io.grpc.ExperimentalApi;
+import io.grpc.Status;
+import io.grpc.alts.internal.AltsProtocolNegotiator.ClientAltsProtocolNegotiatorFactory;
+import io.grpc.internal.ObjectPool;
+import io.grpc.internal.SharedResourcePool;
+import io.grpc.netty.GrpcHttp2ConnectionHandler;
+import io.grpc.netty.InternalNettyChannelCredentials;
+import io.grpc.netty.InternalProtocolNegotiator;
+import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerAdapter;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.AsciiString;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Provides secure and authenticated commmunication between two cloud VMs using ALTS.
+ */
+@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4151")
+public final class AltsChannelCredentials {
+  private static final Logger logger = Logger.getLogger(AltsChannelCredentials.class.getName());
+
+  private AltsChannelCredentials() {}
+
+  public static ChannelCredentials create() {
+    return newBuilder().build();
+  }
+
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  public static final class Builder {
+    private final ImmutableList.Builder<String> targetServiceAccountsBuilder =
+        ImmutableList.builder();
+    private ObjectPool<Channel> handshakerChannelPool =
+        SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL);
+    private boolean enableUntrustedAlts;
+
+    /**
+     * Adds an expected target service accounts. One of the added service accounts should match peer
+     * service account in the handshaker result. Otherwise, the handshake fails.
+     */
+    public Builder addTargetServiceAccount(String targetServiceAccount) {
+      targetServiceAccountsBuilder.add(targetServiceAccount);
+      return this;
+    }
+
+    /**
+     * Enables untrusted ALTS for testing. If this function is called, we will not check whether
+     * ALTS is running on Google Cloud Platform.
+     */
+    public Builder enableUntrustedAltsForTesting() {
+      enableUntrustedAlts = true;
+      return this;
+    }
+
+    /** Sets a new handshaker service address for testing. */
+    public Builder setHandshakerAddressForTesting(String handshakerAddress) {
+      // Instead of using the default shared channel to the handshaker service, create a separate
+      // resource to the test address.
+      handshakerChannelPool =
+          SharedResourcePool.forResource(
+              HandshakerServiceChannel.getHandshakerChannelForTesting(handshakerAddress));
+      return this;
+    }
+
+    public ChannelCredentials build() {
+      return InternalNettyChannelCredentials.create(buildProtocolNegotiatorFactory());
+    }
+
+    InternalProtocolNegotiator.ClientFactory buildProtocolNegotiatorFactory() {
+      if (!CheckGcpEnvironment.isOnGcp()) {
+        if (enableUntrustedAlts) {
+          logger.log(
+              Level.WARNING,
+              "Untrusted ALTS mode is enabled and we cannot guarantee the trustworthiness of the "
+                  + "ALTS handshaker service");
+        } else {
+          Status status = Status.INTERNAL.withDescription(
+              "ALTS is only allowed to run on Google Cloud Platform");
+          return new FailingProtocolNegotiatorFactory(status);
+        }
+      }
+
+      return new ClientAltsProtocolNegotiatorFactory(
+          targetServiceAccountsBuilder.build(), handshakerChannelPool);
+    }
+  }
+
+  private static final class FailingProtocolNegotiatorFactory
+      implements InternalProtocolNegotiator.ClientFactory {
+    private final Status status;
+
+    public FailingProtocolNegotiatorFactory(Status status) {
+      this.status = status;
+    }
+
+    @Override
+    public ProtocolNegotiator newNegotiator() {
+      return new FailingProtocolNegotiator(status);
+    }
+
+    @Override
+    public int getDefaultPort() {
+      return 443;
+    }
+  }
+
+  private static final AsciiString SCHEME = AsciiString.of("https");
+
+  private static final class FailingProtocolNegotiator implements ProtocolNegotiator {
+    private final Status status;
+
+    public FailingProtocolNegotiator(Status status) {
+      this.status = status;
+    }
+
+    @Override
+    public AsciiString scheme() {
+      return SCHEME;
+    }
+
+    @Override
+    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
+      return new ChannelHandlerAdapter() {
+        @Override public void handlerAdded(ChannelHandlerContext ctx) {
+          ctx.fireExceptionCaught(status.asRuntimeException());
+        }
+      };
+    }
+
+    @Override
+    public void close() {}
+  }
+}
diff --git a/alts/src/main/java/io/grpc/alts/CallCredentialsInterceptor.java b/alts/src/main/java/io/grpc/alts/CallCredentialsInterceptor.java
deleted file mode 100644
index b1ab948..0000000
--- a/alts/src/main/java/io/grpc/alts/CallCredentialsInterceptor.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2019 The gRPC Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.grpc.alts;
-
-import io.grpc.CallCredentials;
-import io.grpc.CallOptions;
-import io.grpc.Channel;
-import io.grpc.ClientCall;
-import io.grpc.ClientInterceptor;
-import io.grpc.MethodDescriptor;
-import io.grpc.Status;
-import javax.annotation.Nullable;
-
-/** An implementation of {@link ClientInterceptor} that adds call credentials on each call. */
-final class CallCredentialsInterceptor implements ClientInterceptor {
-
-  @Nullable private final CallCredentials credentials;
-  private final Status status;
-
-  public CallCredentialsInterceptor(@Nullable CallCredentials credentials, Status status) {
-    this.credentials = credentials;
-    this.status = status;
-  }
-
-  @Override
-  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
-      MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
-    if (!status.isOk()) {
-      return new FailingClientCall<>(status);
-    }
-    return next.newCall(method, callOptions.withCallCredentials(credentials));
-  }
-}
diff --git a/alts/src/main/java/io/grpc/alts/ComputeEngineChannelBuilder.java b/alts/src/main/java/io/grpc/alts/ComputeEngineChannelBuilder.java
index d9eaba8..c8c0a08 100644
--- a/alts/src/main/java/io/grpc/alts/ComputeEngineChannelBuilder.java
+++ b/alts/src/main/java/io/grpc/alts/ComputeEngineChannelBuilder.java
@@ -16,23 +16,10 @@
 
 package io.grpc.alts;
 
-import com.google.auth.oauth2.ComputeEngineCredentials;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import io.grpc.CallCredentials;
 import io.grpc.ForwardingChannelBuilder;
 import io.grpc.ManagedChannelBuilder;
-import io.grpc.Status;
-import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
-import io.grpc.auth.MoreCallCredentials;
 import io.grpc.internal.GrpcUtil;
-import io.grpc.internal.SharedResourcePool;
-import io.grpc.netty.GrpcSslContexts;
-import io.grpc.netty.InternalNettyChannelBuilder;
-import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
 import io.grpc.netty.NettyChannelBuilder;
-import io.netty.handler.ssl.SslContext;
-import javax.net.ssl.SSLException;
 
 /**
  * {@code ManagedChannelBuilder} for Google Compute Engine. This class sets up a secure channel
@@ -44,27 +31,7 @@
   private final NettyChannelBuilder delegate;
 
   private ComputeEngineChannelBuilder(String target) {
-    delegate = NettyChannelBuilder.forTarget(target);
-    SslContext sslContext;
-    try {
-      sslContext = GrpcSslContexts.forClient().build();
-    } catch (SSLException e) {
-      throw new RuntimeException(e);
-    }
-    InternalNettyChannelBuilder.setProtocolNegotiatorFactory(
-        delegate(),
-        new GoogleDefaultProtocolNegotiatorFactory(
-            /* targetServiceAccounts= */ ImmutableList.<String>of(),
-            SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
-            sslContext));
-    CallCredentials credentials = MoreCallCredentials.from(ComputeEngineCredentials.create());
-    Status status = Status.OK;
-    if (!CheckGcpEnvironment.isOnGcp()) {
-      status =
-          Status.INTERNAL.withDescription(
-              "Compute Engine Credentials can only be used on Google Cloud Platform");
-    }
-    delegate().intercept(new CallCredentialsInterceptor(credentials, status));
+    delegate = NettyChannelBuilder.forTarget(target, ComputeEngineChannelCredentials.create());
   }
 
   /** "Overrides" the static method in {@link ManagedChannelBuilder}. */
@@ -81,19 +48,4 @@
   protected NettyChannelBuilder delegate() {
     return delegate;
   }
-
-  @VisibleForTesting
-  ProtocolNegotiator getProtocolNegotiatorForTest() {
-    SslContext sslContext;
-    try {
-      sslContext = GrpcSslContexts.forClient().build();
-    } catch (SSLException e) {
-      throw new RuntimeException(e);
-    }
-    return new GoogleDefaultProtocolNegotiatorFactory(
-        /* targetServiceAccounts= */ ImmutableList.<String>of(),
-        SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
-        sslContext)
-        .buildProtocolNegotiator();
-  }
 }
diff --git a/alts/src/main/java/io/grpc/alts/ComputeEngineChannelCredentials.java b/alts/src/main/java/io/grpc/alts/ComputeEngineChannelCredentials.java
new file mode 100644
index 0000000..387fe6d
--- /dev/null
+++ b/alts/src/main/java/io/grpc/alts/ComputeEngineChannelCredentials.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.alts;
+
+import com.google.auth.oauth2.ComputeEngineCredentials;
+import com.google.common.collect.ImmutableList;
+import io.grpc.CallCredentials;
+import io.grpc.ChannelCredentials;
+import io.grpc.CompositeChannelCredentials;
+import io.grpc.ExperimentalApi;
+import io.grpc.Status;
+import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
+import io.grpc.auth.MoreCallCredentials;
+import io.grpc.internal.SharedResourcePool;
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.InternalNettyChannelCredentials;
+import io.grpc.netty.InternalProtocolNegotiator;
+import io.netty.handler.ssl.SslContext;
+import javax.net.ssl.SSLException;
+
+/**
+ * Credentials appropriate to contact Google services when running on Google Compute Engine. This
+ * class sets up a secure channel using ALTS if applicable and using TLS as fallback. It is a subset
+ * of the functionality provided by {@link GoogleDefaultChannelCredentials}.
+ */
+@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
+public final class ComputeEngineChannelCredentials {
+  private ComputeEngineChannelCredentials() {}
+
+  /**
+   * Creates credentials for Google Compute Engine. This class sets up a secure channel using ALTS
+   * if applicable and using TLS as fallback.
+   */
+  public static ChannelCredentials create() {
+    ChannelCredentials nettyCredentials =
+        InternalNettyChannelCredentials.create(createClientFactory());
+    CallCredentials callCredentials;
+    if (CheckGcpEnvironment.isOnGcp()) {
+      callCredentials = MoreCallCredentials.from(ComputeEngineCredentials.create());
+    } else {
+      callCredentials = new FailingCallCredentials(
+          Status.INTERNAL.withDescription(
+              "Compute Engine Credentials can only be used on Google Cloud Platform"));
+    }
+    return CompositeChannelCredentials.create(nettyCredentials, callCredentials);
+  }
+
+  private static InternalProtocolNegotiator.ClientFactory createClientFactory() {
+    SslContext sslContext;
+    try {
+      sslContext = GrpcSslContexts.forClient().build();
+    } catch (SSLException e) {
+      throw new RuntimeException(e);
+    }
+    return new GoogleDefaultProtocolNegotiatorFactory(
+        /* targetServiceAccounts= */ ImmutableList.<String>of(),
+        SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
+        sslContext);
+  }
+}
diff --git a/alts/src/main/java/io/grpc/alts/FailingCallCredentials.java b/alts/src/main/java/io/grpc/alts/FailingCallCredentials.java
new file mode 100644
index 0000000..dbe0821
--- /dev/null
+++ b/alts/src/main/java/io/grpc/alts/FailingCallCredentials.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.alts;
+
+import com.google.common.base.Preconditions;
+import io.grpc.CallCredentials;
+import io.grpc.Status;
+import java.util.concurrent.Executor;
+
+/**
+ * {@code CallCredentials} that always fail the RPC.
+ */
+final class FailingCallCredentials extends CallCredentials {
+  private final Status status;
+
+  public FailingCallCredentials(Status status) {
+    this.status = Preconditions.checkNotNull(status, "status");
+  }
+
+  @Override
+  public void applyRequestMetadata(
+      CallCredentials.RequestInfo requestInfo,
+      Executor appExecutor,
+      CallCredentials.MetadataApplier applier) {
+    applier.fail(status);
+  }
+
+  @Override
+  public void thisUsesUnstableApi() {}
+}
diff --git a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelBuilder.java b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelBuilder.java
index 39bd416..4e006b6 100644
--- a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelBuilder.java
+++ b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelBuilder.java
@@ -16,25 +16,10 @@
 
 package io.grpc.alts;
 
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import io.grpc.CallCredentials;
 import io.grpc.ForwardingChannelBuilder;
 import io.grpc.ManagedChannelBuilder;
-import io.grpc.Status;
-import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
-import io.grpc.auth.MoreCallCredentials;
 import io.grpc.internal.GrpcUtil;
-import io.grpc.internal.SharedResourcePool;
-import io.grpc.netty.GrpcSslContexts;
-import io.grpc.netty.InternalNettyChannelBuilder;
-import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
 import io.grpc.netty.NettyChannelBuilder;
-import io.netty.handler.ssl.SslContext;
-import java.io.IOException;
-import javax.annotation.Nullable;
-import javax.net.ssl.SSLException;
 
 /**
  * Google default version of {@code ManagedChannelBuilder}. This class sets up a secure channel
@@ -46,30 +31,7 @@
   private final NettyChannelBuilder delegate;
 
   private GoogleDefaultChannelBuilder(String target) {
-    delegate = NettyChannelBuilder.forTarget(target);
-    SslContext sslContext;
-    try {
-      sslContext = GrpcSslContexts.forClient().build();
-    } catch (SSLException e) {
-      throw new RuntimeException(e);
-    }
-    InternalNettyChannelBuilder.setProtocolNegotiatorFactory(
-        delegate(),
-        new GoogleDefaultProtocolNegotiatorFactory(
-            /* targetServiceAccounts= */ ImmutableList.<String>of(),
-            SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
-            sslContext));
-    @Nullable CallCredentials credentials = null;
-    Status status = Status.OK;
-    try {
-      credentials = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault());
-    } catch (IOException e) {
-      status =
-          Status.UNAUTHENTICATED
-              .withDescription("Failed to get Google default credentials")
-              .withCause(e);
-    }
-    delegate().intercept(new CallCredentialsInterceptor(credentials, status));
+    delegate = NettyChannelBuilder.forTarget(target, GoogleDefaultChannelCredentials.create());
   }
 
   /** "Overrides" the static method in {@link ManagedChannelBuilder}. */
@@ -86,19 +48,4 @@
   protected NettyChannelBuilder delegate() {
     return delegate;
   }
-
-  @VisibleForTesting
-  ProtocolNegotiator getProtocolNegotiatorForTest() {
-    SslContext sslContext;
-    try {
-      sslContext = GrpcSslContexts.forClient().build();
-    } catch (SSLException e) {
-      throw new RuntimeException(e);
-    }
-    return new GoogleDefaultProtocolNegotiatorFactory(
-        /* targetServiceAccounts= */ ImmutableList.<String>of(),
-        SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
-        sslContext)
-            .buildProtocolNegotiator();
-  }
 }
diff --git a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java
new file mode 100644
index 0000000..9c8b39c
--- /dev/null
+++ b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2020 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.alts;
+
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.common.collect.ImmutableList;
+import io.grpc.CallCredentials;
+import io.grpc.ChannelCredentials;
+import io.grpc.CompositeChannelCredentials;
+import io.grpc.ExperimentalApi;
+import io.grpc.Status;
+import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
+import io.grpc.auth.MoreCallCredentials;
+import io.grpc.internal.SharedResourcePool;
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.InternalNettyChannelCredentials;
+import io.grpc.netty.InternalProtocolNegotiator;
+import io.netty.handler.ssl.SslContext;
+import java.io.IOException;
+import javax.net.ssl.SSLException;
+
+/**
+ * Credentials appropriate to contact Google services. This class sets up a secure channel using
+ * ALTS if applicable and uses TLS as fallback.
+ */
+@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
+public final class GoogleDefaultChannelCredentials {
+  private GoogleDefaultChannelCredentials() {}
+
+  /**
+   * Creates Google default credentials uses a secure channel with ALTS if applicable and uses TLS
+   * as fallback.
+   */
+  public static ChannelCredentials create() {
+    ChannelCredentials nettyCredentials =
+        InternalNettyChannelCredentials.create(createClientFactory());
+    CallCredentials callCredentials;
+    try {
+      callCredentials = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault());
+    } catch (IOException e) {
+      // TODO(ejona): Should this just throw?
+      callCredentials = new FailingCallCredentials(
+          Status.UNAUTHENTICATED
+              .withDescription("Failed to get Google default credentials")
+              .withCause(e));
+    }
+    return CompositeChannelCredentials.create(nettyCredentials, callCredentials);
+  }
+
+  private static InternalProtocolNegotiator.ClientFactory createClientFactory() {
+    SslContext sslContext;
+    try {
+      sslContext = GrpcSslContexts.forClient().build();
+    } catch (SSLException e) {
+      throw new RuntimeException(e);
+    }
+    return new GoogleDefaultProtocolNegotiatorFactory(
+        /* targetServiceAccounts= */ ImmutableList.<String>of(),
+        SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
+        sslContext);
+  }
+}
diff --git a/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java b/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java
index c5adbd7..d2040df 100644
--- a/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java
+++ b/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java
@@ -32,8 +32,7 @@
 import io.grpc.grpclb.GrpclbConstants;
 import io.grpc.internal.ObjectPool;
 import io.grpc.netty.GrpcHttp2ConnectionHandler;
-import io.grpc.netty.InternalNettyChannelBuilder;
-import io.grpc.netty.InternalNettyChannelBuilder.ProtocolNegotiatorFactory;
+import io.grpc.netty.InternalProtocolNegotiator;
 import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
 import io.grpc.netty.InternalProtocolNegotiators;
 import io.netty.channel.ChannelHandler;
@@ -65,23 +64,28 @@
    * channel.
    */
   public static final class ClientAltsProtocolNegotiatorFactory
-      implements InternalNettyChannelBuilder.ProtocolNegotiatorFactory {
+      implements InternalProtocolNegotiator.ClientFactory {
 
     private final ImmutableList<String> targetServiceAccounts;
-    private final LazyChannel lazyHandshakerChannel;
+    private final ObjectPool<Channel> handshakerChannelPool;
 
     public ClientAltsProtocolNegotiatorFactory(
         List<String> targetServiceAccounts,
         ObjectPool<Channel> handshakerChannelPool) {
       this.targetServiceAccounts = ImmutableList.copyOf(targetServiceAccounts);
-      this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
+      this.handshakerChannelPool = checkNotNull(handshakerChannelPool, "handshakerChannelPool");
     }
 
     @Override
-    public ProtocolNegotiator buildProtocolNegotiator() {
+    public ProtocolNegotiator newNegotiator() {
       return new ClientAltsProtocolNegotiator(
-          new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel),
-          lazyHandshakerChannel);
+          targetServiceAccounts,
+          handshakerChannelPool);
+    }
+
+    @Override
+    public int getDefaultPort() {
+      return 443;
     }
   }
 
@@ -90,9 +94,10 @@
     private final LazyChannel lazyHandshakerChannel;
 
     ClientAltsProtocolNegotiator(
-        TsiHandshakerFactory handshakerFactory, LazyChannel lazyHandshakerChannel) {
-      this.handshakerFactory = checkNotNull(handshakerFactory, "handshakerFactory");
-      this.lazyHandshakerChannel = checkNotNull(lazyHandshakerChannel, "lazyHandshakerChannel");
+        ImmutableList<String> targetServiceAccounts, ObjectPool<Channel> handshakerChannelPool) {
+      this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
+      this.handshakerFactory =
+          new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel);
     }
 
     @Override
@@ -177,9 +182,9 @@
    * A Protocol Negotiator factory which can switch between ALTS and TLS based on EAG Attrs.
    */
   public static final class GoogleDefaultProtocolNegotiatorFactory
-      implements ProtocolNegotiatorFactory {
+      implements InternalProtocolNegotiator.ClientFactory {
     private final ImmutableList<String> targetServiceAccounts;
-    private final LazyChannel lazyHandshakerChannel;
+    private final ObjectPool<Channel> handshakerChannelPool;
     private final SslContext sslContext;
 
     /**
@@ -191,17 +196,22 @@
         ObjectPool<Channel> handshakerChannelPool,
         SslContext sslContext) {
       this.targetServiceAccounts = ImmutableList.copyOf(targetServiceAccounts);
-      this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
+      this.handshakerChannelPool = checkNotNull(handshakerChannelPool, "handshakerChannelPool");
       this.sslContext = checkNotNull(sslContext, "sslContext");
     }
 
     @Override
-    public ProtocolNegotiator buildProtocolNegotiator() {
+    public ProtocolNegotiator newNegotiator() {
       return new GoogleDefaultProtocolNegotiator(
-          new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel),
-          lazyHandshakerChannel,
+          targetServiceAccounts,
+          handshakerChannelPool,
           sslContext);
     }
+
+    @Override
+    public int getDefaultPort() {
+      return 443;
+    }
   }
 
   private static final class GoogleDefaultProtocolNegotiator implements ProtocolNegotiator {
@@ -210,11 +220,12 @@
     private final SslContext sslContext;
 
     GoogleDefaultProtocolNegotiator(
-        TsiHandshakerFactory handshakerFactory,
-        LazyChannel lazyHandshakerChannel,
+        ImmutableList<String> targetServiceAccounts,
+        ObjectPool<Channel> handshakerChannelPool,
         SslContext sslContext) {
-      this.handshakerFactory = checkNotNull(handshakerFactory, "handshakerFactory");
-      this.lazyHandshakerChannel = checkNotNull(lazyHandshakerChannel, "lazyHandshakerChannel");
+      this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
+      this.handshakerFactory =
+          new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel);
       this.sslContext = checkNotNull(sslContext, "checkNotNull");
     }
 
diff --git a/alts/src/test/java/io/grpc/alts/ComputeEngineChannelBuilderTest.java b/alts/src/test/java/io/grpc/alts/ComputeEngineChannelBuilderTest.java
index f7752a2..03fd113 100644
--- a/alts/src/test/java/io/grpc/alts/ComputeEngineChannelBuilderTest.java
+++ b/alts/src/test/java/io/grpc/alts/ComputeEngineChannelBuilderTest.java
@@ -16,9 +16,6 @@
 
 package io.grpc.alts;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -30,9 +27,5 @@
   public void buildsNettyChannel() throws Exception {
     ComputeEngineChannelBuilder builder = ComputeEngineChannelBuilder.forTarget("localhost:8080");
     builder.build();
-
-    ProtocolNegotiator protocolNegotiator = builder.getProtocolNegotiatorForTest();
-    assertThat(protocolNegotiator.getClass().getSimpleName())
-        .isEqualTo("GoogleDefaultProtocolNegotiator");
   }
 }
diff --git a/alts/src/test/java/io/grpc/alts/GoogleDefaultChannelBuilderTest.java b/alts/src/test/java/io/grpc/alts/GoogleDefaultChannelBuilderTest.java
index 4336aff..c73ef44 100644
--- a/alts/src/test/java/io/grpc/alts/GoogleDefaultChannelBuilderTest.java
+++ b/alts/src/test/java/io/grpc/alts/GoogleDefaultChannelBuilderTest.java
@@ -16,9 +16,6 @@
 
 package io.grpc.alts;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -30,9 +27,5 @@
   public void buildsNettyChannel() throws Exception {
     GoogleDefaultChannelBuilder builder = GoogleDefaultChannelBuilder.forTarget("localhost:8080");
     builder.build();
-
-    ProtocolNegotiator protocolNegotiator = builder.getProtocolNegotiatorForTest();
-    assertThat(protocolNegotiator.getClass().getSimpleName())
-        .isEqualTo("GoogleDefaultProtocolNegotiator");
   }
 }
diff --git a/alts/src/test/java/io/grpc/alts/internal/GoogleDefaultProtocolNegotiatorTest.java b/alts/src/test/java/io/grpc/alts/internal/GoogleDefaultProtocolNegotiatorTest.java
index 5858fc3..8560363 100644
--- a/alts/src/test/java/io/grpc/alts/internal/GoogleDefaultProtocolNegotiatorTest.java
+++ b/alts/src/test/java/io/grpc/alts/internal/GoogleDefaultProtocolNegotiatorTest.java
@@ -69,7 +69,7 @@
         ImmutableList.<String>of(),
         handshakerChannelPool,
         sslContext)
-        .buildProtocolNegotiator();
+        .newNegotiator();
   }
 
   @After