DO NOT MERGE Network notifications: revamp keying scheme
am: 6cccb3bec3
Change-Id: Ibad7d0210b2c69826462b781f43a82d65849ebfb
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0afb546..2b5afa7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -87,6 +87,13 @@
* sent as an extra; it should be consulted to see what kind of
* connectivity event occurred.
* <p/>
+ * Apps targeting Android 7.0 (API level 24) and higher do not receive this
+ * broadcast if they declare the broadcast receiver in their manifest. Apps
+ * will still receive broadcasts if they register their
+ * {@link android.content.BroadcastReceiver} with
+ * {@link android.content.Context#registerReceiver Context.registerReceiver()}
+ * and that context is still valid.
+ * <p/>
* If this is a connection that was the result of failing over from a
* disconnected network, then the FAILOVER_CONNECTION boolean extra is
* set to true.
@@ -223,6 +230,13 @@
public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
/**
+ * Key for passing a user agent string to the captive portal login activity.
+ * {@hide}
+ */
+ public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT =
+ "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+
+ /**
* Broadcast action to indicate the change of data activity status
* (idle or active) on a network in a recent period.
* The network becomes active when data transmission is started, or
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index fe69320..0c0872a 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -34,9 +34,13 @@
import java.net.UnknownHostException;
import java.net.URL;
import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
import javax.net.SocketFactory;
import com.android.okhttp.ConnectionPool;
+import com.android.okhttp.Dns;
import com.android.okhttp.HttpHandler;
import com.android.okhttp.HttpsHandler;
import com.android.okhttp.OkHttpClient;
@@ -62,10 +66,10 @@
// Objects used to perform per-network operations such as getSocketFactory
// and openConnection, and a lock to protect access to them.
private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
- // mLock should be used to control write access to mConnectionPool and mNetwork.
+ // mLock should be used to control write access to mConnectionPool and mDns.
// maybeInitHttpClient() must be called prior to reading either variable.
private volatile ConnectionPool mConnectionPool = null;
- private volatile com.android.okhttp.internal.Network mNetwork = null;
+ private volatile Dns mDns = null;
private final Object mLock = new Object();
// Default connection pool values. These are evaluated at startup, just
@@ -219,17 +223,17 @@
// out) ConnectionPools.
private void maybeInitHttpClient() {
synchronized (mLock) {
- if (mNetwork == null) {
- mNetwork = new com.android.okhttp.internal.Network() {
+ if (mDns == null) {
+ mDns = new Dns() {
@Override
- public InetAddress[] resolveInetAddresses(String host) throws UnknownHostException {
- return Network.this.getAllByName(host);
+ public List<InetAddress> lookup(String hostname) throws UnknownHostException {
+ return Arrays.asList(Network.this.getAllByName(hostname));
}
};
}
if (mConnectionPool == null) {
mConnectionPool = new ConnectionPool(httpMaxConnections,
- httpKeepAliveDurationMs);
+ httpKeepAliveDurationMs, TimeUnit.MILLISECONDS);
}
}
}
@@ -288,9 +292,8 @@
}
OkHttpClient client = okUrlFactory.client();
client.setSocketFactory(getSocketFactory()).setConnectionPool(mConnectionPool);
-
- // Use internal APIs to change the Network.
- Internal.instance.setNetwork(client, mNetwork);
+ // Let network traffic go via mDns
+ client.setDns(mDns);
return okUrlFactory.open(url);
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 56eba4f..dacea55 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -418,8 +418,15 @@
*/
public static final int TRANSPORT_VPN = 4;
+ /**
+ * Indicates this network uses a Wi-Fi Aware transport.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+ public static final int TRANSPORT_WIFI_AWARE = 5;
+
private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
- private static final int MAX_TRANSPORT = TRANSPORT_VPN;
+ private static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
/**
* Adds the given transport type to this {@code NetworkCapability} instance.
@@ -889,6 +896,7 @@
case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
case TRANSPORT_VPN: transports += "VPN"; break;
+ case TRANSPORT_WIFI_AWARE: transports += "WIFI_AWARE"; break;
}
if (++i < types.length) transports += "|";
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 35e3065..a677d73 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -52,6 +52,17 @@
public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
/**
+ * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
+ *
+ * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
+ *
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param packetType the hardware address type, one of ARPHRD_*.
+ */
+ public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
+ throws SocketException;
+
+ /**
* Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
* @param fd the socket's {@link FileDescriptor}.
* @param ifIndex the interface index.
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 679e882..3e99521 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -25,11 +25,8 @@
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/filter.h>
-#include <linux/if.h>
#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/if_packet.h>
-#include <net/if_ether.h>
+#include <netinet/ether.h>
#include <netinet/icmp6.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
@@ -47,28 +44,33 @@
namespace android {
+static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
+static const uint32_t kEtherHeaderLen = sizeof(ether_header);
+static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
+static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
+static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
+static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
static const uint16_t kDhcpClientPort = 68;
static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
- uint32_t ip_offset = sizeof(ether_header);
- uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
- uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
- uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
struct sock_filter filter_code[] = {
// Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, proto_offset),
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6),
// Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, flags_offset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 4, 0),
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0),
// Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, ip_offset),
+ BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
// Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, dport_indirect_offset),
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1),
// Accept or reject.
@@ -96,17 +98,13 @@
return;
}
- uint32_t ipv6_offset = sizeof(ether_header);
- uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
- uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
- uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
struct sock_filter filter_code[] = {
// Check IPv6 Next Header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset),
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
// Check ICMPv6 type is Router Advertisement.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset),
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
// Accept or reject.
@@ -125,6 +123,81 @@
}
}
+// TODO: Move all this filter code into libnetutils.
+static void android_net_utils_attachControlPacketFilter(
+ JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
+ if (hardwareAddressType != ARPHRD_ETHER) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "attachControlPacketFilter only supports ARPHRD_ETHER");
+ return;
+ }
+
+ // Capture all:
+ // - ARPs
+ // - DHCPv4 packets
+ // - Router Advertisements & Solicitations
+ // - Neighbor Advertisements & Solicitations
+ //
+ // tcpdump:
+ // arp or
+ // '(ip and udp port 68)' or
+ // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
+ struct sock_filter filter_code[] = {
+ // Load the link layer next payload field.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset),
+
+ // Accept all ARP.
+ // TODO: Figure out how to better filter ARPs on noisy networks.
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
+
+ // If IPv4:
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
+
+ // Check the protocol is UDP.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14),
+
+ // Check this is not a fragment.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0),
+
+ // Get the IP header length.
+ BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
+
+ // Check the source port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0),
+
+ // Check the destination port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7),
+
+ // IPv6 ...
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
+ // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4),
+ // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
+ BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2),
+ BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ struct sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
jint ifIndex)
{
@@ -266,6 +339,7 @@
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
+ { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
};
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2693272..750b841 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -132,6 +132,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.KeepaliveTracker;
+import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.NetworkAgentInfo;
@@ -719,16 +720,6 @@
mHandler = new InternalHandler(mHandlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
- // setup our unique device name
- if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
- String id = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.ANDROID_ID);
- if (id != null && id.length() > 0) {
- String name = new String("android-").concat(id);
- SystemProperties.set("net.hostname", name);
- }
- }
-
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
@@ -815,7 +806,8 @@
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager);
+ mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager,
+ IoThread.get().getLooper(), new MockableSystemProperties());
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -4592,28 +4584,9 @@
} catch (Exception e) {
loge("Exception in setDnsConfigurationForNetwork: " + e);
}
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
- if (defaultNai != null && defaultNai.network.netId == netId) {
- setDefaultDnsSystemProperties(dnses);
- }
flushVmDnsCache();
}
- private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
- int last = 0;
- for (InetAddress dns : dnses) {
- ++last;
- String key = "net.dns" + last;
- String value = dns.getHostAddress();
- SystemProperties.set(key, value);
- }
- for (int i = last + 1; i <= mNumDnsEntries; ++i) {
- String key = "net.dns" + i;
- SystemProperties.set(key, "");
- }
- mNumDnsEntries = last;
- }
-
private String getNetworkPermission(NetworkCapabilities nc) {
// TODO: make these permission strings AIDL constants instead.
if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
@@ -4830,7 +4803,6 @@
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
updateTcpBufferSizes(newNetwork);
- setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 7cd1b7b..e084ff8 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -55,9 +55,9 @@
*/
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
- private static final boolean DBG = false;
- private static final boolean SYSTEM = true;
- private static final boolean NETWORK = false;
+ private static final boolean DBG = true;
+ private static final Boolean SYSTEM = Boolean.TRUE;
+ private static final Boolean NETWORK = Boolean.FALSE;
private final Context mContext;
private final PackageManager mPackageManager;
@@ -228,30 +228,40 @@
update(users, mApps, false);
}
+
+ private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+ if (currentPermission == SYSTEM) {
+ return currentPermission;
+ }
+ try {
+ final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
+ final boolean isNetwork = hasNetworkPermission(app);
+ final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
+ if (isNetwork || hasRestrictedPermission) {
+ currentPermission = hasRestrictedPermission;
+ }
+ } catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + name);
+ }
+ return currentPermission;
+ }
+
private synchronized void onAppAdded(String appName, int appUid) {
if (TextUtils.isEmpty(appName) || appUid < 0) {
loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
return;
}
- try {
- PackageInfo app = mPackageManager.getPackageInfo(appName, GET_PERMISSIONS);
- boolean isNetwork = hasNetworkPermission(app);
- boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
- if (isNetwork || hasRestrictedPermission) {
- Boolean permission = mApps.get(appUid);
- // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
- // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- if (permission == null || permission == NETWORK) {
- mApps.put(appUid, hasRestrictedPermission);
+ // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
+ // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
+ final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
+ if (permission != mApps.get(appUid)) {
+ mApps.put(appUid, permission);
- Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(appUid, hasRestrictedPermission);
- update(mUsers, apps, true);
- }
- }
- } catch (NameNotFoundException e) {
- loge("NameNotFoundException in onAppAdded: " + e);
+ Map<Integer, Boolean> apps = new HashMap<>();
+ apps.put(appUid, permission);
+ update(mUsers, apps, true);
}
}
@@ -260,11 +270,33 @@
loge("Invalid app in onAppRemoved: " + appUid);
return;
}
- mApps.remove(appUid);
-
Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
- update(mUsers, apps, false);
+
+ Boolean permission = null;
+ String[] packages = mPackageManager.getPackagesForUid(appUid);
+ if (packages != null && packages.length > 0) {
+ for (String name : packages) {
+ permission = highestPermissionForUid(permission, name);
+ if (permission == SYSTEM) {
+ // An app with this UID still has the SYSTEM permission.
+ // Therefore, this UID must already have the SYSTEM permission.
+ // Nothing to do.
+ return;
+ }
+ }
+ }
+ if (permission == mApps.get(appUid)) {
+ // The permissions of this UID have not changed. Nothing to do.
+ return;
+ } else if (permission != null) {
+ mApps.put(appUid, permission);
+ apps.put(appUid, permission);
+ update(mUsers, apps, true);
+ } else {
+ mApps.remove(appUid);
+ apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
+ update(mUsers, apps, false);
+ }
}
private static void log(String s) {