Merge "Handle setting and deprecating local DNS IPv6 addresses" into nyc-mr1-dev

This commit is contained in:
Erik Kline
2016-08-10 00:47:56 +00:00
committed by Android (Google) Code Review
4 changed files with 329 additions and 159 deletions

View File

@@ -1392,6 +1392,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
for (Integer netType : mUpstreamIfaceTypes) {
NetworkInfo info = cm.getNetworkInfo(netType.intValue());
// TODO: if the network is suspended we should consider
// that to be the same as connected here.
if ((info != null) && info.isConnected()) {
upType = netType.intValue();
break;
@@ -1465,6 +1467,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
// it immediately, because there likely will be no second
// EVENT_ON_AVAILABLE (it was already received).
handleNewUpstreamNetworkState(ns);
} else if (mCurrentUpstreamIface == null) {
// There are no available upstream networks, or none that
// have an IPv4 default route (current metric for success).
handleNewUpstreamNetworkState(null);
}
}
@@ -1639,6 +1645,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
}
@Override
public void exit() {
// TODO: examine if we should check the return value.
@@ -1646,7 +1653,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
mUpstreamNetworkMonitor.stop();
stopListeningForSimChanges();
notifyTetheredOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
@@ -1734,6 +1743,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
// reevaluation is triggered via received CONNECTIVITY_ACTION
// broadcasts that result in being passed a
// TetherMasterSM.CMD_UPSTREAM_CHANGED.
handleNewUpstreamNetworkState(null);
break;
default:
break;

View File

@@ -55,7 +55,7 @@ public class IPv6TetheringCoordinator {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
if (ns == null || ns.network == null) {
if (!canTetherIPv6(ns)) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
@@ -65,8 +65,9 @@ public class IPv6TetheringCoordinator {
!ns.network.equals(mUpstreamNetworkState.network)) {
stopIPv6TetheringOnAllInterfaces();
}
setUpstreamNetworkState(ns);
maybeUpdateIPv6TetheringInterfaces();
updateIPv6TetheringInterfaces();
}
private void stopIPv6TetheringOnAllInterfaces() {
@@ -77,9 +78,10 @@ public class IPv6TetheringCoordinator {
}
private void setUpstreamNetworkState(NetworkState ns) {
if (!canTetherIPv6(ns)) {
if (ns == null) {
mUpstreamNetworkState = null;
} else {
// Make a deep copy of the parts we need.
mUpstreamNetworkState = new NetworkState(
null,
new LinkProperties(ns.linkProperties),
@@ -94,19 +96,17 @@ public class IPv6TetheringCoordinator {
}
}
private void maybeUpdateIPv6TetheringInterfaces() {
if (mUpstreamNetworkState == null) return;
private void updateIPv6TetheringInterfaces() {
for (TetherInterfaceStateMachine sm : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType());
if (lp != null) {
sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
}
sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) {
if (mUpstreamNetworkState == null) return null;
// NOTE: Here, in future, we would have policies to decide how to divvy
// up the available dedicated prefixes among downstream interfaces.
// At this time we have no such mechanism--we only support tethering

View File

@@ -16,6 +16,7 @@
package com.android.server.connectivity.tethering;
import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -27,13 +28,16 @@ import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
/**
@@ -41,13 +45,15 @@ import java.util.HashSet;
*/
class IPv6TetheringInterfaceServices {
private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
private static final int RFC7421_IP_PREFIX_LENGTH = 64;
private final String mIfName;
private final INetworkManagementService mNMService;
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
private ArrayList<RouteInfo> mLastLocalRoutes;
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
@@ -86,8 +92,7 @@ class IPv6TetheringInterfaceServices {
public void stop() {
mNetworkInterface = null;
mHwAddr = null;
updateLocalRoutes(null);
updateRaParams(null);
setRaParams(null);
if (mRaDaemon != null) {
mRaDaemon.stop();
@@ -104,95 +109,178 @@ class IPv6TetheringInterfaceServices {
public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
if (mRaDaemon == null) return;
if (v6only == null) {
updateLocalRoutes(null);
updateRaParams(null);
// Avoid unnecessary work on spurious updates.
if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
return;
}
RaParams params = new RaParams();
params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
RaParams params = null;
ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
final IpPrefix prefix = new IpPrefix(linkAddr.getAddress(),
linkAddr.getPrefixLength());
if (v6only != null) {
params = new RaParams();
params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
// Accumulate routes representing "prefixes to be assigned to the
// local interface", for subsequent addition to the local network
// in the routing rules.
localRoutes.add(new RouteInfo(prefix, null, mIfName));
for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
params.prefixes.add(prefix);
final IpPrefix prefix = new IpPrefix(
linkAddr.getAddress(), linkAddr.getPrefixLength());
params.prefixes.add(prefix);
final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
if (dnsServer != null) {
params.dnses.add(dnsServer);
}
}
}
// If v6only is null, we pass in null to setRaParams(), which handles
// deprecation of any existing RA data.
// We need to be able to send unicast RAs, and clients might like to
// ping the default router's link-local address, so add that as well.
localRoutes.add(new RouteInfo(new IpPrefix("fe80::/64"), null, mIfName));
setRaParams(params);
mLastIPv6LinkProperties = v6only;
}
// TODO: Add a local interface address, update dnsmasq to listen on the
// new address, and use only that address as a DNS server.
for (InetAddress dnsServer : v6only.getDnsServers()) {
if (dnsServer instanceof Inet6Address) {
params.dnses.add((Inet6Address) dnsServer);
private void configureLocalRoutes(
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
// [1] Remove the routes that are deprecated.
if (!deprecatedPrefixes.isEmpty()) {
final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
try {
final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
if (removalFailures > 0) {
Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
removalFailures));
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
}
}
updateLocalRoutes(localRoutes);
updateRaParams(params);
}
// [2] Add only the routes that have not previously been added.
if (newPrefixes != null && !newPrefixes.isEmpty()) {
HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
if (mLastRaParams != null) {
addedPrefixes.removeAll(mLastRaParams.prefixes);
}
private void updateLocalRoutes(ArrayList<RouteInfo> localRoutes) {
if (localRoutes != null) {
// TODO: Compare with mLastLocalRoutes and take appropriate
// appropriate action on the difference between the two.
if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
// We need to be able to send unicast RAs, and clients might
// like to ping the default router's link-local address. Note
// that we never remove the link-local route from the network
// until Tethering disables tethering on the interface.
addedPrefixes.add(LINK_LOCAL_PREFIX);
}
if (!localRoutes.isEmpty()) {
if (!addedPrefixes.isEmpty()) {
final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
try {
mNMService.addInterfaceToLocalNetwork(mIfName, localRoutes);
// It's safe to call addInterfaceToLocalNetwork() even if
// the interface is already in the local_network.
mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
} catch (RemoteException e) {
Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
}
}
} else {
if (mLastLocalRoutes != null && !mLastLocalRoutes.isEmpty()) {
try {
final int removalFailures =
mNMService.removeRoutesFromLocalNetwork(mLastLocalRoutes);
if (removalFailures > 0) {
Log.e(TAG,
String.format("Failed to remove %d IPv6 routes from local table.",
removalFailures));
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
}
}
}
mLastLocalRoutes = localRoutes;
}
private void updateRaParams(RaParams params) {
if (mRaDaemon != null) {
HashSet<IpPrefix> deprecated = null;
if (mLastRaParams != null) {
deprecated = new HashSet<>();
for (IpPrefix ipp : mLastRaParams.prefixes) {
if (params == null || !params.prefixes.contains(ipp)) {
deprecated.add(ipp);
}
}
}
// Currently, we send spurious RAs (5) whenever there's any update.
// TODO: Compare params with mLastParams to avoid spurious updates.
mRaDaemon.buildNewRa(params, deprecated);
private void configureLocalDns(
HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
INetd netd = getNetdServiceOrNull();
if (netd == null) {
if (newDnses != null) newDnses.clear();
Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
return;
}
mLastRaParams = params;
// [1] Remove deprecated local DNS IP addresses.
if (!deprecatedDnses.isEmpty()) {
for (Inet6Address dns : deprecatedDnses) {
final String dnsString = dns.getHostAddress();
try {
netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
} catch (RemoteException e) {
Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
}
}
}
// [2] Add only the local DNS IP addresses that have not previously been added.
if (newDnses != null && !newDnses.isEmpty()) {
final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
if (mLastRaParams != null) {
addedDnses.removeAll(mLastRaParams.dnses);
}
for (Inet6Address dns : addedDnses) {
final String dnsString = dns.getHostAddress();
try {
netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
} catch (RemoteException e) {
Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
newDnses.remove(dns);
}
}
}
try {
netd.tetherApplyDnsInterfaces();
} catch (RemoteException e) {
Log.e(TAG, "Failed to update local DNS caching server");
if (newDnses != null) newDnses.clear();
}
}
private void setRaParams(RaParams newParams) {
if (mRaDaemon != null) {
final RaParams deprecatedParams =
RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
configureLocalRoutes(deprecatedParams.prefixes,
(newParams != null) ? newParams.prefixes : null);
configureLocalDns(deprecatedParams.dnses,
(newParams != null) ? newParams.dnses : null);
mRaDaemon.buildNewRa(deprecatedParams, newParams);
}
mLastRaParams = newParams;
}
// Accumulate routes representing "prefixes to be assigned to the local
// interface", for subsequent modification of local_network routing.
private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
for (IpPrefix ipp : prefixes) {
localRoutes.add(new RouteInfo(ipp, null, mIfName));
}
return localRoutes;
}
private INetd getNetdServiceOrNull() {
if (mNMService != null) {
try {
return mNMService.getNetdService();
} catch (RemoteException ignored) {
// This blocks until netd can be reached, but it can return
// null during a netd crash.
}
}
return null;
}
// Given a prefix like 2001:db8::/64 return 2001:db8::1.
private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
final byte[] dnsBytes = localPrefix.getRawAddress();
dnsBytes[dnsBytes.length - 1] = 0x1;
try {
return Inet6Address.getByAddress(null, dnsBytes, 0);
} catch (UnknownHostException e) {
Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
return null;
}
}
}

View File

@@ -47,6 +47,7 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -61,10 +62,6 @@ import java.util.concurrent.atomic.AtomicInteger;
* - Rewrite using Handler (and friends) so that AlarmManager can deliver
* "kick" messages when it's time to send a multicast RA.
*
* - Support transmitting MAX_URGENT_RTR_ADVERTISEMENTS number of empty
* RAs with zero default router lifetime when transitioning from an
* advertising state to a non-advertising state.
*
* @hide
*/
public class RouterAdvertisementDaemon {
@@ -112,8 +109,7 @@ public class RouterAdvertisementDaemon {
@GuardedBy("mLock")
private int mRaLength;
@GuardedBy("mLock")
private final HashMap<IpPrefix, Integer> mDeprecatedPrefixes;
private final DeprecatedInfoTracker mDeprecatedInfoTracker;
@GuardedBy("mLock")
private RaParams mRaParams;
@@ -140,6 +136,88 @@ public class RouterAdvertisementDaemon {
prefixes = (HashSet) other.prefixes.clone();
dnses = (HashSet) other.dnses.clone();
}
// Returns the subset of RA parameters that become deprecated when
// moving from announcing oldRa to announcing newRa.
//
// Currently only tracks differences in |prefixes| and |dnses|.
public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
RaParams newlyDeprecated = new RaParams();
if (oldRa != null) {
for (IpPrefix ipp : oldRa.prefixes) {
if (newRa == null || !newRa.prefixes.contains(ipp)) {
newlyDeprecated.prefixes.add(ipp);
}
}
for (Inet6Address dns : oldRa.dnses) {
if (newRa == null || !newRa.dnses.contains(dns)) {
newlyDeprecated.dnses.add(dns);
}
}
}
return newlyDeprecated;
}
}
private static class DeprecatedInfoTracker {
private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
void putPrefixes(Set<IpPrefix> prefixes) {
for (IpPrefix ipp : prefixes) {
mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
}
}
void removePrefixes(Set<IpPrefix> prefixes) {
for (IpPrefix ipp : prefixes) {
mPrefixes.remove(ipp);
}
}
Set<Inet6Address> getDnses() { return mDnses.keySet(); }
void putDnses(Set<Inet6Address> dnses) {
for (Inet6Address dns : dnses) {
mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
}
}
void removeDnses(Set<Inet6Address> dnses) {
for (Inet6Address dns : dnses) {
mDnses.remove(dns);
}
}
boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
private boolean decrementCounters() {
boolean removed = decrementCounter(mPrefixes);
removed |= decrementCounter(mDnses);
return removed;
}
private <T> boolean decrementCounter(HashMap<T, Integer> map) {
boolean removed = false;
for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
it.hasNext();) {
Map.Entry<T, Integer> kv = it.next();
if (kv.getValue() == 0) {
it.remove();
removed = true;
} else {
kv.setValue(kv.getValue() - 1);
}
}
return removed;
}
}
@@ -148,29 +226,24 @@ public class RouterAdvertisementDaemon {
mIfIndex = ifindex;
mHwAddr = hwaddr;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
mDeprecatedPrefixes = new HashMap<>();
mDeprecatedInfoTracker = new DeprecatedInfoTracker();
}
public void buildNewRa(RaParams params, HashSet<IpPrefix> newlyDeprecated) {
if (newlyDeprecated != null) {
synchronized (mLock) {
for (IpPrefix ipp : newlyDeprecated) {
mDeprecatedPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
}
}
}
// TODO: Send MAX_URGENT_RTR_ADVERTISEMENTS zero router lifetime RAs,
// iff. we have already sent an RA.
if (params == null || params.prefixes.isEmpty()) {
// No RA to be served at this time.
clearRa();
return;
}
public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
synchronized (mLock) {
mRaParams = params;
assembleRa();
if (deprecatedParams != null) {
mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
}
if (newParams != null) {
// Process information that is no longer deprecated.
mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
mDeprecatedInfoTracker.removeDnses(newParams.dnses);
}
mRaParams = newParams;
assembleRaLocked();
}
maybeNotifyMulticastTransmitter();
@@ -196,73 +269,64 @@ public class RouterAdvertisementDaemon {
mUnicastResponder = null;
}
private void clearRa() {
boolean notifySocket;
synchronized (mLock) {
notifySocket = (mRaLength != 0);
mRaLength = 0;
}
if (notifySocket) {
maybeNotifyMulticastTransmitter();
}
}
private void assembleRa() {
private void assembleRaLocked() {
final ByteBuffer ra = ByteBuffer.wrap(mRA);
ra.order(ByteOrder.BIG_ENDIAN);
synchronized (mLock) {
try {
putHeader(ra, mRaParams.hasDefaultRoute);
boolean shouldSendRA = false;
putSlla(ra, mHwAddr);
try {
putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
putSlla(ra, mHwAddr);
mRaLength = ra.position();
// https://tools.ietf.org/html/rfc5175#section-4 says:
//
// "MUST NOT be added to a Router Advertisement message
// if no flags in the option are set."
//
// putExpandedFlagsOption(ra);
// https://tools.ietf.org/html/rfc5175#section-4 says:
//
// "MUST NOT be added to a Router Advertisement message
// if no flags in the option are set."
//
// putExpandedFlagsOption(ra);
if (mRaParams != null) {
putMtu(ra, mRaParams.mtu);
mRaLength = ra.position();
for (IpPrefix ipp : mRaParams.prefixes) {
putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
mDeprecatedPrefixes.remove(ipp);
}
for (IpPrefix ipp : mDeprecatedPrefixes.keySet()) {
putPio(ra, ipp, 0, 0);
mRaLength = ra.position();
shouldSendRA = true;
}
if (mRaParams.dnses.size() > 0) {
putRdnss(ra, mRaParams.dnses);
putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
mRaLength = ra.position();
shouldSendRA = true;
}
}
for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
putPio(ra, ipp, 0, 0);
mRaLength = ra.position();
} catch (BufferOverflowException e) {
Log.e(TAG, "Could not construct new RA: " + e);
mRaLength = 0;
return;
shouldSendRA = true;
}
}
}
private int decrementDeprecatedPrefixes() {
int removed = 0;
synchronized (mLock) {
for (Map.Entry<IpPrefix, Integer> kv : mDeprecatedPrefixes.entrySet()) {
if (kv.getValue() == 0) {
mDeprecatedPrefixes.remove(kv.getKey());
removed++;
} else {
kv.setValue(kv.getValue() - 1);
}
final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
if (!deprecatedDnses.isEmpty()) {
putRdnss(ra, deprecatedDnses, 0);
mRaLength = ra.position();
shouldSendRA = true;
}
} catch (BufferOverflowException e) {
// The packet up to mRaLength is valid, since it has been updated
// progressively as the RA was built. Log an error, and continue
// on as best as possible.
Log.e(TAG, "Could not construct new RA: " + e);
}
return removed;
// We have nothing worth announcing; indicate as much to maybeSendRA().
if (!shouldSendRA) {
mRaLength = 0;
}
}
private void maybeNotifyMulticastTransmitter() {
@@ -461,7 +525,7 @@ public class RouterAdvertisementDaemon {
}
}
private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses) {
private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
/**
Recursive DNS Server (RDNSS) Option
@@ -483,9 +547,15 @@ public class RouterAdvertisementDaemon {
ra.put(ND_OPTION_RDNSS)
.put(RDNSS_NUM_8OCTETS)
.putShort(asShort(0))
.putInt(DEFAULT_LIFETIME);
.putInt(lifetime);
for (Inet6Address dns : dnses) {
// NOTE: If the full of list DNS servers doesn't fit in the packet,
// this code will cause a buffer overflow and the RA won't include
// include this instance of the option at all.
//
// TODO: Consider looking at ra.remaining() to determine how many
// DNS servers will fit, and adding only those.
ra.put(dns.getAddress());
}
}
@@ -601,10 +671,12 @@ public class RouterAdvertisementDaemon {
}
maybeSendRA(mAllNodes);
if (decrementDeprecatedPrefixes() > 0) {
// At least one deprecated PIO has been removed;
// reassemble the RA.
assembleRa();
synchronized (mLock) {
if (mDeprecatedInfoTracker.decrementCounters()) {
// At least one deprecated PIO has been removed;
// reassemble the RA.
assembleRaLocked();
}
}
}
}
@@ -619,17 +691,17 @@ public class RouterAdvertisementDaemon {
}
private int getNextMulticastTransmitDelaySec() {
int countDeprecatedPrefixes = 0;
boolean deprecationInProgress = false;
synchronized (mLock) {
if (mRaLength < MIN_RA_HEADER_SIZE) {
// No actual RA to send; just sleep for 1 day.
return DAY_IN_SECONDS;
}
countDeprecatedPrefixes = mDeprecatedPrefixes.size();
deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
}
final int urgentPending = mUrgentAnnouncements.getAndDecrement();
if (urgentPending > 0 || countDeprecatedPrefixes > 0) {
if ((urgentPending > 0) || deprecationInProgress) {
return MIN_DELAY_BETWEEN_RAS_SEC;
}