summaryrefslogtreecommitdiff
path: root/packages/Tethering/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/Tethering/src')
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java36
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java203
-rw-r--r--packages/Tethering/src/android/net/ip/DadProxy.java54
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java1422
-rw-r--r--packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java180
-rw-r--r--packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java744
-rw-r--r--packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java75
-rw-r--r--packages/Tethering/src/android/net/util/InterfaceSet.java52
-rw-r--r--packages/Tethering/src/android/net/util/PrefixUtils.java88
-rw-r--r--packages/Tethering/src/android/net/util/TetheringMessageBase.java25
-rw-r--r--packages/Tethering/src/android/net/util/TetheringUtils.java165
-rw-r--r--packages/Tethering/src/android/net/util/VersionedBroadcastListener.java106
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java778
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java183
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java646
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java328
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java811
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java574
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java417
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java501
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java167
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java102
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java362
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java389
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java51
25 files changed, 0 insertions, 8459 deletions
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
deleted file mode 100644
index 9fda1257b4c9..000000000000
--- a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-/**
- * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion().
- * @hide
- */
-public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
- /**
- * Get the version of the aidl interface implemented by the callbacks.
- */
- @Override
- public int getInterfaceVersion() {
- return IDhcpServerCallbacks.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return IDhcpServerCallbacks.HASH;
- }
-}
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
deleted file mode 100644
index aaaec17bf922..000000000000
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
-
-import android.net.LinkAddress;
-import android.util.ArraySet;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.net.Inet4Address;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building.
- *
- * <p>This utility class does not check for validity of the parameters: invalid parameters are
- * reported by the receiving module when unparceling the parcel.
- *
- * @see DhcpServingParams
- * @hide
- */
-public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
- public static final int MTU_UNSET = 0;
-
- /**
- * Set the server address and served prefix for the DHCP server.
- *
- * <p>This parameter is required.
- */
- public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) {
- this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
- this.serverAddrPrefixLength = serverAddr.getPrefixLength();
- return this;
- }
-
- /**
- * Set the default routers to be advertised to DHCP clients.
- *
- * <p>Each router must be inside the served prefix. This may be an empty set, but it must
- * always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
- this.defaultRouters = toIntArray(defaultRouters);
- return this;
- }
-
- /**
- * Set the default routers to be advertised to DHCP clients.
- *
- * <p>Each router must be inside the served prefix. This may be an empty list of routers,
- * but it must always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
- return setDefaultRouters(newArraySet(defaultRouters));
- }
-
- /**
- * Convenience method to build the parameters with no default router.
- *
- * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
- */
- public DhcpServingParamsParcelExt setNoDefaultRouter() {
- return setDefaultRouters();
- }
-
- /**
- * Set the DNS servers to be advertised to DHCP clients.
- *
- * <p>This may be an empty set, but it must always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
- this.dnsServers = toIntArray(dnsServers);
- return this;
- }
-
- /**
- * Set the DNS servers to be advertised to DHCP clients.
- *
- * <p>This may be an empty list of servers, but it must always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
- return setDnsServers(newArraySet(dnsServers));
- }
-
- /**
- * Convenience method to build the parameters with no DNS server.
- *
- * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
- */
- public DhcpServingParamsParcelExt setNoDnsServer() {
- return setDnsServers();
- }
-
- /**
- * Set excluded addresses that the DHCP server is not allowed to assign to clients.
- *
- * <p>This parameter is optional. DNS servers and default routers are always excluded
- * and do not need to be set here.
- */
- public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
- this.excludedAddrs = toIntArray(excludedAddrs);
- return this;
- }
-
- /**
- * Set excluded addresses that the DHCP server is not allowed to assign to clients.
- *
- * <p>This parameter is optional. DNS servers and default routers are always excluded
- * and do not need to be set here.
- */
- public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
- return setExcludedAddrs(newArraySet(excludedAddrs));
- }
-
- /**
- * Set the lease time for leases assigned by the DHCP server.
- *
- * <p>This parameter is required.
- */
- public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
- this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
- return this;
- }
-
- /**
- * Set the link MTU to be advertised to DHCP clients.
- *
- * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
- * is optional and defaults to {@link #MTU_UNSET}.
- */
- public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) {
- this.linkMtu = linkMtu;
- return this;
- }
-
- /**
- * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
- *
- * <p>If not set, the default value is false.
- */
- public DhcpServingParamsParcelExt setMetered(boolean metered) {
- this.metered = metered;
- return this;
- }
-
- /**
- * Set the client address to tell DHCP server only offer this address.
- * The client's prefix length is the same as server's.
- *
- * <p>If not set, the default value is null.
- */
- public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) {
- this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr);
- return this;
- }
-
- /**
- * Set whether the DHCP server should request a new prefix from IpServer when receiving
- * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB
- * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests.
- *
- * <p>If not set, the default value is false.
- */
- public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) {
- this.changePrefixOnDecline = changePrefixOnDecline;
- return this;
- }
-
- private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
- int[] res = new int[addrs.size()];
- int i = 0;
- for (Inet4Address addr : addrs) {
- res[i] = inet4AddressToIntHTH(addr);
- i++;
- }
- return res;
- }
-
- private static ArraySet<Inet4Address> newArraySet(Inet4Address... addrs) {
- ArraySet<Inet4Address> addrSet = new ArraySet<>(addrs.length);
- Collections.addAll(addrSet, addrs);
- return addrSet;
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java
deleted file mode 100644
index e2976b78908c..000000000000
--- a/packages/Tethering/src/android/net/ip/DadProxy.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.net.util.InterfaceParams;
-import android.os.Handler;
-
-import androidx.annotation.VisibleForTesting;
-
-/**
- * Basic Duplicate address detection proxy.
- *
- * @hide
- */
-public class DadProxy {
- private static final String TAG = DadProxy.class.getSimpleName();
-
- @VisibleForTesting
- public static NeighborPacketForwarder naForwarder;
- public static NeighborPacketForwarder nsForwarder;
-
- public DadProxy(Handler h, InterfaceParams tetheredIface) {
- naForwarder = new NeighborPacketForwarder(h, tetheredIface,
- NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
- nsForwarder = new NeighborPacketForwarder(h, tetheredIface,
- NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
- }
-
- /** Stop NS/NA Forwarders. */
- public void stop() {
- naForwarder.stop();
- nsForwarder.stop();
- }
-
- /** Set upstream iface on both forwarders. */
- public void setUpstreamIface(InterfaceParams upstreamIface) {
- naForwarder.setUpstreamIface(upstreamIface);
- nsForwarder.setUpstreamIface(upstreamIface);
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
deleted file mode 100644
index 52d59fcdc19b..000000000000
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ /dev/null
@@ -1,1422 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
-import static android.net.util.NetworkConstants.asByte;
-import static android.net.util.PrefixUtils.asIpPrefix;
-import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
-import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
-
-import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-
-import android.net.INetd;
-import android.net.INetworkStackStatusCallback;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.MacAddress;
-import android.net.RouteInfo;
-import android.net.TetheredClient;
-import android.net.TetheringManager;
-import android.net.TetheringRequestParcel;
-import android.net.dhcp.DhcpLeaseParcelable;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.DhcpServingParamsParcelExt;
-import android.net.dhcp.IDhcpEventCallbacks;
-import android.net.dhcp.IDhcpServer;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.shared.NetdUtils;
-import android.net.shared.RouteUtils;
-import android.net.util.InterfaceParams;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
-import android.net.util.SharedLog;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.networkstack.tethering.BpfCoordinator;
-import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.PrivateAddressCoordinator;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.NetworkInterface;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * Provides the interface to IP-layer serving functionality for a given network
- * interface, e.g. for tethering or "local-only hotspot" mode.
- *
- * @hide
- */
-public class IpServer extends StateMachine {
- public static final int STATE_UNAVAILABLE = 0;
- public static final int STATE_AVAILABLE = 1;
- public static final int STATE_TETHERED = 2;
- public static final int STATE_LOCAL_ONLY = 3;
-
- /** Get string name of |state|.*/
- public static String getStateString(int state) {
- switch (state) {
- case STATE_UNAVAILABLE: return "UNAVAILABLE";
- case STATE_AVAILABLE: return "AVAILABLE";
- case STATE_TETHERED: return "TETHERED";
- case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
- }
- return "UNKNOWN: " + state;
- }
-
- private static final byte DOUG_ADAMS = (byte) 42;
-
- // TODO: have PanService use some visible version of this constant
- private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24";
-
- // TODO: have this configurable
- private static final int DHCP_LEASE_TIME_SECS = 3600;
-
- private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00");
-
- private static final String TAG = "IpServer";
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
- private static final Class[] sMessageClasses = {
- IpServer.class
- };
- private static final SparseArray<String> sMagicDecoderRing =
- MessageUtils.findMessageNames(sMessageClasses);
-
- /** IpServer callback. */
- public static class Callback {
- /**
- * Notify that |who| has changed its tethering state.
- *
- * @param who the calling instance of IpServer
- * @param state one of STATE_*
- * @param lastError one of TetheringManager.TETHER_ERROR_*
- */
- public void updateInterfaceState(IpServer who, int state, int lastError) { }
-
- /**
- * Notify that |who| has new LinkProperties.
- *
- * @param who the calling instance of IpServer
- * @param newLp the new LinkProperties to report
- */
- public void updateLinkProperties(IpServer who, LinkProperties newLp) { }
-
- /**
- * Notify that the DHCP leases changed in one of the IpServers.
- */
- public void dhcpLeasesChanged() { }
-
- /**
- * Request Tethering change.
- *
- * @param tetheringType the downstream type of this IpServer.
- * @param enabled enable or disable tethering.
- */
- public void requestEnableTethering(int tetheringType, boolean enabled) { }
- }
-
- /** Capture IpServer dependencies, for injection. */
- public abstract static class Dependencies {
- /**
- * Create a DadProxy instance to be used by IpServer.
- * To support multiple tethered interfaces concurrently DAD Proxy
- * needs to be supported per IpServer instead of per upstream.
- */
- public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) {
- return new DadProxy(handler, ifParams);
- }
-
- /** Create an IpNeighborMonitor to be used by this IpServer */
- public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
- IpNeighborMonitor.NeighborEventConsumer consumer) {
- return new IpNeighborMonitor(handler, log, consumer);
- }
-
- /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
- return new RouterAdvertisementDaemon(ifParams);
- }
-
- /** Get |ifName|'s interface information.*/
- public InterfaceParams getInterfaceParams(String ifName) {
- return InterfaceParams.getByName(ifName);
- }
-
- /** Get |ifName|'s interface index. */
- public int getIfindex(String ifName) {
- try {
- return NetworkInterface.getByName(ifName).getIndex();
- } catch (IOException | NullPointerException e) {
- Log.e(TAG, "Can't determine interface index for interface " + ifName);
- return 0;
- }
- }
-
- /** Create a DhcpServer instance to be used by IpServer. */
- public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb);
- }
-
- // request from the user that it wants to tether
- public static final int CMD_TETHER_REQUESTED = BASE_IPSERVER + 1;
- // request from the user that it wants to untether
- public static final int CMD_TETHER_UNREQUESTED = BASE_IPSERVER + 2;
- // notification that this interface is down
- public static final int CMD_INTERFACE_DOWN = BASE_IPSERVER + 3;
- // notification from the {@link Tethering.TetherMainSM} that it had trouble enabling IP
- // Forwarding
- public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IPSERVER + 4;
- // notification from the {@link Tethering.TetherMainSM} SM that it had trouble disabling IP
- // Forwarding
- public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5;
- // notification from the {@link Tethering.TetherMainSM} SM that it had trouble starting
- // tethering
- public static final int CMD_START_TETHERING_ERROR = BASE_IPSERVER + 6;
- // notification from the {@link Tethering.TetherMainSM} that it had trouble stopping tethering
- public static final int CMD_STOP_TETHERING_ERROR = BASE_IPSERVER + 7;
- // notification from the {@link Tethering.TetherMainSM} that it had trouble setting the DNS
- // forwarders
- public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IPSERVER + 8;
- // the upstream connection has changed
- public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9;
- // new IPv6 tethering parameters need to be processed
- public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
- // new neighbor cache entry on our interface
- public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
- // request from DHCP server that it wants to have a new prefix
- public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12;
- // request from PrivateAddressCoordinator to restart tethering.
- public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13;
-
- private final State mInitialState;
- private final State mLocalHotspotState;
- private final State mTetheredState;
- private final State mUnavailableState;
- private final State mWaitingForRestartState;
-
- private final SharedLog mLog;
- private final INetd mNetd;
- @NonNull
- private final BpfCoordinator mBpfCoordinator;
- private final Callback mCallback;
- private final InterfaceController mInterfaceCtrl;
- private final PrivateAddressCoordinator mPrivateAddressCoordinator;
-
- private final String mIfaceName;
- private final int mInterfaceType;
- private final LinkProperties mLinkProperties;
- private final boolean mUsingLegacyDhcp;
- private final boolean mUsingBpfOffload;
-
- private final Dependencies mDeps;
-
- private int mLastError;
- private int mServingMode;
- private InterfaceSet mUpstreamIfaceSet; // may change over time
- private InterfaceParams mInterfaceParams;
- // TODO: De-duplicate this with mLinkProperties above. Currently, these link
- // properties are those selected by the IPv6TetheringCoordinator and relayed
- // to us. By comparison, mLinkProperties contains the addresses and directly
- // connected routes that have been formed from these properties iff. we have
- // succeeded in configuring them and are able to announce them within Router
- // Advertisements (otherwise, we do not add them to mLinkProperties at all).
- private LinkProperties mLastIPv6LinkProperties;
- private RouterAdvertisementDaemon mRaDaemon;
- private DadProxy mDadProxy;
-
- // To be accessed only on the handler thread
- private int mDhcpServerStartIndex = 0;
- private IDhcpServer mDhcpServer;
- private RaParams mLastRaParams;
-
- private LinkAddress mStaticIpv4ServerAddr;
- private LinkAddress mStaticIpv4ClientAddr;
-
- @NonNull
- private List<TetheredClient> mDhcpLeases = Collections.emptyList();
-
- private int mLastIPv6UpstreamIfindex = 0;
-
- private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
- public void accept(NeighborEvent e) {
- sendMessage(CMD_NEIGHBOR_EVENT, e);
- }
- }
-
- private final IpNeighborMonitor mIpNeighborMonitor;
-
- private LinkAddress mIpv4Address;
-
- // TODO: Add a dependency object to pass the data members or variables from the tethering
- // object. It helps to reduce the arguments of the constructor.
- public IpServer(
- String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
- boolean usingLegacyDhcp, boolean usingBpfOffload,
- PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
- super(ifaceName, looper);
- mLog = log.forSubComponent(ifaceName);
- mNetd = netd;
- mBpfCoordinator = coordinator;
- mCallback = callback;
- mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
- mIfaceName = ifaceName;
- mInterfaceType = interfaceType;
- mLinkProperties = new LinkProperties();
- mUsingLegacyDhcp = usingLegacyDhcp;
- mUsingBpfOffload = usingBpfOffload;
- mPrivateAddressCoordinator = addressCoordinator;
- mDeps = deps;
- resetLinkProperties();
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
- mServingMode = STATE_AVAILABLE;
-
- mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
- new MyNeighborEventConsumer());
-
- // IP neighbor monitor monitors the neighbor events for adding/removing offload
- // forwarding rules per client. If BPF offload is not supported, don't start listening
- // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule,
- // removeIpv6ForwardingRule.
- if (mUsingBpfOffload && !mIpNeighborMonitor.start()) {
- mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
- }
-
- mInitialState = new InitialState();
- mLocalHotspotState = new LocalHotspotState();
- mTetheredState = new TetheredState();
- mUnavailableState = new UnavailableState();
- mWaitingForRestartState = new WaitingForRestartState();
- addState(mInitialState);
- addState(mLocalHotspotState);
- addState(mTetheredState);
- addState(mWaitingForRestartState, mTetheredState);
- addState(mUnavailableState);
-
- setInitialState(mInitialState);
- }
-
- /** Interface name which IpServer served.*/
- public String interfaceName() {
- return mIfaceName;
- }
-
- /**
- * Tethering downstream type. It would be one of TetheringManager#TETHERING_*.
- */
- public int interfaceType() {
- return mInterfaceType;
- }
-
- /** Last error from this IpServer. */
- public int lastError() {
- return mLastError;
- }
-
- /** Serving mode is the current state of IpServer state machine. */
- public int servingMode() {
- return mServingMode;
- }
-
- /** The properties of the network link which IpServer is serving. */
- public LinkProperties linkProperties() {
- return new LinkProperties(mLinkProperties);
- }
-
- /** The address which IpServer is using. */
- public LinkAddress getAddress() {
- return mIpv4Address;
- }
-
- /**
- * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
- * thread.
- */
- public List<TetheredClient> getAllLeases() {
- return Collections.unmodifiableList(mDhcpLeases);
- }
-
- /** Stop this IpServer. After this is called this IpServer should not be used any more. */
- public void stop() {
- sendMessage(CMD_INTERFACE_DOWN);
- }
-
- /**
- * Tethering is canceled. IpServer state machine will be available and wait for
- * next tethering request.
- */
- public void unwanted() {
- sendMessage(CMD_TETHER_UNREQUESTED);
- }
-
- /** Internals. */
-
- private boolean startIPv4() {
- return configureIPv4(true);
- }
-
- /**
- * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
- * handler.
- *
- * <p>Different instances of this class can be created for each call to IDhcpServer methods,
- * with different implementations of the callback, to differentiate handling of success/error in
- * each call.
- */
- private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub {
- @Override
- public void onStatusAvailable(int statusCode) {
- getHandler().post(() -> callback(statusCode));
- }
-
- public abstract void callback(int statusCode);
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
- }
-
- private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
- private final int mStartIndex;
-
- private DhcpServerCallbacksImpl(int startIndex) {
- mStartIndex = startIndex;
- }
-
- @Override
- public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
- getHandler().post(() -> {
- // We are on the handler thread: mDhcpServerStartIndex can be read safely.
- if (mStartIndex != mDhcpServerStartIndex) {
- // This start request is obsolete. Explicitly stop the DHCP server to shut
- // down its thread. When the |server| binder token goes out of scope, the
- // garbage collector will finalize it, which causes the network stack process
- // garbage collector to collect the server itself.
- try {
- server.stop(null);
- } catch (RemoteException e) { }
- return;
- }
-
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error obtaining DHCP server: " + statusCode);
- handleError();
- return;
- }
-
- mDhcpServer = server;
- try {
- mDhcpServer.startWithCallbacks(new OnHandlerStatusCallback() {
- @Override
- public void callback(int startStatusCode) {
- if (startStatusCode != STATUS_SUCCESS) {
- mLog.e("Error starting DHCP server: " + startStatusCode);
- handleError();
- }
- }
- }, new DhcpEventCallback());
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- });
- }
-
- private void handleError() {
- mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
- transitionTo(mInitialState);
- }
- }
-
- private class DhcpEventCallback extends IDhcpEventCallbacks.Stub {
- @Override
- public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
- final ArrayList<TetheredClient> leases = new ArrayList<>();
- for (DhcpLeaseParcelable lease : leaseParcelables) {
- final LinkAddress address = new LinkAddress(
- intToInet4AddressHTH(lease.netAddr), lease.prefixLength,
- 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */,
- lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */);
-
- final MacAddress macAddress;
- try {
- macAddress = MacAddress.fromBytes(lease.hwAddr);
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Invalid address received from DhcpServer: "
- + Arrays.toString(lease.hwAddr));
- return;
- }
-
- final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
- address, lease.hostname);
- leases.add(new TetheredClient(
- macAddress,
- Collections.singletonList(addressInfo),
- mInterfaceType));
- }
-
- getHandler().post(() -> {
- mDhcpLeases = leases;
- mCallback.dhcpLeasesChanged();
- });
- }
-
- @Override
- public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
- Objects.requireNonNull(currentPrefix);
- sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() throws RemoteException {
- return this.HASH;
- }
- }
-
- private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) {
- Objects.requireNonNull(ipv4Address);
- return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST);
- }
-
- private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter,
- @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr,
- @Nullable Inet4Address clientAddr) {
- final boolean changePrefixOnDecline =
- (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null);
- return new DhcpServingParamsParcelExt()
- .setDefaultRouters(defaultRouter)
- .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
- .setDnsServers(dnsServer)
- .setServerAddr(serverAddr)
- .setMetered(true)
- .setSingleClientAddr(clientAddr)
- .setChangePrefixOnDecline(changePrefixOnDecline);
- // TODO: also advertise link MTU
- }
-
- private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
- if (mUsingLegacyDhcp) {
- return true;
- }
-
- final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
- final Inet4Address clientAddr = clientLinkAddr == null ? null :
- (Inet4Address) clientLinkAddr.getAddress();
-
- final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */,
- addr /* dnsServer */, serverLinkAddr, clientAddr);
- mDhcpServerStartIndex++;
- mDeps.makeDhcpServer(
- mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
- return true;
- }
-
- private void stopDhcp() {
- // Make all previous start requests obsolete so servers are not started later
- mDhcpServerStartIndex++;
-
- if (mDhcpServer != null) {
- try {
- mDhcpServer.stop(new OnHandlerStatusCallback() {
- @Override
- public void callback(int statusCode) {
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error stopping DHCP server: " + statusCode);
- mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
- // Not much more we can do here
- }
- mDhcpLeases.clear();
- getHandler().post(mCallback::dhcpLeasesChanged);
- }
- });
- mDhcpServer = null;
- } catch (RemoteException e) {
- mLog.e("Error stopping DHCP server", e);
- // Not much more we can do here
- }
- }
- }
-
- private boolean configureDhcp(boolean enable, final LinkAddress serverAddr,
- final LinkAddress clientAddr) {
- if (enable) {
- return startDhcp(serverAddr, clientAddr);
- } else {
- stopDhcp();
- return true;
- }
- }
-
- private void stopIPv4() {
- configureIPv4(false);
- // NOTE: All of configureIPv4() will be refactored out of existence
- // into calls to InterfaceController, shared with startIPv4().
- mInterfaceCtrl.clearIPv4Address();
- mPrivateAddressCoordinator.releaseDownstream(this);
- mIpv4Address = null;
- mStaticIpv4ServerAddr = null;
- mStaticIpv4ClientAddr = null;
- }
-
- private boolean configureIPv4(boolean enabled) {
- if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
-
- if (enabled) {
- mIpv4Address = requestIpv4Address(true /* useLastAddress */);
- }
-
- if (mIpv4Address == null) {
- mLog.e("No available ipv4 address");
- return false;
- }
-
- if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
- // BT configures the interface elsewhere: only start DHCP.
- // TODO: make all tethering types behave the same way, and delete the bluetooth
- // code that calls into NetworkManagementService directly.
- return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
- }
-
- final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);
-
- final Boolean setIfaceUp;
- if (mInterfaceType == TetheringManager.TETHERING_WIFI
- || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P
- || mInterfaceType == TetheringManager.TETHERING_ETHERNET
- || mInterfaceType == TetheringManager.TETHERING_WIGIG) {
- // The WiFi and Ethernet stack has ownership of the interface up/down state.
- // It is unclear whether the Bluetooth or USB stacks will manage their own
- // state.
- setIfaceUp = null;
- } else {
- setIfaceUp = enabled;
- }
- if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) {
- mLog.e("Error configuring interface");
- if (!enabled) stopDhcp();
- return false;
- }
-
- if (enabled) {
- mLinkProperties.addLinkAddress(mIpv4Address);
- mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address));
- } else {
- mLinkProperties.removeLinkAddress(mIpv4Address);
- mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address));
- }
- return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
- }
-
- private LinkAddress requestIpv4Address(final boolean useLastAddress) {
- if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
-
- if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
- return new LinkAddress(BLUETOOTH_IFACE_ADDR);
- }
-
- return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
- }
-
- private boolean startIPv6() {
- mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
- if (mInterfaceParams == null) {
- mLog.e("Failed to find InterfaceParams");
- stopIPv6();
- return false;
- }
-
- mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
- if (!mRaDaemon.start()) {
- stopIPv6();
- return false;
- }
-
- // TODO: use ShimUtils instead of explicitly checking the version here.
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME)
- || "T".equals(Build.VERSION.CODENAME)) {
- // DAD Proxy starts forwarding packets after IPv6 upstream is present.
- mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams);
- }
-
- return true;
- }
-
- private void stopIPv6() {
- mInterfaceParams = null;
- setRaParams(null);
-
- if (mRaDaemon != null) {
- mRaDaemon.stop();
- mRaDaemon = null;
- }
-
- if (mDadProxy != null) {
- mDadProxy.stop();
- mDadProxy = null;
- }
- }
-
- // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
- // LinkProperties. These have extraneous data filtered out and only the
- // necessary prefixes included (per its prefix distribution policy).
- //
- // TODO: Evaluate using a data structure than is more directly suited to
- // communicating only the relevant information.
- private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
- if (mRaDaemon == null) return;
-
- // Avoid unnecessary work on spurious updates.
- if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
- return;
- }
-
- RaParams params = null;
- String upstreamIface = null;
- InterfaceParams upstreamIfaceParams = null;
- int upstreamIfIndex = 0;
-
- if (v6only != null) {
- upstreamIface = v6only.getInterfaceName();
- upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface);
- if (upstreamIfaceParams != null) {
- upstreamIfIndex = upstreamIfaceParams.index;
- }
- params = new RaParams();
- params.mtu = v6only.getMtu();
- params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
-
- if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
-
- for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
- if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
-
- 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);
- }
- }
-
- // Add upstream index to name mapping for the tether stats usage in the coordinator.
- // Although this mapping could be added by both class Tethering and IpServer, adding
- // mapping from IpServer guarantees that the mapping is added before the adding
- // forwarding rules. That is because there are different state machines in both
- // classes. It is hard to guarantee the link property update order between multiple
- // state machines.
- mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface);
- }
-
- // If v6only is null, we pass in null to setRaParams(), which handles
- // deprecation of any existing RA data.
-
- setRaParams(params);
- mLastIPv6LinkProperties = v6only;
-
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null);
- mLastIPv6UpstreamIfindex = upstreamIfIndex;
- if (mDadProxy != null) {
- mDadProxy.setUpstreamIface(upstreamIfaceParams);
- }
- }
-
- private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
- final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
- mNetd, toBeRemoved);
- if (removalFailures > 0) {
- mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
- removalFailures));
- }
-
- for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
- }
-
- private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
- try {
- // It's safe to call networkAddInterface() even if
- // the interface is already in the local_network.
- mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
- try {
- // Add routes from local network. Note that adding routes that
- // already exist does not cause an error (EEXIST is silently ignored).
- RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
- } catch (IllegalStateException e) {
- mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
- return;
- }
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
- return;
- }
-
- for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
- }
-
- private void configureLocalIPv6Routes(
- HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
- // [1] Remove the routes that are deprecated.
- if (!deprecatedPrefixes.isEmpty()) {
- removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes));
- }
-
- // [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);
- }
-
- if (!addedPrefixes.isEmpty()) {
- addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes));
- }
- }
- }
-
- private void configureLocalIPv6Dns(
- HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
- // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located?
- if (mNetd == null) {
- if (newDnses != null) newDnses.clear();
- mLog.e("No netd service instance available; not setting local IPv6 addresses");
- return;
- }
-
- // [1] Remove deprecated local DNS IP addresses.
- if (!deprecatedDnses.isEmpty()) {
- for (Inet6Address dns : deprecatedDnses) {
- if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) {
- mLog.e("Failed to remove local dns IP " + dns);
- }
-
- mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
- }
- }
-
- // [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) {
- if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) {
- mLog.e("Failed to add local dns IP " + dns);
- newDnses.remove(dns);
- }
-
- mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
- }
- }
-
- try {
- mNetd.tetherApplyDnsInterfaces();
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to update local DNS caching server");
- if (newDnses != null) newDnses.clear();
- }
- }
-
- private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
- // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
- // offload is disabled. Add this check just in case.
- // TODO: Perhaps remove this protection check.
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleAdd(this, rule);
- }
-
- private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) {
- // TODO: Perhaps remove this protection check.
- // See the related comment in #addIpv6ForwardingRule.
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleRemove(this, rule);
- }
-
- private void clearIpv6ForwardingRules() {
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleClear(this);
- }
-
- private void updateIpv6ForwardingRule(int newIfindex) {
- // TODO: Perhaps remove this protection check.
- // See the related comment in #addIpv6ForwardingRule.
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex);
- }
-
- // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
- // changes or if a neighbor event is received.
- private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
- NeighborEvent e) {
- // If we no longer have an upstream, clear forwarding rules and do nothing else.
- if (upstreamIfindex == 0) {
- clearIpv6ForwardingRules();
- return;
- }
-
- // If the upstream interface has changed, remove all rules and re-add them with the new
- // upstream interface.
- if (prevUpstreamIfindex != upstreamIfindex) {
- updateIpv6ForwardingRule(upstreamIfindex);
- }
-
- // If we're here to process a NeighborEvent, do so now.
- // mInterfaceParams must be non-null or the event would not have arrived.
- if (e == null) return;
- if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
- || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
- return;
- }
-
- // When deleting rules, we still need to pass a non-null MAC, even though it's ignored.
- // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never
- // add rules with a null MAC, only delete them.
- MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS;
- Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
- mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac);
- if (e.isValid()) {
- addIpv6ForwardingRule(rule);
- } else {
- removeIpv6ForwardingRule(rule);
- }
- }
-
- private void handleNeighborEvent(NeighborEvent e) {
- if (mInterfaceParams != null
- && mInterfaceParams.index == e.ifindex
- && mInterfaceParams.hasMacAddress) {
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
- }
- }
-
- private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
- if (!currentPrefix.contains(mIpv4Address.getAddress())
- || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
- Log.e(TAG, "Invalid prefix: " + currentPrefix);
- return;
- }
-
- final LinkAddress deprecatedLinkAddress = mIpv4Address;
- mIpv4Address = requestIpv4Address(false);
- if (mIpv4Address == null) {
- mLog.e("Fail to request a new downstream prefix");
- return;
- }
- final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
-
- // Add new IPv4 address on the interface.
- if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
- mLog.e("Failed to add new IP " + srvAddr);
- return;
- }
-
- // Remove deprecated routes from local network.
- removeRoutesFromLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
- mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
-
- // Add new routes to local network.
- addRoutesToLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
- mLinkProperties.addLinkAddress(mIpv4Address);
-
- // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
- // listen on the interface configured with new IPv4 address, that results DNS validation
- // failure of downstream client even if appropriate routes have been configured.
- try {
- mNetd.tetherApplyDnsInterfaces();
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to update local DNS caching server");
- return;
- }
- sendLinkProperties();
-
- // Notify DHCP server that new prefix/route has been applied on IpServer.
- final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
- (Inet4Address) mStaticIpv4ClientAddr.getAddress();
- final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
- srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
- try {
- mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
- @Override
- public void callback(int statusCode) {
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error updating DHCP serving params: " + statusCode);
- }
- }
- });
- } catch (RemoteException e) {
- mLog.e("Error updating DHCP serving params", e);
- }
- }
-
- private byte getHopLimit(String upstreamIface, int adjustTTL) {
- try {
- int upstreamHopLimit = Integer.parseUnsignedInt(
- mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
- upstreamHopLimit = upstreamHopLimit + adjustTTL;
- // Cap the hop limit to 255.
- return (byte) Integer.min(upstreamHopLimit, 255);
- } catch (Exception e) {
- mLog.e("Failed to find upstream interface hop limit", e);
- }
- return RaParams.DEFAULT_HOPLIMIT;
- }
-
- private void setRaParams(RaParams newParams) {
- if (mRaDaemon != null) {
- final RaParams deprecatedParams =
- RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
-
- configureLocalIPv6Routes(deprecatedParams.prefixes,
- (newParams != null) ? newParams.prefixes : null);
-
- configureLocalIPv6Dns(deprecatedParams.dnses,
- (newParams != null) ? newParams.dnses : null);
-
- mRaDaemon.buildNewRa(deprecatedParams, newParams);
- }
-
- mLastRaParams = newParams;
- }
-
- private void logMessage(State state, int what) {
- mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
- }
-
- private void sendInterfaceState(int newInterfaceState) {
- mServingMode = newInterfaceState;
- mCallback.updateInterfaceState(this, newInterfaceState, mLastError);
- sendLinkProperties();
- }
-
- private void sendLinkProperties() {
- mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties));
- }
-
- private void resetLinkProperties() {
- mLinkProperties.clear();
- mLinkProperties.setInterfaceName(mIfaceName);
- }
-
- private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
- // Ignore static address configuration if they are invalid or null. In theory, static
- // addresses should not be invalid here because TetheringManager do not allow caller to
- // specify invalid static address configuration.
- if (request == null || request.localIPv4Address == null
- || request.staticClientAddress == null || !checkStaticAddressConfiguration(
- request.localIPv4Address, request.staticClientAddress)) {
- return;
- }
-
- mStaticIpv4ServerAddr = request.localIPv4Address;
- mStaticIpv4ClientAddr = request.staticClientAddress;
- }
-
- class InitialState extends State {
- @Override
- public void enter() {
- sendInterfaceState(STATE_AVAILABLE);
- }
-
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
- switch (message.arg1) {
- case STATE_LOCAL_ONLY:
- maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
- transitionTo(mLocalHotspotState);
- break;
- case STATE_TETHERED:
- maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
- transitionTo(mTetheredState);
- break;
- default:
- mLog.e("Invalid tethering interface serving state specified.");
- }
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- break;
- case CMD_IPV6_TETHER_UPDATE:
- updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class BaseServingState extends State {
- @Override
- public void enter() {
- if (!startIPv4()) {
- mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
- return;
- }
-
- try {
- NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
- } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
- mLog.e("Error Tethering", e);
- mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
- return;
- }
-
- if (!startIPv6()) {
- mLog.e("Failed to startIPv6");
- // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
- return;
- }
- }
-
- @Override
- public void exit() {
- // Note that at this point, we're leaving the tethered state. We can fail any
- // of these operations, but it doesn't really change that we have to try them
- // all in sequence.
- stopIPv6();
-
- try {
- NetdUtils.untetherInterface(mNetd, mIfaceName);
- } catch (RemoteException | ServiceSpecificException e) {
- mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
- mLog.e("Failed to untether interface: " + e);
- }
-
- stopIPv4();
-
- resetLinkProperties();
- }
-
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_UNREQUESTED:
- transitionTo(mInitialState);
- if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
- break;
- case CMD_IPV6_TETHER_UPDATE:
- updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
- sendLinkProperties();
- break;
- case CMD_IP_FORWARDING_ENABLE_ERROR:
- case CMD_IP_FORWARDING_DISABLE_ERROR:
- case CMD_START_TETHERING_ERROR:
- case CMD_STOP_TETHERING_ERROR:
- case CMD_SET_DNS_FORWARDERS_ERROR:
- mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
- transitionTo(mInitialState);
- break;
- case CMD_NEW_PREFIX_REQUEST:
- handleNewPrefixRequest((IpPrefix) message.obj);
- break;
- case CMD_NOTIFY_PREFIX_CONFLICT:
- mLog.i("restart tethering: " + mInterfaceType);
- mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
- transitionTo(mWaitingForRestartState);
- break;
- default:
- return false;
- }
- return true;
- }
- }
-
- // Handling errors in BaseServingState.enter() by transitioning is
- // problematic because transitioning during a multi-state jump yields
- // a Log.wtf(). Ultimately, there should be only one ServingState,
- // and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of IpServer.
- class LocalHotspotState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
- sendInterfaceState(STATE_LOCAL_ONLY);
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message)) return true;
-
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
- break;
- case CMD_TETHER_CONNECTION_CHANGED:
- // Ignored in local hotspot state.
- break;
- default:
- return false;
- }
- return true;
- }
- }
-
- // Handling errors in BaseServingState.enter() by transitioning is
- // problematic because transitioning during a multi-state jump yields
- // a Log.wtf(). Ultimately, there should be only one ServingState,
- // and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of IpServer.
- class TetheredState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- sendInterfaceState(STATE_TETHERED);
- }
-
- @Override
- public void exit() {
- cleanupUpstream();
- super.exit();
- }
-
- private void cleanupUpstream() {
- if (mUpstreamIfaceSet == null) return;
-
- for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
- mUpstreamIfaceSet = null;
- clearIpv6ForwardingRules();
- }
-
- private void cleanupUpstreamInterface(String upstreamIface) {
- // Note that we don't care about errors here.
- // Sometimes interfaces are gone before we get
- // to remove their rules, which generates errors.
- // Just do the best we can.
- try {
- mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
- }
- try {
- mNetd.tetherRemoveForward(mIfaceName, upstreamIface);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception in disableNat: " + e.toString());
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message)) return true;
-
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- mLog.e("CMD_TETHER_REQUESTED while already tethering.");
- break;
- case CMD_TETHER_CONNECTION_CHANGED:
- final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
- if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
- if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
- break;
- }
-
- if (newUpstreamIfaceSet == null) {
- cleanupUpstream();
- break;
- }
-
- for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
- cleanupUpstreamInterface(removed);
- }
-
- final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
- // This makes the call to cleanupUpstream() in the error
- // path for any interface neatly cleanup all the interfaces.
- mUpstreamIfaceSet = newUpstreamIfaceSet;
-
- for (String ifname : added) {
- try {
- mNetd.tetherAddForward(mIfaceName, ifname);
- mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception enabling NAT: " + e.toString());
- cleanupUpstream();
- mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
- transitionTo(mInitialState);
- return true;
- }
- }
- break;
- case CMD_NEIGHBOR_EVENT:
- handleNeighborEvent((NeighborEvent) message.obj);
- break;
- default:
- return false;
- }
- return true;
- }
-
- private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
- if (mUpstreamIfaceSet == null && newIfaces == null) return true;
- if (mUpstreamIfaceSet != null && newIfaces != null) {
- return mUpstreamIfaceSet.equals(newIfaces);
- }
- return false;
- }
-
- private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
- if (mUpstreamIfaceSet == null) return new HashSet<>();
-
- final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
- removed.removeAll(newIfaces.ifnames);
- return removed;
- }
-
- private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
- final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
- if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
- return added;
- }
- }
-
- /**
- * This state is terminal for the per interface state machine. At this
- * point, the tethering main state machine should have removed this interface
- * specific state machine from its list of possible recipients of
- * tethering requests. The state machine itself will hang around until
- * the garbage collector finds it.
- */
- class UnavailableState extends State {
- @Override
- public void enter() {
- mIpNeighborMonitor.stop();
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
- sendInterfaceState(STATE_UNAVAILABLE);
- }
- }
-
- class WaitingForRestartState extends State {
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_UNREQUESTED:
- transitionTo(mInitialState);
- mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
- mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- mLog.i("Untethered (interface down) and restarting" + mIfaceName);
- mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
- break;
- default:
- return false;
- }
- return true;
- }
- }
-
- // Accumulate routes representing "prefixes to be assigned to the local
- // interface", for subsequent modification of local_network routing.
- private static ArrayList<RouteInfo> getLocalRoutesFor(
- String ifname, HashSet<IpPrefix> prefixes) {
- final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
- for (IpPrefix ipp : prefixes) {
- localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST));
- }
- return localRoutes;
- }
-
- // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1.
- private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
- final byte[] dnsBytes = localPrefix.getRawAddress();
- dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1));
- try {
- return Inet6Address.getByAddress(null, dnsBytes, 0);
- } catch (UnknownHostException e) {
- Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
- return null;
- }
- }
-
- private static byte getRandomSanitizedByte(byte dflt, byte... excluded) {
- final byte random = (byte) (new Random()).nextInt();
- for (int value : excluded) {
- if (random == value) return dflt;
- }
- return random;
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
deleted file mode 100644
index 73fc833fabf5..000000000000
--- a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_RAW;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOCK_RAW;
-
-import android.net.util.InterfaceParams;
-import android.net.util.PacketReader;
-import android.net.util.SocketUtils;
-import android.net.util.TetheringUtils;
-import android.os.Handler;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-
-/**
- * Basic IPv6 Neighbor Advertisement Forwarder.
- *
- * Forward NA packets from upstream iface to tethered iface
- * and NS packets from tethered iface to upstream iface.
- *
- * @hide
- */
-public class NeighborPacketForwarder extends PacketReader {
- private final String mTag;
-
- private FileDescriptor mFd;
-
- // TODO: get these from NetworkStackConstants.
- private static final int IPV6_ADDR_LEN = 16;
- private static final int IPV6_DST_ADDR_OFFSET = 24;
- private static final int IPV6_HEADER_LEN = 40;
- private static final int ETH_HEADER_LEN = 14;
-
- private InterfaceParams mListenIfaceParams, mSendIfaceParams;
-
- private final int mType;
- public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
- public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
-
- public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) {
- super(h);
- mTag = NeighborPacketForwarder.class.getSimpleName() + "-"
- + tetheredInterface.name + "-" + type;
- mType = type;
-
- if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- mSendIfaceParams = tetheredInterface;
- } else {
- mListenIfaceParams = tetheredInterface;
- }
- }
-
- /** Set new upstream iface and start/stop based on new params. */
- public void setUpstreamIface(InterfaceParams upstreamParams) {
- final InterfaceParams oldUpstreamParams;
-
- if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- oldUpstreamParams = mListenIfaceParams;
- mListenIfaceParams = upstreamParams;
- } else {
- oldUpstreamParams = mSendIfaceParams;
- mSendIfaceParams = upstreamParams;
- }
-
- if (oldUpstreamParams == null && upstreamParams != null) {
- start();
- } else if (oldUpstreamParams != null && upstreamParams == null) {
- stop();
- } else if (oldUpstreamParams != null && upstreamParams != null
- && oldUpstreamParams.index != upstreamParams.index) {
- stop();
- start();
- }
- }
-
- // TODO: move NetworkStackUtils.closeSocketQuietly to
- // frameworks/libs/net/common/device/com/android/net/module/util/[someclass].
- private void closeSocketQuietly(FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
- @Override
- protected FileDescriptor createFd() {
- try {
- // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used.
- // To keep uniformity in both directions PACKET socket can be used.
- mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
-
- // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type?
- if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- TetheringUtils.setupNaSocket(mFd);
- } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) {
- TetheringUtils.setupNsSocket(mFd);
- }
-
- SocketAddress bindAddress = SocketUtils.makePacketSocketAddress(
- ETH_P_IPV6, mListenIfaceParams.index);
- Os.bind(mFd, bindAddress);
- } catch (ErrnoException | SocketException e) {
- Log.wtf(mTag, "Failed to create socket", e);
- closeSocketQuietly(mFd);
- return null;
- }
-
- return mFd;
- }
-
- private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) {
- Inet6Address dstAddr;
- try {
- dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf,
- IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN));
- } catch (UnknownHostException | ClassCastException impossible) {
- throw new AssertionError("16-byte array not valid IPv6 address?");
- }
- return dstAddr;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- if (mSendIfaceParams == null) {
- return;
- }
-
- // The BPF filter should already have checked the length of the packet, but...
- if (length < IPV6_HEADER_LEN) {
- return;
- }
- Inet6Address destv6 = getIpv6DestinationAddress(recvbuf);
- if (!destv6.isMulticastAddress()) {
- return;
- }
- InetSocketAddress dest = new InetSocketAddress(destv6, 0);
-
- FileDescriptor fd = null;
- try {
- fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
- SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name);
-
- int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest);
- } catch (ErrnoException | SocketException e) {
- Log.e(mTag, "handlePacket error: " + e);
- } finally {
- closeSocketQuietly(fd);
- }
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
deleted file mode 100644
index 7c0b7cc7515c..000000000000
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
-import static android.net.util.TetheringUtils.getAllNodesForScopeId;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.SOCK_RAW;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_SNDTIMEO;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
-import android.net.util.SocketUtils;
-import android.net.util.TetheringUtils;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructTimeval;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.TrafficStatsConstants;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-
-
-/**
- * Basic IPv6 Router Advertisement Daemon.
- *
- * TODO:
- *
- * - Rewrite using Handler (and friends) so that AlarmManager can deliver
- * "kick" messages when it's time to send a multicast RA.
- *
- * @hide
- */
-public class RouterAdvertisementDaemon {
- private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName();
- private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133);
- private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134);
- private static final int MIN_RA_HEADER_SIZE = 16;
-
- // Summary of various timers and lifetimes.
- private static final int MIN_RTR_ADV_INTERVAL_SEC = 300;
- private static final int MAX_RTR_ADV_INTERVAL_SEC = 600;
- // In general, router, prefix, and DNS lifetimes are all advised to be
- // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double
- // that to allow for multicast packet loss.
- //
- // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent
- // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of
- // "approximately 7 RAs per hour".
- private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC;
- // From https://tools.ietf.org/html/rfc4861#section-10 .
- private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3;
- // Both initial and final RAs, but also for changes in RA contents.
- // From https://tools.ietf.org/html/rfc4861#section-10 .
- private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5;
-
- private static final int DAY_IN_SECONDS = 86_400;
-
- private final InterfaceParams mInterface;
- private final InetSocketAddress mAllNodes;
-
- // This lock is to protect the RA from being updated while being
- // transmitted on another thread (multicast or unicast).
- //
- // TODO: This should be handled with a more RCU-like approach.
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final byte[] mRA = new byte[IPV6_MIN_MTU];
- @GuardedBy("mLock")
- private int mRaLength;
- @GuardedBy("mLock")
- private final DeprecatedInfoTracker mDeprecatedInfoTracker;
- @GuardedBy("mLock")
- private RaParams mRaParams;
-
- private volatile FileDescriptor mSocket;
- private volatile MulticastTransmitter mMulticastTransmitter;
- private volatile UnicastResponder mUnicastResponder;
-
- /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
- public static class RaParams {
- // Tethered traffic will have the hop limit properly decremented.
- // Consequently, set the hoplimit greater by one than the upstream
- // unicast hop limit.
- //
- // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the
- // upstream interface for more correct behaviour.
- static final byte DEFAULT_HOPLIMIT = 65;
-
- public boolean hasDefaultRoute;
- public byte hopLimit;
- public int mtu;
- public HashSet<IpPrefix> prefixes;
- public HashSet<Inet6Address> dnses;
-
- public RaParams() {
- hasDefaultRoute = false;
- hopLimit = DEFAULT_HOPLIMIT;
- mtu = IPV6_MIN_MTU;
- prefixes = new HashSet<IpPrefix>();
- dnses = new HashSet<Inet6Address>();
- }
-
- public RaParams(RaParams other) {
- hasDefaultRoute = other.hasDefaultRoute;
- hopLimit = other.hopLimit;
- mtu = other.mtu;
- 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;
- }
- }
-
- public RouterAdvertisementDaemon(InterfaceParams ifParams) {
- mInterface = ifParams;
- mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
- mDeprecatedInfoTracker = new DeprecatedInfoTracker();
- }
-
- /** Build new RA.*/
- public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
- synchronized (mLock) {
- 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();
- }
-
- /** Start router advertisement daemon. */
- public boolean start() {
- if (!createSocket()) {
- return false;
- }
-
- mMulticastTransmitter = new MulticastTransmitter();
- mMulticastTransmitter.start();
-
- mUnicastResponder = new UnicastResponder();
- mUnicastResponder.start();
-
- return true;
- }
-
- /** Stop router advertisement daemon. */
- public void stop() {
- closeSocket();
- // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
- // the thread's termination.
- maybeNotifyMulticastTransmitter();
- mMulticastTransmitter = null;
- mUnicastResponder = null;
- }
-
- @GuardedBy("mLock")
- private void assembleRaLocked() {
- final ByteBuffer ra = ByteBuffer.wrap(mRA);
- ra.order(ByteOrder.BIG_ENDIAN);
-
- final boolean haveRaParams = (mRaParams != null);
- boolean shouldSendRA = false;
-
- try {
- putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute,
- haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT);
- putSlla(ra, mInterface.macAddr.toByteArray());
- 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);
-
- if (haveRaParams) {
- putMtu(ra, mRaParams.mtu);
- mRaLength = ra.position();
-
- for (IpPrefix ipp : mRaParams.prefixes) {
- putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
-
- if (mRaParams.dnses.size() > 0) {
- putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
- }
-
- for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
- putPio(ra, ipp, 0, 0);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
-
- 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);
- }
-
- // We have nothing worth announcing; indicate as much to maybeSendRA().
- if (!shouldSendRA) {
- mRaLength = 0;
- }
- }
-
- private void maybeNotifyMulticastTransmitter() {
- final MulticastTransmitter m = mMulticastTransmitter;
- if (m != null) {
- m.hup();
- }
- }
-
- private static byte asByte(int value) {
- return (byte) value;
- }
- private static short asShort(int value) {
- return (short) value;
- }
-
- private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
- /**
- Router Advertisement Message Format
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Code | Checksum |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Reachable Time |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Retrans Timer |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Options ...
- +-+-+-+-+-+-+-+-+-+-+-+-
- */
- ra.put(ICMPV6_ND_ROUTER_ADVERT)
- .put(asByte(0))
- .putShort(asShort(0))
- .put(hopLimit)
- // RFC 4191 "high" preference, iff. advertising a default route.
- .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
- .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
- .putInt(0)
- .putInt(0);
- }
-
- private static void putSlla(ByteBuffer ra, byte[] slla) {
- /**
- Source/Target Link-layer Address
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Link-Layer Address ...
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- if (slla == null || slla.length != 6) {
- // Only IEEE 802.3 6-byte addresses are supported.
- return;
- }
-
- final byte nd_option_slla = 1;
- final byte slla_num_8octets = 1;
- ra.put(nd_option_slla)
- .put(slla_num_8octets)
- .put(slla);
- }
-
- private static void putExpandedFlagsOption(ByteBuffer ra) {
- /**
- Router Advertisement Expanded Flags Option
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Bit fields available ..
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- ... for assignment |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
-
- final byte nd_option__efo = 26;
- final byte efo_num_8octets = 1;
-
- ra.put(nd_option__efo)
- .put(efo_num_8octets)
- .putShort(asShort(0))
- .putInt(0);
- }
-
- private static void putMtu(ByteBuffer ra, int mtu) {
- /**
- MTU
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Reserved |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | MTU |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- final byte nd_option_mtu = 5;
- final byte mtu_num_8octs = 1;
- ra.put(nd_option_mtu)
- .put(mtu_num_8octs)
- .putShort(asShort(0))
- .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
- }
-
- private static void putPio(ByteBuffer ra, IpPrefix ipp,
- int validTime, int preferredTime) {
- /**
- Prefix Information
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Prefix Length |L|A| Reserved1 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Valid Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Preferred Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Reserved2 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | |
- + +
- | |
- + Prefix +
- | |
- + +
- | |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- final int prefixLength = ipp.getPrefixLength();
- if (prefixLength != 64) {
- return;
- }
- final byte nd_option_pio = 3;
- final byte pio_num_8octets = 4;
-
- if (validTime < 0) validTime = 0;
- if (preferredTime < 0) preferredTime = 0;
- if (preferredTime > validTime) preferredTime = validTime;
-
- final byte[] addr = ipp.getAddress().getAddress();
- ra.put(nd_option_pio)
- .put(pio_num_8octets)
- .put(asByte(prefixLength))
- .put(asByte(0xc0)) /* L & A set */
- .putInt(validTime)
- .putInt(preferredTime)
- .putInt(0)
- .put(addr);
- }
-
- private static void putRio(ByteBuffer ra, IpPrefix ipp) {
- /**
- Route Information Option
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Prefix Length |Resvd|Prf|Resvd|
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Route Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Prefix (Variable Length) |
- . .
- . .
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- final int prefixLength = ipp.getPrefixLength();
- if (prefixLength > 64) {
- return;
- }
- final byte nd_option_rio = 24;
- final byte rio_num_8octets = asByte(
- (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
-
- final byte[] addr = ipp.getAddress().getAddress();
- ra.put(nd_option_rio)
- .put(rio_num_8octets)
- .put(asByte(prefixLength))
- .put(asByte(0x18))
- .putInt(DEFAULT_LIFETIME);
-
- // Rely upon an IpPrefix's address being properly zeroed.
- if (prefixLength > 0) {
- ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16);
- }
- }
-
- private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
- /**
- Recursive DNS Server (RDNSS) Option
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Reserved |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | |
- : Addresses of IPv6 Recursive DNS Servers :
- | |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
-
- final HashSet<Inet6Address> filteredDnses = new HashSet<>();
- for (Inet6Address dns : dnses) {
- if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) {
- filteredDnses.add(dns);
- }
- }
- if (filteredDnses.isEmpty()) return;
-
- final byte nd_option_rdnss = 25;
- final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1);
- ra.put(nd_option_rdnss)
- .put(rdnss_num_8octets)
- .putShort(asShort(0))
- .putInt(lifetime);
-
- for (Inet6Address dns : filteredDnses) {
- // 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
- // 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());
- }
- }
-
- private boolean createSocket() {
- final int send_timout_ms = 300;
-
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
- try {
- mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
- // Setting SNDTIMEO is purely for defensive purposes.
- Os.setsockoptTimeval(
- mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
- SocketUtils.bindSocketToInterface(mSocket, mInterface.name);
- TetheringUtils.setupRaSocket(mSocket, mInterface.index);
- } catch (ErrnoException | IOException e) {
- Log.e(TAG, "Failed to create RA daemon socket: " + e);
- return false;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
-
- return true;
- }
-
- private void closeSocket() {
- if (mSocket != null) {
- try {
- SocketUtils.closeSocket(mSocket);
- } catch (IOException ignored) { }
- }
- mSocket = null;
- }
-
- private boolean isSocketValid() {
- final FileDescriptor s = mSocket;
- return (s != null) && s.valid();
- }
-
- private boolean isSuitableDestination(InetSocketAddress dest) {
- if (mAllNodes.equals(dest)) {
- return true;
- }
-
- final InetAddress destip = dest.getAddress();
- return (destip instanceof Inet6Address)
- && destip.isLinkLocalAddress()
- && (((Inet6Address) destip).getScopeId() == mInterface.index);
- }
-
- private void maybeSendRA(InetSocketAddress dest) {
- if (dest == null || !isSuitableDestination(dest)) {
- dest = mAllNodes;
- }
-
- try {
- synchronized (mLock) {
- if (mRaLength < MIN_RA_HEADER_SIZE) {
- // No actual RA to send.
- return;
- }
- Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest);
- }
- Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress());
- } catch (ErrnoException | SocketException e) {
- if (isSocketValid()) {
- Log.e(TAG, "sendto error: " + e);
- }
- }
- }
-
- private final class UnicastResponder extends Thread {
- private final InetSocketAddress mSolicitor = new InetSocketAddress(0);
- // The recycled buffer for receiving Router Solicitations from clients.
- // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
- // This is fine since currently only byte 0 is examined anyway.
- private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
-
- @Override
- public void run() {
- while (isSocketValid()) {
- try {
- // Blocking receive.
- final int rval = Os.recvfrom(
- mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
- // Do the least possible amount of validation.
- if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) {
- continue;
- }
- } catch (ErrnoException | SocketException e) {
- if (isSocketValid()) {
- Log.e(TAG, "recvfrom error: " + e);
- }
- continue;
- }
-
- maybeSendRA(mSolicitor);
- }
- }
- }
-
- // TODO: Consider moving this to run on a provided Looper as a Handler,
- // with WakeupMessage-style messages providing the timer driven input.
- private final class MulticastTransmitter extends Thread {
- private final Random mRandom = new Random();
- private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0);
-
- @Override
- public void run() {
- while (isSocketValid()) {
- try {
- Thread.sleep(getNextMulticastTransmitDelayMs());
- } catch (InterruptedException ignored) {
- // Stop sleeping, immediately send an RA, and continue.
- }
-
- maybeSendRA(mAllNodes);
- synchronized (mLock) {
- if (mDeprecatedInfoTracker.decrementCounters()) {
- // At least one deprecated PIO has been removed;
- // reassemble the RA.
- assembleRaLocked();
- }
- }
- }
- }
-
- public void hup() {
- // Set to one fewer that the desired number, because as soon as
- // the thread interrupt is processed we immediately send an RA
- // and mUrgentAnnouncements is not examined until the subsequent
- // sleep interval computation (i.e. this way we send 3 and not 4).
- mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1);
- interrupt();
- }
-
- private int getNextMulticastTransmitDelaySec() {
- 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;
- }
- deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
- }
-
- final int urgentPending = mUrgentAnnouncements.getAndDecrement();
- if ((urgentPending > 0) || deprecationInProgress) {
- return MIN_DELAY_BETWEEN_RAS_SEC;
- }
-
- return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt(
- MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC);
- }
-
- private long getNextMulticastTransmitDelayMs() {
- return 1000 * (long) getNextMulticastTransmitDelaySec();
- }
- }
-}
diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
deleted file mode 100644
index b1ffdb01f5f3..000000000000
--- a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.util;
-
-import android.net.INetdUnsolicitedEventListener;
-
-import androidx.annotation.NonNull;
-
-/**
- * Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be
- * overridden.
- */
-public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
-
- @Override
- public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs,
- int uid) { }
-
- @Override
- public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { }
-
- @Override
- public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS,
- @NonNull String[] servers) { }
-
- @Override
- public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags,
- int scope) { }
-
- @Override
- public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags,
- int scope) { }
-
- @Override
- public void onInterfaceAdded(@NonNull String ifName) { }
-
- @Override
- public void onInterfaceRemoved(@NonNull String ifName) { }
-
- @Override
- public void onInterfaceChanged(@NonNull String ifName, boolean up) { }
-
- @Override
- public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { }
-
- @Override
- public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway,
- @NonNull String ifName) { }
-
- @Override
- public void onStrictCleartextDetected(int uid, @NonNull String hex) { }
-
- @Override
- public int getInterfaceVersion() {
- return INetdUnsolicitedEventListener.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return INetdUnsolicitedEventListener.HASH;
- }
-}
diff --git a/packages/Tethering/src/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java
deleted file mode 100644
index 758978711bd4..000000000000
--- a/packages/Tethering/src/android/net/util/InterfaceSet.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.StringJoiner;
-
-
-/**
- * @hide
- */
-public class InterfaceSet {
- public final Set<String> ifnames;
-
- public InterfaceSet(String... names) {
- final Set<String> nameSet = new HashSet<>();
- for (String name : names) {
- if (name != null) nameSet.add(name);
- }
- ifnames = Collections.unmodifiableSet(nameSet);
- }
-
- @Override
- public String toString() {
- final StringJoiner sj = new StringJoiner(",", "[", "]");
- for (String ifname : ifnames) sj.add(ifname);
- return sj.toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj != null
- && obj instanceof InterfaceSet
- && ifnames.equals(((InterfaceSet) obj).ifnames);
- }
-}
diff --git a/packages/Tethering/src/android/net/util/PrefixUtils.java b/packages/Tethering/src/android/net/util/PrefixUtils.java
deleted file mode 100644
index f203e9995f3d..000000000000
--- a/packages/Tethering/src/android/net/util/PrefixUtils.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-
-/**
- * @hide
- */
-public class PrefixUtils {
- private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = {
- pfx("127.0.0.0/8"), // IPv4 loopback
- pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8
- pfx("::/3"),
- pfx("fe80::/64"), // IPv6 link-local
- pfx("fc00::/7"), // IPv6 ULA
- pfx("ff02::/8"), // IPv6 link-local multicast
- };
-
- public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24");
-
- /** Get non forwardable prefixes. */
- public static Set<IpPrefix> getNonForwardablePrefixes() {
- final HashSet<IpPrefix> prefixes = new HashSet<>();
- addNonForwardablePrefixes(prefixes);
- return prefixes;
- }
-
- /** Add non forwardable prefixes. */
- public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) {
- Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES);
- }
-
- /** Get local prefixes from |lp|. */
- public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) {
- final HashSet<IpPrefix> localPrefixes = new HashSet<>();
- if (lp == null) return localPrefixes;
-
- for (LinkAddress addr : lp.getAllLinkAddresses()) {
- if (addr.getAddress().isLinkLocalAddress()) continue;
- localPrefixes.add(asIpPrefix(addr));
- }
- // TODO: Add directly-connected routes as well (ones from which we did
- // not also form a LinkAddress)?
-
- return localPrefixes;
- }
-
- /** Convert LinkAddress |addr| to IpPrefix. */
- public static IpPrefix asIpPrefix(LinkAddress addr) {
- return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
- }
-
- /** Convert InetAddress |ip| to IpPrefix. */
- public static IpPrefix ipAddressAsPrefix(InetAddress ip) {
- final int bitLength = (ip instanceof Inet4Address)
- ? NetworkConstants.IPV4_ADDR_BITS
- : NetworkConstants.IPV6_ADDR_BITS;
- return new IpPrefix(ip, bitLength);
- }
-
- private static IpPrefix pfx(String prefixStr) {
- return new IpPrefix(prefixStr);
- }
-}
diff --git a/packages/Tethering/src/android/net/util/TetheringMessageBase.java b/packages/Tethering/src/android/net/util/TetheringMessageBase.java
deleted file mode 100644
index 29c0a817b6f4..000000000000
--- a/packages/Tethering/src/android/net/util/TetheringMessageBase.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.util;
-
-/**
- * This class defines Message.what base addresses for various state machine.
- */
-public class TetheringMessageBase {
- public static final int BASE_MAIN_SM = 0;
- public static final int BASE_IPSERVER = 100;
-
-}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
deleted file mode 100644
index 53b54f7de05d..000000000000
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.util;
-
-import android.net.TetherStatsParcel;
-import android.net.TetheringRequestParcel;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.io.FileDescriptor;
-import java.net.Inet6Address;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * The classes and the methods for tethering utilization.
- *
- * {@hide}
- */
-public class TetheringUtils {
- public static final byte[] ALL_NODES = new byte[] {
- (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
- };
-
- /**
- * Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
- * @param fd the socket's {@link FileDescriptor}.
- */
- public static native void setupNaSocket(FileDescriptor fd)
- throws SocketException;
-
- /**
- * Configures a socket for receiving and sending ICMPv6 neighbor solicitations.
- * @param fd the socket's {@link FileDescriptor}.
- */
- public static native void setupNsSocket(FileDescriptor fd)
- throws SocketException;
-
- /**
- * The object which records offload Tx/Rx forwarded bytes/packets.
- * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
- * this class as well.
- */
- public static class ForwardedStats {
- public final long rxBytes;
- public final long rxPackets;
- public final long txBytes;
- public final long txPackets;
-
- public ForwardedStats() {
- rxBytes = 0;
- rxPackets = 0;
- txBytes = 0;
- txPackets = 0;
- }
-
- public ForwardedStats(long rxBytes, long txBytes) {
- this.rxBytes = rxBytes;
- this.rxPackets = 0;
- this.txBytes = txBytes;
- this.txPackets = 0;
- }
-
- public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) {
- this.rxBytes = rxBytes;
- this.rxPackets = rxPackets;
- this.txBytes = txBytes;
- this.txPackets = txPackets;
- }
-
- public ForwardedStats(@NonNull TetherStatsParcel tetherStats) {
- rxBytes = tetherStats.rxBytes;
- rxPackets = tetherStats.rxPackets;
- txBytes = tetherStats.txBytes;
- txPackets = tetherStats.txPackets;
- }
-
- public ForwardedStats(@NonNull ForwardedStats other) {
- rxBytes = other.rxBytes;
- rxPackets = other.rxPackets;
- txBytes = other.txBytes;
- txPackets = other.txPackets;
- }
-
- /** Add Tx/Rx bytes/packets and return the result as a new object. */
- @NonNull
- public ForwardedStats add(@NonNull ForwardedStats other) {
- return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets,
- txBytes + other.txBytes, txPackets + other.txPackets);
- }
-
- /** Subtract Tx/Rx bytes/packets and return the result as a new object. */
- @NonNull
- public ForwardedStats subtract(@NonNull ForwardedStats other) {
- // TODO: Perhaps throw an exception if any negative difference value just in case.
- final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0);
- final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0);
- final long txBytesDiff = Math.max(txBytes - other.txBytes, 0);
- final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0);
- return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff);
- }
-
- /** Returns the string representation of this object. */
- @NonNull
- public String toString() {
- return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes,
- rxPackets, txBytes, txPackets);
- }
- }
-
- /**
- * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
- * @param fd the socket's {@link FileDescriptor}.
- * @param ifIndex the interface index.
- */
- public static native void setupRaSocket(FileDescriptor fd, int ifIndex)
- throws SocketException;
-
- /**
- * Read s as an unsigned 16-bit integer.
- */
- public static int uint16(short s) {
- return s & 0xffff;
- }
-
- /** Check whether two TetheringRequestParcels are the same. */
- public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
- final TetheringRequestParcel otherRequest) {
- if (request == otherRequest) return true;
-
- return request != null && otherRequest != null
- && request.tetheringType == otherRequest.tetheringType
- && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
- && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
- && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
- && request.showProvisioningUi == otherRequest.showProvisioningUi;
- }
-
- /** Get inet6 address for all nodes given scope ID. */
- public static Inet6Address getAllNodesForScopeId(int scopeId) {
- try {
- return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
- } catch (UnknownHostException uhe) {
- Log.wtf("TetheringUtils", "Failed to construct Inet6Address from "
- + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId);
- return null;
- }
- }
-}
diff --git a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java b/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
deleted file mode 100644
index e2804abd752b..000000000000
--- a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.util.Log;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-
-/**
- * A utility class that runs the provided callback on the provided handler when
- * intents matching the provided filter arrive. Intents received by a stale
- * receiver are safely ignored.
- *
- * Calls to startListening() and stopListening() must happen on the same thread.
- *
- * @hide
- */
-public class VersionedBroadcastListener {
- private static final boolean DBG = false;
-
- private final String mTag;
- private final Context mContext;
- private final Handler mHandler;
- private final IntentFilter mFilter;
- private final Consumer<Intent> mCallback;
- private final AtomicInteger mGenerationNumber;
- private BroadcastReceiver mReceiver;
-
- public VersionedBroadcastListener(String tag, Context ctx, Handler handler,
- IntentFilter filter, Consumer<Intent> callback) {
- mTag = tag;
- mContext = ctx;
- mHandler = handler;
- mFilter = filter;
- mCallback = callback;
- mGenerationNumber = new AtomicInteger(0);
- }
-
- /** Start listening to intent broadcast. */
- public void startListening() {
- if (DBG) Log.d(mTag, "startListening");
- if (mReceiver != null) return;
-
- mReceiver = new Receiver(mTag, mGenerationNumber, mCallback);
- mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
- }
-
- /** Stop listening to intent broadcast. */
- public void stopListening() {
- if (DBG) Log.d(mTag, "stopListening");
- if (mReceiver == null) return;
-
- mGenerationNumber.incrementAndGet();
- mContext.unregisterReceiver(mReceiver);
- mReceiver = null;
- }
-
- private static class Receiver extends BroadcastReceiver {
- public final String tag;
- public final AtomicInteger atomicGenerationNumber;
- public final Consumer<Intent> callback;
- // Used to verify this receiver is still current.
- public final int generationNumber;
-
- Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
- this.tag = tag;
- this.atomicGenerationNumber = atomicGenerationNumber;
- this.callback = callback;
- generationNumber = atomicGenerationNumber.incrementAndGet();
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final int currentGenerationNumber = atomicGenerationNumber.get();
-
- if (DBG) {
- Log.d(tag, "receiver generationNumber=" + generationNumber
- + ", current generationNumber=" + currentGenerationNumber);
- }
- if (generationNumber != currentGenerationNumber) return;
-
- callback.accept(intent);
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
deleted file mode 100644
index 20f30ea7a460..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-
-import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
-
-import android.app.usage.NetworkStatsManager;
-import android.net.INetd;
-import android.net.MacAddress;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
-import android.net.TetherOffloadRuleParcel;
-import android.net.TetherStatsParcel;
-import android.net.ip.IpServer;
-import android.net.netstats.provider.NetworkStatsProvider;
-import android.net.util.SharedLog;
-import android.net.util.TetheringUtils.ForwardedStats;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.net.Inet6Address;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This coordinator is responsible for providing BPF offload relevant functionality.
- * - Get tethering stats.
- * - Set data limit.
- * - Set global alert.
- * - Add/remove forwarding rules.
- *
- * @hide
- */
-public class BpfCoordinator {
- private static final String TAG = BpfCoordinator.class.getSimpleName();
- private static final int DUMP_TIMEOUT_MS = 10_000;
-
- @VisibleForTesting
- enum StatsType {
- STATS_PER_IFACE,
- STATS_PER_UID,
- }
-
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final INetd mNetd;
- @NonNull
- private final SharedLog mLog;
- @NonNull
- private final Dependencies mDeps;
- @Nullable
- private final BpfTetherStatsProvider mStatsProvider;
-
- // True if BPF offload is supported, false otherwise. The BPF offload could be disabled by
- // a runtime resource overlay package or device configuration. This flag is only initialized
- // in the constructor because it is hard to unwind all existing change once device
- // configuration is changed. Especially the forwarding rules. Keep the same setting
- // to make it simpler. See also TetheringConfiguration.
- private final boolean mIsBpfEnabled;
-
- // Tracks whether BPF tethering is started or not. This is set by tethering before it
- // starts the first IpServer and is cleared by tethering shortly before the last IpServer
- // is stopped. Note that rule updates (especially deletions, but sometimes additions as
- // well) may arrive when this is false. If they do, they must be communicated to netd.
- // Changes in data limits may also arrive when this is false, and if they do, they must
- // also be communicated to netd.
- private boolean mPollingStarted = false;
-
- // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
- // quota is interface independent and global for tether offload.
- private long mRemainingAlertQuota = QUOTA_UNLIMITED;
-
- // Maps upstream interface index to offloaded traffic statistics.
- // Always contains the latest total bytes/packets, since each upstream was started, received
- // from the BPF maps for each interface.
- private final SparseArray<ForwardedStats> mStats = new SparseArray<>();
-
- // Maps upstream interface names to interface quotas.
- // Always contains the latest value received from the framework for each interface, regardless
- // of whether offload is currently running (or is even supported) on that interface. Only
- // includes interfaces that have a quota set. Note that this map is used for storing the quota
- // which is set from the service. Because the service uses the interface name to present the
- // interface, this map uses the interface name to be the mapping index.
- private final HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
-
- // Maps upstream interface index to interface names.
- // Store all interface name since boot. Used for lookup what interface name it is from the
- // tether stats got from netd because netd reports interface index to present an interface.
- // TODO: Remove the unused interface name.
- private final SparseArray<String> mInterfaceNames = new SparseArray<>();
-
- // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a
- // given downstream. Each map:
- // - Is owned by the IpServer that is responsible for that downstream.
- // - Must only be modified by that IpServer.
- // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes
- // its last rule (or clears its rules).
- // TODO: Perhaps seal the map and rule operations which communicates with netd into a class.
- // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be
- // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear
- // and tetherOffloadRuleUpdate?
- // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream
- // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream
- // interface index while calling function to clear all rules. IpServer may be calling clear
- // rules function without a valid IPv6 downstream interface index even if it may have one
- // before. IpServer would need to call getInterfaceParams() in the constructor instead of when
- // startIpv6() is called, and make mInterfaceParams final.
- private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
- mIpv6ForwardingRules = new LinkedHashMap<>();
-
- // Runnable that used by scheduling next polling of stats.
- private final Runnable mScheduledPollingTask = () -> {
- updateForwardedStatsFromNetd();
- maybeSchedulePollingStats();
- };
-
- @VisibleForTesting
- public abstract static class Dependencies {
- /** Get handler. */
- @NonNull public abstract Handler getHandler();
-
- /** Get netd. */
- @NonNull public abstract INetd getNetd();
-
- /** Get network stats manager. */
- @NonNull public abstract NetworkStatsManager getNetworkStatsManager();
-
- /** Get shared log. */
- @NonNull public abstract SharedLog getSharedLog();
-
- /** Get tethering configuration. */
- @Nullable public abstract TetheringConfiguration getTetherConfig();
- }
-
- @VisibleForTesting
- public BpfCoordinator(@NonNull Dependencies deps) {
- mDeps = deps;
- mHandler = mDeps.getHandler();
- mNetd = mDeps.getNetd();
- mLog = mDeps.getSharedLog().forSubComponent(TAG);
- mIsBpfEnabled = isBpfEnabled();
- BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
- try {
- mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
- getClass().getSimpleName(), provider);
- } catch (RuntimeException e) {
- // TODO: Perhaps not allow to use BPF offload because the reregistration failure
- // implied that no data limit could be applies on a metered upstream if any.
- Log.wtf(TAG, "Cannot register offload stats provider: " + e);
- provider = null;
- }
- mStatsProvider = provider;
- }
-
- /**
- * Start BPF tethering offload stats polling when the first upstream is started.
- * Note that this can be only called on handler thread.
- * TODO: Perhaps check BPF support before starting.
- * TODO: Start the stats polling only if there is any client on the downstream.
- */
- public void startPolling() {
- if (mPollingStarted) return;
-
- if (!mIsBpfEnabled) {
- mLog.i("Offload disabled");
- return;
- }
-
- mPollingStarted = true;
- maybeSchedulePollingStats();
-
- mLog.i("Polling started");
- }
-
- /**
- * Stop BPF tethering offload stats polling.
- * The data limit cleanup and the tether stats maps cleanup are not implemented here.
- * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the
- * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup
- * functionality.
- * Note that this can be only called on handler thread.
- */
- public void stopPolling() {
- if (!mPollingStarted) return;
-
- // Stop scheduled polling tasks and poll the latest stats from BPF maps.
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
- updateForwardedStatsFromNetd();
- mPollingStarted = false;
-
- mLog.i("Polling stopped");
- }
-
- /**
- * Add forwarding rule. After adding the first rule on a given upstream, must add the data
- * limit on the given upstream.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleAdd(
- @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
- if (!mIsBpfEnabled) return;
-
- try {
- // TODO: Perhaps avoid to add a duplicate rule.
- mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Could not add IPv6 forwarding rule: ", e);
- return;
- }
-
- if (!mIpv6ForwardingRules.containsKey(ipServer)) {
- mIpv6ForwardingRules.put(ipServer, new LinkedHashMap<Inet6Address,
- Ipv6ForwardingRule>());
- }
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
-
- // Setup the data limit on the given upstream if the first rule is added.
- final int upstreamIfindex = rule.upstreamIfindex;
- if (!isAnyRuleOnUpstream(upstreamIfindex)) {
- // If failed to set a data limit, probably should not use this upstream, because
- // the upstream may not want to blow through the data limit that was told to apply.
- // TODO: Perhaps stop the coordinator.
- boolean success = updateDataLimit(upstreamIfindex);
- if (!success) {
- final String iface = mInterfaceNames.get(upstreamIfindex);
- mLog.e("Setting data limit for " + iface + " failed.");
- }
- }
-
- // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to
- // check if it is about adding a first rule for a given upstream.
- rules.put(rule.address, rule);
- }
-
- /**
- * Remove forwarding rule. After removing the last rule on a given upstream, must clear
- * data limit, update the last tether stats and remove the tether stats in the BPF maps.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleRemove(
- @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
- if (!mIsBpfEnabled) return;
-
- try {
- // TODO: Perhaps avoid to remove a non-existent rule.
- mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Could not remove IPv6 forwarding rule: ", e);
- return;
- }
-
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
- if (rules == null) return;
-
- // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if
- // the last rule is removed for a given upstream. If no rule is removed, return early.
- // Avoid unnecessary work on a non-existent rule which may have never been added or
- // removed already.
- if (rules.remove(rule.address) == null) return;
-
- // Remove the downstream entry if it has no more rule.
- if (rules.isEmpty()) {
- mIpv6ForwardingRules.remove(ipServer);
- }
-
- // Do cleanup functionality if there is no more rule on the given upstream.
- final int upstreamIfindex = rule.upstreamIfindex;
- if (!isAnyRuleOnUpstream(upstreamIfindex)) {
- try {
- final TetherStatsParcel stats =
- mNetd.tetherOffloadGetAndClearStats(upstreamIfindex);
- // Update the last stats delta and delete the local cache for a given upstream.
- updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats});
- mStats.remove(upstreamIfindex);
- } catch (RemoteException | ServiceSpecificException e) {
- Log.wtf(TAG, "Exception when cleanup tether stats for upstream index "
- + upstreamIfindex + ": ", e);
- }
- }
- }
-
- /**
- * Clear all forwarding rules for a given downstream.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
- if (!mIsBpfEnabled) return;
-
- final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
- ipServer);
- if (rules == null) return;
-
- // Need to build a rule list because the rule map may be changed in the iteration.
- for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
- tetherOffloadRuleRemove(ipServer, rule);
- }
- }
-
- /**
- * Update existing forwarding rules to new upstream for a given downstream.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) {
- if (!mIsBpfEnabled) return;
-
- final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
- ipServer);
- if (rules == null) return;
-
- // Need to build a rule list because the rule map may be changed in the iteration.
- for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
- // Remove the old rule before adding the new one because the map uses the same key for
- // both rules. Reversing the processing order causes that the new rule is removed as
- // unexpected.
- // TODO: Add new rule first to reduce the latency which has no rule.
- tetherOffloadRuleRemove(ipServer, rule);
- tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex));
- }
- }
-
- /**
- * Add upstream name to lookup table. The lookup table is used for tether stats interface name
- * lookup because the netd only reports interface index in BPF tether stats but the service
- * expects the interface name in NetworkStats object.
- * Note that this can be only called on handler thread.
- */
- public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) {
- if (!mIsBpfEnabled) return;
-
- if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return;
-
- // The same interface index to name mapping may be added by different IpServer objects or
- // re-added by reconnection on the same upstream interface. Ignore the duplicate one.
- final String iface = mInterfaceNames.get(upstreamIfindex);
- if (iface == null) {
- mInterfaceNames.put(upstreamIfindex, upstreamIface);
- } else if (!TextUtils.equals(iface, upstreamIface)) {
- Log.wtf(TAG, "The upstream interface name " + upstreamIface
- + " is different from the existing interface name "
- + iface + " for index " + upstreamIfindex);
- }
- }
-
- /**
- * Dump information.
- * Block the function until all the data are dumped on the handler thread or timed-out. The
- * reason is that dumpsys invokes this function on the thread of caller and the data may only
- * be allowed to be accessed on the handler thread.
- */
- public void dump(@NonNull IndentingPrintWriter pw) {
- final ConditionVariable dumpDone = new ConditionVariable();
- mHandler.post(() -> {
- pw.println("mIsBpfEnabled: " + mIsBpfEnabled);
- pw.println("Polling " + (mPollingStarted ? "started" : "not started"));
- pw.println("Stats provider " + (mStatsProvider != null
- ? "registered" : "not registered"));
- pw.println("Upstream quota: " + mInterfaceQuotas.toString());
- pw.println("Polling interval: " + getPollingInterval() + " ms");
-
- pw.println("Forwarding stats:");
- pw.increaseIndent();
- if (mStats.size() == 0) {
- pw.println("<empty>");
- } else {
- dumpStats(pw);
- }
- pw.decreaseIndent();
-
- pw.println("Forwarding rules:");
- pw.increaseIndent();
- if (mIpv6ForwardingRules.size() == 0) {
- pw.println("<empty>");
- } else {
- dumpIpv6ForwardingRules(pw);
- }
- pw.decreaseIndent();
-
- dumpDone.open();
- });
- if (!dumpDone.block(DUMP_TIMEOUT_MS)) {
- pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms");
- }
- }
-
- private void dumpStats(@NonNull IndentingPrintWriter pw) {
- for (int i = 0; i < mStats.size(); i++) {
- final int upstreamIfindex = mStats.keyAt(i);
- final ForwardedStats stats = mStats.get(upstreamIfindex);
- pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get(
- upstreamIfindex), stats.toString()));
- }
- }
-
- private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) {
- for (Map.Entry<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> entry :
- mIpv6ForwardingRules.entrySet()) {
- IpServer ipServer = entry.getKey();
- // The rule downstream interface index is paired with the interface name from
- // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer.
- final String downstreamIface = ipServer.interfaceName();
- pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac");
-
- pw.increaseIndent();
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = entry.getValue();
- for (Ipv6ForwardingRule rule : rules.values()) {
- final int upstreamIfindex = rule.upstreamIfindex;
- pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex,
- mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex,
- downstreamIface, rule.address, rule.srcMac, rule.dstMac));
- }
- pw.decreaseIndent();
- }
- }
-
- /** IPv6 forwarding rule class. */
- public static class Ipv6ForwardingRule {
- public final int upstreamIfindex;
- public final int downstreamIfindex;
-
- @NonNull
- public final Inet6Address address;
- @NonNull
- public final MacAddress srcMac;
- @NonNull
- public final MacAddress dstMac;
-
- public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex,
- @NonNull Inet6Address address, @NonNull MacAddress srcMac,
- @NonNull MacAddress dstMac) {
- this.upstreamIfindex = upstreamIfindex;
- this.downstreamIfindex = downstreamIfIndex;
- this.address = address;
- this.srcMac = srcMac;
- this.dstMac = dstMac;
- }
-
- /** Return a new rule object which updates with new upstream index. */
- @NonNull
- public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
- return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
- dstMac);
- }
-
- /**
- * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
- * would be error-prone due to generated stable AIDL classes not having a copy constructor.
- */
- @NonNull
- public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
- final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
- parcel.inputInterfaceIndex = upstreamIfindex;
- parcel.outputInterfaceIndex = downstreamIfindex;
- parcel.destination = address.getAddress();
- parcel.prefixLength = 128;
- parcel.srcL2Address = srcMac.toByteArray();
- parcel.dstL2Address = dstMac.toByteArray();
- return parcel;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Ipv6ForwardingRule)) return false;
- Ipv6ForwardingRule that = (Ipv6ForwardingRule) o;
- return this.upstreamIfindex == that.upstreamIfindex
- && this.downstreamIfindex == that.downstreamIfindex
- && Objects.equals(this.address, that.address)
- && Objects.equals(this.srcMac, that.srcMac)
- && Objects.equals(this.dstMac, that.dstMac);
- }
-
- @Override
- public int hashCode() {
- // TODO: if this is ever used in production code, don't pass ifindices
- // to Objects.hash() to avoid autoboxing overhead.
- return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac);
- }
- }
-
- /**
- * A BPF tethering stats provider to provide network statistics to the system.
- * Note that this class' data may only be accessed on the handler thread.
- */
- @VisibleForTesting
- class BpfTetherStatsProvider extends NetworkStatsProvider {
- // The offloaded traffic statistics per interface that has not been reported since the
- // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams
- // and has pending tether stats delta are included in this NetworkStats object.
- private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
-
- // The same stats as above, but counts network stats per uid.
- private NetworkStats mUidStats = new NetworkStats(0L, 0);
-
- @Override
- public void onRequestStatsUpdate(int token) {
- mHandler.post(() -> pushTetherStats());
- }
-
- @Override
- public void onSetAlert(long quotaBytes) {
- mHandler.post(() -> updateAlertQuota(quotaBytes));
- }
-
- @Override
- public void onSetLimit(@NonNull String iface, long quotaBytes) {
- if (quotaBytes < QUOTA_UNLIMITED) {
- throw new IllegalArgumentException("invalid quota value " + quotaBytes);
- }
-
- mHandler.post(() -> {
- final Long curIfaceQuota = mInterfaceQuotas.get(iface);
-
- if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
-
- if (quotaBytes == QUOTA_UNLIMITED) {
- mInterfaceQuotas.remove(iface);
- } else {
- mInterfaceQuotas.put(iface, quotaBytes);
- }
- maybeUpdateDataLimit(iface);
- });
- }
-
- @VisibleForTesting
- void pushTetherStats() {
- try {
- // The token is not used for now. See b/153606961.
- notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats);
-
- // Clear the accumulated tether stats delta after reported. Note that create a new
- // empty object because NetworkStats#clear is @hide.
- mIfaceStats = new NetworkStats(0L, 0);
- mUidStats = new NetworkStats(0L, 0);
- } catch (RuntimeException e) {
- mLog.e("Cannot report network stats: ", e);
- }
- }
-
- private void accumulateDiff(@NonNull NetworkStats ifaceDiff,
- @NonNull NetworkStats uidDiff) {
- mIfaceStats = mIfaceStats.add(ifaceDiff);
- mUidStats = mUidStats.add(uidDiff);
- }
- }
-
- private boolean isBpfEnabled() {
- final TetheringConfiguration config = mDeps.getTetherConfig();
- return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
- }
-
- private int getInterfaceIndexFromRules(@NonNull String ifName) {
- for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
- .values()) {
- for (Ipv6ForwardingRule rule : rules.values()) {
- final int upstreamIfindex = rule.upstreamIfindex;
- if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) {
- return upstreamIfindex;
- }
- }
- }
- return 0;
- }
-
- private long getQuotaBytes(@NonNull String iface) {
- final Long limit = mInterfaceQuotas.get(iface);
- final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED;
-
- return quotaBytes;
- }
-
- private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) {
- if (ifIndex == 0) {
- Log.wtf(TAG, "Invalid interface index.");
- return false;
- }
-
- try {
- mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception when updating quota " + quotaBytes + ": ", e);
- return false;
- }
-
- return true;
- }
-
- // Handle the data limit update from the service which is the stats provider registered for.
- private void maybeUpdateDataLimit(@NonNull String iface) {
- // Set data limit only on a given upstream which has at least one rule. If we can't get
- // an interface index for a given interface name, it means either there is no rule for
- // a given upstream or the interface name is not an upstream which is monitored by the
- // coordinator.
- final int ifIndex = getInterfaceIndexFromRules(iface);
- if (ifIndex == 0) return;
-
- final long quotaBytes = getQuotaBytes(iface);
- sendDataLimitToNetd(ifIndex, quotaBytes);
- }
-
- // Handle the data limit update while adding forwarding rules.
- private boolean updateDataLimit(int ifIndex) {
- final String iface = mInterfaceNames.get(ifIndex);
- if (iface == null) {
- mLog.e("Fail to get the interface name for index " + ifIndex);
- return false;
- }
- final long quotaBytes = getQuotaBytes(iface);
- return sendDataLimitToNetd(ifIndex, quotaBytes);
- }
-
- private boolean isAnyRuleOnUpstream(int upstreamIfindex) {
- for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
- .values()) {
- for (Ipv6ForwardingRule rule : rules.values()) {
- if (upstreamIfindex == rule.upstreamIfindex) return true;
- }
- }
- return false;
- }
-
- @NonNull
- private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
- @NonNull final ForwardedStats diff) {
- NetworkStats stats = new NetworkStats(0L, 0);
- final String iface = mInterfaceNames.get(ifIndex);
- if (iface == null) {
- // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd.
- // For now, netd may add the empty stats for the upstream which is not monitored by
- // the coordinator. Silently ignore it.
- return stats;
- }
- final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
- // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for
- // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked.
- return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets,
- diff.txBytes, diff.txPackets, 0L /* operations */));
- }
-
- private void updateAlertQuota(long newQuota) {
- if (newQuota < QUOTA_UNLIMITED) {
- throw new IllegalArgumentException("invalid quota value " + newQuota);
- }
- if (mRemainingAlertQuota == newQuota) return;
-
- mRemainingAlertQuota = newQuota;
- if (mRemainingAlertQuota == 0) {
- mLog.i("onAlertReached");
- if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
- }
- }
-
- private void updateQuotaAndStatsFromSnapshot(
- @NonNull final TetherStatsParcel[] tetherStatsList) {
- long usedAlertQuota = 0;
- for (TetherStatsParcel tetherStats : tetherStatsList) {
- final Integer ifIndex = tetherStats.ifIndex;
- final ForwardedStats curr = new ForwardedStats(tetherStats);
- final ForwardedStats base = mStats.get(ifIndex);
- final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
- usedAlertQuota += diff.rxBytes + diff.txBytes;
-
- // Update the local cache for counting tether stats delta.
- mStats.put(ifIndex, curr);
-
- // Update the accumulated tether stats delta to the stats provider for the service
- // querying.
- if (mStatsProvider != null) {
- try {
- mStatsProvider.accumulateDiff(
- buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff),
- buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff));
- } catch (ArrayIndexOutOfBoundsException e) {
- Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index "
- + ifIndex + " : ", e);
- }
- }
- }
-
- if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
- // Trim to zero if overshoot.
- final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
- updateAlertQuota(newQuota);
- }
-
- // TODO: Count the used limit quota for notifying data limit reached.
- }
-
- private void updateForwardedStatsFromNetd() {
- final TetherStatsParcel[] tetherStatsList;
- try {
- // The reported tether stats are total data usage for all currently-active upstream
- // interfaces since tethering start.
- tetherStatsList = mNetd.tetherOffloadGetStats();
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Problem fetching tethering stats: ", e);
- return;
- }
- updateQuotaAndStatsFromSnapshot(tetherStatsList);
- }
-
- @VisibleForTesting
- int getPollingInterval() {
- // The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long.
- // Ignore the config value is less than the minimum polling interval. Note that the
- // minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does.
- // TODO: Perhaps define a minimum polling interval constant.
- final TetheringConfiguration config = mDeps.getTetherConfig();
- final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0;
- return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval);
- }
-
- private void maybeSchedulePollingStats() {
- if (!mPollingStarted) return;
-
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
-
- mHandler.postDelayed(mScheduledPollingTask, getPollingInterval());
- }
-
- // Return forwarding rule map. This is used for testing only.
- // Note that this can be only called on handler thread.
- @NonNull
- @VisibleForTesting
- final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
- getForwardingRulesForTesting() {
- return mIpv6ForwardingRules;
- }
-
- // Return upstream interface name map. This is used for testing only.
- // Note that this can be only called on handler thread.
- @NonNull
- @VisibleForTesting
- final SparseArray<String> getInterfaceNamesForTesting() {
- return mInterfaceNames;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
deleted file mode 100644
index 8a96988ae1d1..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.TetheringManager.TETHERING_WIFI;
-
-import android.net.MacAddress;
-import android.net.TetheredClient;
-import android.net.TetheredClient.AddressInfo;
-import android.net.ip.IpServer;
-import android.net.wifi.WifiClient;
-import android.os.SystemClock;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Tracker for clients connected to downstreams.
- *
- * <p>This class is not thread safe, it is intended to be used only from the tethering handler
- * thread.
- */
-public class ConnectedClientsTracker {
- private final Clock mClock;
-
- @NonNull
- private List<WifiClient> mLastWifiClients = Collections.emptyList();
- @NonNull
- private List<TetheredClient> mLastTetheredClients = Collections.emptyList();
-
- @VisibleForTesting
- static class Clock {
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
- }
-
- public ConnectedClientsTracker() {
- this(new Clock());
- }
-
- @VisibleForTesting
- ConnectedClientsTracker(Clock clock) {
- mClock = clock;
- }
-
- /**
- * Update the tracker with new connected clients.
- *
- * <p>The new list can be obtained through {@link #getLastTetheredClients()}.
- * @param ipServers The IpServers used to assign addresses to clients.
- * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last
- * update.
- * @return True if the list of clients changed since the last calculation.
- */
- public boolean updateConnectedClients(
- Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) {
- final long now = mClock.elapsedRealtime();
-
- if (wifiClients != null) {
- mLastWifiClients = wifiClients;
- }
- final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients);
-
- // Build the list of non-expired leases from all IpServers, grouped by mac address
- final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>();
- for (IpServer server : ipServers) {
- for (TetheredClient client : server.getAllLeases()) {
- if (client.getTetheringType() == TETHERING_WIFI
- && !wifiClientMacs.contains(client.getMacAddress())) {
- // Skip leases of WiFi clients that are not (or no longer) L2-connected
- continue;
- }
- final TetheredClient prunedClient = pruneExpired(client, now);
- if (prunedClient == null) continue; // All addresses expired
-
- addLease(clientsMap, prunedClient);
- }
- }
-
- // TODO: add IPv6 addresses from netlink
-
- // Add connected WiFi clients that do not have any known address
- for (MacAddress client : wifiClientMacs) {
- if (clientsMap.containsKey(client)) continue;
- clientsMap.put(client, new TetheredClient(
- client, Collections.emptyList() /* addresses */, TETHERING_WIFI));
- }
-
- final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values());
- final boolean clientsChanged = clients.size() != mLastTetheredClients.size()
- || !clients.containsAll(mLastTetheredClients);
- mLastTetheredClients = Collections.unmodifiableList(new ArrayList<>(clients));
- return clientsChanged;
- }
-
- private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) {
- final TetheredClient aggregateClient = clientsMap.getOrDefault(
- lease.getMacAddress(), lease);
- if (aggregateClient == lease) {
- // This is the first lease with this mac address
- clientsMap.put(lease.getMacAddress(), lease);
- return;
- }
-
- // Only add the address info; this assumes that the tethering type is the same when the mac
- // address is the same. If a client is connected through different tethering types with the
- // same mac address, connected clients callbacks will report all of its addresses under only
- // one of these tethering types. This keeps the API simple considering that such a scenario
- // would really be a rare edge case.
- clientsMap.put(lease.getMacAddress(), aggregateClient.addAddresses(lease));
- }
-
- /**
- * Get the last list of tethered clients, as calculated in {@link #updateConnectedClients}.
- *
- * <p>The returned list is immutable.
- */
- @NonNull
- public List<TetheredClient> getLastTetheredClients() {
- return mLastTetheredClients;
- }
-
- private static boolean hasExpiredAddress(List<AddressInfo> addresses, long now) {
- for (AddressInfo info : addresses) {
- if (info.getExpirationTime() <= now) {
- return true;
- }
- }
- return false;
- }
-
- @Nullable
- private static TetheredClient pruneExpired(TetheredClient client, long now) {
- final List<AddressInfo> addresses = client.getAddresses();
- if (addresses.size() == 0) return null;
- if (!hasExpiredAddress(addresses, now)) return client;
-
- final ArrayList<AddressInfo> newAddrs = new ArrayList<>(addresses.size() - 1);
- for (AddressInfo info : addresses) {
- if (info.getExpirationTime() > now) {
- newAddrs.add(info);
- }
- }
-
- if (newAddrs.size() == 0) {
- return null;
- }
- return new TetheredClient(client.getMacAddress(), newAddrs, client.getTetheringType());
- }
-
- @NonNull
- private static Set<MacAddress> getClientMacs(@NonNull List<WifiClient> clients) {
- final Set<MacAddress> macs = new HashSet<>(clients.size());
- for (WifiClient c : clients) {
- macs.add(c.getMacAddress());
- }
- return macs;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
deleted file mode 100644
index bb7322f2a0d2..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
-import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
-import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
-import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
-import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
-import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_INVALID;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.util.SharedLog;
-import android.os.Bundle;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.PersistableBundle;
-import android.os.ResultReceiver;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.util.BitSet;
-
-/**
- * Re-check tethering provisioning for enabled downstream tether types.
- * Reference TetheringManager.TETHERING_{@code *} for each tether type.
- *
- * All methods of this class must be accessed from the thread of tethering
- * state machine.
- * @hide
- */
-public class EntitlementManager {
- private static final String TAG = EntitlementManager.class.getSimpleName();
- private static final boolean DBG = false;
-
- @VisibleForTesting
- protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
- private static final String ACTION_PROVISIONING_ALARM =
- "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
-
- private final ComponentName mSilentProvisioningService;
- private static final int MS_PER_HOUR = 60 * 60 * 1000;
- private static final int DUMP_TIMEOUT = 10_000;
-
- // The BitSet is the bit map of each enabled downstream types, ex:
- // {@link TetheringManager.TETHERING_WIFI}
- // {@link TetheringManager.TETHERING_USB}
- // {@link TetheringManager.TETHERING_BLUETOOTH}
- private final BitSet mCurrentDownstreams;
- private final BitSet mExemptedDownstreams;
- private final Context mContext;
- private final SharedLog mLog;
- private final SparseIntArray mEntitlementCacheValue;
- private final Handler mHandler;
- // Key: TetheringManager.TETHERING_*(downstream).
- // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
- private final SparseIntArray mCurrentEntitlementResults;
- private final Runnable mPermissionChangeCallback;
- private PendingIntent mProvisioningRecheckAlarm;
- private boolean mLastCellularUpstreamPermitted = true;
- private boolean mUsingCellularAsUpstream = false;
- private boolean mNeedReRunProvisioningUi = false;
- private OnUiEntitlementFailedListener mListener;
- private TetheringConfigurationFetcher mFetcher;
-
- public EntitlementManager(Context ctx, Handler h, SharedLog log,
- Runnable callback) {
- mContext = ctx;
- mLog = log.forSubComponent(TAG);
- mCurrentDownstreams = new BitSet();
- mExemptedDownstreams = new BitSet();
- mCurrentEntitlementResults = new SparseIntArray();
- mEntitlementCacheValue = new SparseIntArray();
- mPermissionChangeCallback = callback;
- mHandler = h;
- mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
- null, mHandler);
- mSilentProvisioningService = ComponentName.unflattenFromString(
- mContext.getResources().getString(R.string.config_wifi_tether_enable));
- }
-
- public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) {
- mListener = listener;
- }
-
- /** Callback fired when UI entitlement failed. */
- public interface OnUiEntitlementFailedListener {
- /**
- * Ui entitlement check fails in |downstream|.
- *
- * @param downstream tethering type from TetheringManager.TETHERING_{@code *}.
- */
- void onUiEntitlementFailed(int downstream);
- }
-
- public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) {
- mFetcher = fetcher;
- }
-
- /** Interface to fetch TetheringConfiguration. */
- public interface TetheringConfigurationFetcher {
- /**
- * Fetch current tethering configuration. This will be called to ensure whether entitlement
- * check is needed.
- * @return TetheringConfiguration instance.
- */
- TetheringConfiguration fetchTetheringConfiguration();
- }
-
- /**
- * Check if cellular upstream is permitted.
- */
- public boolean isCellularUpstreamPermitted() {
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-
- return isCellularUpstreamPermitted(config);
- }
-
- private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) {
- if (!isTetherProvisioningRequired(config)) return true;
-
- // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular
- // upstream should not be enabled. Enable cellular upstream for exempted downstreams only
- // when there is no non-exempted downstream.
- if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty();
-
- return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1;
- }
-
- /**
- * Set exempted downstream type. If there is only exempted downstream type active,
- * corresponding entitlement check will not be run and cellular upstream will be permitted
- * by default. If a privileged app enables tethering without a provisioning check, and then
- * another app enables tethering of the same type but does not disable the provisioning check,
- * then the downstream immediately loses exempt status and a provisioning check is run.
- * If any non-exempted downstream type is active, the cellular upstream will be gated by the
- * result of entitlement check from non-exempted downstreams. If entitlement check is still
- * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any
- * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled.
- */
- public void setExemptedDownstreamType(final int type) {
- mExemptedDownstreams.set(type, true);
- }
-
- /**
- * This is called when tethering starts.
- * Launch provisioning app if upstream is cellular.
- *
- * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *}
- * @param showProvisioningUi a boolean indicating whether to show the
- * provisioning app UI if there is one.
- */
- public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
- if (!isValidDownstreamType(downstreamType)) return;
-
- mCurrentDownstreams.set(downstreamType, true);
-
- mExemptedDownstreams.set(downstreamType, false);
-
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- if (!isTetherProvisioningRequired(config)) return;
-
- // If upstream is not cellular, provisioning app would not be launched
- // till upstream change to cellular.
- if (mUsingCellularAsUpstream) {
- if (showProvisioningUi) {
- runUiTetherProvisioning(downstreamType, config);
- } else {
- runSilentTetherProvisioning(downstreamType, config);
- }
- mNeedReRunProvisioningUi = false;
- } else {
- mNeedReRunProvisioningUi |= showProvisioningUi;
- }
- }
-
- /**
- * Tell EntitlementManager that a given type of tethering has been disabled
- *
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- */
- public void stopProvisioningIfNeeded(int downstreamType) {
- if (!isValidDownstreamType(downstreamType)) return;
-
- mCurrentDownstreams.set(downstreamType, false);
- // There are lurking bugs where the notion of "provisioning required" or
- // "tethering supported" may change without without tethering being notified properly.
- // Remove the mapping all the time no matter provisioning is required or not.
- removeDownstreamMapping(downstreamType);
- mExemptedDownstreams.set(downstreamType, false);
- }
-
- /**
- * Notify EntitlementManager if upstream is cellular or not.
- *
- * @param isCellular whether tethering upstream is cellular.
- */
- public void notifyUpstream(boolean isCellular) {
- if (DBG) {
- mLog.i("notifyUpstream: " + isCellular
- + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted
- + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
- }
- mUsingCellularAsUpstream = isCellular;
-
- if (mUsingCellularAsUpstream) {
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- maybeRunProvisioning(config);
- }
- }
-
- /** Run provisioning if needed */
- public void maybeRunProvisioning() {
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- maybeRunProvisioning(config);
- }
-
- private void maybeRunProvisioning(final TetheringConfiguration config) {
- if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) {
- return;
- }
-
- // Whenever any entitlement value changes, all downstreams will re-evaluate whether they
- // are allowed. Therefore even if the silent check here ends in a failure and the UI later
- // yields success, then the downstream that got a failure will re-evaluate as a result of
- // the change and get the new correct value.
- for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0;
- downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) {
- if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
- if (mNeedReRunProvisioningUi) {
- mNeedReRunProvisioningUi = false;
- runUiTetherProvisioning(downstream, config);
- } else {
- runSilentTetherProvisioning(downstream, config);
- }
- }
- }
- }
-
- /**
- * Check if the device requires a provisioning check in order to enable tethering.
- *
- * @param config an object that encapsulates the various tethering configuration elements.
- * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
- */
- @VisibleForTesting
- protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) {
- if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || config.provisioningApp.length == 0) {
- return false;
- }
- if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) {
- return false;
- }
- return (config.provisioningApp.length == 2);
- }
-
- /**
- * Re-check tethering provisioning for all enabled tether types.
- * Reference TetheringManager.TETHERING_{@code *} for each tether type.
- *
- * @param config an object that encapsulates the various tethering configuration elements.
- * Note: this method is only called from @{link Tethering.TetherMainSM} on the handler thread.
- * If there are new callers from different threads, the logic should move to
- * @{link Tethering.TetherMainSM} handler to avoid race conditions.
- */
- public void reevaluateSimCardProvisioning(final TetheringConfiguration config) {
- if (DBG) mLog.i("reevaluateSimCardProvisioning");
-
- if (!mHandler.getLooper().isCurrentThread()) {
- // Except for test, this log should not appear in normal flow.
- mLog.log("reevaluateSimCardProvisioning() don't run in TetherMainSM thread");
- }
- mEntitlementCacheValue.clear();
- mCurrentEntitlementResults.clear();
-
- // TODO: refine provisioning check to isTetherProvisioningRequired() ??
- if (!config.hasMobileHotspotProvisionApp()
- || carrierConfigAffirmsEntitlementCheckNotRequired(config)) {
- evaluateCellularPermission(config);
- return;
- }
-
- if (mUsingCellularAsUpstream) {
- maybeRunProvisioning(config);
- }
- }
-
- /**
- * Get carrier configuration bundle.
- * @param config an object that encapsulates the various tethering configuration elements.
- * */
- public PersistableBundle getCarrierConfig(final TetheringConfiguration config) {
- final CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) return null;
-
- final PersistableBundle carrierConfig = configManager.getConfigForSubId(
- config.activeDataSubId);
-
- if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
- return carrierConfig;
- }
-
- return null;
- }
-
- // The logic here is aimed solely at confirming that a CarrierConfig exists
- // and affirms that entitlement checks are not required.
- //
- // TODO: find a better way to express this, or alter the checking process
- // entirely so that this is more intuitive.
- private boolean carrierConfigAffirmsEntitlementCheckNotRequired(
- final TetheringConfiguration config) {
- // Check carrier config for entitlement checks
- final PersistableBundle carrierConfig = getCarrierConfig(config);
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- return !isEntitlementCheckRequired;
- }
-
- /**
- * Run no UI tethering provisioning check.
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param subId default data subscription ID.
- */
- @VisibleForTesting
- protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) {
- if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
- // For silent provisioning, settings would stop tethering when entitlement fail.
- ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null);
-
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_RUN_PROVISION, true);
- intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi);
- intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
- intent.setComponent(mSilentProvisioningService);
- // Only admin user can change tethering and SilentTetherProvisioning don't need to
- // show UI, it is fine to always start setting's background service as system user.
- mContext.startService(intent);
- return intent;
- }
-
- private void runUiTetherProvisioning(int type, final TetheringConfiguration config) {
- ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null);
- runUiTetherProvisioning(type, config, receiver);
- }
-
- /**
- * Run the UI-enabled tethering provisioning check.
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param subId default data subscription ID.
- * @param receiver to receive entitlement check result.
- */
- @VisibleForTesting
- protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config,
- ResultReceiver receiver) {
- if (DBG) mLog.i("runUiTetherProvisioning: " + type);
-
- Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // Only launch entitlement UI for system user. Entitlement UI should not appear for other
- // user because only admin user is allowed to change tethering.
- mContext.startActivity(intent);
- return intent;
- }
-
- // Not needed to check if this don't run on the handler thread because it's private.
- private void scheduleProvisioningRechecks(final TetheringConfiguration config) {
- if (mProvisioningRecheckAlarm == null) {
- final int period = config.provisioningCheckPeriod;
- if (period <= 0) return;
-
- Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
- mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
- Context.ALARM_SERVICE);
- long periodMs = period * MS_PER_HOUR;
- long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs;
- alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs,
- mProvisioningRecheckAlarm);
- }
- }
-
- private void cancelTetherProvisioningRechecks() {
- if (mProvisioningRecheckAlarm != null) {
- AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
- Context.ALARM_SERVICE);
- alarmManager.cancel(mProvisioningRecheckAlarm);
- mProvisioningRecheckAlarm = null;
- }
- }
-
- private void evaluateCellularPermission(final TetheringConfiguration config) {
- final boolean permitted = isCellularUpstreamPermitted(config);
-
- if (DBG) {
- mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted
- + " to " + permitted);
- }
-
- if (mLastCellularUpstreamPermitted != permitted) {
- mLog.log("Cellular permission change: " + permitted);
- mPermissionChangeCallback.run();
- }
- // Only schedule periodic re-check when tether is provisioned
- // and the result is ok.
- if (permitted && mCurrentEntitlementResults.size() > 0) {
- scheduleProvisioningRechecks(config);
- } else {
- cancelTetherProvisioningRechecks();
- }
- mLastCellularUpstreamPermitted = permitted;
- }
-
- /**
- * Add the mapping between provisioning result and tethering type.
- * Notify UpstreamNetworkMonitor if Cellular permission changes.
- *
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param resultCode Provisioning result
- */
- protected void addDownstreamMapping(int type, int resultCode) {
- mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
- + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type));
- if (!mCurrentDownstreams.get(type)) return;
-
- mCurrentEntitlementResults.put(type, resultCode);
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- evaluateCellularPermission(config);
- }
-
- /**
- * Remove the mapping for input tethering type.
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- */
- protected void removeDownstreamMapping(int type) {
- mLog.i("removeDownstreamMapping: " + type);
- mCurrentEntitlementResults.delete(type);
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- evaluateCellularPermission(config);
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) {
- mLog.log("Received provisioning alarm");
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- reevaluateSimCardProvisioning(config);
- }
- }
- };
-
- private static boolean isValidDownstreamType(int type) {
- switch (type) {
- case TETHERING_BLUETOOTH:
- case TETHERING_ETHERNET:
- case TETHERING_USB:
- case TETHERING_WIFI:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Dump the infromation of EntitlementManager.
- * @param pw {@link PrintWriter} is used to print formatted
- */
- public void dump(PrintWriter pw) {
- final ConditionVariable mWaiting = new ConditionVariable();
- mHandler.post(() -> {
- pw.print("isCellularUpstreamPermitted: ");
- pw.println(isCellularUpstreamPermitted());
- for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
- type = mCurrentDownstreams.nextSetBit(type + 1)) {
- pw.print("Type: ");
- pw.print(typeString(type));
- if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
- pw.print(", Value: ");
- pw.println(errorString(mCurrentEntitlementResults.get(type)));
- } else {
- pw.println(", Value: empty");
- }
- }
- mWaiting.open();
- });
- if (!mWaiting.block(DUMP_TIMEOUT)) {
- pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms");
- }
- pw.print("Exempted: [");
- for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0;
- type = mExemptedDownstreams.nextSetBit(type + 1)) {
- pw.print(typeString(type));
- pw.print(", ");
- }
- pw.println("]");
- }
-
- private static String typeString(int type) {
- switch (type) {
- case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH";
- case TETHERING_INVALID: return "TETHERING_INVALID";
- case TETHERING_USB: return "TETHERING_USB";
- case TETHERING_WIFI: return "TETHERING_WIFI";
- default:
- return String.format("TETHERING UNKNOWN TYPE (%d)", type);
- }
- }
-
- private static String errorString(int value) {
- switch (value) {
- case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
- case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
- case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED";
- default:
- return String.format("UNKNOWN ERROR (%d)", value);
- }
- }
-
- private ResultReceiver buildProxyReceiver(int type, boolean notifyFail,
- final ResultReceiver receiver) {
- ResultReceiver rr = new ResultReceiver(mHandler) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
- addDownstreamMapping(type, updatedCacheValue);
- if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) {
- mListener.onUiEntitlementFailed(type);
- }
- if (receiver != null) receiver.send(updatedCacheValue, null);
- }
- };
-
- return writeToParcel(rr);
- }
-
- // Instances of ResultReceiver need to be public classes for remote processes to be able
- // to load them (otherwise, ClassNotFoundException). For private classes, this method
- // performs a trick : round-trip parceling any instance of ResultReceiver will return a
- // vanilla instance of ResultReceiver sharing the binder token with the original receiver.
- // The binder token has a reference to the original instance of the private class and will
- // still call its methods, and can be sent over. However it cannot be used for anything
- // else than sending over a Binder call.
- // While round-trip parceling is not great, there is currently no other way of generating
- // a vanilla instance of ResultReceiver because all its fields are private.
- private ResultReceiver writeToParcel(final ResultReceiver receiver) {
- Parcel parcel = Parcel.obtain();
- receiver.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
- parcel.recycle();
- return receiverForSending;
- }
-
- /**
- * Update the last entitlement value to internal cache
- *
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param resultCode last entitlement value
- * @return the last updated entitlement value
- */
- private int updateEntitlementCacheValue(int type, int resultCode) {
- if (DBG) {
- mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode);
- }
- if (resultCode == TETHER_ERROR_NO_ERROR) {
- mEntitlementCacheValue.put(type, resultCode);
- return resultCode;
- } else {
- mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED);
- return TETHER_ERROR_PROVISIONING_FAILED;
- }
- }
-
- /** Get the last value of the tethering entitlement check. */
- public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
- boolean showEntitlementUi) {
- if (!isValidDownstreamType(downstream)) {
- receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
- return;
- }
-
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- if (!isTetherProvisioningRequired(config)) {
- receiver.send(TETHER_ERROR_NO_ERROR, null);
- return;
- }
-
- final int cacheValue = mEntitlementCacheValue.get(
- downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN);
- if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
- receiver.send(cacheValue, null);
- } else {
- ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
- runUiTetherProvisioning(downstream, config, proxy);
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
deleted file mode 100644
index f3dcaa2529e7..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
-import android.net.ip.IpServer;
-import android.net.util.NetworkConstants;
-import android.net.util.SharedLog;
-import android.util.Log;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.Random;
-
-
-/**
- * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
- * This coordinator is responsible for evaluating the dedicated prefixes
- * assigned to the device and deciding how to divvy them up among downstream
- * interfaces.
- *
- * @hide
- */
-public class IPv6TetheringCoordinator {
- private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- private static class Downstream {
- public final IpServer ipServer;
- public final int mode; // IpServer.STATE_*
- // Used to append to a ULA /48, constructing a ULA /64 for local use.
- public final short subnetId;
-
- Downstream(IpServer ipServer, int mode, short subnetId) {
- this.ipServer = ipServer;
- this.mode = mode;
- this.subnetId = subnetId;
- }
- }
-
- private final ArrayList<IpServer> mNotifyList;
- private final SharedLog mLog;
- // NOTE: mActiveDownstreams is a list and not a hash data structure because
- // we keep active downstreams in arrival order. This is done so /64s can
- // be parceled out on a "first come, first served" basis and a /64 used by
- // a downstream that is no longer active can be redistributed to any next
- // waiting active downstream (again, in arrival order).
- private final LinkedList<Downstream> mActiveDownstreams;
- private final byte[] mUniqueLocalPrefix;
- private short mNextSubnetId;
- private UpstreamNetworkState mUpstreamNetworkState;
-
- public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
- mNotifyList = notifyList;
- mLog = log.forSubComponent(TAG);
- mActiveDownstreams = new LinkedList<>();
- mUniqueLocalPrefix = generateUniqueLocalPrefix();
- mNextSubnetId = 0;
- }
-
- /** Add active downstream to ipv6 tethering candidate list. */
- public void addActiveDownstream(IpServer downstream, int mode) {
- if (findDownstream(downstream) == null) {
- // Adding a new downstream appends it to the list. Adding a
- // downstream a second time without first removing it has no effect.
- // We never change the mode of a downstream except by first removing
- // it and then re-adding it (with its new mode specified);
- if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) {
- // Make sure subnet IDs are always positive. They are appended
- // to a ULA /48 to make a ULA /64 for local use.
- mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1);
- }
- updateIPv6TetheringInterfaces();
- }
- }
-
- /** Remove downstream from ipv6 tethering candidate list. */
- public void removeActiveDownstream(IpServer downstream) {
- stopIPv6TetheringOn(downstream);
- if (mActiveDownstreams.remove(findDownstream(downstream))) {
- updateIPv6TetheringInterfaces();
- }
-
- // When tethering is stopping we can reset the subnet counter.
- if (mNotifyList.isEmpty()) {
- if (!mActiveDownstreams.isEmpty()) {
- Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty.");
- }
- mNextSubnetId = 0;
- }
- }
-
- /**
- * Call when UpstreamNetworkState may be changed.
- * If upstream has ipv6 for tethering, update this new UpstreamNetworkState
- * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
- */
- public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
- if (VDBG) {
- Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
- }
- if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
- stopIPv6TetheringOnAllInterfaces();
- setUpstreamNetworkState(null);
- return;
- }
-
- if (mUpstreamNetworkState != null
- && !ns.network.equals(mUpstreamNetworkState.network)) {
- stopIPv6TetheringOnAllInterfaces();
- }
-
- setUpstreamNetworkState(ns);
- updateIPv6TetheringInterfaces();
- }
-
- private void stopIPv6TetheringOnAllInterfaces() {
- for (IpServer ipServer : mNotifyList) {
- stopIPv6TetheringOn(ipServer);
- }
- }
-
- private void setUpstreamNetworkState(UpstreamNetworkState ns) {
- if (ns == null) {
- mUpstreamNetworkState = null;
- } else {
- // Make a deep copy of the parts we need.
- mUpstreamNetworkState = new UpstreamNetworkState(
- new LinkProperties(ns.linkProperties),
- new NetworkCapabilities(ns.networkCapabilities),
- new Network(ns.network));
- }
-
- mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
- }
-
- private void updateIPv6TetheringInterfaces() {
- for (IpServer ipServer : mNotifyList) {
- final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
- ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp);
- break;
- }
- }
-
- private int getTtlAdjustment() {
- if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) {
- return 0;
- }
-
- // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1
- // for carrier requirement.
- if (mUpstreamNetworkState.networkCapabilities.hasTransport(
- NetworkCapabilities.TRANSPORT_CELLULAR)) {
- return -1;
- }
-
- // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary
- // distinction between tethered and untethered traffic.
- return 1;
- }
-
- private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
- final Downstream ds = findDownstream(ipServer);
- if (ds == null) return null;
-
- if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
- // Build a Unique Locally-assigned Prefix configuration.
- return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
- }
-
- // This downstream is in IpServer.STATE_TETHERED mode.
- if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == 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
- // IPv6 toward the oldest (first requested) active downstream.
-
- final Downstream currentActive = mActiveDownstreams.peek();
- if (currentActive != null && currentActive.ipServer == ipServer) {
- final LinkProperties lp = getIPv6OnlyLinkProperties(
- mUpstreamNetworkState.linkProperties);
- if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) {
- return lp;
- }
- }
-
- return null;
- }
-
- Downstream findDownstream(IpServer ipServer) {
- for (Downstream ds : mActiveDownstreams) {
- if (ds.ipServer == ipServer) return ds;
- }
- return null;
- }
-
- private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
- final LinkProperties v6only = new LinkProperties();
- if (lp == null) {
- return v6only;
- }
-
- // NOTE: At this time we don't copy over any information about any
- // stacked links. No current stacked link configuration has IPv6.
-
- v6only.setInterfaceName(lp.getInterfaceName());
-
- v6only.setMtu(lp.getMtu());
-
- for (LinkAddress linkAddr : lp.getLinkAddresses()) {
- if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
- v6only.addLinkAddress(linkAddr);
- }
- }
-
- for (RouteInfo routeInfo : lp.getRoutes()) {
- final IpPrefix destination = routeInfo.getDestination();
- if ((destination.getAddress() instanceof Inet6Address)
- && (destination.getPrefixLength() <= 64)) {
- v6only.addRoute(routeInfo);
- }
- }
-
- for (InetAddress dnsServer : lp.getDnsServers()) {
- if (isIPv6GlobalAddress(dnsServer)) {
- // For now we include ULAs.
- v6only.addDnsServer(dnsServer);
- }
- }
-
- v6only.setDomains(lp.getDomains());
-
- return v6only;
- }
-
- // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
- // announce our own IPv6 address as DNS server.
- private static boolean isIPv6GlobalAddress(InetAddress ip) {
- return (ip instanceof Inet6Address)
- && !ip.isAnyLocalAddress()
- && !ip.isLoopbackAddress()
- && !ip.isLinkLocalAddress()
- && !ip.isSiteLocalAddress()
- && !ip.isMulticastAddress();
- }
-
- private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
- final LinkProperties lp = new LinkProperties();
-
- final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
- lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST));
-
- final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
- // Because this is a locally-generated ULA, we don't have an upstream
- // address. But because the downstream IP address management code gets
- // its prefix from the upstream's IP address, we create a fake one here.
- lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64));
-
- lp.setMtu(NetworkConstants.ETHER_MTU);
- return lp;
- }
-
- private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) {
- final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
- bytes[7] = (byte) (subnetId >> 8);
- bytes[8] = (byte) subnetId;
- final InetAddress addr;
- try {
- addr = InetAddress.getByAddress(bytes);
- } catch (UnknownHostException e) {
- throw new IllegalStateException("Invalid address length: " + bytes.length, e);
- }
- return new IpPrefix(addr, prefixlen);
- }
-
- // Generates a Unique Locally-assigned Prefix:
- //
- // https://tools.ietf.org/html/rfc4193#section-3.1
- //
- // The result is a /48 that can be used for local-only communications.
- private static byte[] generateUniqueLocalPrefix() {
- final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte
- (new Random()).nextBytes(ulp);
-
- final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN);
- in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1
-
- return in6addr;
- }
-
- private static String toDebugString(UpstreamNetworkState ns) {
- if (ns == null) {
- return "UpstreamNetworkState{null}";
- }
- return ns.toString();
- }
-
- private static void stopIPv6TetheringOn(IpServer ipServer) {
- ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
deleted file mode 100644
index 88c77b07e7e3..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ /dev/null
@@ -1,811 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
-
-import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.usage.NetworkStatsManager;
-import android.content.ContentResolver;
-import android.net.InetAddresses;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
-import android.net.RouteInfo;
-import android.net.netlink.ConntrackMessage;
-import android.net.netlink.NetlinkConstants;
-import android.net.netlink.NetlinkSocket;
-import android.net.netstats.provider.NetworkStatsProvider;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
-
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A class to encapsulate the business logic of programming the tethering
- * hardware offload interface.
- *
- * @hide
- */
-public class OffloadController {
- private static final String TAG = OffloadController.class.getSimpleName();
- private static final boolean DBG = false;
- private static final String ANYIP = "0.0.0.0";
- private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
-
- @VisibleForTesting
- enum StatsType {
- STATS_PER_IFACE,
- STATS_PER_UID,
- }
-
- private enum UpdateType { IF_NEEDED, FORCE };
-
- private final Handler mHandler;
- private final OffloadHardwareInterface mHwInterface;
- private final ContentResolver mContentResolver;
- @Nullable
- private final OffloadTetheringStatsProvider mStatsProvider;
- private final SharedLog mLog;
- private final HashMap<String, LinkProperties> mDownstreams;
- private boolean mConfigInitialized;
- private boolean mControlInitialized;
- private LinkProperties mUpstreamLinkProperties;
- // The complete set of offload-exempt prefixes passed in via Tethering from
- // all upstream and downstream sources.
- private Set<IpPrefix> mExemptPrefixes;
- // A strictly "smaller" set of prefixes, wherein offload-approved prefixes
- // (e.g. downstream on-link prefixes) have been removed and replaced with
- // prefixes representing only the locally-assigned IP addresses.
- private Set<String> mLastLocalPrefixStrs;
-
- // Maps upstream interface names to offloaded traffic statistics.
- // Always contains the latest value received from the hardware for each interface, regardless of
- // whether offload is currently running on that interface.
- private ConcurrentHashMap<String, ForwardedStats> mForwardedStats =
- new ConcurrentHashMap<>(16, 0.75F, 1);
-
- // Maps upstream interface names to interface quotas.
- // Always contains the latest value received from the framework for each interface, regardless
- // of whether offload is currently running (or is even supported) on that interface. Only
- // includes upstream interfaces that have a quota set.
- private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
-
- // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
- // quota is interface independent and global for tether offload. Note that this is only
- // accessed on the handler thread and in the constructor.
- private long mRemainingAlertQuota = QUOTA_UNLIMITED;
- // Runnable that used to schedule the next stats poll.
- private final Runnable mScheduledPollingTask = () -> {
- updateStatsForCurrentUpstream();
- maybeSchedulePollingStats();
- };
-
- private int mNatUpdateCallbacksReceived;
- private int mNatUpdateNetlinkErrors;
-
- @NonNull
- private final Dependencies mDeps;
-
- // TODO: Put more parameters in constructor into dependency object.
- interface Dependencies {
- @NonNull
- TetheringConfiguration getTetherConfig();
- }
-
- public OffloadController(Handler h, OffloadHardwareInterface hwi,
- ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log,
- @NonNull Dependencies deps) {
- mHandler = h;
- mHwInterface = hwi;
- mContentResolver = contentResolver;
- mLog = log.forSubComponent(TAG);
- mDownstreams = new HashMap<>();
- mExemptPrefixes = new HashSet<>();
- mLastLocalPrefixStrs = new HashSet<>();
- OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider();
- try {
- nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Cannot register offload stats provider: " + e);
- provider = null;
- }
- mStatsProvider = provider;
- mDeps = deps;
- }
-
- /** Start hardware offload. */
- public boolean start() {
- if (started()) return true;
-
- if (isOffloadDisabled()) {
- mLog.i("tethering offload disabled");
- return false;
- }
-
- if (!mConfigInitialized) {
- mConfigInitialized = mHwInterface.initOffloadConfig();
- if (!mConfigInitialized) {
- mLog.i("tethering offload config not supported");
- stop();
- return false;
- }
- }
-
- mControlInitialized = mHwInterface.initOffloadControl(
- // OffloadHardwareInterface guarantees that these callback
- // methods are called on the handler passed to it, which is the
- // same as mHandler, as coordinated by the setup in Tethering.
- new OffloadHardwareInterface.ControlCallback() {
- @Override
- public void onStarted() {
- if (!started()) return;
- mLog.log("onStarted");
- }
-
- @Override
- public void onStoppedError() {
- if (!started()) return;
- mLog.log("onStoppedError");
- }
-
- @Override
- public void onStoppedUnsupported() {
- if (!started()) return;
- mLog.log("onStoppedUnsupported");
- // Poll for statistics and trigger a sweep of tethering
- // stats by observers. This might not succeed, but it's
- // worth trying anyway. We need to do this because from
- // this point on we continue with software forwarding,
- // and we need to synchronize stats and limits between
- // software and hardware forwarding.
- updateStatsForAllUpstreams();
- if (mStatsProvider != null) mStatsProvider.pushTetherStats();
- }
-
- @Override
- public void onSupportAvailable() {
- if (!started()) return;
- mLog.log("onSupportAvailable");
-
- // [1] Poll for statistics and trigger a sweep of stats
- // by observers. We need to do this to ensure that any
- // limits set take into account any software tethering
- // traffic that has been happening in the meantime.
- updateStatsForAllUpstreams();
- if (mStatsProvider != null) mStatsProvider.pushTetherStats();
- // [2] (Re)Push all state.
- computeAndPushLocalPrefixes(UpdateType.FORCE);
- pushAllDownstreamState();
- pushUpstreamParameters(null);
- }
-
- @Override
- public void onStoppedLimitReached() {
- if (!started()) return;
- mLog.log("onStoppedLimitReached");
-
- // We cannot reliably determine on which interface the limit was reached,
- // because the HAL interface does not specify it. We cannot just use the
- // current upstream, because that might have changed since the time that
- // the HAL queued the callback.
- // TODO: rev the HAL so that it provides an interface name.
-
- updateStatsForCurrentUpstream();
- if (mStatsProvider != null) {
- mStatsProvider.pushTetherStats();
- // Push stats to service does not cause the service react to it
- // immediately. Inform the service about limit reached.
- mStatsProvider.notifyLimitReached();
- }
- }
-
- @Override
- public void onNatTimeoutUpdate(int proto,
- String srcAddr, int srcPort,
- String dstAddr, int dstPort) {
- if (!started()) return;
- updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort);
- }
- });
-
- final boolean isStarted = started();
- if (!isStarted) {
- mLog.i("tethering offload control not supported");
- stop();
- } else {
- mLog.log("tethering offload started");
- mNatUpdateCallbacksReceived = 0;
- mNatUpdateNetlinkErrors = 0;
- maybeSchedulePollingStats();
- }
- return isStarted;
- }
-
- /** Stop hardware offload. */
- public void stop() {
- // Completely stops tethering offload. After this method is called, it is no longer safe to
- // call any HAL method, no callbacks from the hardware will be delivered, and any in-flight
- // callbacks must be ignored. Offload may be started again by calling start().
- final boolean wasStarted = started();
- updateStatsForCurrentUpstream();
- mUpstreamLinkProperties = null;
- mHwInterface.stopOffloadControl();
- mControlInitialized = false;
- mConfigInitialized = false;
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
- if (wasStarted) mLog.log("tethering offload stopped");
- }
-
- private boolean started() {
- return mConfigInitialized && mControlInitialized;
- }
-
- @VisibleForTesting
- class OffloadTetheringStatsProvider extends NetworkStatsProvider {
- // These stats must only ever be touched on the handler thread.
- @NonNull
- private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
- @NonNull
- private NetworkStats mUidStats = new NetworkStats(0L, 0);
-
- /**
- * A helper function that collect tether stats from local hashmap. Note that this does not
- * invoke binder call.
- */
- @VisibleForTesting
- @NonNull
- NetworkStats getTetherStats(@NonNull StatsType how) {
- NetworkStats stats = new NetworkStats(0L, 0);
- final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
-
- for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
- final ForwardedStats value = kv.getValue();
- final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
- stats = stats.addEntry(entry);
- }
-
- return stats;
- }
-
- @Override
- public void onSetLimit(String iface, long quotaBytes) {
- // Listen for all iface is necessary since upstream might be changed after limit
- // is set.
- mHandler.post(() -> {
- final Long curIfaceQuota = mInterfaceQuotas.get(iface);
-
- // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
- // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
- // useful to set it multiple times.
- // Otherwise, the quota needs to be updated to tell HAL to re-count from now even
- // if the quota is the same as the existing one.
- if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
-
- if (quotaBytes == QUOTA_UNLIMITED) {
- mInterfaceQuotas.remove(iface);
- } else {
- mInterfaceQuotas.put(iface, quotaBytes);
- }
- maybeUpdateDataLimit(iface);
- });
- }
-
- /**
- * Push stats to service, but does not cause a force polling. Note that this can only be
- * called on the handler thread.
- */
- public void pushTetherStats() {
- // TODO: remove the accumulated stats and report the diff from HAL directly.
- final NetworkStats ifaceDiff =
- getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
- final NetworkStats uidDiff =
- getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
- try {
- notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
- mIfaceStats = mIfaceStats.add(ifaceDiff);
- mUidStats = mUidStats.add(uidDiff);
- } catch (RuntimeException e) {
- mLog.e("Cannot report network stats: ", e);
- }
- }
-
- @Override
- public void onRequestStatsUpdate(int token) {
- // Do not attempt to update stats by querying the offload HAL
- // synchronously from a different thread than the Handler thread. http://b/64771555.
- mHandler.post(() -> {
- updateStatsForCurrentUpstream();
- pushTetherStats();
- });
- }
-
- @Override
- public void onSetAlert(long quotaBytes) {
- // TODO: Ask offload HAL to notify alert without stopping traffic.
- // Post it to handler thread since it access remaining quota bytes.
- mHandler.post(() -> {
- updateAlertQuota(quotaBytes);
- maybeSchedulePollingStats();
- });
- }
- }
-
- private String currentUpstreamInterface() {
- return (mUpstreamLinkProperties != null)
- ? mUpstreamLinkProperties.getInterfaceName() : null;
- }
-
- private void maybeUpdateStats(String iface) {
- if (TextUtils.isEmpty(iface)) {
- return;
- }
-
- // Always called on the handler thread.
- //
- // Use get()/put() instead of updating ForwardedStats in place because we can be called
- // concurrently with getTetherStats. In combination with the guarantees provided by
- // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of
- // the stats for each interface, and does not observe partial writes where rxBytes is
- // updated and txBytes is not.
- ForwardedStats diff = mHwInterface.getForwardedStats(iface);
- final long usedAlertQuota = diff.rxBytes + diff.txBytes;
- ForwardedStats base = mForwardedStats.get(iface);
- if (base != null) {
- diff.add(base);
- }
-
- // Update remaining alert quota if it is still positive.
- if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
- // Trim to zero if overshoot.
- final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
- updateAlertQuota(newQuota);
- }
-
- mForwardedStats.put(iface, diff);
- // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from
- // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately.
- }
-
- /**
- * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()}
- * callback when it reaches zero. This can be invoked either from service setting the alert, or
- * {@code maybeUpdateStats} when updating stats. Note that this can be only called on
- * handler thread.
- *
- * @param newQuota non-negative value to indicate the new quota, or
- * {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no
- * quota.
- */
- private void updateAlertQuota(long newQuota) {
- if (newQuota < QUOTA_UNLIMITED) {
- throw new IllegalArgumentException("invalid quota value " + newQuota);
- }
- if (mRemainingAlertQuota == newQuota) return;
-
- mRemainingAlertQuota = newQuota;
- if (mRemainingAlertQuota == 0) {
- mLog.i("notifyAlertReached");
- if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
- }
- }
-
- /**
- * Schedule polling if needed, this will be stopped if offload has been
- * stopped or remaining quota reaches zero or upstream is empty.
- * Note that this can be only called on handler thread.
- */
- private void maybeSchedulePollingStats() {
- if (!isPollingStatsNeeded()) return;
-
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
- mHandler.postDelayed(mScheduledPollingTask,
- mDeps.getTetherConfig().getOffloadPollInterval());
- }
-
- private boolean isPollingStatsNeeded() {
- return started() && mRemainingAlertQuota > 0
- && !TextUtils.isEmpty(currentUpstreamInterface())
- && mDeps.getTetherConfig() != null
- && mDeps.getTetherConfig().getOffloadPollInterval()
- >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
- }
-
- private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occurring on this upstream.
- if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
- return true;
- }
-
- Long limit = mInterfaceQuotas.get(iface);
- if (limit == null) {
- limit = Long.MAX_VALUE;
- }
-
- return mHwInterface.setDataLimit(iface, limit);
- }
-
- private void updateStatsForCurrentUpstream() {
- maybeUpdateStats(currentUpstreamInterface());
- }
-
- private void updateStatsForAllUpstreams() {
- // In practice, there should only ever be a single digit number of
- // upstream interfaces over the lifetime of an active tethering session.
- // Roughly speaking, imagine a very ambitious one or two of each of the
- // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ].
- for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
- maybeUpdateStats(kv.getKey());
- }
- }
-
- /** Set current tethering upstream LinkProperties. */
- public void setUpstreamLinkProperties(LinkProperties lp) {
- if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
-
- final String prevUpstream = currentUpstreamInterface();
-
- mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
- // Make sure we record this interface in the ForwardedStats map.
- final String iface = currentUpstreamInterface();
- if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS);
-
- maybeSchedulePollingStats();
-
- // TODO: examine return code and decide what to do if programming
- // upstream parameters fails (probably just wait for a subsequent
- // onOffloadEvent() callback to tell us offload is available again and
- // then reapply all state).
- computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
- pushUpstreamParameters(prevUpstream);
- }
-
- /** Set local prefixes. */
- public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
- mExemptPrefixes = localPrefixes;
-
- if (!started()) return;
- computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
- }
-
- /** Update current downstream LinkProperties. */
- public void notifyDownstreamLinkProperties(LinkProperties lp) {
- final String ifname = lp.getInterfaceName();
- final LinkProperties oldLp = mDownstreams.put(ifname, new LinkProperties(lp));
- if (Objects.equals(oldLp, lp)) return;
-
- if (!started()) return;
- pushDownstreamState(oldLp, lp);
- }
-
- private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) {
- final String ifname = newLp.getInterfaceName();
- final List<RouteInfo> oldRoutes =
- (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST;
- final List<RouteInfo> newRoutes = newLp.getRoutes();
-
- // For each old route, if not in new routes: remove.
- for (RouteInfo ri : oldRoutes) {
- if (shouldIgnoreDownstreamRoute(ri)) continue;
- if (!newRoutes.contains(ri)) {
- mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString());
- }
- }
-
- // For each new route, if not in old routes: add.
- for (RouteInfo ri : newRoutes) {
- if (shouldIgnoreDownstreamRoute(ri)) continue;
- if (!oldRoutes.contains(ri)) {
- mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString());
- }
- }
- }
-
- private void pushAllDownstreamState() {
- for (LinkProperties lp : mDownstreams.values()) {
- pushDownstreamState(null, lp);
- }
- }
-
- /** Remove downstream interface from offload hardware. */
- public void removeDownstreamInterface(String ifname) {
- final LinkProperties lp = mDownstreams.remove(ifname);
- if (lp == null) return;
-
- if (!started()) return;
-
- for (RouteInfo route : lp.getRoutes()) {
- if (shouldIgnoreDownstreamRoute(route)) continue;
- mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString());
- }
- }
-
- private boolean isOffloadDisabled() {
- final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled();
- return (Settings.Global.getInt(
- mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0);
- }
-
- private boolean pushUpstreamParameters(String prevUpstream) {
- final String iface = currentUpstreamInterface();
-
- if (TextUtils.isEmpty(iface)) {
- final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null);
- // Update stats after we've told the hardware to stop forwarding so
- // we don't miss packets.
- maybeUpdateStats(prevUpstream);
- return rval;
- }
-
- // A stacked interface cannot be an upstream for hardware offload.
- // Consequently, we examine only the primary interface name, look at
- // getAddresses() rather than getAllAddresses(), and check getRoutes()
- // rather than getAllRoutes().
- final ArrayList<String> v6gateways = new ArrayList<>();
- String v4addr = null;
- String v4gateway = null;
-
- for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) {
- if (ip instanceof Inet4Address) {
- v4addr = ip.getHostAddress();
- break;
- }
- }
-
- // Find the gateway addresses of all default routes of either address family.
- for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) {
- if (!ri.hasGateway()) continue;
-
- final String gateway = ri.getGateway().getHostAddress();
- final InetAddress address = ri.getDestination().getAddress();
- if (ri.isDefaultRoute() && address instanceof Inet4Address) {
- v4gateway = gateway;
- } else if (ri.isDefaultRoute() && address instanceof Inet6Address) {
- v6gateways.add(gateway);
- }
- }
-
- boolean success = mHwInterface.setUpstreamParameters(
- iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
-
- if (!success) {
- return success;
- }
-
- // Update stats after we've told the hardware to change routing so we don't miss packets.
- maybeUpdateStats(prevUpstream);
-
- // Data limits can only be set once offload is running on the upstream.
- success = maybeUpdateDataLimit(iface);
- if (!success) {
- // If we failed to set a data limit, don't use this upstream, because we don't want to
- // blow through the data limit that we were told to apply.
- mLog.log("Setting data limit for " + iface + " failed, disabling offload.");
- stop();
- }
-
- return success;
- }
-
- private boolean computeAndPushLocalPrefixes(UpdateType how) {
- final boolean force = (how == UpdateType.FORCE);
- final Set<String> localPrefixStrs = computeLocalPrefixStrings(
- mExemptPrefixes, mUpstreamLinkProperties);
- if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
-
- mLastLocalPrefixStrs = localPrefixStrs;
- return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
- }
-
- // TODO: Factor in downstream LinkProperties once that information is available.
- private static Set<String> computeLocalPrefixStrings(
- Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) {
- // Create an editable copy.
- final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes);
-
- // TODO: If a downstream interface (not currently passed in) is reusing
- // the /64 of the upstream (64share) then:
- //
- // [a] remove that /64 from the local prefixes
- // [b] add in /128s for IP addresses on the downstream interface
- // [c] add in /128s for IP addresses on the upstream interface
- //
- // Until downstream information is available here, simply add /128s from
- // the upstream network; they'll just be redundant with their /64.
- if (upstreamLinkProperties != null) {
- for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) {
- if (!linkAddr.isGlobalPreferred()) continue;
- final InetAddress ip = linkAddr.getAddress();
- if (!(ip instanceof Inet6Address)) continue;
- prefixSet.add(new IpPrefix(ip, 128));
- }
- }
-
- final HashSet<String> localPrefixStrs = new HashSet<>();
- for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString());
- return localPrefixStrs;
- }
-
- private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) {
- // Ignore any link-local routes.
- final IpPrefix destination = route.getDestination();
- final LinkAddress linkAddr = new LinkAddress(destination.getAddress(),
- destination.getPrefixLength());
- if (!linkAddr.isGlobalPreferred()) return true;
-
- return false;
- }
-
- /** Dump information. */
- public void dump(IndentingPrintWriter pw) {
- if (isOffloadDisabled()) {
- pw.println("Offload disabled");
- return;
- }
- final boolean isStarted = started();
- pw.println("Offload HALs " + (isStarted ? "started" : "not started"));
- LinkProperties lp = mUpstreamLinkProperties;
- String upstream = (lp != null) ? lp.getInterfaceName() : null;
- pw.println("Current upstream: " + upstream);
- pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
- pw.println("NAT timeout update callbacks received during the "
- + (isStarted ? "current" : "last")
- + " offload session: "
- + mNatUpdateCallbacksReceived);
- pw.println("NAT timeout update netlink errors during the "
- + (isStarted ? "current" : "last")
- + " offload session: "
- + mNatUpdateNetlinkErrors);
- }
-
- private void updateNatTimeout(
- int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) {
- final String protoName = protoNameFor(proto);
- if (protoName == null) {
- mLog.e("Unknown NAT update callback protocol: " + proto);
- return;
- }
-
- final Inet4Address src = parseIPv4Address(srcAddr);
- if (src == null) {
- mLog.e("Failed to parse IPv4 address: " + srcAddr);
- return;
- }
-
- if (!isValidUdpOrTcpPort(srcPort)) {
- mLog.e("Invalid src port: " + srcPort);
- return;
- }
-
- final Inet4Address dst = parseIPv4Address(dstAddr);
- if (dst == null) {
- mLog.e("Failed to parse IPv4 address: " + dstAddr);
- return;
- }
-
- if (!isValidUdpOrTcpPort(dstPort)) {
- mLog.e("Invalid dst port: " + dstPort);
- return;
- }
-
- mNatUpdateCallbacksReceived++;
- final String natDescription = String.format("%s (%s, %s) -> (%s, %s)",
- protoName, srcAddr, srcPort, dstAddr, dstPort);
- if (DBG) {
- mLog.log("NAT timeout update: " + natDescription);
- }
-
- final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
- final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
- proto, src, srcPort, dst, dstPort, timeoutSec);
-
- try {
- NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
- } catch (ErrnoException e) {
- mNatUpdateNetlinkErrors++;
- mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e
- + ", msg: " + NetlinkConstants.hexify(msg));
- mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
- mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
- }
- }
-
- private static Inet4Address parseIPv4Address(String addrString) {
- try {
- final InetAddress ip = InetAddresses.parseNumericAddress(addrString);
- // TODO: Consider other sanitization steps here, including perhaps:
- // not eql to 0.0.0.0
- // not within 169.254.0.0/16
- // not within ::ffff:0.0.0.0/96
- // not within ::/96
- // et cetera.
- if (ip instanceof Inet4Address) {
- return (Inet4Address) ip;
- }
- } catch (IllegalArgumentException iae) { }
- return null;
- }
-
- private static String protoNameFor(int proto) {
- // OsConstants values are not constant expressions; no switch statement.
- if (proto == OsConstants.IPPROTO_UDP) {
- return "UDP";
- } else if (proto == OsConstants.IPPROTO_TCP) {
- return "TCP";
- }
- return null;
- }
-
- private static int connectionTimeoutUpdateSecondsFor(int proto) {
- // TODO: Replace this with more thoughtful work, perhaps reading from
- // and maybe writing to any required
- //
- // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_*
- // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream}
- //
- // entries. TBD.
- if (proto == OsConstants.IPPROTO_TCP) {
- // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
- return 432000;
- } else {
- // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
- return 180;
- }
- }
-
- private static boolean isValidUdpOrTcpPort(int port) {
- return port > 0 && port < 65536;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
deleted file mode 100644
index da5f25b2a596..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static android.net.util.TetheringUtils.uint16;
-
-import android.annotation.NonNull;
-import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
-import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
-import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
-import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.StructNfGenMsg;
-import android.net.netlink.StructNlMsgHdr;
-import android.net.util.SharedLog;
-import android.net.util.SocketUtils;
-import android.os.Handler;
-import android.os.NativeHandle;
-import android.os.RemoteException;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-
-
-/**
- * Capture tethering dependencies, for injection.
- *
- * @hide
- */
-public class OffloadHardwareInterface {
- private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
- private static final String YIELDS = " -> ";
- // Change this value to control whether tether offload is enabled or
- // disabled by default in the absence of an explicit Settings value.
- // See accompanying unittest to distinguish 0 from non-0 values.
- private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
- private static final String NO_INTERFACE_NAME = "";
- private static final String NO_IPV4_ADDRESS = "";
- private static final String NO_IPV4_GATEWAY = "";
- // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
- public static final int NF_NETLINK_CONNTRACK_NEW = 1;
- public static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
- public static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
- // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h
- public static final short NFNL_SUBSYS_CTNETLINK = 1;
- public static final short IPCTNL_MSG_CT_NEW = 0;
- public static final short IPCTNL_MSG_CT_GET = 1;
-
- private final long NETLINK_MESSAGE_TIMEOUT_MS = 500;
-
- private final Handler mHandler;
- private final SharedLog mLog;
- private final Dependencies mDeps;
- private IOffloadControl mOffloadControl;
- private TetheringOffloadCallback mTetheringOffloadCallback;
- private ControlCallback mControlCallback;
-
- /** The callback to notify status of offload management process. */
- public static class ControlCallback {
- /** Offload started. */
- public void onStarted() {}
- /**
- * Offload stopped because an error has occurred in lower layer.
- */
- public void onStoppedError() {}
- /**
- * Offload stopped because the device has moved to a bearer on which hardware offload is
- * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will
- * likely fail and cannot be presumed to be saved inside of the hardware management process.
- * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin
- * offload again.
- */
- public void onStoppedUnsupported() {}
- /** Indicate that offload is able to proivde support for this time. */
- public void onSupportAvailable() {}
- /** Offload stopped because of usage limit reached. */
- public void onStoppedLimitReached() {}
-
- /** Indicate to update NAT timeout. */
- public void onNatTimeoutUpdate(int proto,
- String srcAddr, int srcPort,
- String dstAddr, int dstPort) {}
- }
-
- /** The object which records Tx/Rx forwarded bytes. */
- public static class ForwardedStats {
- public long rxBytes;
- public long txBytes;
-
- public ForwardedStats() {
- rxBytes = 0;
- txBytes = 0;
- }
-
- @VisibleForTesting
- public ForwardedStats(long rxBytes, long txBytes) {
- this.rxBytes = rxBytes;
- this.txBytes = txBytes;
- }
-
- /** Add Tx/Rx bytes. */
- public void add(ForwardedStats other) {
- rxBytes += other.rxBytes;
- txBytes += other.txBytes;
- }
-
- /** Returns the string representation of this object. */
- public String toString() {
- return String.format("rx:%s tx:%s", rxBytes, txBytes);
- }
- }
-
- public OffloadHardwareInterface(Handler h, SharedLog log) {
- this(h, log, new Dependencies(log));
- }
-
- OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
- mHandler = h;
- mLog = log.forSubComponent(TAG);
- mDeps = deps;
- }
-
- /** Capture OffloadHardwareInterface dependencies, for injection. */
- static class Dependencies {
- private final SharedLog mLog;
-
- Dependencies(SharedLog log) {
- mLog = log;
- }
-
- public IOffloadConfig getOffloadConfig() {
- try {
- return IOffloadConfig.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("getIOffloadConfig error " + e);
- return null;
- }
- }
-
- public IOffloadControl getOffloadControl() {
- try {
- return IOffloadControl.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("tethering offload control not supported: " + e);
- return null;
- }
- }
-
- public NativeHandle createConntrackSocket(final int groups) {
- final FileDescriptor fd;
- try {
- fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
- } catch (ErrnoException e) {
- mLog.e("Unable to create conntrack socket " + e);
- return null;
- }
-
- final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
- try {
- Os.bind(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
- try {
- Os.connect(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
-
- return new NativeHandle(fd, true);
- }
- }
-
- /** Get default value indicating whether offload is supported. */
- public int getDefaultTetherOffloadDisabled() {
- return DEFAULT_TETHER_OFFLOAD_DISABLED;
- }
-
- /**
- * Offload management process need to know conntrack rules to support NAT, but it may not have
- * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
- * share them with offload management process.
- */
- public boolean initOffloadConfig() {
- final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
- if (offloadConfig == null) {
- mLog.e("Could not find IOffloadConfig service");
- return false;
- }
- // Per the IConfigOffload definition:
- //
- // h1 provides a file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
- //
- // h2 provides a file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
- final NativeHandle h1 = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
- if (h1 == null) return false;
-
- sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
- (short) (NLM_F_REQUEST | NLM_F_DUMP));
-
- final NativeHandle h2 = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
- if (h2 == null) {
- closeFdInNativeHandle(h1);
- return false;
- }
-
- final CbResults results = new CbResults();
- try {
- offloadConfig.setHandles(h1, h2,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record("initOffloadConfig, setHandles fail", e);
- return false;
- }
- // Explicitly close FDs.
- closeFdInNativeHandle(h1);
- closeFdInNativeHandle(h2);
-
- record("initOffloadConfig, setHandles results:", results);
- return results.mSuccess;
- }
-
- @VisibleForTesting
- public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
- final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
- final byte[] msg = new byte[length];
- final ByteBuffer byteBuffer = ByteBuffer.wrap(msg);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- final StructNlMsgHdr nlh = new StructNlMsgHdr();
- nlh.nlmsg_len = length;
- nlh.nlmsg_type = type;
- nlh.nlmsg_flags = flags;
- nlh.nlmsg_seq = 0;
- nlh.pack(byteBuffer);
-
- // Header needs to be added to buffer since a generic netlink request is being sent.
- final StructNfGenMsg nfh = new StructNfGenMsg((byte) OsConstants.AF_INET);
- nfh.pack(byteBuffer);
-
- try {
- NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length,
- NETLINK_MESSAGE_TIMEOUT_MS);
- } catch (ErrnoException | InterruptedIOException e) {
- mLog.e("Unable to send netfilter message, error: " + e);
- }
- }
-
- private void closeFdInNativeHandle(final NativeHandle h) {
- try {
- h.close();
- } catch (IOException | IllegalStateException e) {
- // IllegalStateException means fd is already closed, do nothing here.
- // Also nothing we can do if IOException.
- }
- }
-
- /** Initialize the tethering offload HAL. */
- public boolean initOffloadControl(ControlCallback controlCb) {
- mControlCallback = controlCb;
-
- if (mOffloadControl == null) {
- mOffloadControl = mDeps.getOffloadControl();
- if (mOffloadControl == null) {
- mLog.e("tethering IOffloadControl.getService() returned null");
- return false;
- }
- }
-
- final String logmsg = String.format("initOffloadControl(%s)",
- (controlCb == null) ? "null"
- : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
-
- mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
- final CbResults results = new CbResults();
- try {
- mOffloadControl.initOffload(
- mTetheringOffloadCallback,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Stop IOffloadControl. */
- public void stopOffloadControl() {
- if (mOffloadControl != null) {
- try {
- mOffloadControl.stopOffload(
- (boolean success, String errMsg) -> {
- if (!success) mLog.e("stopOffload failed: " + errMsg);
- });
- } catch (RemoteException e) {
- mLog.e("failed to stopOffload: " + e);
- }
- }
- mOffloadControl = null;
- mTetheringOffloadCallback = null;
- mControlCallback = null;
- mLog.log("stopOffloadControl()");
- }
-
- /** Get Tx/Rx usage from last query. */
- public ForwardedStats getForwardedStats(String upstream) {
- final String logmsg = String.format("getForwardedStats(%s)", upstream);
-
- final ForwardedStats stats = new ForwardedStats();
- try {
- mOffloadControl.getForwardedStats(
- upstream,
- (long rxBytes, long txBytes) -> {
- stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
- stats.txBytes = (txBytes > 0) ? txBytes : 0;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return stats;
- }
-
- return stats;
- }
-
- /** Set local prefixes to offload management process. */
- public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
- final String logmsg = String.format("setLocalPrefixes([%s])",
- String.join(",", localPrefixes));
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setLocalPrefixes(localPrefixes,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Set data limit value to offload management process. */
- public boolean setDataLimit(String iface, long limit) {
-
- final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setDataLimit(
- iface, limit,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Set upstream parameters to offload management process. */
- public boolean setUpstreamParameters(
- String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
- iface = (iface != null) ? iface : NO_INTERFACE_NAME;
- v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
- v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
- v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
-
- final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
- iface, v4addr, v4gateway, String.join(",", v6gws));
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setUpstreamParameters(
- iface, v4addr, v4gateway, v6gws,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Add downstream prefix to offload management process. */
- public boolean addDownstreamPrefix(String ifname, String prefix) {
- final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.addDownstream(ifname, prefix,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Remove downstream prefix from offload management process. */
- public boolean removeDownstreamPrefix(String ifname, String prefix) {
- final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.removeDownstream(ifname, prefix,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- private void record(String msg, Throwable t) {
- mLog.e(msg + YIELDS + "exception: " + t);
- }
-
- private void record(String msg, CbResults results) {
- final String logmsg = msg + YIELDS + results;
- if (!results.mSuccess) {
- mLog.e(logmsg);
- } else {
- mLog.log(logmsg);
- }
- }
-
- private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
- public final Handler handler;
- public final ControlCallback controlCb;
- public final SharedLog log;
-
- TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
- handler = h;
- controlCb = cb;
- log = sharedLog;
- }
-
- @Override
- public void onEvent(int event) {
- handler.post(() -> {
- switch (event) {
- case OffloadCallbackEvent.OFFLOAD_STARTED:
- controlCb.onStarted();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
- controlCb.onStoppedError();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
- controlCb.onStoppedUnsupported();
- break;
- case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
- controlCb.onSupportAvailable();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
- controlCb.onStoppedLimitReached();
- break;
- default:
- log.e("Unsupported OffloadCallbackEvent: " + event);
- }
- });
- }
-
- @Override
- public void updateTimeout(NatTimeoutUpdate params) {
- handler.post(() -> {
- controlCb.onNatTimeoutUpdate(
- networkProtocolToOsConstant(params.proto),
- params.src.addr, uint16(params.src.port),
- params.dst.addr, uint16(params.dst.port));
- });
- }
- }
-
- private static int networkProtocolToOsConstant(int proto) {
- switch (proto) {
- case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
- case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
- default:
- // The caller checks this value and will log an error. Just make
- // sure it won't collide with valid OsContants.IPPROTO_* values.
- return -Math.abs(proto);
- }
- }
-
- private static class CbResults {
- boolean mSuccess;
- String mErrMsg;
-
- @Override
- public String toString() {
- if (mSuccess) {
- return "ok";
- } else {
- return "fail: " + mErrMsg;
- }
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
deleted file mode 100644
index 0cf14e3f868c..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.networkstack.tethering;
-
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.util.PrefixUtils.asIpPrefix;
-
-import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
-import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
-
-import static java.util.Arrays.asList;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.Network;
-import android.net.ip.IpServer;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * This class coordinate IP addresses conflict problem.
- *
- * Tethering downstream IP addresses may conflict with network assigned addresses. This
- * coordinator is responsible for recording all of network assigned addresses and dispatched
- * free address to downstream interfaces.
- *
- * This class is not thread-safe and should be accessed on the same tethering internal thread.
- * @hide
- */
-public class PrivateAddressCoordinator {
- public static final int PREFIX_LENGTH = 24;
-
- // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
- // address may be requested before coordinator get current upstream notification. To ensure
- // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
- // when tethering is down. Instead tethering would remove all deprecated upstreams from
- // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
- private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
- private final ArraySet<IpServer> mDownstreams;
- private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
- private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
- private final List<IpPrefix> mTetheringPrefixes;
- private final ConnectivityManager mConnectivityMgr;
- private final TetheringConfiguration mConfig;
- // keyed by downstream type(TetheringManager.TETHERING_*).
- private final SparseArray<LinkAddress> mCachedAddresses;
-
- public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
- this(context, config, new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))));
- }
-
- public PrivateAddressCoordinator(Context context, TetheringConfiguration config,
- List<IpPrefix> prefixPools) {
- mDownstreams = new ArraySet<>();
- mUpstreamPrefixMap = new ArrayMap<>();
- mConnectivityMgr = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- mConfig = config;
- mCachedAddresses = new SparseArray<>();
- // Reserved static addresses for bluetooth and wifi p2p.
- mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
- mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
-
- mTetheringPrefixes = prefixPools;
- }
-
- /**
- * Record a new upstream IpPrefix which may conflict with tethering downstreams.
- * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called,
- * UpstreamNetworkState must have an already populated LinkProperties.
- */
- public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
- // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null,
- // but just checking to be sure.
- if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
- removeUpstreamPrefix(ns.network);
- return;
- }
-
- final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(
- ns.linkProperties.getAllLinkAddresses());
- if (ipv4Prefixes.isEmpty()) {
- removeUpstreamPrefix(ns.network);
- return;
- }
-
- mUpstreamPrefixMap.put(ns.network, ipv4Prefixes);
- handleMaybePrefixConflict(ipv4Prefixes);
- }
-
- private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
- final ArrayList<IpPrefix> list = new ArrayList<>();
- for (LinkAddress address : linkAddresses) {
- if (!address.isIpv4()) continue;
-
- list.add(asIpPrefix(address));
- }
-
- return list;
- }
-
- private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
- for (IpServer downstream : mDownstreams) {
- final IpPrefix target = getDownstreamPrefix(downstream);
- if (target == null) continue;
-
- for (IpPrefix source : prefixes) {
- if (isConflictPrefix(source, target)) {
- downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- break;
- }
- }
- }
- }
-
- /** Remove IpPrefix records corresponding to input network. */
- public void removeUpstreamPrefix(final Network network) {
- mUpstreamPrefixMap.remove(network);
- }
-
- /**
- * Maybe remove deprecated upstream records, this would be called once tethering started without
- * any exiting tethered downstream.
- */
- public void maybeRemoveDeprecatedUpstreams() {
- if (mUpstreamPrefixMap.isEmpty()) return;
-
- // Remove all upstreams that are no longer valid networks
- final Set<Network> toBeRemoved = new HashSet<>(mUpstreamPrefixMap.keySet());
- toBeRemoved.removeAll(asList(mConnectivityMgr.getAllNetworks()));
-
- mUpstreamPrefixMap.removeAll(toBeRemoved);
- }
-
- /**
- * Pick a random available address and mark its prefix as in use for the provided IpServer,
- * returns null if there is no available address.
- */
- @Nullable
- public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
- if (mConfig.shouldEnableWifiP2pDedicatedIp()
- && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
- return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
- }
-
- final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
- if (useLastAddress && cachedAddress != null
- && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
- return cachedAddress;
- }
-
- for (IpPrefix prefixRange : mTetheringPrefixes) {
- final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
- if (newAddress != null) {
- mDownstreams.add(ipServer);
- mCachedAddresses.put(ipServer.interfaceType(), newAddress);
- return newAddress;
- }
- }
-
- // No available address.
- return null;
- }
-
- private int getPrefixBaseAddress(final IpPrefix prefix) {
- return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
- }
-
- /**
- * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes.
- * If yes, return one of them.
- */
- private IpPrefix getConflictPrefix(final IpPrefix prefix) {
- final IpPrefix upstream = getConflictWithUpstream(prefix);
- if (upstream != null) return upstream;
-
- return getInUseDownstreamPrefix(prefix);
- }
-
- // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the
- // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix
- // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is
- // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0).
- // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as
- // selected random sub address later.
- private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) {
- final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength());
- // The largest offset within the prefix assignment block that still conflicts with
- // conflictPrefix.
- final int maxConflict =
- (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask;
-
- final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
- // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than
- // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix.
- // There is no need to mask the result with PREFIX_LENGTH bits because this is done by
- // findAvailablePrefixFromRange when it constructs the prefix.
- return maxConflict + (1 << (32 - PREFIX_LENGTH));
- }
-
- private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) {
- // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12).
- final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength());
-
- // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12).
- final int baseAddress = getPrefixBaseAddress(prefixRange);
-
- // The subnet mask corresponding to PREFIX_LENGTH.
- final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
-
- // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH.
- // This may not be the prefix of the address returned by this method:
- // - If it is already in use, the method will return an address in another prefix.
- // - If all prefixes within prefixRange are in use, the method will return null. For
- // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in
- // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00.
- //
- // prefixRangeMask is required to ensure no wrapping. For example, consider:
- // - prefixRange 127.0.0.0/8
- // - randomPrefixStart 127.255.255.0
- // - A conflicting prefix of 127.255.254.0/23
- // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which
- // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix
- // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648
- // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
- //
- // Additionally, it makes debug output easier to read by making the numbers smaller.
- final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
-
- // A random offset within the prefix. Used to determine the local address once the prefix
- // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
- // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
- final int subAddress = getSanitizedSubAddr(~prefixMask);
-
- // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
- // such that the prefix does not conflict with any upstream.
- IpPrefix downstreamPrefix = findAvailablePrefixFromRange(
- randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask);
- if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress);
-
- // If that failed, do the same, but between 0 and randomPrefixStart.
- downstreamPrefix = findAvailablePrefixFromRange(
- 0, randomPrefixStart, baseAddress, prefixRangeMask);
-
- return getLinkAddress(downstreamPrefix, subAddress);
- }
-
- private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) {
- if (prefix == null) return null;
-
- final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress);
- return new LinkAddress(address, PREFIX_LENGTH);
- }
-
- private IpPrefix findAvailablePrefixFromRange(final int start, final int end,
- final int baseAddress, final int prefixRangeMask) {
- int newSubPrefix = start;
- while (newSubPrefix < end) {
- final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix);
- final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH);
-
- final IpPrefix conflictPrefix = getConflictPrefix(prefix);
-
- if (conflictPrefix == null) return prefix;
-
- newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask);
- }
-
- return null;
- }
-
- /** Get random int which could be used to generate random address. */
- @VisibleForTesting
- public int getRandomInt() {
- return (new Random()).nextInt();
- }
-
- /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
- private int getSanitizedSubAddr(final int subAddrMask) {
- final int randomSubAddr = getRandomInt() & subAddrMask;
- // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
- // avoid 3 consecutive address.
- if (PREFIX_LENGTH > 30) return randomSubAddr;
-
- // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering
- // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer
- // than 24
- final int candidate = randomSubAddr & 0xff;
- if (candidate == 0 || candidate == 1 || candidate == 255) {
- return (randomSubAddr & 0xfffffffc) + 2;
- }
-
- return randomSubAddr;
- }
-
- /** Release downstream record for IpServer. */
- public void releaseDownstream(final IpServer ipServer) {
- mDownstreams.remove(ipServer);
- }
-
- /** Clear current upstream prefixes records. */
- public void clearUpstreamPrefixes() {
- mUpstreamPrefixMap.clear();
- }
-
- private IpPrefix getConflictWithUpstream(final IpPrefix prefix) {
- for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
- final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
- for (IpPrefix upstream : list) {
- if (isConflictPrefix(prefix, upstream)) return upstream;
- }
- }
- return null;
- }
-
- private boolean isConflictWithUpstream(final IpPrefix prefix) {
- return getConflictWithUpstream(prefix) != null;
- }
-
- private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
- if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
- return prefix2.contains(prefix1.getAddress());
- }
-
- return prefix1.contains(prefix2.getAddress());
- }
-
- // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
- // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
- private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) {
- for (int i = 0; i < mCachedAddresses.size(); i++) {
- final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i));
- if (isConflictPrefix(prefix, downstream)) return downstream;
- }
-
- // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
- // in mCachedAddresses.
- for (IpServer downstream : mDownstreams) {
- final IpPrefix target = getDownstreamPrefix(downstream);
- if (target == null) continue;
-
- if (isConflictPrefix(prefix, target)) return target;
- }
-
- return null;
- }
-
- private IpPrefix getDownstreamPrefix(final IpServer downstream) {
- final LinkAddress address = downstream.getAddress();
- if (address == null) return null;
-
- return asIpPrefix(address);
- }
-
- void dump(final IndentingPrintWriter pw) {
- pw.println("mTetheringPrefixes:");
- pw.increaseIndent();
- for (IpPrefix prefix : mTetheringPrefixes) {
- pw.println(prefix);
- }
- pw.decreaseIndent();
-
- pw.println("mUpstreamPrefixMap:");
- pw.increaseIndent();
- for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
- pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
- }
- pw.decreaseIndent();
-
- pw.println("mDownstreams:");
- pw.increaseIndent();
- for (IpServer ipServer : mDownstreams) {
- pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
- }
- pw.decreaseIndent();
-
- pw.println("mCachedAddresses:");
- pw.increaseIndent();
- for (int i = 0; i < mCachedAddresses.size(); i++) {
- pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i));
- }
- pw.decreaseIndent();
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
deleted file mode 100644
index 5783805861a3..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.content.Context.TELEPHONY_SERVICE;
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.TetheringConfigurationParcel;
-import android.net.util.SharedLog;
-import android.provider.DeviceConfig;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.StringJoiner;
-
-
-/**
- * A utility class to encapsulate the various tethering configuration elements.
- *
- * This configuration data includes elements describing upstream properties
- * (preferred and required types of upstream connectivity as well as default
- * DNS servers to use if none are available) and downstream properties (such
- * as regular expressions use to match suitable downstream interfaces and the
- * DHCPv4 ranges to use).
- *
- * @hide
- */
-public class TetheringConfiguration {
- private static final String TAG = TetheringConfiguration.class.getSimpleName();
-
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- // Default ranges used for the legacy DHCP server.
- // USB is 192.168.42.1 and 255.255.255.0
- // Wifi is 192.168.43.1 and 255.255.255.0
- // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
- // with 255.255.255.0
- // P2P is 192.168.49.1 and 255.255.255.0
- private static final String[] LEGACY_DHCP_DEFAULT_RANGE = {
- "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
- "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
- "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
- "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
- };
-
- private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
-
- /**
- * Override enabling BPF offload configuration for tethering.
- */
- public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD =
- "override_tether_enable_bpf_offload";
-
- /**
- * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
- */
- public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
- "tether_enable_legacy_dhcp_server";
-
- public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP =
- "use_legacy_wifi_p2p_dedicated_ip";
-
- /**
- * Default value that used to periodic polls tether offload stats from tethering offload HAL
- * to make the data warnings work.
- */
- public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000;
-
- public final String[] tetherableUsbRegexs;
- public final String[] tetherableWifiRegexs;
- public final String[] tetherableWigigRegexs;
- public final String[] tetherableWifiP2pRegexs;
- public final String[] tetherableBluetoothRegexs;
- public final String[] tetherableNcmRegexs;
- public final boolean isDunRequired;
- public final boolean chooseUpstreamAutomatically;
- public final Collection<Integer> preferredUpstreamIfaceTypes;
- public final String[] legacyDhcpRanges;
- public final String[] defaultIPv4DNS;
- public final boolean enableLegacyDhcpServer;
-
- public final String[] provisioningApp;
- public final String provisioningAppNoUi;
- public final int provisioningCheckPeriod;
- public final String provisioningResponse;
-
- public final int activeDataSubId;
-
- private final int mOffloadPollInterval;
- // TODO: Add to TetheringConfigurationParcel if required.
- private final boolean mEnableBpfOffload;
- private final boolean mEnableWifiP2pDedicatedIp;
-
- public TetheringConfiguration(Context ctx, SharedLog log, int id) {
- final SharedLog configLog = log.forSubComponent("config");
-
- activeDataSubId = id;
- Resources res = getResources(ctx, activeDataSubId);
-
- tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
- tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
- // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
- // us an interface name. Careful consideration needs to be given to
- // implications for Settings and for provisioning checks.
- tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
- tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs);
- tetherableWifiP2pRegexs = getResourceStringArray(
- res, R.array.config_tether_wifi_p2p_regexs);
- tetherableBluetoothRegexs = getResourceStringArray(
- res, R.array.config_tether_bluetooth_regexs);
-
- isDunRequired = checkDunRequired(ctx);
-
- chooseUpstreamAutomatically = getResourceBoolean(
- res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
- preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
-
- legacyDhcpRanges = getLegacyDhcpRanges(res);
- defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
- mEnableBpfOffload = getEnableBpfOffload(res);
- enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
-
- provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
- provisioningAppNoUi = getResourceString(res,
- R.string.config_mobile_hotspot_provision_app_no_ui);
- provisioningCheckPeriod = getResourceInteger(res,
- R.integer.config_mobile_hotspot_provision_check_period,
- 0 /* No periodic re-check */);
- provisioningResponse = getResourceString(res,
- R.string.config_mobile_hotspot_provision_response);
-
- mOffloadPollInterval = getResourceInteger(res,
- R.integer.config_tether_offload_poll_interval,
- DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
-
- mEnableWifiP2pDedicatedIp = getResourceBoolean(res,
- R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
- false /* defaultValue */);
-
- configLog.log(toString());
- }
-
- /** Check whether input interface belong to usb.*/
- public boolean isUsb(String iface) {
- return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
- }
-
- /** Check whether input interface belong to wifi.*/
- public boolean isWifi(String iface) {
- return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
- }
-
- /** Check whether input interface belong to wigig.*/
- public boolean isWigig(String iface) {
- return matchesDownstreamRegexs(iface, tetherableWigigRegexs);
- }
-
- /** Check whether this interface is Wifi P2P interface. */
- public boolean isWifiP2p(String iface) {
- return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
- }
-
- /** Check whether using legacy mode for wifi P2P. */
- public boolean isWifiP2pLegacyTetheringMode() {
- return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
- }
-
- /** Check whether input interface belong to bluetooth.*/
- public boolean isBluetooth(String iface) {
- return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
- }
-
- /** Check if interface is ncm */
- public boolean isNcm(String iface) {
- return matchesDownstreamRegexs(iface, tetherableNcmRegexs);
- }
-
- /** Check whether no ui entitlement application is available.*/
- public boolean hasMobileHotspotProvisionApp() {
- return !TextUtils.isEmpty(provisioningAppNoUi);
- }
-
- /** Check whether dedicated wifi p2p address is enabled. */
- public boolean shouldEnableWifiP2pDedicatedIp() {
- return mEnableWifiP2pDedicatedIp;
- }
-
- /** Does the dumping.*/
- public void dump(PrintWriter pw) {
- pw.print("activeDataSubId: ");
- pw.println(activeDataSubId);
-
- dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
- dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
- dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
- dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
- dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs);
-
- pw.print("isDunRequired: ");
- pw.println(isDunRequired);
-
- pw.print("chooseUpstreamAutomatically: ");
- pw.println(chooseUpstreamAutomatically);
- pw.print("legacyPreredUpstreamIfaceTypes: ");
- pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes)));
-
- dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
- dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
-
- pw.print("offloadPollInterval: ");
- pw.println(mOffloadPollInterval);
-
- dumpStringArray(pw, "provisioningApp", provisioningApp);
- pw.print("provisioningAppNoUi: ");
- pw.println(provisioningAppNoUi);
-
- pw.print("enableBpfOffload: ");
- pw.println(mEnableBpfOffload);
-
- pw.print("enableLegacyDhcpServer: ");
- pw.println(enableLegacyDhcpServer);
-
- pw.print("enableWifiP2pDedicatedIp: ");
- pw.println(mEnableWifiP2pDedicatedIp);
- }
-
- /** Returns the string representation of this object.*/
- public String toString() {
- final StringJoiner sj = new StringJoiner(" ");
- sj.add(String.format("activeDataSubId:%d", activeDataSubId));
- sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
- sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
- sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
- sj.add(String.format("tetherableBluetoothRegexs:%s",
- makeString(tetherableBluetoothRegexs)));
- sj.add(String.format("isDunRequired:%s", isDunRequired));
- sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
- sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval));
- sj.add(String.format("preferredUpstreamIfaceTypes:%s",
- toIntArray(preferredUpstreamIfaceTypes)));
- sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
- sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
- sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
- sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
- return String.format("TetheringConfiguration{%s}", sj.toString());
- }
-
- private static void dumpStringArray(PrintWriter pw, String label, String[] values) {
- pw.print(label);
- pw.print(": ");
-
- if (values != null) {
- final StringJoiner sj = new StringJoiner(", ", "[", "]");
- for (String value : values) sj.add(value);
- pw.print(sj.toString());
- } else {
- pw.print("null");
- }
-
- pw.println();
- }
-
- private static String makeString(String[] strings) {
- if (strings == null) return "null";
- final StringJoiner sj = new StringJoiner(",", "[", "]");
- for (String s : strings) sj.add(s);
- return sj.toString();
- }
-
- /** Check whether dun is required. */
- public static boolean checkDunRequired(Context ctx) {
- final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
- // TelephonyManager would uses the active data subscription, which should be the one used
- // by tethering.
- return (tm != null) ? tm.isTetheringApnRequired() : false;
- }
-
- public int getOffloadPollInterval() {
- return mOffloadPollInterval;
- }
-
- public boolean isBpfOffloadEnabled() {
- return mEnableBpfOffload;
- }
-
- private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
- final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
- final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
- for (int i : ifaceTypes) {
- switch (i) {
- case TYPE_MOBILE:
- case TYPE_MOBILE_HIPRI:
- if (dunRequired) continue;
- break;
- case TYPE_MOBILE_DUN:
- if (!dunRequired) continue;
- break;
- }
- upstreamIfaceTypes.add(i);
- }
-
- // Fix up upstream interface types for DUN or mobile. NOTE: independent
- // of the value of |dunRequired|, cell data of one form or another is
- // *always* an upstream, regardless of the upstream interface types
- // specified by configuration resources.
- if (dunRequired) {
- appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN);
- } else {
- // Do not modify if a cellular interface type is already present in the
- // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no
- // cellular interface types are found in the upstream interface types.
- // This preserves backwards compatibility and prevents the DUN and default
- // mobile types incorrectly appearing together, which could happen on
- // previous releases in the common case where checkDunRequired returned
- // DUN_UNSPECIFIED.
- if (!containsOneOf(upstreamIfaceTypes, TYPE_MOBILE, TYPE_MOBILE_HIPRI)) {
- upstreamIfaceTypes.add(TYPE_MOBILE);
- upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
- }
- }
-
- // Always make sure our good friend Ethernet is present.
- // TODO: consider unilaterally forcing this at the front.
- prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET);
-
- return upstreamIfaceTypes;
- }
-
- private static boolean matchesDownstreamRegexs(String iface, String[] regexs) {
- for (String regex : regexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
-
- private static String[] getLegacyDhcpRanges(Resources res) {
- final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range);
- if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
- return fromResource;
- }
- return copy(LEGACY_DHCP_DEFAULT_RANGE);
- }
-
- private static String getResourceString(Resources res, final int resId) {
- try {
- return res.getString(resId);
- } catch (Resources.NotFoundException e) {
- return "";
- }
- }
-
- private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) {
- try {
- return res.getBoolean(resId);
- } catch (Resources.NotFoundException e404) {
- return defaultValue;
- }
- }
-
- private static String[] getResourceStringArray(Resources res, int resId) {
- try {
- final String[] strArray = res.getStringArray(resId);
- return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
- } catch (Resources.NotFoundException e404) {
- return EMPTY_STRING_ARRAY;
- }
- }
-
- private static int getResourceInteger(Resources res, int resId, int defaultValue) {
- try {
- return res.getInteger(resId);
- } catch (Resources.NotFoundException e404) {
- return defaultValue;
- }
- }
-
- private boolean getEnableBpfOffload(final Resources res) {
- // Get BPF offload config
- // Priority 1: Device config
- // Priority 2: Resource config
- // Priority 3: Default value
- final boolean defaultValue = getResourceBoolean(
- res, R.bool.config_tether_enable_bpf_offload, true /** default value */);
-
- return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue);
- }
-
- private boolean getEnableLegacyDhcpServer(final Resources res) {
- return getResourceBoolean(
- res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */)
- || getDeviceConfigBoolean(
- TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */);
- }
-
- private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
- // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
- // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
- // returned boolean value comes from device config or default value (because of null
- // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
- final String value = getDeviceConfigProperty(name);
- return value != null ? Boolean.parseBoolean(value) : defaultValue;
- }
-
- @VisibleForTesting
- protected String getDeviceConfigProperty(String name) {
- return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
- }
-
- private Resources getResources(Context ctx, int subId) {
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return getResourcesForSubIdWrapper(ctx, subId);
- } else {
- return ctx.getResources();
- }
- }
-
- @VisibleForTesting
- protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
- return SubscriptionManager.getResourcesForSubId(ctx, subId);
- }
-
- private static String[] copy(String[] strarray) {
- return Arrays.copyOf(strarray, strarray.length);
- }
-
- private static void prependIfNotPresent(ArrayList<Integer> list, int value) {
- if (list.contains(value)) return;
- list.add(0, value);
- }
-
- private static void appendIfNotPresent(ArrayList<Integer> list, int value) {
- if (list.contains(value)) return;
- list.add(value);
- }
-
- private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) {
- for (Integer value : values) {
- if (list.contains(value)) return true;
- }
- return false;
- }
-
- private static int[] toIntArray(Collection<Integer> values) {
- final int[] result = new int[values.size()];
- int index = 0;
- for (Integer value : values) {
- result[index++] = value;
- }
- return result;
- }
-
- /**
- * Convert this TetheringConfiguration to a TetheringConfigurationParcel.
- */
- public TetheringConfigurationParcel toStableParcelable() {
- final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
- parcel.subId = activeDataSubId;
- parcel.tetherableUsbRegexs = tetherableUsbRegexs;
- parcel.tetherableWifiRegexs = tetherableWifiRegexs;
- parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
- parcel.isDunRequired = isDunRequired;
- parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
-
- parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes);
-
- parcel.legacyDhcpRanges = legacyDhcpRanges;
- parcel.defaultIPv4DNS = defaultIPv4DNS;
- parcel.enableLegacyDhcpServer = enableLegacyDhcpServer;
- parcel.provisioningApp = provisioningApp;
- parcel.provisioningAppNoUi = provisioningAppNoUi;
- parcel.provisioningCheckPeriod = provisioningCheckPeriod;
- return parcel;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
deleted file mode 100644
index 45b914178e97..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import android.app.usage.NetworkStatsManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.net.INetd;
-import android.net.NetworkRequest;
-import android.net.ip.IpServer;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.util.StateMachine;
-
-import java.util.ArrayList;
-
-
-/**
- * Capture tethering dependencies, for injection.
- *
- * @hide
- */
-public abstract class TetheringDependencies {
- /**
- * Get a reference to the BpfCoordinator to be used by tethering.
- */
- public @NonNull BpfCoordinator getBpfCoordinator(
- @NonNull BpfCoordinator.Dependencies deps) {
- return new BpfCoordinator(deps);
- }
-
- /**
- * Get a reference to the offload hardware interface to be used by tethering.
- */
- public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
- return new OffloadHardwareInterface(h, log);
- }
-
- /**
- * Get a reference to the offload controller to be used by tethering.
- */
- @NonNull
- public OffloadController getOffloadController(@NonNull Handler h,
- @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) {
- final NetworkStatsManager statsManager =
- (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
- return new OffloadController(h, getOffloadHardwareInterface(h, log),
- getContext().getContentResolver(), statsManager, log, deps);
- }
-
-
- /**
- * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
- */
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
- SharedLog log, int what) {
- return new UpstreamNetworkMonitor(ctx, target, log, what);
- }
-
- /**
- * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
- */
- public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<IpServer> notifyList, SharedLog log) {
- return new IPv6TetheringCoordinator(notifyList, log);
- }
-
- /**
- * Get dependencies to be used by IpServer.
- */
- public abstract IpServer.Dependencies getIpServerDependencies();
-
- /**
- * Indicates whether tethering is supported on the device.
- */
- public boolean isTetheringSupported() {
- return true;
- }
-
- /**
- * Get the NetworkRequest that should be fulfilled by the default network.
- */
- public abstract NetworkRequest getDefaultNetworkRequest();
-
- /**
- * Get a reference to the EntitlementManager to be used by tethering.
- */
- public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
- Runnable callback) {
- return new EntitlementManager(ctx, h, log, callback);
- }
-
- /**
- * Generate a new TetheringConfiguration according to input sub Id.
- */
- public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
- int subId) {
- return new TetheringConfiguration(ctx, log, subId);
- }
-
- /**
- * Get a reference to INetd to be used by tethering.
- */
- public INetd getINetd(Context context) {
- return INetd.Stub.asInterface(
- (IBinder) context.getSystemService(Context.NETD_SERVICE));
- }
-
- /**
- * Get a reference to the TetheringNotificationUpdater to be used by tethering.
- */
- public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
- @NonNull final Looper looper) {
- return new TetheringNotificationUpdater(ctx, looper);
- }
-
- /**
- * Get tethering thread looper.
- */
- public abstract Looper getTetheringLooper();
-
- /**
- * Get Context of TetheringSerice.
- */
- public abstract Context getContext();
-
- /**
- * Get a reference to BluetoothAdapter to be used by tethering.
- */
- public abstract BluetoothAdapter getBluetoothAdapter();
-
- /**
- * Get SystemProperties which indicate whether tethering is denied.
- */
- public boolean isTetheringDenied() {
- return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true");
- }
-
- /**
- * Get a reference to PrivateAddressCoordinator to be used by Tethering.
- */
- public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
- TetheringConfiguration cfg) {
- return new PrivateAddressCoordinator(ctx, cfg);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
deleted file mode 100644
index ff38f717a121..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import android.annotation.Nullable;
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
-import android.net.util.InterfaceSet;
-
-import com.android.net.module.util.NetUtils;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * @hide
- */
-public final class TetheringInterfaceUtils {
- private static final InetAddress IN6ADDR_ANY = getByAddress(
- new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
- private static final InetAddress INADDR_ANY = getByAddress(new byte[] {0, 0, 0, 0});
-
- /**
- * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
- * @return null if there is no usable interface, or a set of at least one interface otherwise.
- */
- public static @Nullable InterfaceSet getTetheringInterfaces(UpstreamNetworkState ns) {
- if (ns == null) {
- return null;
- }
-
- final LinkProperties lp = ns.linkProperties;
- final String if4 = getInterfaceForDestination(lp, INADDR_ANY);
- final String if6 = getIPv6Interface(ns);
-
- return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
- }
-
- /**
- * Get the upstream interface for IPv6 tethering.
- * @return null if there is no usable interface, or the interface name otherwise.
- */
- public static @Nullable String getIPv6Interface(UpstreamNetworkState ns) {
- // Broadly speaking:
- //
- // [1] does the upstream have an IPv6 default route?
- //
- // and
- //
- // [2] does the upstream have one or more global IPv6 /64s
- // dedicated to this device?
- //
- // In lieu of Prefix Delegation and other evaluation of whether a
- // prefix may or may not be dedicated to this device, for now just
- // check whether the upstream is TRANSPORT_CELLULAR. This works
- // because "[t]he 3GPP network allocates each default bearer a unique
- // /64 prefix", per RFC 6459, Section 5.2.
- final boolean canTether =
- (ns != null) && (ns.network != null)
- && (ns.linkProperties != null) && (ns.networkCapabilities != null)
- // At least one upstream DNS server:
- && ns.linkProperties.hasIpv6DnsServer()
- // Minimal amount of IPv6 provisioning:
- && ns.linkProperties.hasGlobalIpv6Address()
- // Temporary approximation of "dedicated prefix":
- && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
-
- return canTether
- ? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY)
- : null;
- }
-
- private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
- final RouteInfo ri = (lp != null)
- ? NetUtils.selectBestRoute(lp.getAllRoutes(), dst)
- : null;
- return (ri != null) ? ri.getInterface() : null;
- }
-
- private static InetAddress getByAddress(final byte[] addr) {
- try {
- return InetAddress.getByAddress(null, addr);
- } catch (UnknownHostException e) {
- throw new AssertionError("illegal address length" + addr.length);
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
deleted file mode 100644
index a0198cc9c126..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.text.TextUtils.isEmpty;
-
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.net.NetworkCapabilities;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.SparseArray;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.IntDef;
-import androidx.annotation.IntRange;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A class to display tethering-related notifications.
- *
- * <p>This class is not thread safe, it is intended to be used only from the tethering handler
- * thread. However the constructor is an exception, as it is called on another thread ;
- * therefore for thread safety all members of this class MUST either be final or initialized
- * to their default value (0, false or null).
- *
- * @hide
- */
-public class TetheringNotificationUpdater {
- private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
- private static final String CHANNEL_ID = "TETHERING_STATUS";
- private static final String WIFI_DOWNSTREAM = "WIFI";
- private static final String USB_DOWNSTREAM = "USB";
- private static final String BLUETOOTH_DOWNSTREAM = "BT";
- @VisibleForTesting
- static final String ACTION_DISABLE_TETHERING =
- "com.android.server.connectivity.tethering.DISABLE_TETHERING";
- private static final boolean NOTIFY_DONE = true;
- private static final boolean NO_NOTIFY = false;
- @VisibleForTesting
- static final int EVENT_SHOW_NO_UPSTREAM = 1;
- // Id to update and cancel restricted notification. Must be unique within the tethering app.
- @VisibleForTesting
- static final int RESTRICTED_NOTIFICATION_ID = 1001;
- // Id to update and cancel no upstream notification. Must be unique within the tethering app.
- @VisibleForTesting
- static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
- // Id to update and cancel roaming notification. Must be unique within the tethering app.
- @VisibleForTesting
- static final int ROAMING_NOTIFICATION_ID = 1003;
- @VisibleForTesting
- static final int NO_ICON_ID = 0;
- @VisibleForTesting
- static final int DOWNSTREAM_NONE = 0;
- // Refer to TelephonyManager#getSimCarrierId for more details about carrier id.
- @VisibleForTesting
- static final int VERIZON_CARRIER_ID = 1839;
- private final Context mContext;
- private final NotificationManager mNotificationManager;
- private final NotificationChannel mChannel;
- private final Handler mHandler;
-
- // WARNING : the constructor is called on a different thread. Thread safety therefore
- // relies on these values being initialized to 0, false or null, and not any other value. If you
- // need to change this, you will need to change the thread where the constructor is invoked, or
- // to introduce synchronization.
- // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
- // This value has to be made 1 2 and 4, and OR'd with the others.
- private int mDownstreamTypesMask = DOWNSTREAM_NONE;
- private boolean mNoUpstream = false;
- private boolean mRoaming = false;
-
- // WARNING : this value is not able to being initialized to 0 and must have volatile because
- // telephony service is not guaranteed that is up before tethering service starts. If telephony
- // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid
- // subscription id(0) to query resources. Therefore, initialized subscription id must be
- // INVALID_SUBSCRIPTION_ID.
- private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- RESTRICTED_NOTIFICATION_ID,
- NO_UPSTREAM_NOTIFICATION_ID,
- ROAMING_NOTIFICATION_ID
- })
- @interface NotificationId {}
-
- private static final class MccMncOverrideInfo {
- public final String visitedMccMnc;
- public final int homeMcc;
- public final int homeMnc;
- MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) {
- this.visitedMccMnc = visitedMccMnc;
- this.homeMcc = mcc;
- this.homeMnc = mnc;
- }
- }
-
- private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
-
- static {
- sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480));
- }
-
- public TetheringNotificationUpdater(@NonNull final Context context,
- @NonNull final Looper looper) {
- mContext = context;
- mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- mChannel = new NotificationChannel(
- CHANNEL_ID,
- context.getResources().getString(R.string.notification_channel_tethering_status),
- NotificationManager.IMPORTANCE_LOW);
- mNotificationManager.createNotificationChannel(mChannel);
- mHandler = new NotificationHandler(looper);
- }
-
- private class NotificationHandler extends Handler {
- NotificationHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case EVENT_SHOW_NO_UPSTREAM:
- notifyTetheringNoUpstream();
- break;
- }
- }
- }
-
- /** Called when downstream has changed */
- public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
- updateActiveNotifications(
- mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming);
- }
-
- /** Called when active data subscription id changed */
- public void onActiveDataSubscriptionIdChanged(final int subId) {
- updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming);
- }
-
- /** Called when upstream network capabilities changed */
- public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) {
- final boolean isNoUpstream = (capabilities == null);
- final boolean isRoaming = capabilities != null
- && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- updateActiveNotifications(
- mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming);
- }
-
- @NonNull
- @VisibleForTesting
- final Handler getHandler() {
- return mHandler;
- }
-
- @NonNull
- @VisibleForTesting
- Resources getResourcesForSubId(@NonNull final Context context, final int subId) {
- final Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
- final TelephonyManager tm =
- ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mActiveDataSubId);
- final int carrierId = tm.getSimCarrierId();
- final String mccmnc = tm.getSimOperator();
- final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
- if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) {
- // Re-configure MCC/MNC value to specific carrier to get right resources.
- final Configuration config = res.getConfiguration();
- config.mcc = overrideInfo.homeMcc;
- config.mnc = overrideInfo.homeMnc;
- return context.createConfigurationContext(config).getResources();
- }
- return res;
- }
-
- private void updateActiveNotifications(final int subId, final int downstreamTypes,
- final boolean noUpstream, final boolean isRoaming) {
- final boolean tetheringActiveChanged =
- (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE);
- final boolean subIdChanged = subId != mActiveDataSubId;
- final boolean upstreamChanged = noUpstream != mNoUpstream;
- final boolean roamingChanged = isRoaming != mRoaming;
- final boolean updateAll = tetheringActiveChanged || subIdChanged;
- mActiveDataSubId = subId;
- mDownstreamTypesMask = downstreamTypes;
- mNoUpstream = noUpstream;
- mRoaming = isRoaming;
-
- if (updateAll || upstreamChanged) updateNoUpstreamNotification();
- if (updateAll || roamingChanged) updateRoamingNotification();
- }
-
- private void updateNoUpstreamNotification() {
- final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
-
- if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) {
- clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
- mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
- }
- }
-
- private void updateRoamingNotification() {
- final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
-
- if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) {
- clearNotification(ROAMING_NOTIFICATION_ID);
- }
- }
-
- @VisibleForTesting
- void tetheringRestrictionLifted() {
- clearNotification(RESTRICTED_NOTIFICATION_ID);
- }
-
- private void clearNotification(@NotificationId final int id) {
- mNotificationManager.cancel(null /* tag */, id);
- }
-
- @VisibleForTesting
- static String getSettingsPackageName(@NonNull final PackageManager pm) {
- final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
- final ComponentName settingsComponent = settingsIntent.resolveActivity(pm);
- return settingsComponent != null
- ? settingsComponent.getPackageName() : "com.android.settings";
- }
-
- @VisibleForTesting
- void notifyTetheringDisabledByRestriction() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final String title = res.getString(R.string.disable_tether_notification_title);
- final String message = res.getString(R.string.disable_tether_notification_message);
- if (isEmpty(title) || isEmpty(message)) return;
-
- final PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- new Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(mContext.getPackageManager()))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- PendingIntent.FLAG_IMMUTABLE,
- null /* options */);
-
- showNotification(R.drawable.stat_sys_tether_general, title, message,
- RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]);
- }
-
- private void notifyTetheringNoUpstream() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final String title = res.getString(R.string.no_upstream_notification_title);
- final String message = res.getString(R.string.no_upstream_notification_message);
- final String disableButton =
- res.getString(R.string.no_upstream_notification_disable_button);
- if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return;
-
- final Intent intent = new Intent(ACTION_DISABLE_TETHERING);
- intent.setPackage(mContext.getPackageName());
- final PendingIntent pi = PendingIntent.getBroadcast(
- mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- intent,
- PendingIntent.FLAG_IMMUTABLE);
- final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
-
- showNotification(R.drawable.stat_sys_tether_general, title, message,
- NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action);
- }
-
- private boolean setupRoamingNotification() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final boolean upstreamRoamingNotification =
- res.getBoolean(R.bool.config_upstream_roaming_notification);
-
- if (!upstreamRoamingNotification) return NO_NOTIFY;
-
- final String title = res.getString(R.string.upstream_roaming_notification_title);
- final String message = res.getString(R.string.upstream_roaming_notification_message);
- if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
-
- final PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- new Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(mContext.getPackageManager()))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- PendingIntent.FLAG_IMMUTABLE,
- null /* options */);
-
- showNotification(R.drawable.stat_sys_tether_general, title, message,
- ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]);
- return NOTIFY_DONE;
- }
-
- private boolean setupNoUpstreamNotification() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final int delayToShowUpstreamNotification =
- res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul);
-
- if (delayToShowUpstreamNotification < 0) return NO_NOTIFY;
-
- mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM),
- delayToShowUpstreamNotification);
- return NOTIFY_DONE;
- }
-
- private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
- @NonNull final String message, @NotificationId final int id, final boolean ongoing,
- @Nullable PendingIntent pi, @NonNull final Action... actions) {
- final Notification notification =
- new Notification.Builder(mContext, mChannel.getId())
- .setSmallIcon(iconId)
- .setContentTitle(title)
- .setContentText(message)
- .setOngoing(ongoing)
- .setColor(mContext.getColor(
- android.R.color.system_notification_accent_color))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setCategory(Notification.CATEGORY_STATUS)
- .setContentIntent(pi)
- .setActions(actions)
- .build();
-
- mNotificationManager.notify(null /* tag */, id, notification);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
deleted file mode 100644
index d637ba7557fa..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
-import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
-
-import android.app.Service;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.content.Intent;
-import android.net.IIntResultListener;
-import android.net.INetworkStackConnector;
-import android.net.ITetheringConnector;
-import android.net.ITetheringEventCallback;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.NetworkStack;
-import android.net.TetheringRequestParcel;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.ip.IpServer;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Android service used to manage tethering.
- *
- * <p>The service returns a binder for the system server to communicate with the tethering.
- */
-public class TetheringService extends Service {
- private static final String TAG = TetheringService.class.getSimpleName();
-
- private TetheringConnector mConnector;
-
- @Override
- public void onCreate() {
- final TetheringDependencies deps = makeTetheringDependencies();
- // The Tethering object needs a fully functional context to start, so this can't be done
- // in the constructor.
- mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
- }
-
- /**
- * Make a reference to Tethering object.
- */
- @VisibleForTesting
- public Tethering makeTethering(TetheringDependencies deps) {
- System.loadLibrary("tetherutilsjni");
- return new Tethering(deps);
- }
-
- @NonNull
- @Override
- public IBinder onBind(Intent intent) {
- return mConnector;
- }
-
- private static class TetheringConnector extends ITetheringConnector.Stub {
- private final TetheringService mService;
- private final Tethering mTethering;
-
- TetheringConnector(Tethering tether, TetheringService service) {
- mTethering = tether;
- mService = service;
- }
-
- @Override
- public void tether(String iface, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(mTethering.tether(iface));
- } catch (RemoteException e) { }
- }
-
- @Override
- public void untether(String iface, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(mTethering.untether(iface));
- } catch (RemoteException e) { }
- }
-
- @Override
- public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(mTethering.setUsbTethering(enable));
- } catch (RemoteException e) { }
- }
-
- @Override
- public void startTethering(TetheringRequestParcel request, String callerPkg,
- String callingAttributionTag, IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg,
- callingAttributionTag,
- request.exemptFromEntitlementCheck /* onlyAllowPrivileged */,
- listener)) {
- return;
- }
-
- mTethering.startTethering(request, listener);
- }
-
- @Override
- public void stopTethering(int type, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- mTethering.stopTethering(type);
- listener.onResult(TETHER_ERROR_NO_ERROR);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
- boolean showEntitlementUi, String callerPkg, String callingAttributionTag) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, receiver)) return;
-
- mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
- }
-
- @Override
- public void registerTetheringEventCallback(ITetheringEventCallback callback,
- String callerPkg) {
- try {
- if (!hasTetherAccessPermission()) {
- callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
- return;
- }
- mTethering.registerTetheringEventCallback(callback);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
- String callerPkg) {
- try {
- if (!hasTetherAccessPermission()) {
- callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
- return;
- }
- mTethering.unregisterTetheringEventCallback(callback);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void stopAllTethering(String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- mTethering.untetherAll();
- listener.onResult(TETHER_ERROR_NO_ERROR);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void isTetheringSupported(String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(TETHER_ERROR_NO_ERROR);
- } catch (RemoteException e) { }
- }
-
- @Override
- protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
- @Nullable String[] args) {
- mTethering.dump(fd, writer, args);
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final IIntResultListener listener) {
- return checkAndNotifyCommonError(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */, listener);
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final boolean onlyAllowPrivileged,
- final IIntResultListener listener) {
- try {
- if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
- onlyAllowPrivileged)) {
- listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- return true;
- }
- if (!mTethering.isTetheringSupported()) {
- listener.onResult(TETHER_ERROR_UNSUPPORTED);
- return true;
- }
- } catch (RemoteException e) {
- return true;
- }
-
- return false;
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final ResultReceiver receiver) {
- if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */)) {
- receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
- return true;
- }
- if (!mTethering.isTetheringSupported()) {
- receiver.send(TETHER_ERROR_UNSUPPORTED, null);
- return true;
- }
-
- return false;
- }
-
- private boolean hasNetworkStackPermission() {
- return checkCallingOrSelfPermission(NETWORK_STACK)
- || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK);
- }
-
- private boolean hasTetherPrivilegedPermission() {
- return checkCallingOrSelfPermission(TETHER_PRIVILEGED);
- }
-
- private boolean checkCallingOrSelfPermission(final String permission) {
- return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
- }
-
- private boolean hasTetherChangePermission(final String callerPkg,
- final String callingAttributionTag, final boolean onlyAllowPrivileged) {
- if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false;
-
- if (hasTetherPrivilegedPermission()) return true;
-
- if (mTethering.isTetherProvisioningRequired()) return false;
-
- int uid = Binder.getCallingUid();
-
- // If callerPkg's uid is not same as Binder.getCallingUid(),
- // checkAndNoteWriteSettingsOperation will return false and the operation will be
- // denied.
- return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg,
- callingAttributionTag, false /* throwException */);
- }
-
- private boolean hasTetherAccessPermission() {
- if (hasTetherPrivilegedPermission()) return true;
-
- return mService.checkCallingOrSelfPermission(
- ACCESS_NETWORK_STATE) == PERMISSION_GRANTED;
- }
- }
-
- /**
- * Check if the package is a allowed to write settings. This also accounts that such an access
- * happened.
- *
- * @return {@code true} iff the package is allowed to write settings.
- */
- @VisibleForTesting
- boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
- @NonNull String callingPackage, @Nullable String callingAttributionTag,
- boolean throwException) {
- return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
- callingAttributionTag, throwException);
- }
-
- /**
- * An injection method for testing.
- */
- @VisibleForTesting
- public TetheringDependencies makeTetheringDependencies() {
- return new TetheringDependencies() {
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- // TODO: b/147280869, add a proper system API to replace this.
- final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
- return trackDefaultRequest;
- }
-
- @Override
- public Looper getTetheringLooper() {
- final HandlerThread tetherThread = new HandlerThread("android.tethering");
- tetherThread.start();
- return tetherThread.getLooper();
- }
-
- @Override
- public Context getContext() {
- return TetheringService.this;
- }
-
- @Override
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies() {
- @Override
- public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb) {
- try {
- final INetworkStackConnector service = getNetworkStackConnector();
- if (service == null) return;
-
- service.makeDhcpServer(ifName, params, cb);
- } catch (RemoteException e) {
- Log.e(TAG, "Fail to make dhcp server");
- try {
- cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
- } catch (RemoteException re) { }
- }
- }
- };
- }
-
- // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
- // networkStackClient.
- static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
- private INetworkStackConnector getNetworkStackConnector() {
- IBinder connector;
- try {
- final long before = System.currentTimeMillis();
- while ((connector = NetworkStack.getService()) == null) {
- if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
- return null;
- }
- Thread.sleep(200);
- }
- } catch (InterruptedException e) {
- Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
- return null;
- }
- return INetworkStackConnector.Stub.asInterface(connector);
- }
-
- @Override
- public BluetoothAdapter getBluetoothAdapter() {
- return BluetoothAdapter.getDefaultAdapter();
- }
- };
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
deleted file mode 100644
index bab9f84cf762..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.networkstack.tethering;
-
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-
-import androidx.annotation.NonNull;
-
-/**
- * Snapshot of tethering upstream network state.
- */
-public class UpstreamNetworkState {
- /** {@link LinkProperties}. */
- public final LinkProperties linkProperties;
- /** {@link NetworkCapabilities}. */
- public final NetworkCapabilities networkCapabilities;
- /** {@link Network}. */
- public final Network network;
-
- /** Constructs a new UpstreamNetworkState. */
- public UpstreamNetworkState(LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, Network network) {
- this.linkProperties = linkProperties;
- this.networkCapabilities = networkCapabilities;
- this.network = network;
- }
-
- @NonNull
- @Override
- public String toString() {
- return String.format("UpstreamNetworkState{%s, %s, %s}",
- network == null ? "null" : network,
- networkCapabilities == null ? "null" : networkCapabilities,
- linkProperties == null ? "null" : linkProperties);
- }
-}