Commit 915c6928 authored by Derek Cheng's avatar Derek Cheng Committed by Commit Bot

[CastMRP] Fix a couple of bugs in CMSSImpl.

- On channel error, erase the sink from |current_sinks_map|.
  crrev.com/541271 added a check in OpenChannel() to short circuit if
  a sink already exists on the map. In order for retry on error to kick
  in properly, we will need to erase the entry in OnError().
- In OnChannelOpenSucceeded, erase stale sink with the same IP. This is
  done to maintain the invariant of having at most 1 sink of a given ID
  in the map. Stale sink is defined as having the same sink ID but
  different IP endpoint than the current one.
- In OnChannelOpenFailed, when erasing the sink from
  |current_sinks_map_|, also verify the sink ID. It is possible that
  a different sink now occcupies the IP endpoint that it is trying to
  erase.

Change-Id: I12bfd5af5f664c4773e7587dc30c6709d2352021
Bug: 698940
Reviewed-on: https://chromium-review.googlesource.com/961555Reviewed-by: default avatarBin Zhao <zhaobin@chromium.org>
Commit-Queue: Derek Cheng <imcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543439}
parent 82b329f0
......@@ -350,6 +350,16 @@ void CastMediaSinkServiceImpl::OnError(const cast_channel::CastSocket& socket,
base::BindOnce(&CastMediaSinkServiceImpl::OpenChannel, GetWeakPtr(),
ip_endpoint, cast_sink_it->second, nullptr,
SinkSource::kConnectionRetry));
// We erase the sink here so that OpenChannel would not find an existing
// sink.
// Note: a better longer term solution is to introduce a state field to the
// sink. We would set it to ERROR here. In OpenChannel(), we would check
// create a socket only if the state is not already CONNECTED.
if (observer_)
observer_->OnSinkRemoved(cast_sink_it->second);
current_sinks_map_.erase(cast_sink_it);
MediaSinkServiceBase::RestartTimer();
return;
}
......@@ -382,6 +392,9 @@ void CastMediaSinkServiceImpl::OnNetworksChanged(
}
sink_cache_[last_network_id] = std::move(current_sinks);
}
// TODO(imcheng): Maybe this should clear |current_sinks_map_| and call
// |RestartTimer()| so it is more responsive?
if (IsNetworkIdUnknownOrDisconnected(network_id))
return;
......@@ -511,7 +524,7 @@ void CastMediaSinkServiceImpl::OnChannelErrorMayRetry(
<< ip_endpoint.ToString() << " [error_state]: "
<< cast_channel::ChannelErrorToString(error_state);
OnChannelOpenFailed(ip_endpoint);
OnChannelOpenFailed(ip_endpoint, cast_sink);
CastAnalytics::RecordCastChannelConnectResult(
MediaRouterChannelConnectResults::FAILURE);
return;
......@@ -572,6 +585,18 @@ void CastMediaSinkServiceImpl::OnChannelOpenSucceeded(
sink_it->second = cast_sink;
}
// If the sink was under a different IP address previously, remove it from
// |current_sinks_map_|.
auto old_sink_it = std::find_if(
current_sinks_map_.begin(), current_sinks_map_.end(),
[&cast_sink, &ip_endpoint](
const std::pair<net::IPEndPoint, MediaSinkInternal>& entry) {
return !(entry.first == ip_endpoint) &&
entry.second.sink().id() == cast_sink.sink().id();
});
if (old_sink_it != current_sinks_map_.end())
current_sinks_map_.erase(old_sink_it);
if (observer_)
observer_->OnSinkAddedOrUpdated(cast_sink, socket);
......@@ -580,9 +605,14 @@ void CastMediaSinkServiceImpl::OnChannelOpenSucceeded(
}
void CastMediaSinkServiceImpl::OnChannelOpenFailed(
const net::IPEndPoint& ip_endpoint) {
const net::IPEndPoint& ip_endpoint,
const MediaSinkInternal& sink) {
// It is possible that the old sink in |current_sinks_map_| is replaced with
// a new sink if a network change happened. Check the sink ID to make sure
// this is the sink we want to erase.
auto it = current_sinks_map_.find(ip_endpoint);
if (it == current_sinks_map_.end())
if (it == current_sinks_map_.end() ||
it->second.sink().id() != sink.sink().id())
return;
if (observer_)
......
......@@ -169,6 +169,10 @@ class CastMediaSinkServiceImpl
TestInitRetryParametersWithDefaultValue);
FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
TestOnDialSinkAddedSkipsIfNonCastDevice);
FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
TestOnChannelErrorRetry);
FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
OpenChannelNewIPSameSink);
// Holds Finch field trial parameters controlling Cast channel retry strategy.
struct RetryParams {
......@@ -276,7 +280,7 @@ class CastMediaSinkServiceImpl
cast_channel::ChannelError error_state,
SinkSource sink_source);
// Invoked when opening cast channel on IO thread succeeds.
// Invoked when opening cast channel succeeds.
// |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
// |socket|: raw pointer of newly created cast channel. Does not take
// ownership of |socket|.
......@@ -284,11 +288,12 @@ class CastMediaSinkServiceImpl
cast_channel::CastSocket* socket,
SinkSource sink_source);
// Invoked when opening cast channel on IO thread fails after all retry
// Invoked when opening cast channel fails after all retry
// attempts.
// |ip_endpoint|: ip endpoint of cast channel failing to connect to.
// |sink_source|: Method of sink discovery.
void OnChannelOpenFailed(const net::IPEndPoint& ip_endpoint);
// |sink|: The sink for which channel open failed.
void OnChannelOpenFailed(const net::IPEndPoint& ip_endpoint,
const MediaSinkInternal& sink);
// Returns whether the given DIAL-discovered |sink| is probably a non-Cast
// device. This is heuristically determined by two things: |sink| has been
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment