code: @GuardedBy("lock")
    private void scheduleBackoff(final Status status) {
      class EndOfCurrentBackoff implements Runnable {
        @Override
        public void run() {
          try {
            synchronized (lock) {
              reconnectTask = null;
              if (reconnectCanceled) {
                // Even though cancelReconnectTask() will cancel this task, the task may have already
                // started when it's being canceled.
                return;
              }
              channelLogger.log(ChannelLogLevel.INFO, "CONNECTING after backoff");
              gotoNonErrorState(CONNECTING);
              startNewTransport();
            }
          } catch (Throwable t) {
            // TODO(zhangkun): we may consider using SynchronizationContext to schedule the reconnect
            // timer, so that we don't need this catch, since SynchronizationContext would catch it.
            log.log(Level.WARNING, "Exception handling end of backoff", t);
          } finally {
            syncContext.drain();
          }
        }
      }
  
      gotoState(ConnectivityStateInfo.forTransientFailure(status));
      if (reconnectPolicy == null) {
        reconnectPolicy = backoffPolicyProvider.get();
      }
      long delayNanos =
          reconnectPolicy.nextBackoffNanos() - connectingTimer.elapsed(TimeUnit.NANOSECONDS);
      channelLogger.log(
          ChannelLogLevel.INFO,
          "TRANSIENT_FAILURE ({0}). Will reconnect after {1} ns",
          printShortStatus(status), delayNanos);
      Preconditions.checkState(reconnectTask == null, "previous reconnectTask is not done");
      reconnectCanceled = false;
      reconnectTask = scheduledExecutor.schedule(
          new LogExceptionRunnable(new EndOfCurrentBackoff()),
          delayNanos,
          TimeUnit.NANOSECONDS);
    }
docs: Only called after all addresses attempted and failed (TRANSIENT_FAILURE).
  @param status the causal status when the channel begins transition to
  TRANSIENT_FAILURE.