summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/net/dhcp/DhcpLease.java37
-rw-r--r--src/android/net/dhcp/DhcpLeaseRepository.java66
-rw-r--r--src/android/net/dhcp/DhcpServer.java19
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) {