Repository: grpc/grpc-go

Issue:
Flaky test: Test/RBAC_WithBadRouteConfiguration
```
--- FAIL: Test (6.29s)
    --- FAIL: Test/RBAC_WithBadRouteConfiguration (0.01s)
        setup.go:45: Created new snapshot cache...
        tlogger.go:116: INFO server.go:690 [core] [Server #1312]Server created  (t=+145.502µs)
        setup.go:45: Registered Aggregated Discovery Service (ADS)...
        setup.go:45: xDS management server serving at: 127.0.0.1:44393...
        tlogger.go:116: INFO server.go:690 [core] [Server #1313]Server created  (t=+765.571µs)
        tlogger.go:116: INFO pool.go:260 [xds] xDS node ID: d2aba3d7-7d86-4592-881e-e8ba40b97972  (t=+822.457µs)
        tlogger.go:116: INFO server.go:124 [xds] [xds-server 0xc00059c400] Created xds.GRPCServer  (t=+839.98µs)
        stubserver.go:300: Started test service backend at "127.0.0.1:46397"
        tlogger.go:116: INFO server.go:183 [xds] [xds-server 0xc00059c400] Serve() passed a net.Listener on 127.0.0.1:34455  (t=+1.072064ms)
        tlogger.go:116: INFO server.go:886 [core] [Server #1312 ListenSocket #1314]ListenSocket created  (t=+1.125073ms)
        tlogger.go:116: INFO clientconn.go:1687 [core] original dial target is: "passthrough:///127.0.0.1:44393"  (t=+1.166831ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1315]Channel created  (t=+1.188481ms)
        tlogger.go:116: INFO clientconn.go:207 [core] [Channel #1315]parsed dial target is: resolver.Target{URL:url.URL{Scheme:"passthrough", Opaque:"", User:(*url.Userinfo)(nil), Host:"", Path:"/127.0.0.1:44393", RawPath:"", OmitHost:false, ForceQuery:false, RawQuery:"", Fragment:"", RawFragment:""}}  (t=+1.209901ms)
        tlogger.go:116: INFO clientconn.go:208 [core] [Channel #1315]Channel authority set to "127.0.0.1:44393"  (t=+1.225ms)
        tlogger.go:116: INFO server.go:183 [xds] [xds-server 0xc00059c400] Serve() passed a net.Listener on 127.0.0.1:46397  (t=+1.252471ms)
        tlogger.go:116: INFO resolver_wrapper.go:210 [core] [Channel #1315]Resolver state updated: {
              "Addresses": [
                {
                  "Addr": "127.0.0.1:44393",
                  "ServerName": "",
                  "Attributes": null,
                  "BalancerAttributes": null,
                  "Metadata": null
                }
              ],
              "Endpoints": [
                {
                  "Addresses": [
                    {
                      "Addr": "127.0.0.1:44393",
                      "ServerName": "",
                      "Attributes": null,
                      "BalancerAttributes": null,
                      "Metadata": null
                    }
                  ],
                  "Attributes": null
                }
              ],
              "ServiceConfig": null,
              "Attributes": null
            } (resolver returned new addresses)  (t=+1.306922ms)
        tlogger.go:116: INFO balancer_wrapper.go:122 [core] [Channel #1315]Channel switches to new LB policy "pick_first"  (t=+1.336147ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1315]Channel Connectivity change to CONNECTING  (t=+1.368428ms)
        tlogger.go:116: INFO clientconn.go:866 [core] [Channel #1315 SubChannel #1316]Subchannel created  (t=+1.390799ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1315]Channel exiting idle mode  (t=+1.410877ms)
        tlogger.go:116: INFO server.go:886 [core] [Server #1313 ListenSocket #1317]ListenSocket created  (t=+1.456322ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1315 SubChannel #1316]Subchannel Connectivity change to CONNECTING  (t=+1.494213ms)
        tlogger.go:116: INFO clientconn.go:1352 [core] [Channel #1315 SubChannel #1316]Subchannel picks a new address "127.0.0.1:44393" to connect  (t=+1.50894ms)
        tlogger.go:116: INFO server.go:886 [core] [Server #1313 ListenSocket #1318]ListenSocket created  (t=+1.632581ms)
        server.go:229: Created new resource snapshot...
        server.go:235: Updated snapshot cache with resource snapshot...
        tlogger.go:116: INFO clientconn.go:1687 [core] original dial target is: "xds:///my-service-fallback"  (t=+1.741795ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1319]Channel created  (t=+1.759438ms)
        tlogger.go:116: INFO clientconn.go:207 [core] [Channel #1319]parsed dial target is: resolver.Target{URL:url.URL{Scheme:"xds", Opaque:"", User:(*url.Userinfo)(nil), Host:"", Path:"/my-service-fallback", RawPath:"", OmitHost:false, ForceQuery:false, RawQuery:"", Fragment:"", RawFragment:""}}  (t=+1.800475ms)
        tlogger.go:116: INFO clientconn.go:208 [core] [Channel #1319]Channel authority set to "my-service-fallback"  (t=+1.817507ms)
        tlogger.go:116: INFO xds_resolver.go:125 [xds] [xds-resolver 0xc00052d680] Creating resolver for target: xds:///my-service-fallback  (t=+1.855879ms)
        tlogger.go:116: INFO pool.go:260 [xds] xDS node ID: d2aba3d7-7d86-4592-881e-e8ba40b97972  (t=+1.963901ms)
        tlogger.go:116: INFO clientconn.go:1687 [core] original dial target is: "passthrough:///127.0.0.1:44393"  (t=+1.996391ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1320]Channel created  (t=+2.011579ms)
        tlogger.go:116: INFO clientconn.go:207 [core] [Channel #1320]parsed dial target is: resolver.Target{URL:url.URL{Scheme:"passthrough", Opaque:"", User:(*url.Userinfo)(nil), Host:"", Path:"/127.0.0.1:44393", RawPath:"", OmitHost:false, ForceQuery:false, RawQuery:"", Fragment:"", RawFragment:""}}  (t=+2.029092ms)
        tlogger.go:116: INFO clientconn.go:208 [core] [Channel #1320]Channel authority set to "127.0.0.1:44393"  (t=+2.042848ms)
        tlogger.go:116: INFO resolver_wrapper.go:210 [core] [Channel #1320]Resolver state updated: {
              "Addresses": [
                {
                  "Addr": "127.0.0.1:44393",
                  "ServerName": "",
                  "Attributes": null,
                  "BalancerAttributes": null,
                  "Metadata": null
                }
              ],
              "Endpoints": [
                {
                  "Addresses": [
                    {
                      "Addr": "127.0.0.1:44393",
                      "ServerName": "",
                      "Attributes": null,
                      "BalancerAttributes": null,
                      "Metadata": null
                    }
                  ],
                  "Attributes": null
                }
              ],
              "ServiceConfig": null,
              "Attributes": null
            } (resolver returned new addresses)  (t=+2.087872ms)
        tlogger.go:116: INFO balancer_wrapper.go:122 [core] [Channel #1320]Channel switches to new LB policy "pick_first"  (t=+2.11326ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1320]Channel Connectivity change to CONNECTING  (t=+2.139609ms)
        tlogger.go:116: INFO clientconn.go:866 [core] [Channel #1320 SubChannel #1321]Subchannel created  (t=+2.157773ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1320]Channel exiting idle mode  (t=+2.178972ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1319]Channel exiting idle mode  (t=+2.207345ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1320 SubChannel #1321]Subchannel Connectivity change to CONNECTING  (t=+2.24168ms)
        tlogger.go:116: INFO clientconn.go:1352 [core] [Channel #1320 SubChannel #1321]Subchannel picks a new address "127.0.0.1:44393" to connect  (t=+2.257449ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1315 SubChannel #1316]Subchannel Connectivity change to READY  (t=+2.655313ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1315]Channel Connectivity change to READY  (t=+2.684948ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1320 SubChannel #1321]Subchannel Connectivity change to READY  (t=+2.70178ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1320]Channel Connectivity change to READY  (t=+2.721967ms)
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.listener.v3.Listener[grpc/server?xds.resource.listening_address=127.0.0.1:34455 grpc/server?xds.resource.listening_address=127.0.0.1:46397] and known map[]. Diff [grpc/server?xds.resource.listening_address=127.0.0.1:34455 grpc/server?xds.resource.listening_address=127.0.0.1:46397]
        logging.go:30: respond type.googleapis.com/envoy.config.listener.v3.Listener[grpc/server?xds.resource.listening_address=127.0.0.1:34455 grpc/server?xds.resource.listening_address=127.0.0.1:46397] version "" with version "1"
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.listener.v3.Listener[my-service-fallback] and known map[]. Diff [my-service-fallback]
        logging.go:30: respond type.googleapis.com/envoy.config.listener.v3.Listener[my-service-fallback] version "" with version "1"
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.listener.v3.Listener[my-service-fallback] and known map[my-service-fallback:{}]. Diff []
        logging.go:30: open watch 1 for type.googleapis.com/envoy.config.listener.v3.Listener[my-service-fallback] from nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972", version "1"
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.route.v3.RouteConfiguration[route-my-service-fallback] and known map[]. Diff [route-my-service-fallback]
        logging.go:30: respond type.googleapis.com/envoy.config.route.v3.RouteConfiguration[route-my-service-fallback] version "" with version "1"
        tlogger.go:116: INFO xds_resolver.go:311 [xds] [xds-resolver 0xc00052d680] For Listener resource "my-service-fallback" and RouteConfiguration resource "route-my-service-fallback", generated service config: {
              "loadBalancingConfig": [
                {
                  "xds_cluster_manager_experimental": {
                    "children": {
                      "cluster:cluster-my-service-fallback": {
                        "childPolicy": [
                          {
                            "cds_experimental": {
                              "cluster": "cluster-my-service-fallback"
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              ]
            }  (t=+4.009696ms)
        tlogger.go:116: INFO resolver_wrapper.go:210 [core] [Channel #1319]Resolver state updated: {
              "Addresses": null,
              "Endpoints": [],
              "ServiceConfig": {
                "Config": {
                  "Config": null,
                  "Methods": {}
                },
                "Err": null
              },
              "Attributes": {
                "\u003c%!p(xdsclient.clientKeyType=grpc.xds.internal.client.Client)\u003e": "\u003c0xc000616b60\u003e",
                "\u003c%!p(resolver.csKeyType=grpc.internal.resolver.configSelector)\u003e": "\u003c0xc0008c4a20\u003e"
              }
            } (service config updated)  (t=+4.095296ms)
        tlogger.go:116: INFO balancer_wrapper.go:122 [core] [Channel #1319]Channel switches to new LB policy "xds_cluster_manager_experimental"  (t=+4.119[201](https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904#step:8:202)ms)
        tlogger.go:116: INFO clustermanager.go:58 [xds] [xds-cluster-manager-lb 0xc0005575e0] Created  (t=+4.14025ms)
        tlogger.go:116: INFO balancergroup.go:275 [xds] [xds-cluster-manager-lb 0xc0005575e0] Adding child policy of type "cds_experimental" for child "cluster:cluster-my-service-fallback"  (t=+4.155309ms)
        tlogger.go:116: INFO balancergroup.go:102 [xds] [xds-cluster-manager-lb 0xc0005575e0] Creating child policy of type "cds_experimental" for child "cluster:cluster-my-service-fallback"  (t=+4.167952ms)
        tlogger.go:116: INFO cdsbalancer.go:114 [xds] [cds-lb 0xc000626180] Created  (t=+4.183631ms)
        tlogger.go:116: INFO cdsbalancer.go:126 [xds] [cds-lb 0xc000626180] xDS credentials in use: false  (t=+4.198199ms)
        tlogger.go:116: INFO cdsbalancer.go:301 [xds] [cds-lb 0xc000626180] Received balancer config update: {
              "LoadBalancingConfig": null,
              "Cluster": "cluster-my-service-fallback"
            }  (t=+4.215842ms)
        tlogger.go:116: INFO balancerstateaggregator.go:191 [xds] [xds-cluster-manager-lb 0xc0005575e0] Child pickers: map[cluster:cluster-my-service-fallback:picker:0xc0007d3100,state:CONNECTING,stateToAggregate:CONNECTING]  (t=+4.24726ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1319]Channel Connectivity change to CONNECTING  (t=+4.262569ms)
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.route.v3.RouteConfiguration[route-my-service-fallback] and known map[route-my-service-fallback:{}]. Diff []
        logging.go:30: open watch 2 for type.googleapis.com/envoy.config.route.v3.RouteConfiguration[route-my-service-fallback] from nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972", version "1"
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.cluster.v3.Cluster[cluster-my-service-fallback] and known map[]. Diff [cluster-my-service-fallback]
        logging.go:30: respond type.googleapis.com/envoy.config.cluster.v3.Cluster[cluster-my-service-fallback] version "" with version "1"
        tlogger.go:116: INFO cdsbalancer.go:460 [xds] [cds-lb 0xc000626180] Received Cluster resource: {
              "ClusterType": 0,
              "ClusterName": "cluster-my-service-fallback",
              "EDSServiceName": "endpoints-my-service-fallback",
              "LRSServerConfig": null,
              "SecurityCfg": null,
              "MaxRequests": null,
              "DNSHostName": "",
              "PrioritizedClusterNames": null,
              "LBPolicy": [
                {
                  "xds_wrr_locality_experimental": {
                    "childPolicy": [
                      {
                        "round_robin": {}
                      }
                    ]
                  }
                }
              ],
              "OutlierDetection": null,
              "Raw": {
                "type_url": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
                "value": "ChtjbHVzdGVyLW15LXNlcnZpY2UtZmFsbGJhY2saIwoCGgASHWVuZHBvaW50cy1teS1zZXJ2aWNlLWZhbGxiYWNrEAM="
              },
              "TelemetryLabels": {
                "csm.service_name": "unknown",
                "csm.service_namespace_name": "unknown"
              }
            }  (t=+4.50[202](https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904#step:8:203)6ms)
        tlogger.go:116: INFO clusterresolver.go:85 [xds] [xds-cluster-resolver-lb 0xc0009336c0] Created  (t=+4.521202ms)
        tlogger.go:116: INFO cdsbalancer.go:501 [xds] [cds-lb 0xc000626180] Created child policy 0xc0009336c0 of type cluster_resolver_experimental  (t=+4.53603ms)
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.cluster.v3.Cluster[cluster-my-service-fallback] and known map[cluster-my-service-fallback:{}]. Diff []
        logging.go:30: open watch 3 for type.googleapis.com/envoy.config.cluster.v3.Cluster[cluster-my-service-fallback] from nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972", version "1"
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment[endpoints-my-service-fallback] and known map[]. Diff [endpoints-my-service-fallback]
        logging.go:30: respond type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment[endpoints-my-service-fallback] version "" with version "1"
        tlogger.go:116: INFO balancer.go:75 [xds] [priority-lb 0xc000931110] Created  (t=+4.906793ms)
        tlogger.go:116: INFO balancergroup.go:525 [xds] [xds-cluster-manager-lb 0xc0005575e0] Balancer state update from child cluster:cluster-my-service-fallback, new state: {ConnectivityState:CONNECTING Picker:0xc000a16400}  (t=+5.059188ms)
        tlogger.go:116: INFO balancerstateaggregator.go:147 [xds] [xds-cluster-manager-lb 0xc0005575e0] State update from sub-balancer "cluster:cluster-my-service-fallback": {ConnectivityState:CONNECTING Picker:0xc000a16400}  (t=+5.082732ms)
        tlogger.go:116: INFO balancerstateaggregator.go:191 [xds] [xds-cluster-manager-lb 0xc0005575e0] Child pickers: map[cluster:cluster-my-service-fallback:picker:0xc000a16400,state:CONNECTING,stateToAggregate:CONNECTING]  (t=+5.096628ms)
        tlogger.go:116: INFO balancergroup.go:275 [xds] [priority-lb 0xc000931110] Adding child policy of type "outlier_detection_experimental" for child "priority-0-0"  (t=+5.111025ms)
        tlogger.go:116: INFO balancergroup.go:102 [xds] [priority-lb 0xc000931110] Creating child policy of type "outlier_detection_experimental" for child "priority-0-0"  (t=+5.123488ms)
        tlogger.go:116: INFO balancer.go:74 [xds] [outlier-detection-lb 0xc0008c3d40] Created  (t=+5.137634ms)
        tlogger.go:116: INFO clusterimpl.go:76 [xds] [xds-cluster-impl-lb 0xc000259000] Created  (t=+5.154636ms)
        tlogger.go:116: INFO weightedtarget.go:65 [xds] [weighted-target-lb 0xc0006368e0] Created  (t=+5.212885ms)
        tlogger.go:116: INFO balancer.go:88 [xds] [wrrlocality-lb 0xc0009496b0] Created  (t=+5.228965ms)
        tlogger.go:116: INFO balancergroup.go:275 [xds] [weighted-target-lb 0xc0006368e0] Adding child policy of type "round_robin" for child "{region=\"region-1\", zone=\"zone-1\", sub_zone=\"subzone-1\"}"  (t=+5.260174ms)
        tlogger.go:116: INFO balancergroup.go:102 [xds] [weighted-target-lb 0xc0006368e0] Creating child policy of type "round_robin" for child "{region=\"region-1\", zone=\"zone-1\", sub_zone=\"subzone-1\"}"  (t=+5.275652ms)
        tlogger.go:116: INFO roundrobin.go:56 [roundrobin] [0xc0009499b0] Created  (t=+5.292[203](https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904#step:8:204)ms)
        tlogger.go:116: INFO clientconn.go:866 [core] [Channel #1319 SubChannel #1326]Subchannel created  (t=+5.328622ms)
        tlogger.go:116: INFO balancergroup.go:525 [xds] [weighted-target-lb 0xc0006368e0] Balancer state update from child {region="region-1", zone="zone-1", sub_zone="subzone-1"}, new state: {ConnectivityState:CONNECTING Picker:0xc000bb9200}  (t=+5.360421ms)
        tlogger.go:116: INFO aggregator.go:253 [xds] [weighted-target-lb 0xc0006368e0] Child pickers with config: map[{region="region-1", zone="zone-1", sub_zone="subzone-1"}:weight:1,picker:0xc000bb9200,state:CONNECTING,stateToAggregate:CONNECTING]  (t=+5.382532ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1319 SubChannel #1326]Subchannel Connectivity change to CONNECTING  (t=+5.415033ms)
        tlogger.go:116: INFO clientconn.go:1352 [core] [Channel #1319 SubChannel #1326]Subchannel picks a new address "127.0.0.1:34455" to connect  (t=+5.428939ms)
        tlogger.go:116: INFO balancergroup.go:525 [xds] [priority-lb 0xc000931110] Balancer state update from child priority-0-0, new state: {ConnectivityState:CONNECTING Picker:0xc000815488}  (t=+5.527864ms)
        tlogger.go:116: INFO balancergroup.go:525 [xds] [xds-cluster-manager-lb 0xc0005575e0] Balancer state update from child cluster:cluster-my-service-fallback, new state: {ConnectivityState:CONNECTING Picker:0xc000815488}  (t=+5.551027ms)
        tlogger.go:116: INFO balancerstateaggregator.go:147 [xds] [xds-cluster-manager-lb 0xc0005575e0] State update from sub-balancer "cluster:cluster-my-service-fallback": {ConnectivityState:CONNECTING Picker:0xc000815488}  (t=+5.56325ms)
        tlogger.go:116: INFO balancerstateaggregator.go:191 [xds] [xds-cluster-manager-lb 0xc0005575e0] Child pickers: map[cluster:cluster-my-service-fallback:picker:0xc000815488,state:CONNECTING,stateToAggregate:CONNECTING]  (t=+5.576615ms)
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment[endpoints-my-service-fallback] and known map[endpoints-my-service-fallback:{}]. Diff []
        logging.go:30: open watch 4 for type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment[endpoints-my-service-fallback] from nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972", version "1"
        tlogger.go:116: WARNING clientconn.go:1414 [core] [Channel #1319 SubChannel #1326]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:34455", ServerName: "my-service-fallback", Attributes: {"<%!p(xds.handshakeClusterNameKey={})>": "cluster-my-service-fallback" , "<%!p(xds.handshakeAttrKey={})>": "<0xc00008a340>" }, BalancerAttributes: {"<%!p(weight.attributeKey={})>": "Weight: 1" , "<%!p(internal.localityKeyType=grpc.xds.internal.address.locality)>": "<%!p(internal.LocalityID={region-1 zone-1 subzone-1})>" , "<%!p(hierarchy.pathKeyType=grpc.internal.address.hierarchical_path)>": "<0xc000636400>" , "<%!p(wrrlocality.attributeKey={})>": "Locality Weight: 1" , "<%!p(pickfirstleaf.managedByPickfirstKeyType={})>": "<%!p(bool=true)>" }}. Err: connection error: desc = "error reading server preface: read tcp 127.0.0.1:34908->127.0.0.1:34455: read: connection reset by peer"  (t=+5.885179ms)
        tlogger.go:116: INFO clientconn.go:1235 [core] [Channel #1319 SubChannel #1326]Subchannel Connectivity change to TRANSIENT_FAILURE, last error: connection error: desc = "error reading server preface: read tcp 127.0.0.1:34908->127.0.0.1:34455: read: connection reset by peer"  (t=+5.911539ms)
        xds_server_integration_test.go:61: Serving mode for listener "127.0.0.1:34455" changed to "SERVING", err: <nil>
        tlogger.go:116: INFO balancergroup.go:525 [xds] [weighted-target-lb 0xc0006368e0] Balancer state update from child {region="region-1", zone="zone-1", sub_zone="subzone-1"}, new state: {ConnectivityState:TRANSIENT_FAILURE Picker:0xc000702300}  (t=+5.938649ms)
        tlogger.go:116: INFO aggregator.go:253 [xds] [weighted-target-lb 0xc0006368e0] Child pickers with config: map[{region="region-1", zone="zone-1", sub_zone="subzone-1"}:weight:1,picker:0xc000702300,state:TRANSIENT_FAILURE,stateToAggregate:TRANSIENT_FAILURE]  (t=+5.983213ms)
        tlogger.go:116: INFO balancergroup.go:525 [xds] [priority-lb 0xc000931110] Balancer state update from child priority-0-0, new state: {ConnectivityState:TRANSIENT_FAILURE Picker:0xc00011edc8}  (t=+6.009331ms)
        tlogger.go:116: INFO balancergroup.go:525 [xds] [xds-cluster-manager-lb 0xc0005575e0] Balancer state update from child cluster:cluster-my-service-fallback, new state: {ConnectivityState:TRANSIENT_FAILURE Picker:0xc00011edc8}  (t=+6.027656ms)
        tlogger.go:116: INFO balancerstateaggregator.go:147 [xds] [xds-cluster-manager-lb 0xc0005575e0] State update from sub-balancer "cluster:cluster-my-service-fallback": {ConnectivityState:TRANSIENT_FAILURE Picker:0xc00011edc8}  (t=+6.042113ms)
        logging.go:30: nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972" requested type.googleapis.com/envoy.config.listener.v3.Listener[grpc/server?xds.resource.listening_address=127.0.0.1:34455 grpc/server?xds.resource.listening_address=127.0.0.1:46397] and known map[grpc/server?xds.resource.listening_address=127.0.0.1:34455:{} grpc/server?xds.resource.listening_address=127.0.0.1:46397:{}]. Diff []
        tlogger.go:116: INFO balancerstateaggregator.go:191 [xds] [xds-cluster-manager-lb 0xc0005575e0] Child pickers: map[cluster:cluster-my-service-fallback:picker:0xc00011edc8,state:TRANSIENT_FAILURE,stateToAggregate:TRANSIENT_FAILURE]  (t=+6.061419ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1319]Channel Connectivity change to TRANSIENT_FAILURE  (t=+6.081617ms)
        xds_server_rbac_test.go:889: EmptyCall() = rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: read tcp 127.0.0.1:34908->127.0.0.1:34455: read: connection reset by peer", want xDS node id "d2aba3d7-7d86-4592-881e-e8ba40b97972"
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1319]Channel Connectivity change to SHUTDOWN  (t=+6.126801ms)
        logging.go:30: open watch 5 for type.googleapis.com/envoy.config.listener.v3.Listener[grpc/server?xds.resource.listening_address=127.0.0.1:34455 grpc/server?xds.resource.listening_address=127.0.0.1:46397] from nodeID "d2aba3d7-7d86-4592-881e-e8ba40b97972", version "1"
        tlogger.go:116: INFO resolver_wrapper.go:111 [core] [Channel #1319]Closing the name resolver  (t=+6.141849ms)
        tlogger.go:116: INFO balancer_wrapper.go:160 [core] [Channel #1319]ccBalancerWrapper: closing  (t=+6.161626ms)
        tlogger.go:116: INFO clusterimpl.go:350 [xds] [xds-cluster-impl-lb 0xc000259000] Shutdown  (t=+6.193736ms)
        tlogger.go:116: INFO balancergroup.go:342 [xds] [priority-lb 0xc000931110] Removing child policy for child "priority-0-0"  (t=+6.[210](https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904#step:8:211)437ms)
        tlogger.go:116: INFO clusterresolver.go:360 [xds] [xds-cluster-resolver-lb 0xc0009336c0] Shutdown  (t=+6.221688ms)
        tlogger.go:116: INFO cdsbalancer.go:411 [xds] [cds-lb 0xc000626180] Shutdown  (t=+6.231437ms)
        tlogger.go:116: INFO clustermanager.go:196 [xds] [xds-cluster-manager-lb 0xc0005575e0] Shutdown  (t=+6.241536ms)
        tlogger.go:116: INFO watch_service.go:56 [xds] [xds-resolver 0xc00052d680] Canceling watch on Listener resource "my-service-fallback"  (t=+6.257666ms)
        tlogger.go:116: WARNING ads_stream.go:471 [xds] [xds-client 0xc000926240] [xds-channel 0xc000346540] [ads-stream 0xc000b88e00] Sending ADS request for type "type.googleapis.com/envoy.config.cluster.v3.Cluster", resources: [], version: "1", nonce: "3" failed: EOF  (t=+6.293513ms)
        tlogger.go:116: WARNING ads_stream.go:514 [xds] [xds-client 0xc000926240] [xds-channel 0xc000346540] [ads-stream 0xc000b88e00] ADS stream closed: rpc error: code = Canceled desc = context canceled  (t=+6.314482ms)
        tlogger.go:116: INFO ads_stream.go:178 [xds] [xds-client 0xc000926240] [xds-channel 0xc000346540] [ads-stream 0xc000b88e00] Stopping ADS stream  (t=+6.326685ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1320]Channel Connectivity change to SHUTDOWN  (t=+6.339719ms)
        tlogger.go:116: INFO resolver_wrapper.go:111 [core] [Channel #1320]Closing the name resolver  (t=+6.351631ms)
        tlogger.go:116: INFO balancer_wrapper.go:160 [core] [Channel #1320]ccBalancerWrapper: closing  (t=+6.365187ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1320 SubChannel #1321]Subchannel Connectivity change to SHUTDOWN  (t=+6.39883ms)
        tlogger.go:116: INFO clientconn.go:1560 [core] [Channel #1320 SubChannel #1321]Subchannel deleted  (t=+6.412184ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1320]Channel deleted  (t=+6.486062ms)
        tlogger.go:116: INFO channel.go:145 [xds] [xds-client 0xc000926240] [xds-channel 0xc000346540] Shutdown  (t=+6.50082ms)
        tlogger.go:116: INFO watch_service.go:91 [xds] [xds-resolver 0xc00052d680] Canceling watch on RouteConfiguration resource "route-my-service-fallback"  (t=+6.513123ms)
        tlogger.go:116: INFO clientimpl.go:223 [xds] [xds-client 0xc000926240] Shutdown  (t=+6.52754ms)
        tlogger.go:116: INFO xds_resolver.go:278 [xds] [xds-resolver 0xc00052d680] Shutdown  (t=+6.540504ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1319 SubChannel #1326]Subchannel Connectivity change to SHUTDOWN  (t=+6.569037ms)
        tlogger.go:116: INFO clientconn.go:1560 [core] [Channel #1319 SubChannel #1326]Subchannel deleted  (t=+6.582022ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1319]Channel deleted  (t=+6.592742ms)
        tlogger.go:116: INFO server.go:822 [core] [Server #1313 ListenSocket #1317]ListenSocket deleted  (t=+6.630242ms)
        tlogger.go:116: WARNING ads_stream.go:471 [xds] [xds-client 0xc0009e1d40] [xds-channel 0xc0005e5500] [ads-stream 0xc000b88800] Sending ADS request for type "type.googleapis.com/envoy.config.listener.v3.Listener", resources: [], version: "1", nonce: "1" failed: EOF  (t=+6.679494ms)
        tlogger.go:116: WARNING ads_stream.go:514 [xds] [xds-client 0xc0009e1d40] [xds-channel 0xc0005e5500] [ads-stream 0xc000b88800] ADS stream closed: rpc error: code = Canceled desc = context canceled  (t=+6.696526ms)
        tlogger.go:116: INFO ads_stream.go:178 [xds] [xds-client 0xc0009e1d40] [xds-channel 0xc0005e5500] [ads-stream 0xc000b88800] Stopping ADS stream  (t=+6.716463ms)
        tlogger.go:116: INFO clientconn.go:563 [core] [Channel #1315]Channel Connectivity change to SHUTDOWN  (t=+6.733164ms)
        tlogger.go:116: INFO resolver_wrapper.go:111 [core] [Channel #1315]Closing the name resolver  (t=+6.746229ms)
        tlogger.go:116: INFO balancer_wrapper.go:160 [core] [Channel #1315]ccBalancerWrapper: closing  (t=+6.759994ms)
        tlogger.go:116: INFO clientconn.go:1233 [core] [Channel #1315 SubChannel #1316]Subchannel Connectivity change to SHUTDOWN  (t=+6.807203ms)
        tlogger.go:116: INFO clientconn.go:1560 [core] [Channel #1315 SubChannel #1316]Subchannel deleted  (t=+6.8[216](https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904#step:8:217)8ms)
        tlogger.go:116: INFO clientconn.go:333 [core] [Channel #1315]Channel deleted  (t=+6.88606ms)
        tlogger.go:116: INFO channel.go:145 [xds] [xds-client 0xc0009e1d40] [xds-channel 0xc0005e5500] Shutdown  (t=+6.90202ms)
        tlogger.go:116: INFO server.go:822 [core] [Server #1313 ListenSocket #1318]ListenSocket deleted  (t=+6.917449ms)
        tlogger.go:116: INFO clientimpl.go:[223](https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904#step:8:224) [xds] [xds-client 0xc0009e1d40] Shutdown  (t=+6.939921ms)
        tlogger.go:116: INFO server.go:822 [core] [Server #1312 ListenSocket #1314]ListenSocket deleted  (t=+6.971189ms)
```

Hints from issue comments:
@purnesh42H: https://github.com/grpc/grpc-go/actions/runs/14829216216/job/41627163904@arjan-bal: http://github.com/grpc/grpc-go/actions/runs/14731655554/job/41347165010?pr=8196@arjan-bal: Based on the gRPC server seems to be listening on 2 ports:
```
stubserver.go:300: Started test service backend at "127.0.0.1:46397"
tlogger.go:116: INFO server.go:183 [xds] [xds-server 0xc00059c400] Serve() passed a net.Listener on 127.0.0.1:34455  (t=+1.072064ms)
```

This  behaviour seems to be introduced in https://github.com/grpc/grpc-go/commit/ada6787961b37252f830fb554022642dbf25933d

Update: This change doesn't seem to be the root cause though. The test flakes even when using a single port.@arjan-bal: The following log lines indicate the race that is happening:
```
tlogger.go:116: WARNING clientconn.go:1414 [core] [Channel #1319 SubChannel #1326]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:34455", ServerName: "my-service-fallback", Attributes: {"<%!p(xds.handshakeClusterNameKey={})>": "cluster-my-service-fallback" , "<%!p(xds.handshakeAttrKey={})>": "<0xc00008a340>" }, BalancerAttributes: {"<%!p(weight.attributeKey={})>": "Weight: 1" , "<%!p(internal.localityKeyType=grpc.xds.internal.address.locality)>": "<%!p(internal.LocalityID={region-1 zone-1 subzone-1})>" , "<%!p(hierarchy.pathKeyType=grpc.internal.address.hierarchical_path)>": "<0xc000636400>" , "<%!p(wrrlocality.attributeKey={})>": "Locality Weight: 1" , "<%!p(pickfirstleaf.managedByPickfirstKeyType={})>": "<%!p(bool=true)>" }}. Err: connection error: desc = "error reading server preface: read tcp 127.0.0.1:34908->127.0.0.1:34455: read: connection reset by peer"  (t=+5.885179ms)
tlogger.go:116: INFO clientconn.go:1235 [core] [Channel #1319 SubChannel #1326]Subchannel Connectivity change to TRANSIENT_FAILURE, last error: connection error: desc = "error reading server preface: read tcp 127.0.0.1:34908->127.0.0.1:34455: read: connection reset by peer"  (t=+5.911539ms)
xds_server_integration_test.go:61: Serving mode for listener "127.0.0.1:34455" changed to "SERVING", err: <nil>
```

The server is closing the connection because it hasn't received the xDS configuration and is in NON_SERVING mode. The test needs to wait for the server to enter SERVING mode before making RPCs.

Solution patch:
diff --git a/credentials/alts/alts.go b/credentials/alts/alts.go
index afcdb8a0..35539eb1 100644
--- a/credentials/alts/alts.go
+++ b/credentials/alts/alts.go
@@ -133,10 +133,11 @@ func DefaultServerOptions() *ServerOptions {
 // altsTC is the credentials required for authenticating a connection using ALTS.
 // It implements credentials.TransportCredentials interface.
 type altsTC struct {
-	info      *credentials.ProtocolInfo
-	side      core.Side
-	accounts  []string
-	hsAddress string
+	info             *credentials.ProtocolInfo
+	side             core.Side
+	accounts         []string
+	hsAddress        string
+	boundAccessToken string
 }
 
 // NewClientCreds constructs a client-side ALTS TransportCredentials object.
@@ -198,6 +199,7 @@ func (g *altsTC) ClientHandshake(ctx context.Context, addr string, rawConn net.C
 		MaxRpcVersion: maxRPCVersion,
 		MinRpcVersion: minRPCVersion,
 	}
+	opts.BoundAccessToken = g.boundAccessToken
 	chs, err := handshaker.NewClientHandshaker(ctx, hsConn, rawConn, opts)
 	if err != nil {
 		return nil, nil, err
diff --git a/credentials/alts/internal/handshaker/handshaker.go b/credentials/alts/internal/handshaker/handshaker.go
index becd2f3b..0360842e 100644
--- a/credentials/alts/internal/handshaker/handshaker.go
+++ b/credentials/alts/internal/handshaker/handshaker.go
@@ -88,6 +88,8 @@ type ClientHandshakerOptions struct {
 	TargetServiceAccounts []string
 	// RPCVersions specifies the gRPC versions accepted by the client.
 	RPCVersions *altspb.RpcProtocolVersions
+	// BoundAccessToken is a bound access token to be sent to the server for authentication.
+	BoundAccessToken string
 }
 
 // ServerHandshakerOptions contains the server handshaker options that can
@@ -195,7 +197,9 @@ func (h *altsHandshaker) ClientHandshake(ctx context.Context) (net.Conn, credent
 			},
 		},
 	}
-
+	if h.clientOpts.BoundAccessToken != "" {
+		req.GetClientStart().AccessToken = h.clientOpts.BoundAccessToken
+	}
 	conn, result, err := h.doHandshake(req)
 	if err != nil {
 		return nil, nil, err
diff --git a/stats/opentelemetry/client_metrics.go b/stats/opentelemetry/client_metrics.go
index 34e52de1..7422bebd 100644
--- a/stats/opentelemetry/client_metrics.go
+++ b/stats/opentelemetry/client_metrics.go
@@ -21,27 +21,23 @@ import (
 	"sync/atomic"
 	"time"
 
-	otelcodes "go.opentelemetry.io/otel/codes"
-	"go.opentelemetry.io/otel/trace"
+	otelattribute "go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"google.golang.org/grpc"
-	grpccodes "google.golang.org/grpc/codes"
 	estats "google.golang.org/grpc/experimental/stats"
 	istats "google.golang.org/grpc/internal/stats"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/stats"
 	"google.golang.org/grpc/status"
-
-	otelattribute "go.opentelemetry.io/otel/attribute"
-	otelmetric "go.opentelemetry.io/otel/metric"
 )
 
-type clientStatsHandler struct {
+type clientMetricsHandler struct {
 	estats.MetricsRecorder
 	options       Options
 	clientMetrics clientMetrics
 }
 
-func (h *clientStatsHandler) initializeMetrics() {
+func (h *clientMetricsHandler) initializeMetrics() {
 	// Will set no metrics to record, logically making this stats handler a
 	// no-op.
 	if h.options.MetricsOptions.MeterProvider == nil {
@@ -71,12 +67,25 @@ func (h *clientStatsHandler) initializeMetrics() {
 	rm.registerMetrics(metrics, meter)
 }
 
-func (h *clientStatsHandler) unaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
-	ci := &callInfo{
-		target: cc.CanonicalTarget(),
-		method: h.determineMethod(method, opts...),
+// getOrCreateCallInfo returns the existing callInfo from context if present,
+// or creates and attaches a new one.
+func getOrCreateCallInfo(ctx context.Context, cc *grpc.ClientConn, method string, opts ...grpc.CallOption) (context.Context, *callInfo) {
+	ci := getCallInfo(ctx)
+	if ci == nil {
+		if logger.V(2) {
+			logger.Info("Creating new CallInfo since its not present in context")
+		}
+		ci = &callInfo{
+			target: cc.CanonicalTarget(),
+			method: determineMethod(method, opts...),
+		}
+		ctx = setCallInfo(ctx, ci)
 	}
-	ctx = setCallInfo(ctx, ci)
+	return ctx, ci
+}
+
+func (h *clientMetricsHandler) unaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+	ctx, ci := getOrCreateCallInfo(ctx, cc, method, opts...)
 
 	if h.options.MetricsOptions.pluginOption != nil {
 		md := h.options.MetricsOptions.pluginOption.GetMetadata()
@@ -88,19 +97,15 @@ func (h *clientStatsHandler) unaryInterceptor(ctx context.Context, method string
 	}
 
 	startTime := time.Now()
-	var span trace.Span
-	if h.options.isTracingEnabled() {
-		ctx, span = h.createCallTraceSpan(ctx, method)
-	}
 	err := invoker(ctx, method, req, reply, cc, opts...)
-	h.perCallTracesAndMetrics(ctx, err, startTime, ci, span)
+	h.perCallMetrics(ctx, err, startTime, ci)
 	return err
 }
 
 // determineMethod determines the method to record attributes with. This will be
 // "other" if StaticMethod isn't specified or if method filter is set and
 // specifies, the method name as is otherwise.
-func (h *clientStatsHandler) determineMethod(method string, opts ...grpc.CallOption) string {
+func determineMethod(method string, opts ...grpc.CallOption) string {
 	for _, opt := range opts {
 		if _, ok := opt.(grpc.StaticMethodCallOption); ok {
 			return removeLeadingSlash(method)
@@ -109,12 +114,8 @@ func (h *clientStatsHandler) determineMethod(method string, opts ...grpc.CallOpt
 	return "other"
 }
 
-func (h *clientStatsHandler) streamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
-	ci := &callInfo{
-		target: cc.CanonicalTarget(),
-		method: h.determineMethod(method, opts...),
-	}
-	ctx = setCallInfo(ctx, ci)
+func (h *clientMetricsHandler) streamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
+	ctx, ci := getOrCreateCallInfo(ctx, cc, method, opts...)
 
 	if h.options.MetricsOptions.pluginOption != nil {
 		md := h.options.MetricsOptions.pluginOption.GetMetadata()
@@ -126,49 +127,45 @@ func (h *clientStatsHandler) streamInterceptor(ctx context.Context, desc *grpc.S
 	}
 
 	startTime := time.Now()
-	var span trace.Span
-	if h.options.isTracingEnabled() {
-		ctx, span = h.createCallTraceSpan(ctx, method)
-	}
 	callback := func(err error) {
-		h.perCallTracesAndMetrics(ctx, err, startTime, ci, span)
+		h.perCallMetrics(ctx, err, startTime, ci)
 	}
 	opts = append([]grpc.CallOption{grpc.OnFinish(callback)}, opts...)
 	return streamer(ctx, desc, cc, method, opts...)
 }
 
-// perCallTracesAndMetrics records per call trace spans and metrics.
-func (h *clientStatsHandler) perCallTracesAndMetrics(ctx context.Context, err error, startTime time.Time, ci *callInfo, ts trace.Span) {
-	if h.options.isTracingEnabled() {
-		s := status.Convert(err)
-		if s.Code() == grpccodes.OK {
-			ts.SetStatus(otelcodes.Ok, s.Message())
-		} else {
-			ts.SetStatus(otelcodes.Error, s.Message())
-		}
-		ts.End()
-	}
-	if h.options.isMetricsEnabled() {
-		callLatency := float64(time.Since(startTime)) / float64(time.Second)
-		attrs := otelmetric.WithAttributeSet(otelattribute.NewSet(
-			otelattribute.String("grpc.method", ci.method),
-			otelattribute.String("grpc.target", ci.target),
-			otelattribute.String("grpc.status", canonicalString(status.Code(err))),
-		))
-		h.clientMetrics.callDuration.Record(ctx, callLatency, attrs)
-	}
+// perCallMetrics records per call metrics for both unary and stream calls.
+func (h *clientMetricsHandler) perCallMetrics(ctx context.Context, err error, startTime time.Time, ci *callInfo) {
+	callLatency := float64(time.Since(startTime)) / float64(time.Second)
+	attrs := otelmetric.WithAttributeSet(otelattribute.NewSet(
+		otelattribute.String("grpc.method", ci.method),
+		otelattribute.String("grpc.target", ci.target),
+		otelattribute.String("grpc.status", canonicalString(status.Code(err))),
+	))
+	h.clientMetrics.callDuration.Record(ctx, callLatency, attrs)
 }
 
 // TagConn exists to satisfy stats.Handler.
-func (h *clientStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
+func (h *clientMetricsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
 	return ctx
 }
 
 // HandleConn exists to satisfy stats.Handler.
-func (h *clientStatsHandler) HandleConn(context.Context, stats.ConnStats) {}
+func (h *clientMetricsHandler) HandleConn(context.Context, stats.ConnStats) {}
+
+// getOrCreateRPCAttemptInfo retrieves or creates an rpc attemptInfo object
+// and ensures it is set in the context along with the rpcInfo.
+func getOrCreateRPCAttemptInfo(ctx context.Context) (context.Context, *attemptInfo) {
+	ri := getRPCInfo(ctx)
+	if ri != nil {
+		return ctx, ri.ai
+	}
+	ri = &rpcInfo{ai: &attemptInfo{}}
+	return setRPCInfo(ctx, ri), ri.ai
+}
 
-// TagRPC implements per RPC attempt context management.
-func (h *clientStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
+// TagRPC implements per RPC attempt context management for metrics.
+func (h *clientMetricsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
 	// Numerous stats handlers can be used for the same channel. The cluster
 	// impl balancer which writes to this will only write once, thus have this
 	// stats handler's per attempt scoped context point to the same optional
@@ -185,34 +182,25 @@ func (h *clientStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
 		}
 		ctx = istats.SetLabels(ctx, labels)
 	}
-	ai := &attemptInfo{
-		startTime: time.Now(),
-		xdsLabels: labels.TelemetryLabels,
-		method:    removeLeadingSlash(info.FullMethodName),
-	}
-	if h.options.isTracingEnabled() {
-		ctx, ai = h.traceTagRPC(ctx, ai, info.NameResolutionDelay)
-	}
-	return setRPCInfo(ctx, &rpcInfo{
-		ai: ai,
-	})
+	ctx, ai := getOrCreateRPCAttemptInfo(ctx)
+	ai.startTime = time.Now()
+	ai.xdsLabels = labels.TelemetryLabels
+	ai.method = removeLeadingSlash(info.FullMethodName)
+
+	return setRPCInfo(ctx, &rpcInfo{ai: ai})
 }
 
-func (h *clientStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
+// HandleRPC handles per RPC stats implementation.
+func (h *clientMetricsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
 	ri := getRPCInfo(ctx)
 	if ri == nil {
 		logger.Error("ctx passed into client side stats handler metrics event handling has no client attempt data present")
 		return
 	}
-	if h.options.isMetricsEnabled() {
-		h.processRPCEvent(ctx, rs, ri.ai)
-	}
-	if h.options.isTracingEnabled() {
-		populateSpan(rs, ri.ai)
-	}
+	h.processRPCEvent(ctx, rs, ri.ai)
 }
 
-func (h *clientStatsHandler) processRPCEvent(ctx context.Context, s stats.RPCStats, ai *attemptInfo) {
+func (h *clientMetricsHandler) processRPCEvent(ctx context.Context, s stats.RPCStats, ai *attemptInfo) {
 	switch st := s.(type) {
 	case *stats.Begin:
 		ci := getCallInfo(ctx)
@@ -240,7 +228,7 @@ func (h *clientStatsHandler) processRPCEvent(ctx context.Context, s stats.RPCSta
 	}
 }
 
-func (h *clientStatsHandler) setLabelsFromPluginOption(ai *attemptInfo, incomingMetadata metadata.MD) {
+func (h *clientMetricsHandler) setLabelsFromPluginOption(ai *attemptInfo, incomingMetadata metadata.MD) {
 	if ai.pluginOptionLabels == nil && h.options.MetricsOptions.pluginOption != nil {
 		labels := h.options.MetricsOptions.pluginOption.GetLabels(incomingMetadata)
 		if labels == nil {
@@ -250,7 +238,7 @@ func (h *clientStatsHandler) setLabelsFromPluginOption(ai *attemptInfo, incoming
 	}
 }
 
-func (h *clientStatsHandler) processRPCEnd(ctx context.Context, ai *attemptInfo, e *stats.End) {
+func (h *clientMetricsHandler) processRPCEnd(ctx context.Context, ai *attemptInfo, e *stats.End) {
 	ci := getCallInfo(ctx)
 	if ci == nil {
 		logger.Error("ctx passed into client side stats handler metrics event handling has no metrics data present")
diff --git a/stats/opentelemetry/client_tracing.go b/stats/opentelemetry/client_tracing.go
index 4ddaf095..868d6a2f 100644
--- a/stats/opentelemetry/client_tracing.go
+++ b/stats/opentelemetry/client_tracing.go
@@ -18,11 +18,16 @@ package opentelemetry
 
 import (
 	"context"
+	"log"
 	"strings"
 
+	otelcodes "go.opentelemetry.io/otel/codes"
 	"go.opentelemetry.io/otel/trace"
 	"google.golang.org/grpc"
+	grpccodes "google.golang.org/grpc/codes"
+	"google.golang.org/grpc/stats"
 	otelinternaltracing "google.golang.org/grpc/stats/opentelemetry/internal/tracing"
+	"google.golang.org/grpc/status"
 )
 
 const (
@@ -30,12 +35,55 @@ const (
 	tracerName                 = "grpc-go"
 )
 
+type clientTracingHandler struct {
+	options Options
+}
+
+func (h *clientTracingHandler) initializeTraces() {
+	if h.options.TraceOptions.TracerProvider == nil {
+		log.Printf("TracerProvider is not provided in client TraceOptions")
+		return
+	}
+}
+
+func (h *clientTracingHandler) unaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+	ctx, _ = getOrCreateCallInfo(ctx, cc, method, opts...)
+
+	var span trace.Span
+	ctx, span = h.createCallTraceSpan(ctx, method)
+	err := invoker(ctx, method, req, reply, cc, opts...)
+	h.finishTrace(err, span)
+	return err
+}
+
+func (h *clientTracingHandler) streamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
+	ctx, _ = getOrCreateCallInfo(ctx, cc, method, opts...)
+
+	var span trace.Span
+	ctx, span = h.createCallTraceSpan(ctx, method)
+	callback := func(err error) { h.finishTrace(err, span) }
+	opts = append([]grpc.CallOption{grpc.OnFinish(callback)}, opts...)
+	return streamer(ctx, desc, cc, method, opts...)
+}
+
+// finishTrace sets the span status based on the RPC result and ends the span.
+// It is used to finalize tracing for both unary and streaming calls.
+func (h *clientTracingHandler) finishTrace(err error, ts trace.Span) {
+	s := status.Convert(err)
+	if s.Code() == grpccodes.OK {
+		ts.SetStatus(otelcodes.Ok, s.Message())
+	} else {
+		ts.SetStatus(otelcodes.Error, s.Message())
+	}
+	ts.End()
+}
+
 // traceTagRPC populates provided context with a new span using the
 // TextMapPropagator supplied in trace options and internal itracing.carrier.
 // It creates a new outgoing carrier which serializes information about this
 // span into gRPC Metadata, if TextMapPropagator is provided in the trace
 // options. if TextMapPropagator is not provided, it returns the context as is.
-func (h *clientStatsHandler) traceTagRPC(ctx context.Context, ai *attemptInfo, nameResolutionDelayed bool) (context.Context, *attemptInfo) {
+func (h *clientTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo, nameResolutionDelayed bool) (context.Context, *attemptInfo) {
 	// Add a "Delayed name resolution complete" event to the call span
 	// if there was name resolution delay. In case of multiple retry attempts,
 	// ensure that event is added only once.
@@ -55,13 +103,34 @@ func (h *clientStatsHandler) traceTagRPC(ctx context.Context, ai *attemptInfo, n
 
 // createCallTraceSpan creates a call span to put in the provided context using
 // provided TraceProvider. If TraceProvider is nil, it returns context as is.
-func (h *clientStatsHandler) createCallTraceSpan(ctx context.Context, method string) (context.Context, trace.Span) {
-	if h.options.TraceOptions.TracerProvider == nil {
-		logger.Error("TraceProvider is not provided in trace options")
-		return ctx, nil
-	}
+func (h *clientTracingHandler) createCallTraceSpan(ctx context.Context, method string) (context.Context, trace.Span) {
 	mn := "Sent." + strings.Replace(removeLeadingSlash(method), "/", ".", -1)
 	tracer := h.options.TraceOptions.TracerProvider.Tracer(tracerName, trace.WithInstrumentationVersion(grpc.Version))
 	ctx, span := tracer.Start(ctx, mn, trace.WithSpanKind(trace.SpanKindClient))
 	return ctx, span
 }
+
+// TagConn exists to satisfy stats.Handler for tracing.
+func (h *clientTracingHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
+	return ctx
+}
+
+// HandleConn exists to satisfy stats.Handler for tracing.
+func (h *clientTracingHandler) HandleConn(context.Context, stats.ConnStats) {}
+
+// TagRPC implements per RPC attempt context management for traces.
+func (h *clientTracingHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
+	ctx, ai := getOrCreateRPCAttemptInfo(ctx)
+	ctx, ai = h.traceTagRPC(ctx, ai, info.NameResolutionDelay)
+	return setRPCInfo(ctx, &rpcInfo{ai: ai})
+}
+
+// HandleRPC handles per RPC tracing implementation.
+func (h *clientTracingHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
+	ri := getRPCInfo(ctx)
+	if ri == nil {
+		logger.Error("ctx passed into client side tracing handler trace event handling has no client attempt data present")
+		return
+	}
+	populateSpan(rs, ri.ai)
+}
diff --git a/stats/opentelemetry/opentelemetry.go b/stats/opentelemetry/opentelemetry.go
index b4b4518b..cd01f86c 100644
--- a/stats/opentelemetry/opentelemetry.go
+++ b/stats/opentelemetry/opentelemetry.go
@@ -118,10 +118,23 @@ type MetricsOptions struct {
 // MeterProvider. If the passed in Meter Provider does not have the view
 // configured for an individual metric turned on, the API call in this component
 // will create a default view for that metric.
+//
+// For the traces supported by this instrumentation code, provide an
+// implementation of a TextMapPropagator and OpenTelemetry TracerProvider.
 func DialOption(o Options) grpc.DialOption {
-	csh := &clientStatsHandler{options: o}
-	csh.initializeMetrics()
-	return joinDialOptions(grpc.WithChainUnaryInterceptor(csh.unaryInterceptor), grpc.WithChainStreamInterceptor(csh.streamInterceptor), grpc.WithStatsHandler(csh))
+	var metricsOpts, tracingOpts []grpc.DialOption
+
+	if o.isMetricsEnabled() {
+		metricsHandler := &clientMetricsHandler{options: o}
+		metricsHandler.initializeMetrics()
+		metricsOpts = append(metricsOpts, grpc.WithChainUnaryInterceptor(metricsHandler.unaryInterceptor), grpc.WithChainStreamInterceptor(metricsHandler.streamInterceptor), grpc.WithStatsHandler(metricsHandler))
+	}
+	if o.isTracingEnabled() {
+		tracingHandler := &clientTracingHandler{options: o}
+		tracingHandler.initializeTraces()
+		tracingOpts = append(tracingOpts, grpc.WithChainUnaryInterceptor(tracingHandler.unaryInterceptor), grpc.WithChainStreamInterceptor(tracingHandler.streamInterceptor), grpc.WithStatsHandler(tracingHandler))
+	}
+	return joinDialOptions(append(metricsOpts, tracingOpts...)...)
 }
 
 var joinServerOptions = internal.JoinServerOptions.(func(...grpc.ServerOption) grpc.ServerOption)
@@ -138,10 +151,23 @@ var joinServerOptions = internal.JoinServerOptions.(func(...grpc.ServerOption) g
 // MeterProvider. If the passed in Meter Provider does not have the view
 // configured for an individual metric turned on, the API call in this component
 // will create a default view for that metric.
+//
+// For the traces supported by this instrumentation code, provide an
+// implementation of a TextMapPropagator and OpenTelemetry TracerProvider.
 func ServerOption(o Options) grpc.ServerOption {
-	ssh := &serverStatsHandler{options: o}
-	ssh.initializeMetrics()
-	return joinServerOptions(grpc.ChainUnaryInterceptor(ssh.unaryInterceptor), grpc.ChainStreamInterceptor(ssh.streamInterceptor), grpc.StatsHandler(ssh))
+	var metricsOpts, tracingOpts []grpc.ServerOption
+
+	if o.isMetricsEnabled() {
+		metricsHandler := &serverMetricsHandler{options: o}
+		metricsHandler.initializeMetrics()
+		metricsOpts = append(metricsOpts, grpc.ChainUnaryInterceptor(metricsHandler.unaryInterceptor), grpc.ChainStreamInterceptor(metricsHandler.streamInterceptor), grpc.StatsHandler(metricsHandler))
+	}
+	if o.isTracingEnabled() {
+		tracingHandler := &serverTracingHandler{options: o}
+		tracingHandler.initializeTraces()
+		tracingOpts = append(tracingOpts, grpc.StatsHandler(tracingHandler))
+	}
+	return joinServerOptions(append(metricsOpts, tracingOpts...)...)
 }
 
 // callInfo is information pertaining to the lifespan of the RPC client side.
diff --git a/stats/opentelemetry/server_metrics.go b/stats/opentelemetry/server_metrics.go
index da3f60a9..a02fc33b 100644
--- a/stats/opentelemetry/server_metrics.go
+++ b/stats/opentelemetry/server_metrics.go
@@ -21,24 +21,24 @@ import (
 	"sync/atomic"
 	"time"
 
+	otelattribute "go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
+
 	"google.golang.org/grpc"
 	estats "google.golang.org/grpc/experimental/stats"
 	"google.golang.org/grpc/internal"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/stats"
 	"google.golang.org/grpc/status"
-
-	otelattribute "go.opentelemetry.io/otel/attribute"
-	otelmetric "go.opentelemetry.io/otel/metric"
 )
 
-type serverStatsHandler struct {
+type serverMetricsHandler struct {
 	estats.MetricsRecorder
 	options       Options
 	serverMetrics serverMetrics
 }
 
-func (h *serverStatsHandler) initializeMetrics() {
+func (h *serverMetricsHandler) initializeMetrics() {
 	// Will set no metrics to record, logically making this stats handler a
 	// no-op.
 	if h.options.MetricsOptions.MeterProvider == nil {
@@ -90,7 +90,7 @@ func (s *attachLabelsTransportStream) SendHeader(md metadata.MD) error {
 	return s.ServerTransportStream.SendHeader(md)
 }
 
-func (h *serverStatsHandler) unaryInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
+func (h *serverMetricsHandler) unaryInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
 	var metadataExchangeLabels metadata.MD
 	if h.options.MetricsOptions.pluginOption != nil {
 		metadataExchangeLabels = h.options.MetricsOptions.pluginOption.GetMetadata()
@@ -151,7 +151,7 @@ func (s *attachLabelsStream) SendMsg(m any) error {
 	return s.ServerStream.SendMsg(m)
 }
 
-func (h *serverStatsHandler) streamInterceptor(srv any, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
+func (h *serverMetricsHandler) streamInterceptor(srv any, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
 	var metadataExchangeLabels metadata.MD
 	if h.options.MetricsOptions.pluginOption != nil {
 		metadataExchangeLabels = h.options.MetricsOptions.pluginOption.GetMetadata()
@@ -171,15 +171,15 @@ func (h *serverStatsHandler) streamInterceptor(srv any, ss grpc.ServerStream, _
 }
 
 // TagConn exists to satisfy stats.Handler.
-func (h *serverStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
+func (h *serverMetricsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
 	return ctx
 }
 
 // HandleConn exists to satisfy stats.Handler.
-func (h *serverStatsHandler) HandleConn(context.Context, stats.ConnStats) {}
+func (h *serverMetricsHandler) HandleConn(context.Context, stats.ConnStats) {}
 
-// TagRPC implements per RPC context management.
-func (h *serverStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
+// TagRPC implements per RPC context management for metrics.
+func (h *serverMetricsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
 	method := info.FullMethodName
 	if h.options.MetricsOptions.MethodAttributeFilter != nil {
 		if !h.options.MetricsOptions.MethodAttributeFilter(method) {
@@ -196,35 +196,24 @@ func (h *serverStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
 			method = "other"
 		}
 	}
+	ctx, ai := getOrCreateRPCAttemptInfo(ctx)
+	ai.startTime = time.Now()
+	ai.method = removeLeadingSlash(method)
 
-	ai := &attemptInfo{
-		startTime: time.Now(),
-		method:    removeLeadingSlash(method),
-	}
-	if h.options.isTracingEnabled() {
-		ctx, ai = h.traceTagRPC(ctx, ai)
-	}
-	return setRPCInfo(ctx, &rpcInfo{
-		ai: ai,
-	})
+	return setRPCInfo(ctx, &rpcInfo{ai: ai})
 }
 
-// HandleRPC implements per RPC tracing and stats implementation.
-func (h *serverStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
+// HandleRPC handles per RPC stats implementation.
+func (h *serverMetricsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
 	ri := getRPCInfo(ctx)
 	if ri == nil {
 		logger.Error("ctx passed into server side stats handler metrics event handling has no server call data present")
 		return
 	}
-	if h.options.isTracingEnabled() {
-		populateSpan(rs, ri.ai)
-	}
-	if h.options.isMetricsEnabled() {
-		h.processRPCData(ctx, rs, ri.ai)
-	}
+	h.processRPCData(ctx, rs, ri.ai)
 }
 
-func (h *serverStatsHandler) processRPCData(ctx context.Context, s stats.RPCStats, ai *attemptInfo) {
+func (h *serverMetricsHandler) processRPCData(ctx context.Context, s stats.RPCStats, ai *attemptInfo) {
 	switch st := s.(type) {
 	case *stats.InHeader:
 		if ai.pluginOptionLabels == nil && h.options.MetricsOptions.pluginOption != nil {
@@ -248,7 +237,7 @@ func (h *serverStatsHandler) processRPCData(ctx context.Context, s stats.RPCStat
 	}
 }
 
-func (h *serverStatsHandler) processRPCEnd(ctx context.Context, ai *attemptInfo, e *stats.End) {
+func (h *serverMetricsHandler) processRPCEnd(ctx context.Context, ai *attemptInfo, e *stats.End) {
 	latency := float64(time.Since(ai.startTime)) / float64(time.Second)
 	st := "OK"
 	if e.Error != nil {
diff --git a/stats/opentelemetry/server_tracing.go b/stats/opentelemetry/server_tracing.go
index 26185519..0e2181bf 100644
--- a/stats/opentelemetry/server_tracing.go
+++ b/stats/opentelemetry/server_tracing.go
@@ -18,13 +18,33 @@ package opentelemetry
 
 import (
 	"context"
+	"log"
 	"strings"
 
 	"go.opentelemetry.io/otel/trace"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/stats"
 	otelinternaltracing "google.golang.org/grpc/stats/opentelemetry/internal/tracing"
 )
 
+type serverTracingHandler struct {
+	options Options
+}
+
+func (h *serverTracingHandler) initializeTraces() {
+	if h.options.TraceOptions.TracerProvider == nil {
+		log.Printf("TracerProvider is not provided in server TraceOptions")
+		return
+	}
+}
+
+// TagRPC implements per RPC attempt context management for traces.
+func (h *serverTracingHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) context.Context {
+	ctx, ai := getOrCreateRPCAttemptInfo(ctx)
+	ctx, ai = h.traceTagRPC(ctx, ai)
+	return setRPCInfo(ctx, &rpcInfo{ai: ai})
+}
+
 // traceTagRPC populates context with new span data using the TextMapPropagator
 // supplied in trace options and internal itracing.Carrier. It creates a new
 // incoming carrier which extracts an existing span context (if present) by
@@ -32,7 +52,7 @@ import (
 // is set as parent of the new span otherwise new span remains the root span.
 // If TextMapPropagator is not provided in the trace options, it returns context
 // as is.
-func (h *serverStatsHandler) traceTagRPC(ctx context.Context, ai *attemptInfo) (context.Context, *attemptInfo) {
+func (h *serverTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo) (context.Context, *attemptInfo) {
 	mn := "Recv." + strings.Replace(ai.method, "/", ".", -1)
 	var span trace.Span
 	tracer := h.options.TraceOptions.TracerProvider.Tracer(tracerName, trace.WithInstrumentationVersion(grpc.Version))
@@ -44,3 +64,21 @@ func (h *serverStatsHandler) traceTagRPC(ctx context.Context, ai *attemptInfo) (
 	ai.traceSpan = span
 	return ctx, ai
 }
+
+// HandleRPC handles per RPC tracing implementation.
+func (h *serverTracingHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
+	ri := getRPCInfo(ctx)
+	if ri == nil {
+		logger.Error("ctx passed into server side tracing handler trace event handling has no server call data present")
+		return
+	}
+	populateSpan(rs, ri.ai)
+}
+
+// TagConn exists to satisfy stats.Handler for tracing.
+func (h *serverTracingHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {
+	return ctx
+}
+
+// HandleConn exists to satisfy stats.Handler for tracing.
+func (h *serverTracingHandler) HandleConn(context.Context, stats.ConnStats) {}



Test patch:
diff --git a/credentials/alts/alts_test.go b/credentials/alts/alts_test.go
index 48f871a0..4710a32b 100644
--- a/credentials/alts/alts_test.go
+++ b/credentials/alts/alts_test.go
@@ -336,6 +336,28 @@ func (s) TestFullHandshake(t *testing.T) {
 	}
 }
 
+// TestHandshakeWithAccessToken performs an ALTS handshake between a test client and
+// server, where both client and server offload to a local, fake handshaker
+// service, and expects the StartClient request to include a bound access token.
+func (s) TestHandshakeWithAccessToken(t *testing.T) {
+	// Start the fake handshaker service and the server.
+	var wait sync.WaitGroup
+	defer wait.Wait()
+	boundAccessToken := "fake-bound-access-token"
+	stopHandshaker, handshakerAddress := startFakeHandshakerServiceWithExpectedBoundAccessToken(t, &wait, boundAccessToken)
+	defer stopHandshaker()
+	stopServer, serverAddress := startServer(t, handshakerAddress)
+	defer stopServer()
+
+	// Ping the server, authenticating with ALTS and a bound access token.
+	establishAltsConnectionWithBoundAccessToken(t, handshakerAddress, serverAddress, boundAccessToken)
+
+	// Close open connections to the fake handshaker service.
+	if err := service.CloseForTesting(); err != nil {
+		t.Errorf("service.CloseForTesting() failed: %v", err)
+	}
+}
+
 // TestConcurrentHandshakes performs a several, concurrent ALTS handshakes
 // between a test client and server, where both client and server offload to a
 // local, fake handshaker service.
@@ -385,7 +407,15 @@ func versions(minMajor, minMinor, maxMajor, maxMinor uint32) *altspb.RpcProtocol
 }
 
 func establishAltsConnection(t *testing.T, handshakerAddress, serverAddress string) {
+	establishAltsConnectionWithBoundAccessToken(t, handshakerAddress, serverAddress, "")
+}
+
+func establishAltsConnectionWithBoundAccessToken(t *testing.T, handshakerAddress, serverAddress, boundAccessToken string) {
 	clientCreds := NewClientCreds(&ClientOptions{HandshakerServiceAddress: handshakerAddress})
+	if boundAccessToken != "" {
+		altsCreds := clientCreds.(*altsTC)
+		altsCreds.boundAccessToken = boundAccessToken
+	}
 	conn, err := grpc.NewClient(serverAddress, grpc.WithTransportCredentials(clientCreds))
 	if err != nil {
 		t.Fatalf("grpc.NewClient(%v) failed: %v", serverAddress, err)
@@ -429,12 +459,20 @@ func establishAltsConnection(t *testing.T, handshakerAddress, serverAddress stri
 }
 
 func startFakeHandshakerService(t *testing.T, wait *sync.WaitGroup) (stop func(), address string) {
+	return startFakeHandshakerServiceWithExpectedBoundAccessToken(t, wait, "")
+}
+
+func startFakeHandshakerServiceWithExpectedBoundAccessToken(t *testing.T, wait *sync.WaitGroup, boundAccessToken string) (stop func(), address string) {
 	listener, err := testutils.LocalTCPListener()
 	if err != nil {
 		t.Fatalf("LocalTCPListener() failed: %v", err)
 	}
 	s := grpc.NewServer()
-	altsgrpc.RegisterHandshakerServiceServer(s, &testutil.FakeHandshaker{})
+	hs := &testutil.FakeHandshaker{}
+	if boundAccessToken != "" {
+		hs.ExpectedBoundAccessToken = boundAccessToken
+	}
+	altsgrpc.RegisterHandshakerServiceServer(s, hs)
 	wait.Add(1)
 	go func() {
 		defer wait.Done()
diff --git a/credentials/alts/internal/testutil/testutil.go b/credentials/alts/internal/testutil/testutil.go
index 8ab94133..1dfccdc0 100644
--- a/credentials/alts/internal/testutil/testutil.go
+++ b/credentials/alts/internal/testutil/testutil.go
@@ -145,6 +145,8 @@ func MakeFrame(pl string) []byte {
 // FakeHandshaker is a fake implementation of the ALTS handshaker service.
 type FakeHandshaker struct {
 	altsgrpc.HandshakerServiceServer
+	// ExpectedBoundAccessToken is the expected bound access token in the ClientStart request.
+	ExpectedBoundAccessToken string
 }
 
 // DoHandshake performs a fake ALTS handshake.
@@ -221,6 +223,9 @@ func (h *FakeHandshaker) processStartClient(req *altspb.StartClientHandshakeReq)
 	if len(req.RecordProtocols) != 1 || req.RecordProtocols[0] != "ALTSRP_GCM_AES128_REKEY" {
 		return nil, fmt.Errorf("unexpected record protocols: %v", req.RecordProtocols)
 	}
+	if h.ExpectedBoundAccessToken != req.GetAccessToken() {
+		return nil, fmt.Errorf("unexpected access token: %v", req.GetAccessToken())
+	}
 	return &altspb.HandshakerResp{
 		OutFrames:     []byte("ClientInit"),
 		BytesConsumed: 0,
diff --git a/stats/opentelemetry/metricsregistry_test.go b/stats/opentelemetry/metricsregistry_test.go
index 67a2d7fa..497005b1 100644
--- a/stats/opentelemetry/metricsregistry_test.go
+++ b/stats/opentelemetry/metricsregistry_test.go
@@ -47,11 +47,11 @@ type metricsRecorderForTest interface {
 }
 
 func newClientStatsHandler(options MetricsOptions) metricsRecorderForTest {
-	return &clientStatsHandler{options: Options{MetricsOptions: options}}
+	return &clientMetricsHandler{options: Options{MetricsOptions: options}}
 }
 
 func newServerStatsHandler(options MetricsOptions) metricsRecorderForTest {
-	return &serverStatsHandler{options: Options{MetricsOptions: options}}
+	return &serverMetricsHandler{options: Options{MetricsOptions: options}}
 }
 
 // TestMetricsRegistryMetrics tests the OpenTelemetry behavior with respect to
diff --git a/test/xds/xds_server_integration_test.go b/test/xds/xds_server_integration_test.go
index 3f3849db..f2976a5e 100644
--- a/test/xds/xds_server_integration_test.go
+++ b/test/xds/xds_server_integration_test.go
@@ -83,7 +83,7 @@ func (l *acceptNotifyingListener) Accept() (net.Conn, error) {
 // Returns the following:
 // - local listener on which the xDS-enabled gRPC server is serving on
 // - cleanup function to be invoked by the tests when done
-func setupGRPCServer(t *testing.T, bootstrapContents []byte) (net.Listener, func()) {
+func setupGRPCServer(t *testing.T, bootstrapContents []byte, opts ...grpc.ServerOption) (net.Listener, func()) {
 	t.Helper()
 
 	// Configure xDS credentials to be used on the server-side.
@@ -113,12 +113,15 @@ func setupGRPCServer(t *testing.T, bootstrapContents []byte) (net.Listener, func
 		},
 	}
 
-	if stub.S, err = xds.NewGRPCServer(grpc.Creds(creds), testModeChangeServerOption(t), xds.BootstrapContentsForTesting(bootstrapContents)); err != nil {
+	opts = append([]grpc.ServerOption{
+		grpc.Creds(creds),
+		testModeChangeServerOption(t),
+		xds.BootstrapContentsForTesting(bootstrapContents),
+	}, opts...)
+	if stub.S, err = xds.NewGRPCServer(opts...); err != nil {
 		t.Fatalf("Failed to create an xDS enabled gRPC server: %v", err)
 	}
 
-	stubserver.StartTestService(t, stub)
-
 	// Create a local listener and pass it to Serve().
 	lis, err := testutils.LocalTCPListener()
 	if err != nil {
@@ -130,11 +133,8 @@ func setupGRPCServer(t *testing.T, bootstrapContents []byte) (net.Listener, func
 		serverReady: *grpcsync.NewEvent(),
 	}
 
-	go func() {
-		if err := stub.S.Serve(readyLis); err != nil {
-			t.Errorf("Serve() failed: %v", err)
-		}
-	}()
+	stub.Listener = readyLis
+	stubserver.StartTestService(t, stub)
 
 	// Wait for the server to start running.
 	select {
diff --git a/test/xds/xds_server_rbac_test.go b/test/xds/xds_server_rbac_test.go
index c0e51f5f..55174ed5 100644
--- a/test/xds/xds_server_rbac_test.go
+++ b/test/xds/xds_server_rbac_test.go
@@ -33,12 +33,14 @@ import (
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/authz/audit"
 	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/connectivity"
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/internal"
 	"google.golang.org/grpc/internal/testutils"
 	"google.golang.org/grpc/internal/testutils/xds/e2e"
 	"google.golang.org/grpc/internal/testutils/xds/e2e/setup"
 	"google.golang.org/grpc/status"
+	"google.golang.org/grpc/xds"
 	"google.golang.org/protobuf/types/known/anypb"
 	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
@@ -842,10 +844,23 @@ func serverListenerWithBadRouteConfiguration(t *testing.T, host string, port uin
 
 func (s) TestRBAC_WithBadRouteConfiguration(t *testing.T) {
 	managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolver(t)
-
-	lis, cleanup2 := setupGRPCServer(t, bootstrapContents)
+	// We need to wait for the server to enter SERVING mode before making RPCs
+	// to avoid flakes due to the server closing connections.
+	servingCh := make(chan struct{})
+
+	// Initialize a test gRPC server, assign it to the stub server, and start
+	// the test service.
+	opt := xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) {
+		if args.Mode == connectivity.ServingModeServing {
+			close(servingCh)
+		}
+	})
+	lis, cleanup2 := setupGRPCServer(t, bootstrapContents, opt)
 	defer cleanup2()
 
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+
 	host, port, err := hostPortFromListener(lis)
 	if err != nil {
 		t.Fatalf("failed to retrieve host and port of server: %v", err)
@@ -867,13 +882,17 @@ func (s) TestRBAC_WithBadRouteConfiguration(t *testing.T) {
 	inboundLis := serverListenerWithBadRouteConfiguration(t, host, port)
 	resources.Listeners = append(resources.Listeners, inboundLis)
 
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
 	// Setup the management server with client and server-side resources.
 	if err := managementServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
 
+	select {
+	case <-ctx.Done():
+		t.Fatal("Timeout waiting for the xDS-enabled gRPC server to go SERVING")
+	case <-servingCh:
+	}
+
 	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(xdsResolver))
 	if err != nil {
 		t.Fatalf("grpc.NewClient() failed: %v", err)