Move module sources to packages/Connectivity
Files that are planned to be part of the connectivity module are grouped in packages/Connectivity, so they can be built separately and moved in one operation with their history into packages/modules/Connectivity. This places the files in the existing framework-connectivity-sources filegroup instead of the current framework-core-sources filegroup. Both are used the same way in framework-non-updatable-sources. Bug: 171540887 Test: m Change-Id: I62d9d91574ace6f5c4624035d190260c3126b91e
This commit is contained in:
379
framework/src/android/net/util/DnsUtils.java
Normal file
379
framework/src/android/net/util/DnsUtils.java
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* 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 static android.system.OsConstants.AF_INET;
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.Network;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.BitUtils;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class DnsUtils {
|
||||
private static final String TAG = "DnsUtils";
|
||||
private static final int CHAR_BIT = 8;
|
||||
public static final int IPV6_ADDR_SCOPE_NODELOCAL = 0x01;
|
||||
public static final int IPV6_ADDR_SCOPE_LINKLOCAL = 0x02;
|
||||
public static final int IPV6_ADDR_SCOPE_SITELOCAL = 0x05;
|
||||
public static final int IPV6_ADDR_SCOPE_GLOBAL = 0x0e;
|
||||
private static final Comparator<SortableAddress> sRfc6724Comparator = new Rfc6724Comparator();
|
||||
|
||||
/**
|
||||
* Comparator to sort SortableAddress in Rfc6724 style.
|
||||
*/
|
||||
public static class Rfc6724Comparator implements Comparator<SortableAddress> {
|
||||
// This function matches the behaviour of _rfc6724_compare in the native resolver.
|
||||
@Override
|
||||
public int compare(SortableAddress span1, SortableAddress span2) {
|
||||
// Rule 1: Avoid unusable destinations.
|
||||
if (span1.hasSrcAddr != span2.hasSrcAddr) {
|
||||
return span2.hasSrcAddr - span1.hasSrcAddr;
|
||||
}
|
||||
|
||||
// Rule 2: Prefer matching scope.
|
||||
if (span1.scopeMatch != span2.scopeMatch) {
|
||||
return span2.scopeMatch - span1.scopeMatch;
|
||||
}
|
||||
|
||||
// TODO: Implement rule 3: Avoid deprecated addresses.
|
||||
// TODO: Implement rule 4: Prefer home addresses.
|
||||
|
||||
// Rule 5: Prefer matching label.
|
||||
if (span1.labelMatch != span2.labelMatch) {
|
||||
return span2.labelMatch - span1.labelMatch;
|
||||
}
|
||||
|
||||
// Rule 6: Prefer higher precedence.
|
||||
if (span1.precedence != span2.precedence) {
|
||||
return span2.precedence - span1.precedence;
|
||||
}
|
||||
|
||||
// TODO: Implement rule 7: Prefer native transport.
|
||||
|
||||
// Rule 8: Prefer smaller scope.
|
||||
if (span1.scope != span2.scope) {
|
||||
return span1.scope - span2.scope;
|
||||
}
|
||||
|
||||
// Rule 9: Use longest matching prefix. IPv6 only.
|
||||
if (span1.prefixMatchLen != span2.prefixMatchLen) {
|
||||
return span2.prefixMatchLen - span1.prefixMatchLen;
|
||||
}
|
||||
|
||||
// Rule 10: Leave the order unchanged. Collections.sort is a stable sort.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used to sort with RFC 6724
|
||||
*/
|
||||
public static class SortableAddress {
|
||||
public final int label;
|
||||
public final int labelMatch;
|
||||
public final int scope;
|
||||
public final int scopeMatch;
|
||||
public final int precedence;
|
||||
public final int prefixMatchLen;
|
||||
public final int hasSrcAddr;
|
||||
public final InetAddress address;
|
||||
|
||||
public SortableAddress(@NonNull InetAddress addr, @Nullable InetAddress srcAddr) {
|
||||
address = addr;
|
||||
hasSrcAddr = (srcAddr != null) ? 1 : 0;
|
||||
label = findLabel(addr);
|
||||
scope = findScope(addr);
|
||||
precedence = findPrecedence(addr);
|
||||
labelMatch = ((srcAddr != null) && (label == findLabel(srcAddr))) ? 1 : 0;
|
||||
scopeMatch = ((srcAddr != null) && (scope == findScope(srcAddr))) ? 1 : 0;
|
||||
if (isIpv6Address(addr) && isIpv6Address(srcAddr)) {
|
||||
prefixMatchLen = compareIpv6PrefixMatchLen(srcAddr, addr);
|
||||
} else {
|
||||
prefixMatchLen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the given address list in RFC6724 order.
|
||||
* Will leave the list unchanged if an error occurs.
|
||||
*
|
||||
* This function matches the behaviour of _rfc6724_sort in the native resolver.
|
||||
*/
|
||||
public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network,
|
||||
@NonNull List<InetAddress> answers) {
|
||||
final ArrayList<SortableAddress> sortableAnswerList = new ArrayList<>();
|
||||
for (InetAddress addr : answers) {
|
||||
sortableAnswerList.add(new SortableAddress(addr, findSrcAddress(network, addr)));
|
||||
}
|
||||
|
||||
Collections.sort(sortableAnswerList, sRfc6724Comparator);
|
||||
|
||||
final List<InetAddress> sortedAnswers = new ArrayList<>();
|
||||
for (SortableAddress ans : sortableAnswerList) {
|
||||
sortedAnswers.add(ans.address);
|
||||
}
|
||||
|
||||
return sortedAnswers;
|
||||
}
|
||||
|
||||
private static @Nullable InetAddress findSrcAddress(@Nullable Network network,
|
||||
@NonNull InetAddress addr) {
|
||||
final int domain;
|
||||
if (isIpv4Address(addr)) {
|
||||
domain = AF_INET;
|
||||
} else if (isIpv6Address(addr)) {
|
||||
domain = AF_INET6;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
final FileDescriptor socket;
|
||||
try {
|
||||
socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "findSrcAddress:" + e.toString());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (network != null) network.bindSocket(socket);
|
||||
Os.connect(socket, new InetSocketAddress(addr, 0));
|
||||
return ((InetSocketAddress) Os.getsockname(socket)).getAddress();
|
||||
} catch (IOException | ErrnoException e) {
|
||||
return null;
|
||||
} finally {
|
||||
IoUtils.closeQuietly(socket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label for a given IPv4/IPv6 address.
|
||||
* RFC 6724, section 2.1.
|
||||
*
|
||||
* Note that Java will return an IPv4-mapped address as an IPv4 address.
|
||||
*/
|
||||
private static int findLabel(@NonNull InetAddress addr) {
|
||||
if (isIpv4Address(addr)) {
|
||||
return 4;
|
||||
} else if (isIpv6Address(addr)) {
|
||||
if (addr.isLoopbackAddress()) {
|
||||
return 0;
|
||||
} else if (isIpv6Address6To4(addr)) {
|
||||
return 2;
|
||||
} else if (isIpv6AddressTeredo(addr)) {
|
||||
return 5;
|
||||
} else if (isIpv6AddressULA(addr)) {
|
||||
return 13;
|
||||
} else if (((Inet6Address) addr).isIPv4CompatibleAddress()) {
|
||||
return 3;
|
||||
} else if (addr.isSiteLocalAddress()) {
|
||||
return 11;
|
||||
} else if (isIpv6Address6Bone(addr)) {
|
||||
return 12;
|
||||
} else {
|
||||
// All other IPv6 addresses, including global unicast addresses.
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// This should never happen.
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isIpv6Address(@Nullable InetAddress addr) {
|
||||
return addr instanceof Inet6Address;
|
||||
}
|
||||
|
||||
private static boolean isIpv4Address(@Nullable InetAddress addr) {
|
||||
return addr instanceof Inet4Address;
|
||||
}
|
||||
|
||||
private static boolean isIpv6Address6To4(@NonNull InetAddress addr) {
|
||||
if (!isIpv6Address(addr)) return false;
|
||||
final byte[] byteAddr = addr.getAddress();
|
||||
return byteAddr[0] == 0x20 && byteAddr[1] == 0x02;
|
||||
}
|
||||
|
||||
private static boolean isIpv6AddressTeredo(@NonNull InetAddress addr) {
|
||||
if (!isIpv6Address(addr)) return false;
|
||||
final byte[] byteAddr = addr.getAddress();
|
||||
return byteAddr[0] == 0x20 && byteAddr[1] == 0x01 && byteAddr[2] == 0x00
|
||||
&& byteAddr[3] == 0x00;
|
||||
}
|
||||
|
||||
private static boolean isIpv6AddressULA(@NonNull InetAddress addr) {
|
||||
return isIpv6Address(addr) && (addr.getAddress()[0] & 0xfe) == 0xfc;
|
||||
}
|
||||
|
||||
private static boolean isIpv6Address6Bone(@NonNull InetAddress addr) {
|
||||
if (!isIpv6Address(addr)) return false;
|
||||
final byte[] byteAddr = addr.getAddress();
|
||||
return byteAddr[0] == 0x3f && byteAddr[1] == (byte) 0xfe;
|
||||
}
|
||||
|
||||
private static int getIpv6MulticastScope(@NonNull InetAddress addr) {
|
||||
return !isIpv6Address(addr) ? 0 : (addr.getAddress()[1] & 0x0f);
|
||||
}
|
||||
|
||||
private static int findScope(@NonNull InetAddress addr) {
|
||||
if (isIpv6Address(addr)) {
|
||||
if (addr.isMulticastAddress()) {
|
||||
return getIpv6MulticastScope(addr);
|
||||
} else if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
|
||||
/**
|
||||
* RFC 4291 section 2.5.3 says loopback is to be treated as having
|
||||
* link-local scope.
|
||||
*/
|
||||
return IPV6_ADDR_SCOPE_LINKLOCAL;
|
||||
} else if (addr.isSiteLocalAddress()) {
|
||||
return IPV6_ADDR_SCOPE_SITELOCAL;
|
||||
} else {
|
||||
return IPV6_ADDR_SCOPE_GLOBAL;
|
||||
}
|
||||
} else if (isIpv4Address(addr)) {
|
||||
if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
|
||||
return IPV6_ADDR_SCOPE_LINKLOCAL;
|
||||
} else {
|
||||
/**
|
||||
* RFC 6724 section 3.2. Other IPv4 addresses, including private addresses
|
||||
* and shared addresses (100.64.0.0/10), are assigned global scope.
|
||||
*/
|
||||
return IPV6_ADDR_SCOPE_GLOBAL;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* This should never happen.
|
||||
* Return a scope with low priority as a last resort.
|
||||
*/
|
||||
return IPV6_ADDR_SCOPE_NODELOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precedence for a given IPv4/IPv6 address.
|
||||
* RFC 6724, section 2.1.
|
||||
*
|
||||
* Note that Java will return an IPv4-mapped address as an IPv4 address.
|
||||
*/
|
||||
private static int findPrecedence(@NonNull InetAddress addr) {
|
||||
if (isIpv4Address(addr)) {
|
||||
return 35;
|
||||
} else if (isIpv6Address(addr)) {
|
||||
if (addr.isLoopbackAddress()) {
|
||||
return 50;
|
||||
} else if (isIpv6Address6To4(addr)) {
|
||||
return 30;
|
||||
} else if (isIpv6AddressTeredo(addr)) {
|
||||
return 5;
|
||||
} else if (isIpv6AddressULA(addr)) {
|
||||
return 3;
|
||||
} else if (((Inet6Address) addr).isIPv4CompatibleAddress() || addr.isSiteLocalAddress()
|
||||
|| isIpv6Address6Bone(addr)) {
|
||||
return 1;
|
||||
} else {
|
||||
// All other IPv6 addresses, including global unicast addresses.
|
||||
return 40;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find number of matching initial bits between the two addresses.
|
||||
*/
|
||||
private static int compareIpv6PrefixMatchLen(@NonNull InetAddress srcAddr,
|
||||
@NonNull InetAddress dstAddr) {
|
||||
final byte[] srcByte = srcAddr.getAddress();
|
||||
final byte[] dstByte = dstAddr.getAddress();
|
||||
|
||||
// This should never happen.
|
||||
if (srcByte.length != dstByte.length) return 0;
|
||||
|
||||
for (int i = 0; i < dstByte.length; ++i) {
|
||||
if (srcByte[i] == dstByte[i]) {
|
||||
continue;
|
||||
}
|
||||
int x = BitUtils.uint8(srcByte[i]) ^ BitUtils.uint8(dstByte[i]);
|
||||
return i * CHAR_BIT + (Integer.numberOfLeadingZeros(x) - 24); // Java ints are 32 bits
|
||||
}
|
||||
return dstByte.length * CHAR_BIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given network has Ipv4 capability
|
||||
* This function matches the behaviour of have_ipv4 in the native resolver.
|
||||
*/
|
||||
public static boolean haveIpv4(@Nullable Network network) {
|
||||
final SocketAddress addrIpv4 =
|
||||
new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
|
||||
return checkConnectivity(network, AF_INET, addrIpv4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given network has Ipv6 capability
|
||||
* This function matches the behaviour of have_ipv6 in the native resolver.
|
||||
*/
|
||||
public static boolean haveIpv6(@Nullable Network network) {
|
||||
final SocketAddress addrIpv6 =
|
||||
new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
|
||||
return checkConnectivity(network, AF_INET6, addrIpv6);
|
||||
}
|
||||
|
||||
private static boolean checkConnectivity(@Nullable Network network,
|
||||
int domain, @NonNull SocketAddress addr) {
|
||||
final FileDescriptor socket;
|
||||
try {
|
||||
socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||
} catch (ErrnoException e) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (network != null) network.bindSocket(socket);
|
||||
Os.connect(socket, addr);
|
||||
} catch (IOException | ErrnoException e) {
|
||||
return false;
|
||||
} finally {
|
||||
IoUtils.closeQuietly(socket);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
115
framework/src/android/net/util/KeepaliveUtils.java
Normal file
115
framework/src/android/net/util/KeepaliveUtils.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AndroidRuntimeException;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
/**
|
||||
* Collection of utilities for socket keepalive offload.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class KeepaliveUtils {
|
||||
|
||||
public static final String TAG = "KeepaliveUtils";
|
||||
|
||||
public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException {
|
||||
public KeepaliveDeviceConfigurationException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read supported keepalive count for each transport type from overlay resource. This should be
|
||||
* used to create a local variable store of resource customization, and use it as the input for
|
||||
* {@link getSupportedKeepalivesForNetworkCapabilities}.
|
||||
*
|
||||
* @param context The context to read resource from.
|
||||
* @return An array of supported keepalive count for each transport type.
|
||||
*/
|
||||
@NonNull
|
||||
public static int[] getSupportedKeepalives(@NonNull Context context) {
|
||||
String[] res = null;
|
||||
try {
|
||||
res = context.getResources().getStringArray(
|
||||
R.array.config_networkSupportedKeepaliveCount);
|
||||
} catch (Resources.NotFoundException unused) {
|
||||
}
|
||||
if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
|
||||
|
||||
final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1];
|
||||
for (final String row : res) {
|
||||
if (TextUtils.isEmpty(row)) {
|
||||
throw new KeepaliveDeviceConfigurationException("Empty string");
|
||||
}
|
||||
final String[] arr = row.split(",");
|
||||
if (arr.length != 2) {
|
||||
throw new KeepaliveDeviceConfigurationException("Invalid parameter length");
|
||||
}
|
||||
|
||||
int transport;
|
||||
int supported;
|
||||
try {
|
||||
transport = Integer.parseInt(arr[0]);
|
||||
supported = Integer.parseInt(arr[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new KeepaliveDeviceConfigurationException("Invalid number format");
|
||||
}
|
||||
|
||||
if (!NetworkCapabilities.isValidTransport(transport)) {
|
||||
throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport);
|
||||
}
|
||||
|
||||
if (supported < 0) {
|
||||
throw new KeepaliveDeviceConfigurationException(
|
||||
"Invalid supported count " + supported + " for "
|
||||
+ NetworkCapabilities.transportNameOf(transport));
|
||||
}
|
||||
ret[transport] = supported;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported keepalive count for the given {@link NetworkCapabilities}.
|
||||
*
|
||||
* @param supportedKeepalives An array of supported keepalive count for each transport type.
|
||||
* @param nc The {@link NetworkCapabilities} of the network the socket keepalive is on.
|
||||
*
|
||||
* @return Supported keepalive count for the given {@link NetworkCapabilities}.
|
||||
*/
|
||||
public static int getSupportedKeepalivesForNetworkCapabilities(
|
||||
@NonNull int[] supportedKeepalives, @NonNull NetworkCapabilities nc) {
|
||||
final int[] transports = nc.getTransportTypes();
|
||||
if (transports.length == 0) return 0;
|
||||
int supportedCount = supportedKeepalives[transports[0]];
|
||||
// Iterate through transports and return minimum supported value.
|
||||
for (final int transport : transports) {
|
||||
if (supportedCount > supportedKeepalives[transport]) {
|
||||
supportedCount = supportedKeepalives[transport];
|
||||
}
|
||||
}
|
||||
return supportedCount;
|
||||
}
|
||||
}
|
||||
217
framework/src/android/net/util/MultinetworkPolicyTracker.java
Normal file
217
framework/src/android/net/util/MultinetworkPolicyTracker.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
|
||||
import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class to encapsulate management of the "Smart Networking" capability of
|
||||
* avoiding bad Wi-Fi when, for example upstream connectivity is lost or
|
||||
* certain critical link failures occur.
|
||||
*
|
||||
* This enables the device to switch to another form of connectivity, like
|
||||
* mobile, if it's available and working.
|
||||
*
|
||||
* The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied
|
||||
* Handler' whenever the computed "avoid bad wifi" value changes.
|
||||
*
|
||||
* Disabling this reverts the device to a level of networking sophistication
|
||||
* circa 2012-13 by disabling disparate code paths each of which contribute to
|
||||
* maintaining continuous, working Internet connectivity.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class MultinetworkPolicyTracker {
|
||||
private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private final Runnable mAvoidBadWifiCallback;
|
||||
private final List<Uri> mSettingsUris;
|
||||
private final ContentResolver mResolver;
|
||||
private final SettingObserver mSettingObserver;
|
||||
private final BroadcastReceiver mBroadcastReceiver;
|
||||
|
||||
private volatile boolean mAvoidBadWifi = true;
|
||||
private volatile int mMeteredMultipathPreference;
|
||||
private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
|
||||
public MultinetworkPolicyTracker(Context ctx, Handler handler) {
|
||||
this(ctx, handler, null);
|
||||
}
|
||||
|
||||
public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
|
||||
mContext = ctx;
|
||||
mHandler = handler;
|
||||
mAvoidBadWifiCallback = avoidBadWifiCallback;
|
||||
mSettingsUris = Arrays.asList(
|
||||
Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
|
||||
Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
|
||||
mResolver = mContext.getContentResolver();
|
||||
mSettingObserver = new SettingObserver();
|
||||
mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
reevaluateInternal();
|
||||
}
|
||||
};
|
||||
|
||||
ctx.getSystemService(TelephonyManager.class).listen(
|
||||
new PhoneStateListener(handler.getLooper()) {
|
||||
@Override
|
||||
public void onActiveDataSubscriptionIdChanged(int subId) {
|
||||
mActiveSubId = subId;
|
||||
reevaluateInternal();
|
||||
}
|
||||
}, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
|
||||
|
||||
updateAvoidBadWifi();
|
||||
updateMeteredMultipathPreference();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
for (Uri uri : mSettingsUris) {
|
||||
mResolver.registerContentObserver(uri, false, mSettingObserver);
|
||||
}
|
||||
|
||||
final IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||
mContext.registerReceiverForAllUsers(mBroadcastReceiver, intentFilter,
|
||||
null /* broadcastPermission */, mHandler);
|
||||
|
||||
reevaluate();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
mResolver.unregisterContentObserver(mSettingObserver);
|
||||
|
||||
mContext.unregisterReceiver(mBroadcastReceiver);
|
||||
}
|
||||
|
||||
public boolean getAvoidBadWifi() {
|
||||
return mAvoidBadWifi;
|
||||
}
|
||||
|
||||
// TODO: move this to MultipathPolicyTracker.
|
||||
public int getMeteredMultipathPreference() {
|
||||
return mMeteredMultipathPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device or carrier configuration disables avoiding bad wifi by default.
|
||||
*/
|
||||
public boolean configRestrictsAvoidBadWifi() {
|
||||
return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Resources getResourcesForActiveSubId() {
|
||||
return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we should display a notification when wifi becomes unvalidated.
|
||||
*/
|
||||
public boolean shouldNotifyWifiUnvalidated() {
|
||||
return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
|
||||
}
|
||||
|
||||
public String getAvoidBadWifiSetting() {
|
||||
return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void reevaluate() {
|
||||
mHandler.post(this::reevaluateInternal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reevaluate the settings. Must be called on the handler thread.
|
||||
*/
|
||||
private void reevaluateInternal() {
|
||||
if (updateAvoidBadWifi() && mAvoidBadWifiCallback != null) {
|
||||
mAvoidBadWifiCallback.run();
|
||||
}
|
||||
updateMeteredMultipathPreference();
|
||||
}
|
||||
|
||||
public boolean updateAvoidBadWifi() {
|
||||
final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
|
||||
final boolean prev = mAvoidBadWifi;
|
||||
mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
|
||||
return mAvoidBadWifi != prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default (device and carrier-dependent) value for metered multipath preference.
|
||||
*/
|
||||
public int configMeteredMultipathPreference() {
|
||||
return mContext.getResources().getInteger(
|
||||
R.integer.config_networkMeteredMultipathPreference);
|
||||
}
|
||||
|
||||
public void updateMeteredMultipathPreference() {
|
||||
String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
|
||||
try {
|
||||
mMeteredMultipathPreference = Integer.parseInt(setting);
|
||||
} catch (NumberFormatException e) {
|
||||
mMeteredMultipathPreference = configMeteredMultipathPreference();
|
||||
}
|
||||
}
|
||||
|
||||
private class SettingObserver extends ContentObserver {
|
||||
public SettingObserver() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
Log.wtf(TAG, "Should never be reached.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
if (!mSettingsUris.contains(uri)) {
|
||||
Log.wtf(TAG, "Unexpected settings observation: " + uri);
|
||||
}
|
||||
reevaluate();
|
||||
}
|
||||
}
|
||||
}
|
||||
121
framework/src/android/net/util/SocketUtils.java
Normal file
121
framework/src/android/net/util/SocketUtils.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 static android.system.OsConstants.SOL_SOCKET;
|
||||
import static android.system.OsConstants.SO_BINDTODEVICE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.net.NetworkUtils;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.NetlinkSocketAddress;
|
||||
import android.system.Os;
|
||||
import android.system.PacketSocketAddress;
|
||||
|
||||
import libcore.io.IoBridge;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* Collection of utilities to interact with raw sockets.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class SocketUtils {
|
||||
/**
|
||||
* Create a raw datagram socket that is bound to an interface.
|
||||
*
|
||||
* <p>Data sent through the socket will go directly to the underlying network, ignoring VPNs.
|
||||
*/
|
||||
public static void bindSocketToInterface(@NonNull FileDescriptor socket, @NonNull String iface)
|
||||
throws ErrnoException {
|
||||
// SO_BINDTODEVICE actually takes a string. This works because the first member
|
||||
// of struct ifreq is a NULL-terminated interface name.
|
||||
// TODO: add a setsockoptString()
|
||||
Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface);
|
||||
NetworkUtils.protectFromVpn(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a socket address to communicate with netlink.
|
||||
*/
|
||||
@NonNull
|
||||
public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) {
|
||||
return new NetlinkSocketAddress(portId, groupsMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make socket address that packet sockets can bind to.
|
||||
*
|
||||
* @param protocol the layer 2 protocol of the packets to receive. One of the {@code ETH_P_*}
|
||||
* constants in {@link android.system.OsConstants}.
|
||||
* @param ifIndex the interface index on which packets will be received.
|
||||
*/
|
||||
@NonNull
|
||||
public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) {
|
||||
return new PacketSocketAddress(
|
||||
protocol /* sll_protocol */,
|
||||
ifIndex /* sll_ifindex */,
|
||||
null /* sll_addr */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a socket address that packet socket can send packets to.
|
||||
* @deprecated Use {@link #makePacketSocketAddress(int, int, byte[])} instead.
|
||||
*
|
||||
* @param ifIndex the interface index on which packets will be sent.
|
||||
* @param hwAddr the hardware address to which packets will be sent.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) {
|
||||
return new PacketSocketAddress(
|
||||
0 /* sll_protocol */,
|
||||
ifIndex /* sll_ifindex */,
|
||||
hwAddr /* sll_addr */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a socket address that a packet socket can send packets to.
|
||||
*
|
||||
* @param protocol the layer 2 protocol of the packets to send. One of the {@code ETH_P_*}
|
||||
* constants in {@link android.system.OsConstants}.
|
||||
* @param ifIndex the interface index on which packets will be sent.
|
||||
* @param hwAddr the hardware address to which packets will be sent.
|
||||
*/
|
||||
@NonNull
|
||||
public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex,
|
||||
@NonNull byte[] hwAddr) {
|
||||
return new PacketSocketAddress(
|
||||
protocol /* sll_protocol */,
|
||||
ifIndex /* sll_ifindex */,
|
||||
hwAddr /* sll_addr */);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor)
|
||||
*/
|
||||
public static void closeSocket(@Nullable FileDescriptor fd) throws IOException {
|
||||
IoBridge.closeAndSignalBlockedThreads(fd);
|
||||
}
|
||||
|
||||
private SocketUtils() {}
|
||||
}
|
||||
Reference in New Issue
Block a user