diff options
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) { |