diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2019-09-02 19:20:54 +0900 |
---|---|---|
committer | Remi NGUYEN VAN <reminv@google.com> | 2020-02-12 10:39:55 +0900 |
commit | dc018dd943f0c11e0b9172dee0db0966871af20d (patch) | |
tree | 964a774fb259941aa3f548a49b80992e268a88cc /src | |
parent | ee23002c1f0a547ea46fa399d20caf542e68ab57 (diff) |
Add DhcpLeaseCallbacks
The callbacks will be used by Tethering to provide callbacks when DHCP
leases are updated.
The current design only supports one client as Tethering may want to
send callbacks to multiple callers, but DhcpServer is only owned by
Tethering.
Bug: 135411507
Test: atest NetworkStackTests
Change-Id: I1e44221d6fbd1b1f2d0d0057a29c7445af1cdbcf
Diffstat (limited to 'src')
-rw-r--r-- | src/android/net/dhcp/DhcpLease.java | 37 | ||||
-rw-r--r-- | src/android/net/dhcp/DhcpLeaseRepository.java | 66 | ||||
-rw-r--r-- | src/android/net/dhcp/DhcpServer.java | 19 |
3 files changed, 106 insertions, 16 deletions
diff --git a/src/android/net/dhcp/DhcpLease.java b/src/android/net/dhcp/DhcpLease.java index 37d9cc0..3226f28 100644 --- a/src/android/net/dhcp/DhcpLease.java +++ b/src/android/net/dhcp/DhcpLease.java @@ -16,6 +16,8 @@ package android.net.dhcp; +import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; + import android.net.MacAddress; import android.os.SystemClock; import android.text.TextUtils; @@ -43,6 +45,7 @@ public class DhcpLease { private final MacAddress mHwAddr; @NonNull private final Inet4Address mNetAddr; + private final int mPrefixLength; /** * Expiration time for the lease, to compare with {@link SystemClock#elapsedRealtime()}. */ @@ -51,10 +54,12 @@ public class DhcpLease { private final String mHostname; public DhcpLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - @NonNull Inet4Address netAddr, long expTime, @Nullable String hostname) { + @NonNull Inet4Address netAddr, int prefixLength, long expTime, + @Nullable String hostname) { mClientId = (clientId == null ? null : Arrays.copyOf(clientId, clientId.length)); mHwAddr = hwAddr; mNetAddr = netAddr; + mPrefixLength = prefixLength; mExpTime = expTime; mHostname = hostname; } @@ -87,6 +92,10 @@ public class DhcpLease { return mNetAddr; } + public int getPrefixLength() { + return mPrefixLength; + } + public long getExpTime() { return mExpTime; } @@ -99,7 +108,8 @@ public class DhcpLease { * @return A {@link DhcpLease} with expiration time set to max(expTime, currentExpTime) */ public DhcpLease renewedLease(long expTime, @Nullable String hostname) { - return new DhcpLease(mClientId, mHwAddr, mNetAddr, Math.max(expTime, mExpTime), + return new DhcpLease(mClientId, mHwAddr, mNetAddr, mPrefixLength, + Math.max(expTime, mExpTime), (hostname == null ? mHostname : hostname)); } @@ -125,13 +135,14 @@ public class DhcpLease { return Arrays.equals(mClientId, other.mClientId) && mHwAddr.equals(other.mHwAddr) && mNetAddr.equals(other.mNetAddr) + && mPrefixLength == other.mPrefixLength && mExpTime == other.mExpTime && TextUtils.equals(mHostname, other.mHostname); } @Override public int hashCode() { - return Objects.hash(mClientId, mHwAddr, mNetAddr, mHostname, mExpTime); + return Objects.hash(mClientId, mHwAddr, mNetAddr, mPrefixLength, mHostname, mExpTime); } static String clientIdToString(byte[] bytes) { @@ -147,8 +158,24 @@ public class DhcpLease { @Override public String toString() { - return String.format("clientId: %s, hwAddr: %s, netAddr: %s, expTime: %d, hostname: %s", + return String.format("clientId: %s, hwAddr: %s, netAddr: %s/%d, expTime: %d," + + "hostname: %s", clientIdToString(mClientId), mHwAddr.toString(), inet4AddrToString(mNetAddr), - mExpTime, mHostname); + mPrefixLength, mExpTime, mHostname); + } + + /** + * Create a {@link DhcpLeaseParcelable} containing the information held in this lease. + */ + public DhcpLeaseParcelable toParcelable() { + final DhcpLeaseParcelable p = new DhcpLeaseParcelable(); + p.clientId = mClientId == null ? null : Arrays.copyOf(mClientId, mClientId.length); + p.hwAddr = mHwAddr.toByteArray(); + p.netAddr = inet4AddressToIntHTH(mNetAddr); + p.prefixLength = mPrefixLength; + p.expTime = mExpTime; + p.hostname = mHostname; + + return p; } } diff --git a/src/android/net/dhcp/DhcpLeaseRepository.java b/src/android/net/dhcp/DhcpLeaseRepository.java index 4e74dc8..1dc2f7f 100644 --- a/src/android/net/dhcp/DhcpLeaseRepository.java +++ b/src/android/net/dhcp/DhcpLeaseRepository.java @@ -31,6 +31,8 @@ import android.net.IpPrefix; import android.net.MacAddress; import android.net.dhcp.DhcpServer.Clock; import android.net.util.SharedLog; +import android.os.RemoteCallbackList; +import android.os.RemoteException; import android.util.ArrayMap; import androidx.annotation.NonNull; @@ -45,6 +47,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -73,6 +76,7 @@ class DhcpLeaseRepository { @NonNull private Set<Inet4Address> mReservedAddrs; private int mSubnetAddr; + private int mPrefixLength; private int mSubnetMask; private int mNumAddresses; private long mLeaseTimeMs; @@ -84,6 +88,9 @@ class DhcpLeaseRepository { */ private long mNextExpirationCheck = EXPIRATION_NEVER; + @NonNull + private RemoteCallbackList<IDhcpLeaseCallbacks> mLeaseCallbacks = new RemoteCallbackList<>(); + static class DhcpLeaseException extends Exception { DhcpLeaseException(String message) { super(message); @@ -131,27 +138,34 @@ class DhcpLeaseRepository { long leaseTimeMs) { mPrefix = prefix; mReservedAddrs = Collections.unmodifiableSet(new HashSet<>(reservedAddrs)); - mSubnetMask = prefixLengthToV4NetmaskIntHTH(prefix.getPrefixLength()); + mPrefixLength = prefix.getPrefixLength(); + mSubnetMask = prefixLengthToV4NetmaskIntHTH(mPrefixLength); mSubnetAddr = inet4AddressToIntHTH((Inet4Address) prefix.getAddress()) & mSubnetMask; mNumAddresses = 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength()); mLeaseTimeMs = leaseTimeMs; - cleanMap(mCommittedLeases); cleanMap(mDeclinedAddrs); + if (cleanMap(mCommittedLeases)) { + notifyLeasesChanged(); + } } /** * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address. + * @return true iff at least one entry was removed. */ - private <T> void cleanMap(Map<Inet4Address, T> map) { + private <T> boolean cleanMap(Map<Inet4Address, T> map) { final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator(); + boolean removed = false; while (it.hasNext()) { final Inet4Address addr = it.next().getKey(); if (!isValidAddress(addr) || mReservedAddrs.contains(addr)) { it.remove(); + removed = true; } } + return removed; } /** @@ -181,7 +195,7 @@ class DhcpLeaseRepository { mLog.log("Offering extended lease " + newLease); // Do not update lease time in the map: the offer is not committed yet. } else if (reqAddr != null && isValidAddress(reqAddr) && isAvailable(reqAddr)) { - newLease = new DhcpLease(clientId, hwAddr, reqAddr, expTime, hostname); + newLease = new DhcpLease(clientId, hwAddr, reqAddr, mPrefixLength, expTime, hostname); mLog.log("Offering requested lease " + newLease); } else { newLease = makeNewOffer(clientId, hwAddr, expTime, hostname); @@ -267,7 +281,8 @@ class DhcpLeaseRepository { if (assignedLease != null) { if (sidSet && reqAddr != null) { // Client in SELECTING state; remove any current lease before creating a new one. - mCommittedLeases.remove(assignedLease.getNetAddr()); + // Do not notify of change as it will be done when the new lease is committed. + removeLease(assignedLease.getNetAddr(), false /* notifyChange */); } else if (!assignedLease.getNetAddr().equals(leaseAddr)) { // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr. // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration. @@ -314,7 +329,7 @@ class DhcpLeaseRepository { final DhcpLease lease; if (currentLease == null) { if (isValidAddress(addr) && !mReservedAddrs.contains(addr)) { - lease = new DhcpLease(clientId, hwAddr, addr, expTime, hostname); + lease = new DhcpLease(clientId, hwAddr, addr, mPrefixLength, expTime, hostname); } else { throw new InvalidAddressException("Lease not found and address unavailable"); } @@ -328,6 +343,13 @@ class DhcpLeaseRepository { private void commitLease(@NonNull DhcpLease lease) { mCommittedLeases.put(lease.getNetAddr(), lease); maybeUpdateEarliestExpiration(lease.getExpTime()); + notifyLeasesChanged(); + } + + private void removeLease(@NonNull Inet4Address address, boolean notifyChange) { + // Earliest expiration remains <= the first expiry time on remove, so no need to update it. + mCommittedLeases.remove(address); + if (notifyChange) notifyLeasesChanged(); } /** @@ -343,8 +365,8 @@ class DhcpLeaseRepository { return false; } if (currentLease.matchesClient(clientId, hwAddr)) { - mCommittedLeases.remove(addr); mLog.log("Released lease " + currentLease); + removeLease(addr, true /* notifyChange */); return true; } mLog.w(String.format("Not releasing lease %s: does not match client (cid %s, hwAddr %s)", @@ -352,6 +374,24 @@ class DhcpLeaseRepository { return false; } + private void notifyLeasesChanged() { + final List<DhcpLeaseParcelable> leaseParcelables = + new ArrayList<>(mCommittedLeases.size()); + for (DhcpLease committedLease : mCommittedLeases.values()) { + leaseParcelables.add(committedLease.toParcelable()); + } + + final int cbCount = mLeaseCallbacks.beginBroadcast(); + for (int i = 0; i < cbCount; i++) { + try { + mLeaseCallbacks.getBroadcastItem(i).onLeasesChanged(leaseParcelables); + } catch (RemoteException e) { + mLog.e("Could not send lease callback", e); + } + } + mLeaseCallbacks.finishBroadcast(); + } + public void markLeaseDeclined(@NonNull Inet4Address addr) { if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) { mLog.logf("Not marking %s as declined: already declined or not assignable", @@ -383,6 +423,14 @@ class DhcpLeaseRepository { } /** + * Add callbacks that will be called on leases update. + */ + public void addLeaseCallbacks(@NonNull IDhcpLeaseCallbacks cb) { + Objects.requireNonNull(cb, "Callbacks must be non-null"); + mLeaseCallbacks.register(cb); + } + + /** * Given the expiration time of a new committed lease or declined address, update * {@link #mNextExpirationCheck} so it stays lower than or equal to the time for the first lease * to expire. @@ -541,7 +589,7 @@ class DhcpLeaseRepository { for (int i = 0; i < mNumAddresses; i++) { final Inet4Address addr = intToInet4AddressHTH(intAddr); if (isAvailable(addr) && !mDeclinedAddrs.containsKey(addr)) { - return new DhcpLease(clientId, hwAddr, addr, expTime, hostname); + return new DhcpLease(clientId, hwAddr, addr, mPrefixLength, expTime, hostname); } intAddr = getNextAddress(intAddr); } @@ -557,7 +605,7 @@ class DhcpLeaseRepository { // However declined addresses may have been requested (typically by the machine that was // already using the address) after being declined. if (isAvailable(addr)) { - return new DhcpLease(clientId, hwAddr, addr, expTime, hostname); + return new DhcpLease(clientId, hwAddr, addr, mPrefixLength, expTime, hostname); } } diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java index 6aadc04..bcca47a 100644 --- a/src/android/net/dhcp/DhcpServer.java +++ b/src/android/net/dhcp/DhcpServer.java @@ -274,10 +274,22 @@ public class DhcpServer extends IDhcpServer.Stub { */ @Override public void start(@Nullable INetworkStackStatusCallback cb) { + startWithCallbacks(cb, null); + } + + /** + * Start listening for and responding to packets, with optional callbacks for lease events. + * + * <p>It is not legal to call this method more than once; in particular the server cannot be + * restarted after being stopped. + */ + @Override + public void startWithCallbacks(@Nullable INetworkStackStatusCallback statusCb, + @Nullable IDhcpLeaseCallbacks leaseCb) { mDeps.checkCaller(); mHandlerThread.start(); mHandler = new ServerHandler(mHandlerThread.getLooper()); - sendMessage(CMD_START_DHCP_SERVER, cb); + sendMessage(CMD_START_DHCP_SERVER, new Pair<>(statusCb, leaseCb)); } /** @@ -344,9 +356,12 @@ public class DhcpServer extends IDhcpServer.Stub { cb = pair.second; break; case CMD_START_DHCP_SERVER: + final Pair<INetworkStackStatusCallback, IDhcpLeaseCallbacks> obj = + (Pair<INetworkStackStatusCallback, IDhcpLeaseCallbacks>) msg.obj; + cb = obj.first; + mLeaseRepo.addLeaseCallbacks(obj.second); mPacketListener = mDeps.makePacketListener(); mPacketListener.start(); - cb = (INetworkStackStatusCallback) msg.obj; break; case CMD_STOP_DHCP_SERVER: if (mPacketListener != null) { |