Merge "Add startActivityAsUser to SystemApi"
diff --git a/Android.bp b/Android.bp
index 580df85..a21cd28 100644
--- a/Android.bp
+++ b/Android.bp
@@ -824,20 +824,28 @@
name: "networkstack-aidl-interfaces",
local_include_dir: "core/java",
srcs: [
+ "core/java/android/net/ApfCapabilitiesParcelable.aidl",
+ "core/java/android/net/DhcpResultsParcelable.aidl",
"core/java/android/net/INetworkMonitor.aidl",
"core/java/android/net/INetworkMonitorCallbacks.aidl",
"core/java/android/net/IIpMemoryStore.aidl",
"core/java/android/net/INetworkStackConnector.aidl",
"core/java/android/net/INetworkStackStatusCallback.aidl",
+ "core/java/android/net/InitialConfigurationParcelable.aidl",
"core/java/android/net/IpPrefixParcelable.aidl",
"core/java/android/net/LinkAddressParcelable.aidl",
"core/java/android/net/LinkPropertiesParcelable.aidl",
+ "core/java/android/net/NetworkParcelable.aidl",
"core/java/android/net/PrivateDnsConfigParcel.aidl",
+ "core/java/android/net/ProvisioningConfigurationParcelable.aidl",
"core/java/android/net/ProxyInfoParcelable.aidl",
"core/java/android/net/RouteInfoParcelable.aidl",
+ "core/java/android/net/StaticIpConfigurationParcelable.aidl",
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
"core/java/android/net/dhcp/IDhcpServer.aidl",
"core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
+ "core/java/android/net/ip/IIpClient.aidl",
+ "core/java/android/net/ip/IIpClientCallbacks.aidl",
"core/java/android/net/ipmemorystore/**/*.aidl",
],
api_dir: "aidl/networkstack",
diff --git a/Android.mk b/Android.mk
index 9f7bf99..e405345 100644
--- a/Android.mk
+++ b/Android.mk
@@ -87,14 +87,11 @@
frameworks/base/config/hiddenapi-greylist-max-p.txt \
frameworks/base/config/hiddenapi-greylist-max-o.txt \
frameworks/base/config/hiddenapi-force-blacklist.txt \
- $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \
$(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
$(SOONG_HIDDENAPI_FLAGS)
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
- --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
- --csv $(PRIVATE_FLAGS_INPUTS) \
+ --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \
--greylist frameworks/base/config/hiddenapi-greylist.txt \
--greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
--greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
diff --git a/api/current.txt b/api/current.txt
index a7cc380..bd769e0 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -27553,6 +27553,7 @@
method public android.net.IpPrefix getDestination();
method public java.net.InetAddress getGateway();
method public String getInterface();
+ method public boolean hasGateway();
method public boolean isDefaultRoute();
method public boolean matches(java.net.InetAddress);
method public void writeToParcel(android.os.Parcel, int);
@@ -40455,6 +40456,8 @@
method public static String gaiName(int);
field public static final int AF_INET;
field public static final int AF_INET6;
+ field public static final int AF_NETLINK;
+ field public static final int AF_PACKET;
field public static final int AF_UNIX;
field public static final int AF_UNSPEC;
field public static final int AI_ADDRCONFIG;
@@ -40464,6 +40467,7 @@
field public static final int AI_NUMERICSERV;
field public static final int AI_PASSIVE;
field public static final int AI_V4MAPPED;
+ field public static final int ARPHRD_ETHER;
field public static final int CAP_AUDIT_CONTROL;
field public static final int CAP_AUDIT_WRITE;
field public static final int CAP_BLOCK_SUSPEND;
@@ -40587,6 +40591,10 @@
field public static final int ESPIPE;
field public static final int ESRCH;
field public static final int ESTALE;
+ field public static final int ETH_P_ALL;
+ field public static final int ETH_P_ARP;
+ field public static final int ETH_P_IP;
+ field public static final int ETH_P_IPV6;
field public static final int ETIME;
field public static final int ETIMEDOUT;
field public static final int ETXTBSY;
@@ -40684,6 +40692,8 @@
field public static final int MS_ASYNC;
field public static final int MS_INVALIDATE;
field public static final int MS_SYNC;
+ field public static final int NETLINK_INET_DIAG;
+ field public static final int NETLINK_ROUTE;
field public static final int NI_DGRAM;
field public static final int NI_NAMEREQD;
field public static final int NI_NOFQDN;
@@ -40720,6 +40730,7 @@
field public static final int PR_GET_DUMPABLE;
field public static final int PR_SET_DUMPABLE;
field public static final int PR_SET_NO_NEW_PRIVS;
+ field public static final int RTMGRP_NEIGH;
field public static final int RT_SCOPE_HOST;
field public static final int RT_SCOPE_LINK;
field public static final int RT_SCOPE_NOWHERE;
diff --git a/api/system-current.txt b/api/system-current.txt
index 958ebf5..fa1529c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3045,6 +3045,10 @@
public class LinkAddress implements android.os.Parcelable {
ctor public LinkAddress(java.net.InetAddress, int);
ctor public LinkAddress(String);
+ method public boolean isGlobalPreferred();
+ method public boolean isIPv4();
+ method public boolean isIPv6();
+ method public boolean isSameAddressAs(android.net.LinkAddress);
}
public final class LinkProperties implements android.os.Parcelable {
@@ -3059,6 +3063,10 @@
method public void setMtu(int);
}
+ public class Network implements android.os.Parcelable {
+ method public android.net.Network getPrivateDnsBypassingCopy();
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
method public int getSignalStrength();
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -3100,6 +3108,13 @@
field public static final String EXTRA_PACKAGE_NAME = "packageName";
}
+ public final class RouteInfo implements android.os.Parcelable {
+ method public int getType();
+ field public static final int RTN_THROW = 9; // 0x9
+ field public static final int RTN_UNICAST = 1; // 0x1
+ field public static final int RTN_UNREACHABLE = 7; // 0x7
+ }
+
public class RssiCurve implements android.os.Parcelable {
ctor public RssiCurve(int, int, byte[]);
ctor public RssiCurve(int, int, byte[], int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 5e7ce65..9574520 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -602,11 +602,29 @@
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
}
+ public class LinkAddress implements android.os.Parcelable {
+ method public boolean isGlobalPreferred();
+ method public boolean isIPv4();
+ method public boolean isIPv6();
+ method public boolean isSameAddressAs(android.net.LinkAddress);
+ }
+
+ public class Network implements android.os.Parcelable {
+ method public android.net.Network getPrivateDnsBypassingCopy();
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
method public int[] getCapabilities();
method public int[] getTransportTypes();
}
+ public final class RouteInfo implements android.os.Parcelable {
+ method public int getType();
+ field public static final int RTN_THROW = 9; // 0x9
+ field public static final int RTN_UNICAST = 1; // 0x1
+ field public static final int RTN_UNREACHABLE = 7; // 0x7
+ }
+
public class TrafficStats {
method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets();
diff --git a/core/java/android/net/ApfCapabilitiesParcelable.aidl b/core/java/android/net/ApfCapabilitiesParcelable.aidl
new file mode 100644
index 0000000..f0645d2
--- /dev/null
+++ b/core/java/android/net/ApfCapabilitiesParcelable.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+parcelable ApfCapabilitiesParcelable {
+ int apfVersionSupported;
+ int maximumApfProgramSize;
+ int apfPacketFormat;
+}
\ No newline at end of file
diff --git a/core/java/android/net/DhcpResultsParcelable.aidl b/core/java/android/net/DhcpResultsParcelable.aidl
new file mode 100644
index 0000000..cf5629b
--- /dev/null
+++ b/core/java/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,27 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.StaticIpConfigurationParcelable;
+
+parcelable DhcpResultsParcelable {
+ StaticIpConfigurationParcelable baseConfiguration;
+ int leaseDuration;
+ int mtu;
+ String serverAddress;
+ String vendorInfo;
+}
\ No newline at end of file
diff --git a/core/java/android/net/InitialConfigurationParcelable.aidl b/core/java/android/net/InitialConfigurationParcelable.aidl
new file mode 100644
index 0000000..bdda355
--- /dev/null
+++ b/core/java/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import android.net.IpPrefixParcelable;
+import android.net.LinkAddressParcelable;
+
+parcelable InitialConfigurationParcelable {
+ LinkAddressParcelable[] ipAddresses;
+ IpPrefixParcelable[] directlyConnectedRoutes;
+ String[] dnsServers;
+ String gateway;
+}
\ No newline at end of file
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index b40f15a..a536d08 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -26,6 +26,7 @@
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -117,7 +118,8 @@
* @return true if the address is IPv6.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean isIPv6() {
return address instanceof Inet6Address;
}
@@ -126,6 +128,8 @@
* @return true if the address is IPv4 or is a mapped IPv4 address.
* @hide
*/
+ @TestApi
+ @SystemApi
public boolean isIPv4() {
return address instanceof Inet4Address;
}
@@ -263,7 +267,8 @@
* otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean isSameAddressAs(LinkAddress other) {
return address.equals(other.address) && prefixLength == other.prefixLength;
}
@@ -310,6 +315,8 @@
* Returns true if this {@code LinkAddress} is global scope and preferred.
* @hide
*/
+ @TestApi
+ @SystemApi
public boolean isGlobalPreferred() {
/**
* Note that addresses flagged as IFA_F_OPTIMISTIC are
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 058cb94..c2b7d2c 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -52,6 +52,8 @@
/**
* The MacAddress zero MAC address.
+ *
+ * <p>Not publicly exposed or treated specially since the OUI 00:00:00 is registered.
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index bf2344d..2c831de 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -157,6 +159,8 @@
*
* @hide
*/
+ @TestApi
+ @SystemApi
public Network getPrivateDnsBypassingCopy() {
return new Network(netId, true);
}
diff --git a/core/java/android/net/NetworkParcelable.aidl b/core/java/android/net/NetworkParcelable.aidl
new file mode 100644
index 0000000..c26352e
--- /dev/null
+++ b/core/java/android/net/NetworkParcelable.aidl
@@ -0,0 +1,22 @@
+/*
+**
+** 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;
+
+parcelable NetworkParcelable {
+ long networkHandle;
+}
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 2eac6de..af043ee 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -50,6 +50,8 @@
public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
+ private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
@NonNull
@GuardedBy("mPendingNetStackRequests")
private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
@@ -57,6 +59,8 @@
@GuardedBy("mPendingNetStackRequests")
private INetworkStackConnector mConnector;
+ private volatile boolean mNetworkStackStartRequested = false;
+
private interface NetworkStackCallback {
void onNetworkStackConnected(INetworkStackConnector connector);
}
@@ -134,6 +138,7 @@
* started.
*/
public void start(Context context) {
+ mNetworkStackStartRequested = true;
// Try to bind in-process if the library is available
IBinder connector = null;
try {
@@ -170,15 +175,54 @@
}
}
- // TODO: use this method to obtain the connector when implementing network stack operations
+ /**
+ * For non-system server clients, get the connector registered by the system server.
+ */
+ private INetworkStackConnector getRemoteConnector() {
+ // Block until the NetworkStack connector is registered in ServiceManager.
+ // <p>This is only useful for non-system processes that do not have a way to be notified of
+ // registration completion. Adding a callback system would be too heavy weight considering
+ // that the connector is registered on boot, so it is unlikely that a client would request
+ // it before it is registered.
+ // TODO: consider blocking boot on registration and simplify much of the logic in this class
+ IBinder connector;
+ try {
+ final long before = System.currentTimeMillis();
+ while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+ Thread.sleep(20);
+ if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+ Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+ return null;
+ }
+ }
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+ return null;
+ }
+
+ return INetworkStackConnector.Stub.asInterface(connector);
+ }
+
private void requestConnector(@NonNull NetworkStackCallback request) {
// TODO: PID check.
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ final int caller = Binder.getCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
// Don't even attempt to obtain the connector and give a nice error message
throw new SecurityException(
"Only the system server should try to bind to the network stack.");
}
+ if (!mNetworkStackStartRequested) {
+ // The network stack is not being started in this process, e.g. this process is not
+ // the system server. Get a remote connector registered by the system server.
+ final INetworkStackConnector connector = getRemoteConnector();
+ synchronized (mPendingNetStackRequests) {
+ mConnector = connector;
+ }
+ request.onNetworkStackConnected(connector);
+ return;
+ }
+
final INetworkStackConnector connector;
synchronized (mPendingNetStackRequests) {
connector = mConnector;
diff --git a/core/java/android/net/ProvisioningConfigurationParcelable.aidl b/core/java/android/net/ProvisioningConfigurationParcelable.aidl
new file mode 100644
index 0000000..2a144f2
--- /dev/null
+++ b/core/java/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,38 @@
+/*
+**
+** 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;
+
+import android.net.ApfCapabilitiesParcelable;
+import android.net.InitialConfigurationParcelable;
+import android.net.NetworkParcelable;
+import android.net.StaticIpConfigurationParcelable;
+
+parcelable ProvisioningConfigurationParcelable {
+ boolean enableIPv4;
+ boolean enableIPv6;
+ boolean usingMultinetworkPolicyTracker;
+ boolean usingIpReachabilityMonitor;
+ int requestedPreDhcpActionMs;
+ InitialConfigurationParcelable initialConfig;
+ StaticIpConfigurationParcelable staticIpConfig;
+ ApfCapabilitiesParcelable apfCapabilities;
+ int provisioningTimeoutMs;
+ int ipv6AddrGenMode;
+ NetworkParcelable network;
+ String displayName;
+}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 37ab9ff..6bf2c67 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -16,16 +16,17 @@
package android.net;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import java.net.UnknownHostException;
-import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
-
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Objects;
@@ -67,12 +68,18 @@
/** Unicast route. @hide */
+ @SystemApi
+ @TestApi
public static final int RTN_UNICAST = 1;
/** Unreachable route. @hide */
+ @SystemApi
+ @TestApi
public static final int RTN_UNREACHABLE = 7;
/** Throw route. @hide */
+ @SystemApi
+ @TestApi
public static final int RTN_THROW = 9;
/**
@@ -317,6 +324,8 @@
*
* @hide
*/
+ @TestApi
+ @SystemApi
public int getType() {
return mType;
}
@@ -362,9 +371,7 @@
* ({@code false}).
*
* @return {@code true} if a gateway is specified
- * @hide
*/
- @UnsupportedAppUsage
public boolean hasGateway() {
return mHasGateway;
}
diff --git a/core/java/android/net/StaticIpConfigurationParcelable.aidl b/core/java/android/net/StaticIpConfigurationParcelable.aidl
new file mode 100644
index 0000000..45dc021
--- /dev/null
+++ b/core/java/android/net/StaticIpConfigurationParcelable.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** 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;
+
+import android.net.LinkAddressParcelable;
+
+parcelable StaticIpConfigurationParcelable {
+ LinkAddressParcelable ipAddress;
+ String gateway;
+ String[] dnsServers;
+ String domains;
+}
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
similarity index 78%
rename from services/net/java/android/net/apf/ApfCapabilities.java
rename to core/java/android/net/apf/ApfCapabilities.java
index dec8ca2..f28cdc9 100644
--- a/services/net/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -38,18 +38,28 @@
*/
public final int apfPacketFormat;
- public ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat)
- {
+ public ApfCapabilities(
+ int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) {
this.apfVersionSupported = apfVersionSupported;
this.maximumApfProgramSize = maximumApfProgramSize;
this.apfPacketFormat = apfPacketFormat;
}
+ @Override
public String toString() {
return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
}
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ApfCapabilities)) return false;
+ final ApfCapabilities other = (ApfCapabilities) obj;
+ return apfVersionSupported == other.apfVersionSupported
+ && maximumApfProgramSize == other.maximumApfProgramSize
+ && apfPacketFormat == other.apfPacketFormat;
+ }
+
/**
* Returns true if the APF interpreter advertises support for the data buffer access opcodes
* LDDW and STDW.
diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl
new file mode 100644
index 0000000..7769ec2
--- /dev/null
+++ b/core/java/android/net/ip/IIpClient.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package android.net.ip;
+
+import android.net.ProxyInfoParcelable;
+import android.net.ProvisioningConfigurationParcelable;
+
+/** @hide */
+oneway interface IIpClient {
+ void completedPreDhcpAction();
+ void confirmConfiguration();
+ void readPacketFilterComplete(in byte[] data);
+ void shutdown();
+ void startProvisioning(in ProvisioningConfigurationParcelable req);
+ void stop();
+ void setTcpBufferSizes(in String tcpBufferSizes);
+ void setHttpProxy(in ProxyInfoParcelable proxyInfo);
+ void setMulticastFilter(boolean enabled);
+}
diff --git a/core/java/android/net/ip/IIpClientCallbacks.aidl b/core/java/android/net/ip/IIpClientCallbacks.aidl
new file mode 100644
index 0000000..f077e3b
--- /dev/null
+++ b/core/java/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,66 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package android.net.ip;
+
+import android.net.LinkPropertiesParcelable;
+import android.net.ip.IIpClient;
+import android.net.DhcpResultsParcelable;
+
+/** @hide */
+oneway interface IIpClientCallbacks {
+ void onIpClientCreated(in IIpClient ipClient);
+
+ void onPreDhcpAction();
+ void onPostDhcpAction();
+
+ // This is purely advisory and not an indication of provisioning
+ // success or failure. This is only here for callers that want to
+ // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+ // DHCPv4 or static IPv4 configuration failure or success can be
+ // determined by whether or not the passed-in DhcpResults object is
+ // null or not.
+ void onNewDhcpResults(in DhcpResultsParcelable dhcpResults);
+
+ void onProvisioningSuccess(in LinkPropertiesParcelable newLp);
+ void onProvisioningFailure(in LinkPropertiesParcelable newLp);
+
+ // Invoked on LinkProperties changes.
+ void onLinkPropertiesChange(in LinkPropertiesParcelable newLp);
+
+ // Called when the internal IpReachabilityMonitor (if enabled) has
+ // detected the loss of a critical number of required neighbors.
+ void onReachabilityLost(in String logMsg);
+
+ // Called when the IpClient state machine terminates.
+ void onQuit();
+
+ // Install an APF program to filter incoming packets.
+ void installPacketFilter(in byte[] filter);
+
+ // Asynchronously read back the APF program & data buffer from the wifi driver.
+ // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+ // buffer. In response to this request, the driver returns the data buffer asynchronously
+ // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+ void startReadPacketFilter();
+
+ // If multicast filtering cannot be accomplished with APF, this function will be called to
+ // actuate multicast filtering using another means.
+ void setFallbackMulticastFilter(boolean enabled);
+
+ // Enabled/disable Neighbor Discover offload functionality. This is
+ // called, for example, whenever 464xlat is being started or stopped.
+ void setNeighborDiscoveryOffload(boolean enable);
+}
\ No newline at end of file
diff --git a/core/java/android/net/ip/IpClientCallbacks.java b/core/java/android/net/ip/IpClientCallbacks.java
new file mode 100644
index 0000000..db01ae4
--- /dev/null
+++ b/core/java/android/net/ip/IpClientCallbacks.java
@@ -0,0 +1,119 @@
+/*
+ * 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.ip;
+
+import android.net.DhcpResults;
+import android.net.LinkProperties;
+
+/**
+ * Callbacks for handling IpClient events.
+ *
+ * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks,
+ * and avoid unparceling arguments.
+ * These methods are called asynchronously on a Binder thread, as IpClient lives in a different
+ * process.
+ * @hide
+ */
+public class IpClientCallbacks {
+
+ /**
+ * Callback called upon IpClient creation.
+ *
+ * @param ipClient The Binder token to communicate with IpClient.
+ */
+ public void onIpClientCreated(IIpClient ipClient) {}
+
+ /**
+ * Callback called prior to DHCP discovery/renewal.
+ *
+ * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a
+ * ProvisioningConfiguration.
+ *
+ * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to
+ * indicate that DHCP is clear to proceed.
+ */
+ public void onPreDhcpAction() {}
+
+ /**
+ * Callback called after DHCP discovery/renewal.
+ */
+ public void onPostDhcpAction() {}
+
+ /**
+ * Callback called when new DHCP results are available.
+ *
+ * <p>This is purely advisory and not an indication of provisioning success or failure. This is
+ * only here for callers that want to expose DHCPv4 results to other APIs
+ * (e.g., WifiInfo#setInetAddress).
+ *
+ * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
+ * the passed-in DhcpResults object is null.
+ */
+ public void onNewDhcpResults(DhcpResults dhcpResults) {}
+
+ /**
+ * Indicates that provisioning was successful.
+ */
+ public void onProvisioningSuccess(LinkProperties newLp) {}
+
+ /**
+ * Indicates that provisioning failed.
+ */
+ public void onProvisioningFailure(LinkProperties newLp) {}
+
+ /**
+ * Invoked on LinkProperties changes.
+ */
+ public void onLinkPropertiesChange(LinkProperties newLp) {}
+
+ /**Called when the internal IpReachabilityMonitor (if enabled) has
+ * detected the loss of a critical number of required neighbors.
+ */
+ public void onReachabilityLost(String logMsg) {}
+
+ /**
+ * Called when the IpClient state machine terminates.
+ */
+ public void onQuit() {}
+
+ /**
+ * Called to indicate that a new APF program must be installed to filter incoming packets.
+ */
+ public void installPacketFilter(byte[] filter) {}
+
+ /**
+ * Called to indicate that the APF Program & data buffer must be read asynchronously from the
+ * wifi driver.
+ *
+ * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+ * buffer. In response to this request, the driver returns the data buffer asynchronously
+ * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+ */
+ public void startReadPacketFilter() {}
+
+ /**
+ * If multicast filtering cannot be accomplished with APF, this function will be called to
+ * actuate multicast filtering using another means.
+ */
+ public void setFallbackMulticastFilter(boolean enabled) {}
+
+ /**
+ * Enabled/disable Neighbor Discover offload functionality. This is called, for example,
+ * whenever 464xlat is being started or stopped.
+ */
+ public void setNeighborDiscoveryOffload(boolean enable) {}
+}
diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java
index 95e5042..cacd42d 100644
--- a/core/java/android/net/ipmemorystore/Status.java
+++ b/core/java/android/net/ipmemorystore/Status.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* A parcelable status representing the result of an operation.
* Parcels as StatusParceled.
@@ -26,7 +28,10 @@
public class Status {
public static final int SUCCESS = 0;
- public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -1;
+ public static final int ERROR_GENERIC = -1;
+ public static final int ERROR_ILLEGAL_ARGUMENT = -2;
+ public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -3;
+ public static final int ERROR_STORAGE = -4;
public final int resultCode;
@@ -34,7 +39,8 @@
this.resultCode = resultCode;
}
- Status(@NonNull final StatusParcelable parcelable) {
+ @VisibleForTesting
+ public Status(@NonNull final StatusParcelable parcelable) {
this(parcelable.resultCode);
}
@@ -55,7 +61,12 @@
public String toString() {
switch (resultCode) {
case SUCCESS: return "SUCCESS";
+ case ERROR_GENERIC: return "GENERIC ERROR";
+ case ERROR_ILLEGAL_ARGUMENT: return "ILLEGAL ARGUMENT";
case ERROR_DATABASE_CANNOT_BE_OPENED: return "DATABASE CANNOT BE OPENED";
+ // "DB storage error" is not very helpful but SQLite does not provide specific error
+ // codes upon store failure. Thus this indicates SQLite returned some error upon store
+ case ERROR_STORAGE: return "DATABASE STORAGE ERROR";
default: return "Unknown value ?!";
}
}
diff --git a/core/java/android/net/ipmemorystore/Utils.java b/core/java/android/net/ipmemorystore/Utils.java
index 73d8c83..b361aca 100644
--- a/core/java/android/net/ipmemorystore/Utils.java
+++ b/core/java/android/net/ipmemorystore/Utils.java
@@ -17,18 +17,25 @@
package android.net.ipmemorystore;
import android.annotation.NonNull;
+import android.annotation.Nullable;
/** {@hide} */
public class Utils {
/** Pretty print */
- public static String blobToString(final Blob blob) {
- final StringBuilder sb = new StringBuilder("Blob : [");
- if (blob.data.length <= 24) {
- appendByteArray(sb, blob.data, 0, blob.data.length);
+ public static String blobToString(@Nullable final Blob blob) {
+ return "Blob : " + byteArrayToString(null == blob ? null : blob.data);
+ }
+
+ /** Pretty print */
+ public static String byteArrayToString(@Nullable final byte[] data) {
+ if (null == data) return "null";
+ final StringBuilder sb = new StringBuilder("[");
+ if (data.length <= 24) {
+ appendByteArray(sb, data, 0, data.length);
} else {
- appendByteArray(sb, blob.data, 0, 16);
+ appendByteArray(sb, data, 0, 16);
sb.append("...");
- appendByteArray(sb, blob.data, blob.data.length - 8, blob.data.length);
+ appendByteArray(sb, data, data.length - 8, data.length);
}
sb.append("]");
return sb.toString();
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 31e799d..0985ac3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -378,8 +378,8 @@
Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
[IP config] Optional. If empty or not specified - DHCP will be used, otherwise
use the following format to specify static IP configuration:
- ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
- domains=<comma-sep-domains>
+ ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
+ domains=<comma-sep-domains>
-->
<string-array translatable="false" name="config_ethernet_interfaces">
<!--
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 057012d..cca71e7 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -20,6 +20,7 @@
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
+import static com.android.server.util.PermissionUtil.checkDumpPermission;
import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
import android.annotation.NonNull;
@@ -139,7 +140,7 @@
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
- checkNetworkStackCallingPermission();
+ checkDumpPermission();
final IndentingPrintWriter pw = new IndentingPrintWriter(fout, " ");
pw.println("NetworkStack logs:");
mLog.dump(fd, pw, args);
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
index 733f873..82bf038 100644
--- a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -31,8 +31,21 @@
*/
public static void checkNetworkStackCallingPermission() {
// TODO: check that the calling PID is the system server.
- if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
- throw new SecurityException("Invalid caller: " + getCallingUid());
+ final int caller = getCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
+ throw new SecurityException("Invalid caller: " + caller);
+ }
+ }
+
+ /**
+ * Check that the caller is allowed to dump the network stack, e.g. dumpsys.
+ * @throws SecurityException The caller is not allowed to dump the network stack.
+ */
+ public static void checkDumpPermission() {
+ final int caller = getCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID
+ && caller != Process.SHELL_UID) {
+ throw new SecurityException("No dump permissions for caller: " + caller);
}
}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 0f8fc17..8a3cdca 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -185,13 +185,13 @@
}
void start(int slot) {
+ mSlot = slot;
int error = isValid();
if (error == SUCCESS) {
- mSlot = slot;
Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket);
} else {
- notifyMessenger(NO_KEEPALIVE, error);
+ handleStopKeepalive(mNai, mSlot, error);
return;
}
}
@@ -277,6 +277,7 @@
return;
}
ki.stop(reason);
+ Log.d(TAG, "Stopped keepalive " + slot + " on " + networkName);
networkKeepalives.remove(slot);
if (networkKeepalives.isEmpty()) {
mKeepalives.remove(nai);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index b0adf95..2e7cbc6 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -955,12 +955,64 @@
@Override
public long getIfaceStats(String iface, int type) {
- return nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
+ long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
+ if (nativeIfaceStats == -1) {
+ return nativeIfaceStats;
+ } else {
+ // When tethering offload is in use, nativeIfaceStats does not contain usage from
+ // offload, add it back here.
+ // When tethering offload is not in use, nativeIfaceStats contains tethering usage.
+ // this does not cause double-counting of tethering traffic, because
+ // NetdTetheringStatsProvider returns zero NetworkStats
+ // when called with STATS_PER_IFACE.
+ return nativeIfaceStats + getTetherStats(iface, type);
+ }
}
@Override
public long getTotalStats(int type) {
- return nativeGetTotalStat(type, checkBpfStatsEnable());
+ long nativeTotalStats = nativeGetTotalStat(type, checkBpfStatsEnable());
+ if (nativeTotalStats == -1) {
+ return nativeTotalStats;
+ } else {
+ // Refer to comment in getIfaceStats
+ return nativeTotalStats + getTetherStats(IFACE_ALL, type);
+ }
+ }
+
+ private long getTetherStats(String iface, int type) {
+ final NetworkStats tetherSnapshot;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error get TetherStats: " + e);
+ return 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ HashSet<String> limitIfaces;
+ if (iface == IFACE_ALL) {
+ limitIfaces = null;
+ } else {
+ limitIfaces = new HashSet<String>();
+ limitIfaces.add(iface);
+ }
+ NetworkStats.Entry entry = tetherSnapshot.getTotal(null, limitIfaces);
+ if (LOGD) Slog.d(TAG, "TetherStats: iface=" + iface + " type=" + type +
+ " entry=" + entry);
+ switch (type) {
+ case 0: // TYPE_RX_BYTES
+ return entry.rxBytes;
+ case 1: // TYPE_RX_PACKETS
+ return entry.rxPackets;
+ case 2: // TYPE_TX_BYTES
+ return entry.txBytes;
+ case 3: // TYPE_TX_PACKETS
+ return entry.txPackets;
+ default:
+ return 0;
+ }
}
private boolean checkBpfStatsEnable() {
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
index eaab650..238f077 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -17,9 +17,24 @@
package com.android.server.net.ipmemorystore;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
import android.content.Context;
+import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
+import android.net.NetworkUtils;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.Status;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
/**
* Encapsulating class for using the SQLite database backing the memory store.
@@ -30,6 +45,8 @@
* @hide
*/
public class IpMemoryStoreDatabase {
+ private static final String TAG = IpMemoryStoreDatabase.class.getSimpleName();
+
/**
* Contract class for the Network Attributes table.
*/
@@ -57,7 +74,7 @@
public static final String COLTYPE_DNSADDRESSES = "BLOB";
public static final String COLNAME_MTU = "mtu";
- public static final String COLTYPE_MTU = "INTEGER";
+ public static final String COLTYPE_MTU = "INTEGER DEFAULT -1";
public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
+ TABLENAME + " ("
@@ -108,7 +125,7 @@
/** The SQLite DB helper */
public static class DbHelper extends SQLiteOpenHelper {
// Update this whenever changing the schema.
- private static final int SCHEMA_VERSION = 1;
+ private static final int SCHEMA_VERSION = 2;
private static final String DATABASE_FILENAME = "IpMemoryStore.db";
public DbHelper(@NonNull final Context context) {
@@ -140,4 +157,216 @@
onCreate(db);
}
}
+
+ @NonNull
+ private static byte[] encodeAddressList(@NonNull final List<InetAddress> addresses) {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for (final InetAddress address : addresses) {
+ final byte[] b = address.getAddress();
+ os.write(b.length);
+ os.write(b, 0, b.length);
+ }
+ return os.toByteArray();
+ }
+
+ @NonNull
+ private static ArrayList<InetAddress> decodeAddressList(@NonNull final byte[] encoded) {
+ final ByteArrayInputStream is = new ByteArrayInputStream(encoded);
+ final ArrayList<InetAddress> addresses = new ArrayList<>();
+ int d = -1;
+ while ((d = is.read()) != -1) {
+ final byte[] bytes = new byte[d];
+ is.read(bytes, 0, d);
+ try {
+ addresses.add(InetAddress.getByAddress(bytes));
+ } catch (UnknownHostException e) { /* Hopefully impossible */ }
+ }
+ return addresses;
+ }
+
+ // Convert a NetworkAttributes object to content values to store them in a table compliant
+ // with the contract defined in NetworkAttributesContract.
+ @NonNull
+ private static ContentValues toContentValues(@NonNull final String key,
+ @Nullable final NetworkAttributes attributes, final long expiry) {
+ final ContentValues values = new ContentValues();
+ values.put(NetworkAttributesContract.COLNAME_L2KEY, key);
+ values.put(NetworkAttributesContract.COLNAME_EXPIRYDATE, expiry);
+ if (null != attributes) {
+ if (null != attributes.assignedV4Address) {
+ values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
+ NetworkUtils.inet4AddressToIntHTH(attributes.assignedV4Address));
+ }
+ if (null != attributes.groupHint) {
+ values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
+ }
+ if (null != attributes.dnsAddresses) {
+ values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES,
+ encodeAddressList(attributes.dnsAddresses));
+ }
+ if (null != attributes.mtu) {
+ values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu);
+ }
+ }
+ return values;
+ }
+
+ // Convert a byte array into content values to store it in a table compliant with the
+ // contract defined in PrivateDataContract.
+ @NonNull
+ private static ContentValues toContentValues(@NonNull final String key,
+ @NonNull final String clientId, @NonNull final String name,
+ @NonNull final byte[] data) {
+ final ContentValues values = new ContentValues();
+ values.put(PrivateDataContract.COLNAME_L2KEY, key);
+ values.put(PrivateDataContract.COLNAME_CLIENT, clientId);
+ values.put(PrivateDataContract.COLNAME_DATANAME, name);
+ values.put(PrivateDataContract.COLNAME_DATA, data);
+ return values;
+ }
+
+ private static final String[] EXPIRY_COLUMN = new String[] {
+ NetworkAttributesContract.COLNAME_EXPIRYDATE
+ };
+ static final int EXPIRY_ERROR = -1; // Legal values for expiry are positive
+
+ static final String SELECT_L2KEY = NetworkAttributesContract.COLNAME_L2KEY + " = ?";
+
+ // Returns the expiry date of the specified row, or one of the error codes above if the
+ // row is not found or some other error
+ static long getExpiry(@NonNull final SQLiteDatabase db, @NonNull final String key) {
+ final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
+ EXPIRY_COLUMN, // columns
+ SELECT_L2KEY, // selection
+ new String[] { key }, // selectionArgs
+ null, // groupBy
+ null, // having
+ null // orderBy
+ );
+ // L2KEY is the primary key ; it should not be possible to get more than one
+ // result here. 0 results means the key was not found.
+ if (cursor.getCount() != 1) return EXPIRY_ERROR;
+ cursor.moveToFirst();
+ return cursor.getLong(0); // index in the EXPIRY_COLUMN array
+ }
+
+ static final int RELEVANCE_ERROR = -1; // Legal values for relevance are positive
+
+ // Returns the relevance of the specified row, or one of the error codes above if the
+ // row is not found or some other error
+ static int getRelevance(@NonNull final SQLiteDatabase db, @NonNull final String key) {
+ final long expiry = getExpiry(db, key);
+ return expiry < 0 ? (int) expiry : RelevanceUtils.computeRelevanceForNow(expiry);
+ }
+
+ // If the attributes are null, this will only write the expiry.
+ // Returns an int out of Status.{SUCCESS,ERROR_*}
+ static int storeNetworkAttributes(@NonNull final SQLiteDatabase db, @NonNull final String key,
+ final long expiry, @Nullable final NetworkAttributes attributes) {
+ final ContentValues cv = toContentValues(key, attributes, expiry);
+ db.beginTransaction();
+ try {
+ // Unfortunately SQLite does not have any way to do INSERT OR UPDATE. Options are
+ // to either insert with on conflict ignore then update (like done here), or to
+ // construct a custom SQL INSERT statement with nested select.
+ final long resultId = db.insertWithOnConflict(NetworkAttributesContract.TABLENAME,
+ null, cv, SQLiteDatabase.CONFLICT_IGNORE);
+ if (resultId < 0) {
+ db.update(NetworkAttributesContract.TABLENAME, cv, SELECT_L2KEY, new String[]{key});
+ }
+ db.setTransactionSuccessful();
+ return Status.SUCCESS;
+ } catch (SQLiteException e) {
+ // No space left on disk or something
+ Log.e(TAG, "Could not write to the memory store", e);
+ } finally {
+ db.endTransaction();
+ }
+ return Status.ERROR_STORAGE;
+ }
+
+ // Returns an int out of Status.{SUCCESS,ERROR_*}
+ static int storeBlob(@NonNull final SQLiteDatabase db, @NonNull final String key,
+ @NonNull final String clientId, @NonNull final String name,
+ @NonNull final byte[] data) {
+ final long res = db.insertWithOnConflict(PrivateDataContract.TABLENAME, null,
+ toContentValues(key, clientId, name, data), SQLiteDatabase.CONFLICT_REPLACE);
+ return (res == -1) ? Status.ERROR_STORAGE : Status.SUCCESS;
+ }
+
+ @Nullable
+ static NetworkAttributes retrieveNetworkAttributes(@NonNull final SQLiteDatabase db,
+ @NonNull final String key) {
+ final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
+ null, // columns, null means everything
+ NetworkAttributesContract.COLNAME_L2KEY + " = ?", // selection
+ new String[] { key }, // selectionArgs
+ null, // groupBy
+ null, // having
+ null); // orderBy
+ // L2KEY is the primary key ; it should not be possible to get more than one
+ // result here. 0 results means the key was not found.
+ if (cursor.getCount() != 1) return null;
+ cursor.moveToFirst();
+
+ // Make sure the data hasn't expired
+ final long expiry = cursor.getLong(
+ cursor.getColumnIndexOrThrow(NetworkAttributesContract.COLNAME_EXPIRYDATE));
+ if (expiry < System.currentTimeMillis()) return null;
+
+ final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+ final int assignedV4AddressInt = getInt(cursor,
+ NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
+ final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
+ final byte[] dnsAddressesBlob =
+ getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
+ final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
+ if (0 != assignedV4AddressInt) {
+ builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
+ }
+ builder.setGroupHint(groupHint);
+ if (null != dnsAddressesBlob) {
+ builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
+ }
+ if (mtu >= 0) {
+ builder.setMtu(mtu);
+ }
+ return builder.build();
+ }
+
+ private static final String[] DATA_COLUMN = new String[] {
+ PrivateDataContract.COLNAME_DATA
+ };
+ @Nullable
+ static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key,
+ @NonNull final String clientId, @NonNull final String name) {
+ final Cursor cursor = db.query(PrivateDataContract.TABLENAME,
+ DATA_COLUMN, // columns
+ PrivateDataContract.COLNAME_L2KEY + " = ? AND " // selection
+ + PrivateDataContract.COLNAME_CLIENT + " = ? AND "
+ + PrivateDataContract.COLNAME_DATANAME + " = ?",
+ new String[] { key, clientId, name }, // selectionArgs
+ null, // groupBy
+ null, // having
+ null); // orderBy
+ // The query above is querying by (composite) primary key, so it should not be possible to
+ // get more than one result here. 0 results means the key was not found.
+ if (cursor.getCount() != 1) return null;
+ cursor.moveToFirst();
+ return cursor.getBlob(0); // index in the DATA_COLUMN array
+ }
+
+ // Helper methods
+ static String getString(final Cursor cursor, final String columnName) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getString(columnIndex) : null;
+ }
+ static byte[] getBlob(final Cursor cursor, final String columnName) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getBlob(columnIndex) : null;
+ }
+ static int getInt(final Cursor cursor, final String columnName, final int defaultValue) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getInt(columnIndex) : defaultValue;
+ }
}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
index 55a72190..444b299 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -16,6 +16,13 @@
package com.android.server.net.ipmemorystore;
+import static android.net.ipmemorystore.Status.ERROR_DATABASE_CANNOT_BE_OPENED;
+import static android.net.ipmemorystore.Status.ERROR_GENERIC;
+import static android.net.ipmemorystore.Status.ERROR_ILLEGAL_ARGUMENT;
+import static android.net.ipmemorystore.Status.SUCCESS;
+
+import static com.android.server.net.ipmemorystore.IpMemoryStoreDatabase.EXPIRY_ERROR;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -28,7 +35,12 @@
import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
import android.net.ipmemorystore.IOnSameNetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.Status;
+import android.net.ipmemorystore.StatusParcelable;
+import android.net.ipmemorystore.Utils;
+import android.os.RemoteException;
import android.util.Log;
import java.util.concurrent.ExecutorService;
@@ -45,6 +57,7 @@
public class IpMemoryStoreService extends IIpMemoryStore.Stub {
private static final String TAG = IpMemoryStoreService.class.getSimpleName();
private static final int MAX_CONCURRENT_THREADS = 4;
+ private static final boolean DBG = true;
@NonNull
final Context mContext;
@@ -114,6 +127,11 @@
if (mDb != null) mDb.close();
}
+ /** Helper function to make a status object */
+ private StatusParcelable makeStatus(final int code) {
+ return new Status(code).toParcelable();
+ }
+
/**
* Store network attributes for a given L2 key.
*
@@ -128,11 +146,27 @@
* Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
* If the call failed, the L2 key will be null.
*/
+ // Note that while l2Key and attributes are non-null in spirit, they are received from
+ // another process. If the remote process decides to ignore everything and send null, this
+ // process should still not crash.
@Override
- public void storeNetworkAttributes(@NonNull final String l2Key,
- @NonNull final NetworkAttributesParcelable attributes,
+ public void storeNetworkAttributes(@Nullable final String l2Key,
+ @Nullable final NetworkAttributesParcelable attributes,
@Nullable final IOnStatusListener listener) {
- // TODO : implement this.
+ // Because the parcelable is 100% mutable, the thread may not see its members initialized.
+ // Therefore either an immutable object is created on this same thread before it's passed
+ // to the executor, or there need to be a write barrier here and a read barrier in the
+ // remote thread.
+ final NetworkAttributes na = null == attributes ? null : new NetworkAttributes(attributes);
+ mExecutor.execute(() -> {
+ try {
+ final int code = storeNetworkAttributesAndBlobSync(l2Key, na,
+ null /* clientId */, null /* name */, null /* data */);
+ if (null != listener) listener.onComplete(makeStatus(code));
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
/**
@@ -141,16 +175,63 @@
* @param l2Key The L2 key for this network.
* @param clientId The ID of the client.
* @param name The name of this data.
- * @param data The data to store.
+ * @param blob The data to store.
* @param listener The listener that will be invoked to return the answer, or null if the
* is not interested in learning about success/failure.
* Through the listener, returns a status to indicate success or failure.
*/
@Override
- public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
- @NonNull final String name, @NonNull final Blob data,
+ public void storeBlob(@Nullable final String l2Key, @Nullable final String clientId,
+ @Nullable final String name, @Nullable final Blob blob,
@Nullable final IOnStatusListener listener) {
- // TODO : implement this.
+ final byte[] data = null == blob ? null : blob.data;
+ mExecutor.execute(() -> {
+ try {
+ final int code = storeNetworkAttributesAndBlobSync(l2Key,
+ null /* NetworkAttributes */, clientId, name, data);
+ if (null != listener) listener.onComplete(makeStatus(code));
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
+ }
+
+ /**
+ * Helper method for storeNetworkAttributes and storeBlob.
+ *
+ * Either attributes or none of clientId, name and data may be null. This will write the
+ * passed data if non-null, and will write attributes if non-null, but in any case it will
+ * bump the relevance up.
+ * Returns a success code from Status.
+ */
+ private int storeNetworkAttributesAndBlobSync(@Nullable final String l2Key,
+ @Nullable final NetworkAttributes attributes,
+ @Nullable final String clientId,
+ @Nullable final String name, @Nullable final byte[] data) {
+ if (null == l2Key) return ERROR_ILLEGAL_ARGUMENT;
+ if (null == attributes && null == data) return ERROR_ILLEGAL_ARGUMENT;
+ if (null != data && (null == clientId || null == name)) return ERROR_ILLEGAL_ARGUMENT;
+ if (null == mDb) return ERROR_DATABASE_CANNOT_BE_OPENED;
+ try {
+ final long oldExpiry = IpMemoryStoreDatabase.getExpiry(mDb, l2Key);
+ final long newExpiry = RelevanceUtils.bumpExpiryDate(
+ oldExpiry == EXPIRY_ERROR ? System.currentTimeMillis() : oldExpiry);
+ final int errorCode =
+ IpMemoryStoreDatabase.storeNetworkAttributes(mDb, l2Key, newExpiry, attributes);
+ // If no blob to store, the client is interested in the result of storing the attributes
+ if (null == data) return errorCode;
+ // Otherwise it's interested in the result of storing the blob
+ return IpMemoryStoreDatabase.storeBlob(mDb, l2Key, clientId, name, data);
+ } catch (Exception e) {
+ if (DBG) {
+ Log.e(TAG, "Exception while storing for key {" + l2Key
+ + "} ; NetworkAttributes {" + (null == attributes ? "null" : attributes)
+ + "} ; clientId {" + (null == clientId ? "null" : clientId)
+ + "} ; name {" + (null == name ? "null" : name)
+ + "} ; data {" + Utils.byteArrayToString(data) + "}", e);
+ }
+ }
+ return ERROR_GENERIC;
}
/**
@@ -198,9 +279,32 @@
* the query.
*/
@Override
- public void retrieveNetworkAttributes(@NonNull final String l2Key,
- @NonNull final IOnNetworkAttributesRetrieved listener) {
- // TODO : implement this.
+ public void retrieveNetworkAttributes(@Nullable final String l2Key,
+ @Nullable final IOnNetworkAttributesRetrieved listener) {
+ if (null == listener) return;
+ mExecutor.execute(() -> {
+ try {
+ if (null == l2Key) {
+ listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, null);
+ return;
+ }
+ if (null == mDb) {
+ listener.onL2KeyResponse(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key,
+ null);
+ return;
+ }
+ try {
+ final NetworkAttributes attributes =
+ IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key);
+ listener.onL2KeyResponse(makeStatus(SUCCESS), l2Key,
+ null == attributes ? null : attributes.toParcelable());
+ } catch (final Exception e) {
+ listener.onL2KeyResponse(makeStatus(ERROR_GENERIC), l2Key, null);
+ }
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
/**
@@ -217,6 +321,28 @@
@Override
public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
@NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
- // TODO : implement this.
+ if (null == listener) return;
+ mExecutor.execute(() -> {
+ try {
+ if (null == l2Key) {
+ listener.onBlobRetrieved(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, name, null);
+ return;
+ }
+ if (null == mDb) {
+ listener.onBlobRetrieved(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key,
+ name, null);
+ return;
+ }
+ try {
+ final Blob b = new Blob();
+ b.data = IpMemoryStoreDatabase.retrieveBlob(mDb, l2Key, clientId, name);
+ listener.onBlobRetrieved(makeStatus(SUCCESS), l2Key, name, b);
+ } catch (final Exception e) {
+ listener.onBlobRetrieved(makeStatus(ERROR_GENERIC), l2Key, name, null);
+ }
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index f037905..d8e2869 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -16,8 +16,19 @@
package android.net.apf;
-import static android.net.util.NetworkConstants.*;
-import static android.system.OsConstants.*;
+import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ARPHRD_ETHER;
+import static android.system.OsConstants.ETH_P_ARP;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.ETH_P_IPV6;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_RAW;
+
import static com.android.internal.util.BitUtils.bytesToBEInt;
import static com.android.internal.util.BitUtils.getUint16;
import static com.android.internal.util.BitUtils.getUint32;
@@ -34,7 +45,7 @@
import android.net.NetworkUtils;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClient;
+import android.net.ip.IpClientCallbacks;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.IpConnectivityLog;
@@ -48,10 +59,14 @@
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
+
+import libcore.io.IoBridge;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet4Address;
@@ -63,7 +78,6 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
-import libcore.io.IoBridge;
/**
* For networks that support packet filtering via APF programs, {@code ApfFilter}
@@ -308,7 +322,7 @@
private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
private final ApfCapabilities mApfCapabilities;
- private final IpClient.Callback mIpClientCallback;
+ private final IpClientCallbacks mIpClientCallback;
private final InterfaceParams mInterfaceParams;
private final IpConnectivityLog mMetricsLog;
@@ -349,7 +363,7 @@
@VisibleForTesting
ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
- IpClient.Callback ipClientCallback, IpConnectivityLog log) {
+ IpClientCallbacks ipClientCallback, IpConnectivityLog log) {
mApfCapabilities = config.apfCapabilities;
mIpClientCallback = ipClientCallback;
mInterfaceParams = ifParams;
@@ -1390,7 +1404,7 @@
* filtering using APF programs.
*/
public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
- InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
+ InterfaceParams ifParams, IpClientCallbacks ipClientCallback) {
if (context == null || config == null || ifParams == null) return null;
ApfCapabilities apfCapabilities = config.apfCapabilities;
if (apfCapabilities == null) return null;
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 0176dd4..ff4e280 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -16,19 +16,19 @@
package android.net.ip;
-import com.android.internal.util.HexDump;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.WakeupMessage;
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
import android.content.Context;
import android.net.DhcpResults;
import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
-import android.net.LinkProperties.ProvisioningChange;
import android.net.LinkProperties;
+import android.net.LinkProperties.ProvisioningChange;
import android.net.Network;
+import android.net.ProvisioningConfigurationParcelable;
import android.net.ProxyInfo;
+import android.net.ProxyInfoParcelable;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.apf.ApfCapabilities;
@@ -36,10 +36,10 @@
import android.net.dhcp.DhcpClient;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
+import android.net.shared.InitialConfiguration;
import android.net.util.InterfaceParams;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
-import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
@@ -52,29 +52,24 @@
import android.util.Log;
import android.util.SparseArray;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IState;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
import com.android.server.net.NetlinkTracker;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.SocketException;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Objects;
import java.util.List;
-import java.util.Set;
-import java.util.StringJoiner;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
@@ -134,83 +129,17 @@
}
/**
- * Callbacks for handling IpClient events.
- *
- * These methods are called by IpClient on its own thread. Implementations
- * of this class MUST NOT carry out long-running computations or hold locks
- * for which there might be contention with other code calling public
- * methods of the same IpClient instance.
+ * TODO: remove after migrating clients to use IpClientCallbacks directly
+ * @see IpClientCallbacks
*/
- public static class Callback {
- // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
- // when constructing a ProvisioningConfiguration.
- //
- // Implementations of onPreDhcpAction() must call
- // IpClient#completedPreDhcpAction() to indicate that DHCP is clear
- // to proceed.
- public void onPreDhcpAction() {}
- public void onPostDhcpAction() {}
+ public static class Callback extends IpClientCallbacks {}
- // This is purely advisory and not an indication of provisioning
- // success or failure. This is only here for callers that want to
- // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
- // DHCPv4 or static IPv4 configuration failure or success can be
- // determined by whether or not the passed-in DhcpResults object is
- // null or not.
- public void onNewDhcpResults(DhcpResults dhcpResults) {}
-
- public void onProvisioningSuccess(LinkProperties newLp) {}
- public void onProvisioningFailure(LinkProperties newLp) {}
-
- // Invoked on LinkProperties changes.
- public void onLinkPropertiesChange(LinkProperties newLp) {}
-
- // Called when the internal IpReachabilityMonitor (if enabled) has
- // detected the loss of a critical number of required neighbors.
- public void onReachabilityLost(String logMsg) {}
-
- // Called when the IpClient state machine terminates.
- public void onQuit() {}
-
- // Install an APF program to filter incoming packets.
- public void installPacketFilter(byte[] filter) {}
-
- // Asynchronously read back the APF program & data buffer from the wifi driver.
- // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
- // buffer. In response to this request, the driver returns the data buffer asynchronously
- // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
- public void startReadPacketFilter() {}
-
- // If multicast filtering cannot be accomplished with APF, this function will be called to
- // actuate multicast filtering using another means.
- public void setFallbackMulticastFilter(boolean enabled) {}
-
- // Enabled/disable Neighbor Discover offload functionality. This is
- // called, for example, whenever 464xlat is being started or stopped.
- public void setNeighborDiscoveryOffload(boolean enable) {}
- }
-
- public static class WaitForProvisioningCallback extends Callback {
- private final ConditionVariable mCV = new ConditionVariable();
- private LinkProperties mCallbackLinkProperties;
-
- public LinkProperties waitForProvisioning() {
- mCV.block();
- return mCallbackLinkProperties;
- }
-
- @Override
- public void onProvisioningSuccess(LinkProperties newLp) {
- mCallbackLinkProperties = newLp;
- mCV.open();
- }
-
- @Override
- public void onProvisioningFailure(LinkProperties newLp) {
- mCallbackLinkProperties = null;
- mCV.open();
- }
- }
+ /**
+ * TODO: remove once clients are migrated to IpClientUtil.WaitForProvisioningCallback
+ * @see IpClientUtil.WaitForProvisioningCallbacks
+ */
+ public static class WaitForProvisioningCallback
+ extends IpClientUtil.WaitForProvisioningCallbacks {}
// Use a wrapper class to log in order to ensure complete and detailed
// logging. This method is lighter weight than annotations/reflection
@@ -232,12 +161,12 @@
// once passed on to the callback they may be modified by another thread.
//
// TODO: Find an lighter weight approach.
- private class LoggingCallbackWrapper extends Callback {
+ private class LoggingCallbackWrapper extends IpClientCallbacks {
private static final String PREFIX = "INVOKE ";
- private final Callback mCallback;
+ private final IpClientCallbacks mCallback;
- public LoggingCallbackWrapper(Callback callback) {
- mCallback = (callback != null) ? callback : new Callback();
+ LoggingCallbackWrapper(IpClientCallbacks callback) {
+ mCallback = (callback != null) ? callback : new IpClientCallbacks();
}
private void log(String msg) {
@@ -307,288 +236,102 @@
}
/**
- * This class encapsulates parameters to be passed to
- * IpClient#startProvisioning(). A defensive copy is made by IpClient
- * and the values specified herein are in force until IpClient#stop()
- * is called.
- *
- * Example use:
- *
- * final ProvisioningConfiguration config =
- * mIpClient.buildProvisioningConfiguration()
- * .withPreDhcpAction()
- * .withProvisioningTimeoutMs(36 * 1000)
- * .build();
- * mIpClient.startProvisioning(config);
- * ...
- * mIpClient.stop();
- *
- * The specified provisioning configuration will only be active until
- * IpClient#stop() is called. Future calls to IpClient#startProvisioning()
- * must specify the configuration again.
+ * TODO: remove after migrating clients to use the shared configuration class directly.
+ * @see android.net.shared.ProvisioningConfiguration
*/
- public static class ProvisioningConfiguration {
- // TODO: Delete this default timeout once those callers that care are
- // fixed to pass in their preferred timeout.
- //
- // We pick 36 seconds so we can send DHCP requests at
- //
- // t=0, t=2, t=6, t=14, t=30
- //
- // allowing for 10% jitter.
- private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
-
- public static class Builder {
- private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
-
- public Builder withoutIPv4() {
- mConfig.mEnableIPv4 = false;
- return this;
- }
-
- public Builder withoutIPv6() {
- mConfig.mEnableIPv6 = false;
- return this;
- }
-
- public Builder withoutMultinetworkPolicyTracker() {
- mConfig.mUsingMultinetworkPolicyTracker = false;
- return this;
- }
-
- public Builder withoutIpReachabilityMonitor() {
- mConfig.mUsingIpReachabilityMonitor = false;
- return this;
- }
-
- public Builder withPreDhcpAction() {
- mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
- return this;
- }
-
- public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
- mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
- return this;
- }
-
- public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
- mConfig.mInitialConfig = initialConfig;
- return this;
- }
-
- public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
- mConfig.mStaticIpConfig = staticConfig;
- return this;
- }
-
- public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
- mConfig.mApfCapabilities = apfCapabilities;
- return this;
- }
-
- public Builder withProvisioningTimeoutMs(int timeoutMs) {
- mConfig.mProvisioningTimeoutMs = timeoutMs;
- return this;
- }
-
- public Builder withRandomMacAddress() {
- mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
- return this;
- }
-
- public Builder withStableMacAddress() {
- mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
- return this;
- }
-
- public Builder withNetwork(Network network) {
- mConfig.mNetwork = network;
- return this;
- }
-
- public Builder withDisplayName(String displayName) {
- mConfig.mDisplayName = displayName;
- return this;
- }
-
- public ProvisioningConfiguration build() {
- return new ProvisioningConfiguration(mConfig);
- }
- }
-
- /* package */ boolean mEnableIPv4 = true;
- /* package */ boolean mEnableIPv6 = true;
- /* package */ boolean mUsingMultinetworkPolicyTracker = true;
- /* package */ boolean mUsingIpReachabilityMonitor = true;
- /* package */ int mRequestedPreDhcpActionMs;
- /* package */ InitialConfiguration mInitialConfig;
- /* package */ StaticIpConfiguration mStaticIpConfig;
- /* package */ ApfCapabilities mApfCapabilities;
- /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
- /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
- /* package */ Network mNetwork = null;
- /* package */ String mDisplayName = null;
-
- public ProvisioningConfiguration() {} // used by Builder
-
- public ProvisioningConfiguration(ProvisioningConfiguration other) {
- mEnableIPv4 = other.mEnableIPv4;
- mEnableIPv6 = other.mEnableIPv6;
- mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker;
- mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
- mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
- mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
- mStaticIpConfig = other.mStaticIpConfig;
- mApfCapabilities = other.mApfCapabilities;
- mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
- mIPv6AddrGenMode = other.mIPv6AddrGenMode;
- mNetwork = other.mNetwork;
- mDisplayName = other.mDisplayName;
- }
-
- @Override
- public String toString() {
- return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
- .add("mEnableIPv4: " + mEnableIPv4)
- .add("mEnableIPv6: " + mEnableIPv6)
- .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
- .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
- .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
- .add("mInitialConfig: " + mInitialConfig)
- .add("mStaticIpConfig: " + mStaticIpConfig)
- .add("mApfCapabilities: " + mApfCapabilities)
- .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
- .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
- .add("mNetwork: " + mNetwork)
- .add("mDisplayName: " + mDisplayName)
- .toString();
- }
-
- public boolean isValid() {
- return (mInitialConfig == null) || mInitialConfig.isValid();
- }
- }
-
- public static class InitialConfiguration {
- public final Set<LinkAddress> ipAddresses = new HashSet<>();
- public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
- public final Set<InetAddress> dnsServers = new HashSet<>();
- public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config
-
- public static InitialConfiguration copy(InitialConfiguration config) {
- if (config == null) {
- return null;
- }
- InitialConfiguration configCopy = new InitialConfiguration();
- configCopy.ipAddresses.addAll(config.ipAddresses);
- configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
- configCopy.dnsServers.addAll(config.dnsServers);
- return configCopy;
- }
-
- @Override
- public String toString() {
- return String.format(
- "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)",
- join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
- join(", ", dnsServers), gateway);
- }
-
- public boolean isValid() {
- if (ipAddresses.isEmpty()) {
- return false;
- }
-
- // For every IP address, there must be at least one prefix containing that address.
- for (LinkAddress addr : ipAddresses) {
- if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
- return false;
- }
- }
- // For every dns server, there must be at least one prefix containing that address.
- for (InetAddress addr : dnsServers) {
- if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
- return false;
- }
- }
- // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
- // (read: compliant with RFC4291#section2.5.4).
- if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
- return false;
- }
- // If directlyConnectedRoutes contains an IPv6 default route
- // then ipAddresses MUST contain at least one non-ULA GUA.
- if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
- && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
- return false;
- }
- // The prefix length of routes in directlyConnectedRoutes be within reasonable
- // bounds for IPv6: /48-/64 just as we’d accept in RIOs.
- if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
- return false;
- }
- // There no more than one IPv4 address
- if (ipAddresses.stream().filter(LinkAddress::isIPv4).count() > 1) {
- return false;
- }
-
- return true;
+ public static class ProvisioningConfiguration
+ extends android.net.shared.ProvisioningConfiguration {
+ public ProvisioningConfiguration(android.net.shared.ProvisioningConfiguration other) {
+ super(other);
}
/**
- * @return true if the given list of addressess and routes satisfies provisioning for this
- * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality
- * because addresses and routes seen by Netlink will contain additional fields like flags,
- * interfaces, and so on. If this InitialConfiguration has no IP address specified, the
- * provisioning check always fails.
- *
- * If the given list of routes is null, only addresses are taken into considerations.
+ * @see android.net.shared.ProvisioningConfiguration.Builder
*/
- public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) {
- if (ipAddresses.isEmpty()) {
- return false;
+ public static class Builder extends android.net.shared.ProvisioningConfiguration.Builder {
+ // Override all methods to have a return type matching this Builder
+ @Override
+ public Builder withoutIPv4() {
+ super.withoutIPv4();
+ return this;
}
- for (LinkAddress addr : ipAddresses) {
- if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) {
- return false;
- }
+ @Override
+ public Builder withoutIPv6() {
+ super.withoutIPv6();
+ return this;
}
- if (routes != null) {
- for (IpPrefix prefix : directlyConnectedRoutes) {
- if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) {
- return false;
- }
- }
+ @Override
+ public Builder withoutMultinetworkPolicyTracker() {
+ super.withoutMultinetworkPolicyTracker();
+ return this;
}
- return true;
- }
+ @Override
+ public Builder withoutIpReachabilityMonitor() {
+ super.withoutIpReachabilityMonitor();
+ return this;
+ }
- private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) {
- return !route.hasGateway() && prefix.equals(route.getDestination());
- }
+ @Override
+ public Builder withPreDhcpAction() {
+ super.withPreDhcpAction();
+ return this;
+ }
- private static boolean isPrefixLengthCompliant(LinkAddress addr) {
- return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength());
- }
+ @Override
+ public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
+ super.withPreDhcpAction(dhcpActionTimeoutMs);
+ return this;
+ }
- private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
- return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
- }
+ @Override
+ public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
+ super.withStaticConfiguration(staticConfig);
+ return this;
+ }
- private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
- return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
- && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH);
- }
+ @Override
+ public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
+ super.withApfCapabilities(apfCapabilities);
+ return this;
+ }
- private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
- return prefix.getAddress().equals(Inet6Address.ANY);
- }
+ @Override
+ public Builder withProvisioningTimeoutMs(int timeoutMs) {
+ super.withProvisioningTimeoutMs(timeoutMs);
+ return this;
+ }
- private static boolean isIPv6GUA(LinkAddress addr) {
- return addr.isIPv6() && addr.isGlobalPreferred();
+ @Override
+ public Builder withRandomMacAddress() {
+ super.withRandomMacAddress();
+ return this;
+ }
+
+ @Override
+ public Builder withStableMacAddress() {
+ super.withStableMacAddress();
+ return this;
+ }
+
+ @Override
+ public Builder withNetwork(Network network) {
+ super.withNetwork(network);
+ return this;
+ }
+
+ @Override
+ public Builder withDisplayName(String displayName) {
+ super.withDisplayName(displayName);
+ return this;
+ }
+
+ @Override
+ public ProvisioningConfiguration build() {
+ return new ProvisioningConfiguration(mConfig);
+ }
}
}
@@ -638,7 +381,7 @@
private final String mInterfaceName;
private final String mClatInterfaceName;
@VisibleForTesting
- protected final Callback mCallback;
+ protected final IpClientCallbacks mCallback;
private final Dependencies mDependencies;
private final CountDownLatch mShutdownLatch;
private final INetworkManagementService mNwService;
@@ -657,7 +400,7 @@
* Non-final member variables accessed only from within our StateMachine.
*/
private LinkProperties mLinkProperties;
- private ProvisioningConfiguration mConfiguration;
+ private android.net.shared.ProvisioningConfiguration mConfiguration;
private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private IpReachabilityMonitor mIpReachabilityMonitor;
private DhcpClient mDhcpClient;
@@ -691,7 +434,7 @@
}
}
- public IpClient(Context context, String ifName, Callback callback) {
+ public IpClient(Context context, String ifName, IpClientCallbacks callback) {
this(context, ifName, callback, new Dependencies());
}
@@ -699,7 +442,7 @@
* An expanded constructor, useful for dependency injection.
* TODO: migrate all test users to mock IpClient directly and remove this ctor.
*/
- public IpClient(Context context, String ifName, Callback callback,
+ public IpClient(Context context, String ifName, IpClientCallbacks callback,
INetworkManagementService nwService) {
this(context, ifName, callback, new Dependencies() {
@Override
@@ -708,7 +451,7 @@
}
@VisibleForTesting
- IpClient(Context context, String ifName, Callback callback, Dependencies deps) {
+ IpClient(Context context, String ifName, IpClientCallbacks callback, Dependencies deps) {
super(IpClient.class.getSimpleName() + "." + ifName);
Preconditions.checkNotNull(ifName);
Preconditions.checkNotNull(callback);
@@ -795,6 +538,57 @@
startStateMachineUpdaters();
}
+ /**
+ * Make a IIpClient connector to communicate with this IpClient.
+ */
+ public IIpClient makeConnector() {
+ return new IpClientConnector();
+ }
+
+ class IpClientConnector extends IIpClient.Stub {
+ @Override
+ public void completedPreDhcpAction() {
+ IpClient.this.completedPreDhcpAction();
+ }
+ @Override
+ public void confirmConfiguration() {
+ IpClient.this.confirmConfiguration();
+ }
+ @Override
+ public void readPacketFilterComplete(byte[] data) {
+ IpClient.this.readPacketFilterComplete(data);
+ }
+ @Override
+ public void shutdown() {
+ IpClient.this.shutdown();
+ }
+ @Override
+ public void startProvisioning(ProvisioningConfigurationParcelable req) {
+ IpClient.this.startProvisioning(
+ android.net.shared.ProvisioningConfiguration.fromStableParcelable(req));
+ }
+ @Override
+ public void stop() {
+ IpClient.this.stop();
+ }
+ @Override
+ public void setTcpBufferSizes(String tcpBufferSizes) {
+ IpClient.this.setTcpBufferSizes(tcpBufferSizes);
+ }
+ @Override
+ public void setHttpProxy(ProxyInfoParcelable proxyInfo) {
+ IpClient.this.setHttpProxy(fromStableParcelable(proxyInfo));
+ }
+ @Override
+ public void setMulticastFilter(boolean enabled) {
+ IpClient.this.setMulticastFilter(enabled);
+ }
+ // TODO: remove and have IpClient logs dumped in NetworkStack dumpsys
+ public void dumpIpClientLogs(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IpClient.this.dump(fd, pw, args);
+ }
+ }
+
private void configureAndStartStateMachine() {
addState(mStoppedState);
addState(mStartedState);
@@ -828,7 +622,9 @@
mShutdownLatch.countDown();
}
- // Shut down this IpClient instance altogether.
+ /**
+ * Shut down this IpClient instance altogether.
+ */
public void shutdown() {
stop();
sendMessage(CMD_TERMINATE_AFTER_STOP);
@@ -849,7 +645,10 @@
return new ProvisioningConfiguration.Builder();
}
- public void startProvisioning(ProvisioningConfiguration req) {
+ /**
+ * Start provisioning with the provided parameters.
+ */
+ public void startProvisioning(android.net.shared.ProvisioningConfiguration req) {
if (!req.isValid()) {
doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
return;
@@ -863,7 +662,7 @@
}
mCallback.setNeighborDiscoveryOffload(true);
- sendMessage(CMD_START, new ProvisioningConfiguration(req));
+ sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
}
// TODO: Delete this.
@@ -874,7 +673,7 @@
}
public void startProvisioning() {
- startProvisioning(new ProvisioningConfiguration());
+ startProvisioning(new android.net.shared.ProvisioningConfiguration());
}
public void stop() {
@@ -930,7 +729,7 @@
// Thread-unsafe access to mApfFilter but just used for debugging.
final ApfFilter apfFilter = mApfFilter;
- final ProvisioningConfiguration provisioningConfig = mConfiguration;
+ final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
final ApfCapabilities apfCapabilities = (provisioningConfig != null)
? provisioningConfig.mApfCapabilities : null;
@@ -1463,7 +1262,7 @@
break;
case CMD_START:
- mConfiguration = (ProvisioningConfiguration) msg.obj;
+ mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
transitionTo(mStartedState);
break;
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
new file mode 100644
index 0000000..0aec101
--- /dev/null
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -0,0 +1,92 @@
+/*
+ * 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.ip;
+
+import android.content.Context;
+import android.net.LinkProperties;
+import android.os.ConditionVariable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+
+/**
+ * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack
+ * process.
+ *
+ * @hide
+ */
+public class IpClientUtil {
+ // TODO: remove once IpClient dumps are moved to NetworkStack and callers don't need this arg
+ public static final String DUMP_ARG = IpClient.DUMP_ARG;
+
+ /**
+ * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is
+ * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}.
+ */
+ public static class WaitForProvisioningCallbacks extends IpClientCallbacks {
+ private final ConditionVariable mCV = new ConditionVariable();
+ private LinkProperties mCallbackLinkProperties;
+
+ /**
+ * Block until either {@link #onProvisioningSuccess(LinkProperties)} or
+ * {@link #onProvisioningFailure(LinkProperties)} is called.
+ */
+ public LinkProperties waitForProvisioning() {
+ mCV.block();
+ return mCallbackLinkProperties;
+ }
+
+ @Override
+ public void onProvisioningSuccess(LinkProperties newLp) {
+ mCallbackLinkProperties = newLp;
+ mCV.open();
+ }
+
+ @Override
+ public void onProvisioningFailure(LinkProperties newLp) {
+ mCallbackLinkProperties = null;
+ mCV.open();
+ }
+ }
+
+ /**
+ * Create a new IpClient.
+ *
+ * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
+ * {@link IIpClientCallbacks}.
+ */
+ public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
+ // TODO: request IpClient asynchronously from NetworkStack.
+ final IpClient ipClient = new IpClient(context, ifName, callback);
+ callback.onIpClientCreated(ipClient.makeConnector());
+ }
+
+ /**
+ * Dump logs for the specified IpClient.
+ * TODO: remove logging from this method once IpClient logs are dumped in NetworkStack dumpsys,
+ * then remove callers and delete.
+ */
+ public static void dumpIpClient(
+ IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!(connector instanceof IpClient.IpClientConnector)) {
+ pw.println("Invalid connector");
+ return;
+ }
+ ((IpClient.IpClientConnector) connector).dumpIpClientLogs(fd, pw, args);
+ }
+}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
deleted file mode 100644
index 2eb36a2..0000000
--- a/services/net/java/android/net/ip/IpManager.java
+++ /dev/null
@@ -1,124 +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.ip;
-
-import android.content.Context;
-import android.net.INetd;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.StaticIpConfiguration;
-import android.net.apf.ApfCapabilities;
-import android.net.util.NetdService;
-import android.os.INetworkManagementService;
-import android.os.ServiceManager;
-import android.net.apf.ApfCapabilities;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-
-/*
- * TODO: Delete this altogether in favor of its renamed successor: IpClient.
- *
- * @hide
- */
-public class IpManager extends IpClient {
- public static class ProvisioningConfiguration extends IpClient.ProvisioningConfiguration {
- public ProvisioningConfiguration(IpClient.ProvisioningConfiguration ipcConfig) {
- super(ipcConfig);
- }
-
- public static class Builder extends IpClient.ProvisioningConfiguration.Builder {
- @Override
- public Builder withoutIPv4() {
- super.withoutIPv4();
- return this;
- }
- @Override
- public Builder withoutIPv6() {
- super.withoutIPv6();
- return this;
- }
- @Override
- public Builder withoutIpReachabilityMonitor() {
- super.withoutIpReachabilityMonitor();
- return this;
- }
- @Override
- public Builder withPreDhcpAction() {
- super.withPreDhcpAction();
- return this;
- }
- @Override
- public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
- super.withPreDhcpAction(dhcpActionTimeoutMs);
- return this;
- }
- // No Override; locally defined type.
- public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
- super.withInitialConfiguration((IpClient.InitialConfiguration) initialConfig);
- return this;
- }
- @Override
- public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
- super.withStaticConfiguration(staticConfig);
- return this;
- }
- @Override
- public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
- super.withApfCapabilities(apfCapabilities);
- return this;
- }
- @Override
- public Builder withProvisioningTimeoutMs(int timeoutMs) {
- super.withProvisioningTimeoutMs(timeoutMs);
- return this;
- }
- @Override
- public Builder withNetwork(Network network) {
- super.withNetwork(network);
- return this;
- }
- @Override
- public Builder withDisplayName(String displayName) {
- super.withDisplayName(displayName);
- return this;
- }
- @Override
- public ProvisioningConfiguration build() {
- return new ProvisioningConfiguration(super.build());
- }
- }
- }
-
- public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
- return new ProvisioningConfiguration.Builder();
- }
-
- public static class InitialConfiguration extends IpClient.InitialConfiguration {
- }
-
- public static class Callback extends IpClient.Callback {
- }
-
- public IpManager(Context context, String ifName, Callback callback) {
- super(context, ifName, callback);
- }
-
- public void startProvisioning(ProvisioningConfiguration req) {
- super.startProvisioning((IpClient.ProvisioningConfiguration) req);
- }
-}
diff --git a/services/net/java/android/net/netlink/ConntrackMessage.java b/services/net/java/android/net/netlink/ConntrackMessage.java
index 605c46b..4ee6432 100644
--- a/services/net/java/android/net/netlink/ConntrackMessage.java
+++ b/services/net/java/android/net/netlink/ConntrackMessage.java
@@ -28,7 +28,6 @@
import android.system.OsConstants;
import android.util.Log;
-import libcore.io.SizeOf;
import java.net.Inet4Address;
import java.net.Inet6Address;
diff --git a/services/net/java/android/net/netlink/StructNfGenMsg.java b/services/net/java/android/net/netlink/StructNfGenMsg.java
index 99695e2..8155977 100644
--- a/services/net/java/android/net/netlink/StructNfGenMsg.java
+++ b/services/net/java/android/net/netlink/StructNfGenMsg.java
@@ -16,8 +16,6 @@
package android.net.netlink;
-import libcore.io.SizeOf;
-
import java.nio.ByteBuffer;
@@ -29,7 +27,7 @@
* @hide
*/
public class StructNfGenMsg {
- public static final int STRUCT_SIZE = 2 + SizeOf.SHORT;
+ public static final int STRUCT_SIZE = 2 + Short.BYTES;
public static final int NFNETLINK_V0 = 0;
diff --git a/services/net/java/android/net/netlink/StructNlAttr.java b/services/net/java/android/net/netlink/StructNlAttr.java
index 811bdbb..28a4e88 100644
--- a/services/net/java/android/net/netlink/StructNlAttr.java
+++ b/services/net/java/android/net/netlink/StructNlAttr.java
@@ -17,7 +17,6 @@
package android.net.netlink;
import android.net.netlink.NetlinkConstants;
-import libcore.io.SizeOf;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -117,7 +116,7 @@
public StructNlAttr(short type, short value, ByteOrder order) {
this(order);
nla_type = type;
- setValue(new byte[SizeOf.SHORT]);
+ setValue(new byte[Short.BYTES]);
getValueAsByteBuffer().putShort(value);
}
@@ -128,7 +127,7 @@
public StructNlAttr(short type, int value, ByteOrder order) {
this(order);
nla_type = type;
- setValue(new byte[SizeOf.INT]);
+ setValue(new byte[Integer.BYTES]);
getValueAsByteBuffer().putInt(value);
}
@@ -164,7 +163,7 @@
public int getValueAsInt(int defaultValue) {
final ByteBuffer byteBuffer = getValueAsByteBuffer();
- if (byteBuffer == null || byteBuffer.remaining() != SizeOf.INT) {
+ if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
return defaultValue;
}
return getValueAsByteBuffer().getInt();
diff --git a/services/net/java/android/net/netlink/StructNlMsgErr.java b/services/net/java/android/net/netlink/StructNlMsgErr.java
index f095af4..6fcc6e69d 100644
--- a/services/net/java/android/net/netlink/StructNlMsgErr.java
+++ b/services/net/java/android/net/netlink/StructNlMsgErr.java
@@ -18,7 +18,6 @@
import android.net.netlink.NetlinkConstants;
import android.net.netlink.StructNlMsgHdr;
-import libcore.io.SizeOf;
import java.nio.ByteBuffer;
@@ -31,7 +30,7 @@
* @hide
*/
public class StructNlMsgErr {
- public static final int STRUCT_SIZE = SizeOf.INT + StructNlMsgHdr.STRUCT_SIZE;
+ public static final int STRUCT_SIZE = Integer.BYTES + StructNlMsgHdr.STRUCT_SIZE;
public static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
diff --git a/services/net/java/android/net/shared/InitialConfiguration.java b/services/net/java/android/net/shared/InitialConfiguration.java
new file mode 100644
index 0000000..bc2373f
--- /dev/null
+++ b/services/net/java/android/net/shared/InitialConfiguration.java
@@ -0,0 +1,234 @@
+/*
+ * 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.shared;
+
+import static android.net.shared.ParcelableUtil.fromParcelableArray;
+import static android.net.shared.ParcelableUtil.toParcelableArray;
+import static android.text.TextUtils.join;
+
+import android.net.InitialConfigurationParcelable;
+import android.net.IpPrefix;
+import android.net.IpPrefixParcelable;
+import android.net.LinkAddress;
+import android.net.LinkAddressParcelable;
+import android.net.RouteInfo;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+/** @hide */
+public class InitialConfiguration {
+ public final Set<LinkAddress> ipAddresses = new HashSet<>();
+ public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
+ public final Set<InetAddress> dnsServers = new HashSet<>();
+
+ private static final int RFC6177_MIN_PREFIX_LENGTH = 48;
+ private static final int RFC7421_PREFIX_LENGTH = 64;
+
+ /**
+ * Create a InitialConfiguration that is a copy of the specified configuration.
+ */
+ public static InitialConfiguration copy(InitialConfiguration config) {
+ if (config == null) {
+ return null;
+ }
+ InitialConfiguration configCopy = new InitialConfiguration();
+ configCopy.ipAddresses.addAll(config.ipAddresses);
+ configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
+ configCopy.dnsServers.addAll(config.dnsServers);
+ return configCopy;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s})",
+ join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
+ join(", ", dnsServers));
+ }
+
+ /**
+ * Tests whether the contents of this IpConfiguration represent a valid configuration.
+ */
+ public boolean isValid() {
+ if (ipAddresses.isEmpty()) {
+ return false;
+ }
+
+ // For every IP address, there must be at least one prefix containing that address.
+ for (LinkAddress addr : ipAddresses) {
+ if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
+ return false;
+ }
+ }
+ // For every dns server, there must be at least one prefix containing that address.
+ for (InetAddress addr : dnsServers) {
+ if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
+ return false;
+ }
+ }
+ // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
+ // (read: compliant with RFC4291#section2.5.4).
+ if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
+ return false;
+ }
+ // If directlyConnectedRoutes contains an IPv6 default route
+ // then ipAddresses MUST contain at least one non-ULA GUA.
+ if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
+ && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
+ return false;
+ }
+ // The prefix length of routes in directlyConnectedRoutes be within reasonable
+ // bounds for IPv6: /48-/64 just as we’d accept in RIOs.
+ if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
+ return false;
+ }
+ // There no more than one IPv4 address
+ if (ipAddresses.stream().filter(LinkAddress::isIPv4).count() > 1) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return true if the given list of addressess and routes satisfies provisioning for this
+ * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality
+ * because addresses and routes seen by Netlink will contain additional fields like flags,
+ * interfaces, and so on. If this InitialConfiguration has no IP address specified, the
+ * provisioning check always fails.
+ *
+ * If the given list of routes is null, only addresses are taken into considerations.
+ */
+ public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) {
+ if (ipAddresses.isEmpty()) {
+ return false;
+ }
+
+ for (LinkAddress addr : ipAddresses) {
+ if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) {
+ return false;
+ }
+ }
+
+ if (routes != null) {
+ for (IpPrefix prefix : directlyConnectedRoutes) {
+ if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Convert this configuration to a {@link InitialConfigurationParcelable}.
+ */
+ public InitialConfigurationParcelable toStableParcelable() {
+ final InitialConfigurationParcelable p = new InitialConfigurationParcelable();
+ p.ipAddresses = toParcelableArray(ipAddresses,
+ LinkPropertiesParcelableUtil::toStableParcelable, LinkAddressParcelable.class);
+ p.directlyConnectedRoutes = toParcelableArray(directlyConnectedRoutes,
+ LinkPropertiesParcelableUtil::toStableParcelable, IpPrefixParcelable.class);
+ p.dnsServers = toParcelableArray(
+ dnsServers, IpConfigurationParcelableUtil::parcelAddress, String.class);
+ return p;
+ }
+
+ /**
+ * Create an instance of {@link InitialConfiguration} based on the contents of the specified
+ * {@link InitialConfigurationParcelable}.
+ */
+ public static InitialConfiguration fromStableParcelable(InitialConfigurationParcelable p) {
+ if (p == null) return null;
+ final InitialConfiguration config = new InitialConfiguration();
+ config.ipAddresses.addAll(fromParcelableArray(
+ p.ipAddresses, LinkPropertiesParcelableUtil::fromStableParcelable));
+ config.directlyConnectedRoutes.addAll(fromParcelableArray(
+ p.directlyConnectedRoutes, LinkPropertiesParcelableUtil::fromStableParcelable));
+ config.dnsServers.addAll(
+ fromParcelableArray(p.dnsServers, IpConfigurationParcelableUtil::unparcelAddress));
+ return config;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof InitialConfiguration)) return false;
+ final InitialConfiguration other = (InitialConfiguration) obj;
+ return ipAddresses.equals(other.ipAddresses)
+ && directlyConnectedRoutes.equals(other.directlyConnectedRoutes)
+ && dnsServers.equals(other.dnsServers);
+ }
+
+ private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) {
+ return !route.hasGateway() && prefix.equals(route.getDestination());
+ }
+
+ private static boolean isPrefixLengthCompliant(LinkAddress addr) {
+ return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength());
+ }
+
+ private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
+ return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
+ }
+
+ private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
+ return (RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
+ && (prefixLength <= RFC7421_PREFIX_LENGTH);
+ }
+
+ private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
+ return prefix.getAddress().equals(Inet6Address.ANY);
+ }
+
+ private static boolean isIPv6GUA(LinkAddress addr) {
+ return addr.isIPv6() && addr.isGlobalPreferred();
+ }
+
+ // TODO: extract out into CollectionUtils.
+
+ /**
+ * Indicate whether any element of the specified iterable verifies the specified predicate.
+ */
+ public static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
+ for (T t : coll) {
+ if (fn.test(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Indicate whether all elements of the specified iterable verifies the specified predicate.
+ */
+ public static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
+ return !any(coll, not(fn));
+ }
+
+ /**
+ * Create a predicate that returns the opposite value of the specified predicate.
+ */
+ public static <T> Predicate<T> not(Predicate<T> fn) {
+ return (t) -> !fn.test(t);
+ }
+}
diff --git a/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java b/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java
new file mode 100644
index 0000000..2c368c8
--- /dev/null
+++ b/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java
@@ -0,0 +1,135 @@
+/*
+ * 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.shared;
+
+import static android.net.shared.ParcelableUtil.fromParcelableArray;
+import static android.net.shared.ParcelableUtil.toParcelableArray;
+
+import android.annotation.Nullable;
+import android.net.ApfCapabilitiesParcelable;
+import android.net.DhcpResults;
+import android.net.DhcpResultsParcelable;
+import android.net.InetAddresses;
+import android.net.StaticIpConfiguration;
+import android.net.StaticIpConfigurationParcelable;
+import android.net.apf.ApfCapabilities;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+/**
+ * Collection of utility methods to convert to and from stable AIDL parcelables for IpClient
+ * configuration classes.
+ * @hide
+ */
+public final class IpConfigurationParcelableUtil {
+ /**
+ * Convert a StaticIpConfiguration to a StaticIpConfigurationParcelable.
+ */
+ public static StaticIpConfigurationParcelable toStableParcelable(
+ @Nullable StaticIpConfiguration config) {
+ if (config == null) return null;
+ final StaticIpConfigurationParcelable p = new StaticIpConfigurationParcelable();
+ p.ipAddress = LinkPropertiesParcelableUtil.toStableParcelable(config.ipAddress);
+ p.gateway = parcelAddress(config.gateway);
+ p.dnsServers = toParcelableArray(
+ config.dnsServers, IpConfigurationParcelableUtil::parcelAddress, String.class);
+ p.domains = config.domains;
+ return p;
+ }
+
+ /**
+ * Convert a StaticIpConfigurationParcelable to a StaticIpConfiguration.
+ */
+ public static StaticIpConfiguration fromStableParcelable(
+ @Nullable StaticIpConfigurationParcelable p) {
+ if (p == null) return null;
+ final StaticIpConfiguration config = new StaticIpConfiguration();
+ config.ipAddress = LinkPropertiesParcelableUtil.fromStableParcelable(p.ipAddress);
+ config.gateway = unparcelAddress(p.gateway);
+ config.dnsServers.addAll(fromParcelableArray(
+ p.dnsServers, IpConfigurationParcelableUtil::unparcelAddress));
+ config.domains = p.domains;
+ return config;
+ }
+
+ /**
+ * Convert DhcpResults to a DhcpResultsParcelable.
+ */
+ public static DhcpResultsParcelable toStableParcelable(@Nullable DhcpResults results) {
+ if (results == null) return null;
+ final DhcpResultsParcelable p = new DhcpResultsParcelable();
+ p.baseConfiguration = toStableParcelable((StaticIpConfiguration) results);
+ p.leaseDuration = results.leaseDuration;
+ p.mtu = results.mtu;
+ p.serverAddress = parcelAddress(results.serverAddress);
+ p.vendorInfo = results.vendorInfo;
+ return p;
+ }
+
+ /**
+ * Convert a DhcpResultsParcelable to DhcpResults.
+ */
+ public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) {
+ if (p == null) return null;
+ final DhcpResults results = new DhcpResults(fromStableParcelable(p.baseConfiguration));
+ results.leaseDuration = p.leaseDuration;
+ results.mtu = p.mtu;
+ results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress);
+ results.vendorInfo = p.vendorInfo;
+ return results;
+ }
+
+ /**
+ * Convert ApfCapabilities to ApfCapabilitiesParcelable.
+ */
+ public static ApfCapabilitiesParcelable toStableParcelable(@Nullable ApfCapabilities caps) {
+ if (caps == null) return null;
+ final ApfCapabilitiesParcelable p = new ApfCapabilitiesParcelable();
+ p.apfVersionSupported = caps.apfVersionSupported;
+ p.maximumApfProgramSize = caps.maximumApfProgramSize;
+ p.apfPacketFormat = caps.apfPacketFormat;
+ return p;
+ }
+
+ /**
+ * Convert ApfCapabilitiesParcelable toApfCapabilities.
+ */
+ public static ApfCapabilities fromStableParcelable(@Nullable ApfCapabilitiesParcelable p) {
+ if (p == null) return null;
+ return new ApfCapabilities(
+ p.apfVersionSupported, p.maximumApfProgramSize, p.apfPacketFormat);
+ }
+
+ /**
+ * Convert InetAddress to String.
+ * TODO: have an InetAddressParcelable
+ */
+ public static String parcelAddress(@Nullable InetAddress addr) {
+ if (addr == null) return null;
+ return addr.getHostAddress();
+ }
+
+ /**
+ * Convert String to InetAddress.
+ * TODO: have an InetAddressParcelable
+ */
+ public static InetAddress unparcelAddress(@Nullable String addr) {
+ if (addr == null) return null;
+ return InetAddresses.parseNumericAddress(addr);
+ }
+}
diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
index 5b77f54..d5213df 100644
--- a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
+++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
@@ -16,11 +16,12 @@
package android.net.shared;
+import static android.net.shared.IpConfigurationParcelableUtil.parcelAddress;
+import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress;
import static android.net.shared.ParcelableUtil.fromParcelableArray;
import static android.net.shared.ParcelableUtil.toParcelableArray;
import android.annotation.Nullable;
-import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpPrefixParcelable;
import android.net.LinkAddress;
@@ -33,7 +34,6 @@
import android.net.RouteInfoParcelable;
import android.net.Uri;
-import java.net.InetAddress;
import java.util.Arrays;
/**
@@ -81,7 +81,7 @@
return null;
}
final IpPrefixParcelable parcel = new IpPrefixParcelable();
- parcel.address = ipPrefix.getAddress().getHostAddress();
+ parcel.address = parcelAddress(ipPrefix.getAddress());
parcel.prefixLength = ipPrefix.getPrefixLength();
return parcel;
}
@@ -93,7 +93,7 @@
if (parcel == null) {
return null;
}
- return new IpPrefix(InetAddresses.parseNumericAddress(parcel.address), parcel.prefixLength);
+ return new IpPrefix(unparcelAddress(parcel.address), parcel.prefixLength);
}
/**
@@ -105,7 +105,7 @@
}
final RouteInfoParcelable parcel = new RouteInfoParcelable();
parcel.destination = toStableParcelable(routeInfo.getDestination());
- parcel.gatewayAddr = routeInfo.getGateway().getHostAddress();
+ parcel.gatewayAddr = parcelAddress(routeInfo.getGateway());
parcel.ifaceName = routeInfo.getInterface();
parcel.type = routeInfo.getType();
return parcel;
@@ -120,7 +120,7 @@
}
final IpPrefix destination = fromStableParcelable(parcel.destination);
return new RouteInfo(
- destination, InetAddresses.parseNumericAddress(parcel.gatewayAddr),
+ destination, unparcelAddress(parcel.gatewayAddr),
parcel.ifaceName, parcel.type);
}
@@ -132,7 +132,7 @@
return null;
}
final LinkAddressParcelable parcel = new LinkAddressParcelable();
- parcel.address = la.getAddress().getHostAddress();
+ parcel.address = parcelAddress(la.getAddress());
parcel.prefixLength = la.getPrefixLength();
parcel.flags = la.getFlags();
parcel.scope = la.getScope();
@@ -147,7 +147,7 @@
return null;
}
return new LinkAddress(
- InetAddresses.parseNumericAddress(parcel.address),
+ unparcelAddress(parcel.address),
parcel.prefixLength,
parcel.flags,
parcel.scope);
@@ -167,11 +167,11 @@
LinkPropertiesParcelableUtil::toStableParcelable,
LinkAddressParcelable.class);
parcel.dnses = toParcelableArray(
- lp.getDnsServers(), InetAddress::getHostAddress, String.class);
+ lp.getDnsServers(), IpConfigurationParcelableUtil::parcelAddress, String.class);
parcel.pcscfs = toParcelableArray(
- lp.getPcscfServers(), InetAddress::getHostAddress, String.class);
- parcel.validatedPrivateDnses = toParcelableArray(
- lp.getValidatedPrivateDnsServers(), InetAddress::getHostAddress, String.class);
+ lp.getPcscfServers(), IpConfigurationParcelableUtil::parcelAddress, String.class);
+ parcel.validatedPrivateDnses = toParcelableArray(lp.getValidatedPrivateDnsServers(),
+ IpConfigurationParcelableUtil::parcelAddress, String.class);
parcel.usePrivateDns = lp.isPrivateDnsActive();
parcel.privateDnsServerName = lp.getPrivateDnsServerName();
parcel.domains = lp.getDomains();
@@ -199,11 +199,13 @@
lp.setInterfaceName(parcel.ifaceName);
lp.setLinkAddresses(fromParcelableArray(parcel.linkAddresses,
LinkPropertiesParcelableUtil::fromStableParcelable));
- lp.setDnsServers(fromParcelableArray(parcel.dnses, InetAddresses::parseNumericAddress));
- lp.setPcscfServers(fromParcelableArray(parcel.pcscfs, InetAddresses::parseNumericAddress));
+ lp.setDnsServers(fromParcelableArray(
+ parcel.dnses, IpConfigurationParcelableUtil::unparcelAddress));
+ lp.setPcscfServers(fromParcelableArray(
+ parcel.pcscfs, IpConfigurationParcelableUtil::unparcelAddress));
lp.setValidatedPrivateDnsServers(
fromParcelableArray(parcel.validatedPrivateDnses,
- InetAddresses::parseNumericAddress));
+ IpConfigurationParcelableUtil::unparcelAddress));
lp.setUsePrivateDns(parcel.usePrivateDns);
lp.setPrivateDnsServerName(parcel.privateDnsServerName);
lp.setDomains(parcel.domains);
diff --git a/services/net/java/android/net/shared/NetworkParcelableUtil.java b/services/net/java/android/net/shared/NetworkParcelableUtil.java
new file mode 100644
index 0000000..d0b54b8
--- /dev/null
+++ b/services/net/java/android/net/shared/NetworkParcelableUtil.java
@@ -0,0 +1,50 @@
+/*
+ * 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.shared;
+
+import android.annotation.Nullable;
+import android.net.Network;
+import android.net.NetworkParcelable;
+
+/**
+ * Utility methods to convert to/from stable AIDL parcelables for network attribute classes.
+ * @hide
+ */
+public final class NetworkParcelableUtil {
+ /**
+ * Convert from a Network to a NetworkParcelable.
+ */
+ public static NetworkParcelable toStableParcelable(@Nullable Network network) {
+ if (network == null) {
+ return null;
+ }
+ final NetworkParcelable p = new NetworkParcelable();
+ p.networkHandle = network.getNetworkHandle();
+
+ return p;
+ }
+
+ /**
+ * Convert from a NetworkParcelable to a Network.
+ */
+ public static Network fromStableParcelable(@Nullable NetworkParcelable p) {
+ if (p == null) {
+ return null;
+ }
+ return Network.fromNetworkHandle(p.networkHandle);
+ }
+}
diff --git a/services/net/java/android/net/shared/ParcelableUtil.java b/services/net/java/android/net/shared/ParcelableUtil.java
index a18976c..3f40300 100644
--- a/services/net/java/android/net/shared/ParcelableUtil.java
+++ b/services/net/java/android/net/shared/ParcelableUtil.java
@@ -20,7 +20,7 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
import java.util.function.Function;
/**
@@ -36,7 +36,7 @@
* converter function.
*/
public static <ParcelableType, BaseType> ParcelableType[] toParcelableArray(
- @NonNull List<BaseType> base,
+ @NonNull Collection<BaseType> base,
@NonNull Function<BaseType, ParcelableType> conv,
@NonNull Class<ParcelableType> parcelClass) {
final ParcelableType[] out = (ParcelableType[]) Array.newInstance(parcelClass, base.size());
diff --git a/services/net/java/android/net/shared/PrivateDnsConfig.java b/services/net/java/android/net/shared/PrivateDnsConfig.java
index 41e0bad..c7dc530 100644
--- a/services/net/java/android/net/shared/PrivateDnsConfig.java
+++ b/services/net/java/android/net/shared/PrivateDnsConfig.java
@@ -16,7 +16,9 @@
package android.net.shared;
-import android.net.InetAddresses;
+import static android.net.shared.ParcelableUtil.fromParcelableArray;
+import static android.net.shared.ParcelableUtil.toParcelableArray;
+
import android.net.PrivateDnsConfigParcel;
import android.text.TextUtils;
@@ -70,12 +72,8 @@
public PrivateDnsConfigParcel toParcel() {
final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel();
parcel.hostname = hostname;
-
- final String[] parceledIps = new String[ips.length];
- for (int i = 0; i < ips.length; i++) {
- parceledIps[i] = ips[i].getHostAddress();
- }
- parcel.ips = parceledIps;
+ parcel.ips = toParcelableArray(
+ Arrays.asList(ips), IpConfigurationParcelableUtil::parcelAddress, String.class);
return parcel;
}
@@ -84,11 +82,9 @@
* Build a configuration from a stable AIDL-compatible parcel.
*/
public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) {
- final InetAddress[] ips = new InetAddress[parcel.ips.length];
- for (int i = 0; i < ips.length; i++) {
- ips[i] = InetAddresses.parseNumericAddress(parcel.ips[i]);
- }
-
+ InetAddress[] ips = new InetAddress[parcel.ips.length];
+ ips = fromParcelableArray(parcel.ips, IpConfigurationParcelableUtil::unparcelAddress)
+ .toArray(ips);
return new PrivateDnsConfig(parcel.hostname, ips);
}
}
diff --git a/services/net/java/android/net/shared/ProvisioningConfiguration.java b/services/net/java/android/net/shared/ProvisioningConfiguration.java
new file mode 100644
index 0000000..f937065
--- /dev/null
+++ b/services/net/java/android/net/shared/ProvisioningConfiguration.java
@@ -0,0 +1,310 @@
+/*
+ * 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.shared;
+
+import android.annotation.Nullable;
+import android.net.INetd;
+import android.net.Network;
+import android.net.ProvisioningConfigurationParcelable;
+import android.net.StaticIpConfiguration;
+import android.net.apf.ApfCapabilities;
+import android.net.ip.IIpClient;
+
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * This class encapsulates parameters to be passed to
+ * IpClient#startProvisioning(). A defensive copy is made by IpClient
+ * and the values specified herein are in force until IpClient#stop()
+ * is called.
+ *
+ * Example use:
+ *
+ * final ProvisioningConfiguration config =
+ * new ProvisioningConfiguration.Builder()
+ * .withPreDhcpAction()
+ * .withProvisioningTimeoutMs(36 * 1000)
+ * .build();
+ * mIpClient.startProvisioning(config.toStableParcelable());
+ * ...
+ * mIpClient.stop();
+ *
+ * The specified provisioning configuration will only be active until
+ * IIpClient#stop() is called. Future calls to IIpClient#startProvisioning()
+ * must specify the configuration again.
+ * @hide
+ */
+public class ProvisioningConfiguration {
+ // TODO: Delete this default timeout once those callers that care are
+ // fixed to pass in their preferred timeout.
+ //
+ // We pick 36 seconds so we can send DHCP requests at
+ //
+ // t=0, t=2, t=6, t=14, t=30
+ //
+ // allowing for 10% jitter.
+ private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
+
+ /**
+ * Builder to create a {@link ProvisioningConfiguration}.
+ */
+ public static class Builder {
+ protected ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
+
+ /**
+ * Specify that the configuration should not enable IPv4. It is enabled by default.
+ */
+ public Builder withoutIPv4() {
+ mConfig.mEnableIPv4 = false;
+ return this;
+ }
+
+ /**
+ * Specify that the configuration should not enable IPv6. It is enabled by default.
+ */
+ public Builder withoutIPv6() {
+ mConfig.mEnableIPv6 = false;
+ return this;
+ }
+
+ /**
+ * Specify that the configuration should not use a MultinetworkPolicyTracker. It is used
+ * by default.
+ */
+ public Builder withoutMultinetworkPolicyTracker() {
+ mConfig.mUsingMultinetworkPolicyTracker = false;
+ return this;
+ }
+
+ /**
+ * Specify that the configuration should not use a IpReachabilityMonitor. It is used by
+ * default.
+ */
+ public Builder withoutIpReachabilityMonitor() {
+ mConfig.mUsingIpReachabilityMonitor = false;
+ return this;
+ }
+
+ /**
+ * Identical to {@link #withPreDhcpAction(int)}, using a default timeout.
+ * @see #withPreDhcpAction(int)
+ */
+ public Builder withPreDhcpAction() {
+ mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
+ return this;
+ }
+
+ /**
+ * Specify that {@link IpClientCallbacks#onPreDhcpAction()} should be called. Clients must
+ * call {@link IIpClient#completedPreDhcpAction()} when the callback called. This behavior
+ * is disabled by default.
+ * @param dhcpActionTimeoutMs Timeout for clients to call completedPreDhcpAction().
+ */
+ public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
+ mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
+ return this;
+ }
+
+ /**
+ * Specify the initial provisioning configuration.
+ */
+ public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
+ mConfig.mInitialConfig = initialConfig;
+ return this;
+ }
+
+ /**
+ * Specify a static configuration for provisioning.
+ */
+ public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
+ mConfig.mStaticIpConfig = staticConfig;
+ return this;
+ }
+
+ /**
+ * Specify ApfCapabilities.
+ */
+ public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
+ mConfig.mApfCapabilities = apfCapabilities;
+ return this;
+ }
+
+ /**
+ * Specify the timeout to use for provisioning.
+ */
+ public Builder withProvisioningTimeoutMs(int timeoutMs) {
+ mConfig.mProvisioningTimeoutMs = timeoutMs;
+ return this;
+ }
+
+ /**
+ * Specify that IPv6 address generation should use a random MAC address.
+ */
+ public Builder withRandomMacAddress() {
+ mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
+ return this;
+ }
+
+ /**
+ * Specify that IPv6 address generation should use a stable MAC address.
+ */
+ public Builder withStableMacAddress() {
+ mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ return this;
+ }
+
+ /**
+ * Specify the network to use for provisioning.
+ */
+ public Builder withNetwork(Network network) {
+ mConfig.mNetwork = network;
+ return this;
+ }
+
+ /**
+ * Specify the display name that the IpClient should use.
+ */
+ public Builder withDisplayName(String displayName) {
+ mConfig.mDisplayName = displayName;
+ return this;
+ }
+
+ /**
+ * Build the configuration using previously specified parameters.
+ */
+ public ProvisioningConfiguration build() {
+ return new ProvisioningConfiguration(mConfig);
+ }
+ }
+
+ public boolean mEnableIPv4 = true;
+ public boolean mEnableIPv6 = true;
+ public boolean mUsingMultinetworkPolicyTracker = true;
+ public boolean mUsingIpReachabilityMonitor = true;
+ public int mRequestedPreDhcpActionMs;
+ public InitialConfiguration mInitialConfig;
+ public StaticIpConfiguration mStaticIpConfig;
+ public ApfCapabilities mApfCapabilities;
+ public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
+ public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ public Network mNetwork = null;
+ public String mDisplayName = null;
+
+ public ProvisioningConfiguration() {} // used by Builder
+
+ public ProvisioningConfiguration(ProvisioningConfiguration other) {
+ mEnableIPv4 = other.mEnableIPv4;
+ mEnableIPv6 = other.mEnableIPv6;
+ mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker;
+ mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
+ mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
+ mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
+ mStaticIpConfig = other.mStaticIpConfig == null
+ ? null
+ : new StaticIpConfiguration(other.mStaticIpConfig);
+ mApfCapabilities = other.mApfCapabilities;
+ mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
+ mIPv6AddrGenMode = other.mIPv6AddrGenMode;
+ mNetwork = other.mNetwork;
+ mDisplayName = other.mDisplayName;
+ }
+
+ /**
+ * Create a ProvisioningConfigurationParcelable from this ProvisioningConfiguration.
+ */
+ public ProvisioningConfigurationParcelable toStableParcelable() {
+ final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable();
+ p.enableIPv4 = mEnableIPv4;
+ p.enableIPv6 = mEnableIPv6;
+ p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker;
+ p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor;
+ p.requestedPreDhcpActionMs = mRequestedPreDhcpActionMs;
+ p.initialConfig = mInitialConfig == null ? null : mInitialConfig.toStableParcelable();
+ p.staticIpConfig = IpConfigurationParcelableUtil.toStableParcelable(mStaticIpConfig);
+ p.apfCapabilities = IpConfigurationParcelableUtil.toStableParcelable(mApfCapabilities);
+ p.provisioningTimeoutMs = mProvisioningTimeoutMs;
+ p.ipv6AddrGenMode = mIPv6AddrGenMode;
+ p.network = NetworkParcelableUtil.toStableParcelable(mNetwork);
+ p.displayName = mDisplayName;
+ return p;
+ }
+
+ /**
+ * Create a ProvisioningConfiguration from a ProvisioningConfigurationParcelable.
+ */
+ public static ProvisioningConfiguration fromStableParcelable(
+ @Nullable ProvisioningConfigurationParcelable p) {
+ if (p == null) return null;
+ final ProvisioningConfiguration config = new ProvisioningConfiguration();
+ config.mEnableIPv4 = p.enableIPv4;
+ config.mEnableIPv6 = p.enableIPv6;
+ config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker;
+ config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor;
+ config.mRequestedPreDhcpActionMs = p.requestedPreDhcpActionMs;
+ config.mInitialConfig = InitialConfiguration.fromStableParcelable(p.initialConfig);
+ config.mStaticIpConfig = IpConfigurationParcelableUtil.fromStableParcelable(
+ p.staticIpConfig);
+ config.mApfCapabilities = IpConfigurationParcelableUtil.fromStableParcelable(
+ p.apfCapabilities);
+ config.mProvisioningTimeoutMs = p.provisioningTimeoutMs;
+ config.mIPv6AddrGenMode = p.ipv6AddrGenMode;
+ config.mNetwork = NetworkParcelableUtil.fromStableParcelable(p.network);
+ config.mDisplayName = p.displayName;
+ return config;
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
+ .add("mEnableIPv4: " + mEnableIPv4)
+ .add("mEnableIPv6: " + mEnableIPv6)
+ .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
+ .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
+ .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
+ .add("mInitialConfig: " + mInitialConfig)
+ .add("mStaticIpConfig: " + mStaticIpConfig)
+ .add("mApfCapabilities: " + mApfCapabilities)
+ .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
+ .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
+ .add("mNetwork: " + mNetwork)
+ .add("mDisplayName: " + mDisplayName)
+ .toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ProvisioningConfiguration)) return false;
+ final ProvisioningConfiguration other = (ProvisioningConfiguration) obj;
+ return mEnableIPv4 == other.mEnableIPv4
+ && mEnableIPv6 == other.mEnableIPv6
+ && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker
+ && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor
+ && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs
+ && Objects.equals(mInitialConfig, other.mInitialConfig)
+ && Objects.equals(mStaticIpConfig, other.mStaticIpConfig)
+ && Objects.equals(mApfCapabilities, other.mApfCapabilities)
+ && mProvisioningTimeoutMs == other.mProvisioningTimeoutMs
+ && mIPv6AddrGenMode == other.mIPv6AddrGenMode
+ && Objects.equals(mNetwork, other.mNetwork)
+ && Objects.equals(mDisplayName, other.mDisplayName);
+ }
+
+ public boolean isValid() {
+ return (mInitialConfig == null) || mInitialConfig.isValid();
+ }
+}
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
index a4b2fbb..7b060da 100644
--- a/services/net/java/android/net/util/InterfaceParams.java
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -16,9 +16,9 @@
package android.net.util;
-import static android.net.MacAddress.ALL_ZEROS_ADDRESS;
import static android.net.util.NetworkConstants.ETHER_MTU;
import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
+
import static com.android.internal.util.Preconditions.checkArgument;
import android.net.MacAddress;
@@ -67,7 +67,8 @@
checkArgument((index > 0), "invalid interface index");
this.name = name;
this.index = index;
- this.macAddr = (macAddr != null) ? macAddr : ALL_ZEROS_ADDRESS;
+ this.macAddr = (macAddr != null) ? macAddr : MacAddress.fromBytes(new byte[] {
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 });
this.defaultMtu = (defaultMtu > IPV6_MIN_MTU) ? defaultMtu : IPV6_MIN_MTU;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c9d0eb1..6724c034 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1173,17 +1173,33 @@
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
- List<SubscriptionInfo> result = null;
+ return getActiveSubscriptionInfoList(false);
+ }
+
+ /**
+ * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
+ * is true, it will filter out the hidden subscriptions.
+ *
+ * @hide
+ */
+ public List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+ List<SubscriptionInfo> activeList = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
}
} catch (RemoteException ex) {
// ignore it
}
- return result;
+
+ if (!userVisibleOnly || activeList == null) {
+ return activeList;
+ } else {
+ return activeList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
+ .collect(Collectors.toList());
+ }
}
/**
@@ -2717,8 +2733,7 @@
if (availableList == null) {
return null;
} else {
- return getAvailableSubscriptionInfoList().stream()
- .filter(subInfo -> !shouldHideSubscription(subInfo))
+ return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
.collect(Collectors.toList());
}
}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 151b559..aaed659 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -16,10 +16,19 @@
package android.net.apf;
-import static android.net.util.NetworkConstants.*;
-import static android.system.OsConstants.*;
+import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.ARPHRD_ETHER;
+import static android.system.OsConstants.ETH_P_ARP;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.ETH_P_IPV6;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_STREAM;
+
import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.internal.util.BitUtils.put;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -33,7 +42,7 @@
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClient;
+import android.net.ip.IpClientCallbacks;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
@@ -47,8 +56,20 @@
import android.system.Os;
import android.text.format.DateUtils;
import android.util.Log;
+
import com.android.frameworks.tests.net.R;
import com.android.internal.util.HexDump;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -59,14 +80,6 @@
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
* Tests for APF program generator and interpreter.
@@ -902,7 +915,7 @@
HexDump.toHexString(data, false), result);
}
- private class MockIpClientCallback extends IpClient.Callback {
+ private class MockIpClientCallback extends IpClientCallbacks {
private final ConditionVariable mGotApfProgram = new ConditionVariable();
private byte[] mLastApfProgram;
@@ -933,7 +946,7 @@
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
public TestApfFilter(Context context, ApfConfiguration config,
- IpClient.Callback ipClientCallback, IpConnectivityLog log) throws Exception {
+ IpClientCallbacks ipClientCallback, IpConnectivityLog log) throws Exception {
super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
}
@@ -1062,7 +1075,7 @@
private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
// Helper to initialize a default apfFilter.
- private ApfFilter setupApfFilter(IpClient.Callback ipClientCallback, ApfConfiguration config)
+ private ApfFilter setupApfFilter(IpClientCallbacks ipClientCallback, ApfConfiguration config)
throws Exception {
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index cba3c65..a2dcfef 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -23,7 +23,6 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
@@ -40,9 +39,8 @@
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.ip.IpClient.Callback;
-import android.net.ip.IpClient.InitialConfiguration;
-import android.net.ip.IpClient.ProvisioningConfiguration;
+import android.net.shared.InitialConfiguration;
+import android.net.shared.ProvisioningConfiguration;
import android.net.util.InterfaceParams;
import android.os.INetworkManagementService;
import android.provider.Settings;
@@ -50,8 +48,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
-import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.net.BaseNetworkObserver;
import org.junit.Before;
@@ -63,8 +61,8 @@
import java.net.InetAddress;
import java.util.Arrays;
-import java.util.List;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -87,7 +85,7 @@
@Mock private INetworkManagementService mNMService;
@Mock private INetd mNetd;
@Mock private Resources mResources;
- @Mock private Callback mCb;
+ @Mock private IpClientCallbacks mCb;
@Mock private AlarmManager mAlarm;
@Mock private IpClient.Dependencies mDependecies;
private MockContentResolver mContentResolver;
@@ -179,7 +177,7 @@
public void testInterfaceNotFoundFailsImmediately() throws Exception {
setTestInterfaceParams(null);
final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies);
- ipc.startProvisioning(new IpClient.ProvisioningConfiguration());
+ ipc.startProvisioning(new ProvisioningConfiguration());
verify(mCb, times(1)).onProvisioningFailure(any());
ipc.shutdown();
}
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index a9f9758..1fc67a8 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -27,6 +27,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Modifier;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
@@ -60,6 +61,12 @@
builder.setMtu(null);
in = builder.build();
assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+ // Verify that this test does not miss any new field added later.
+ // If any field is added to NetworkAttributes it must be tested here for parceling
+ // roundtrip.
+ assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
}
@Test
diff --git a/tests/net/java/android/net/shared/InitialConfigurationTest.java b/tests/net/java/android/net/shared/InitialConfigurationTest.java
new file mode 100644
index 0000000..78792bd
--- /dev/null
+++ b/tests/net/java/android/net/shared/InitialConfigurationTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.shared;
+
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link InitialConfiguration}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InitialConfigurationTest {
+ private InitialConfiguration mConfig;
+
+ @Before
+ public void setUp() {
+ mConfig = new InitialConfiguration();
+ mConfig.ipAddresses.addAll(Arrays.asList(
+ new LinkAddress(parseNumericAddress("192.168.45.45"), 16),
+ new LinkAddress(parseNumericAddress("2001:db8::45"), 33)));
+ mConfig.directlyConnectedRoutes.addAll(Arrays.asList(
+ new IpPrefix(parseNumericAddress("192.168.46.46"), 17),
+ new IpPrefix(parseNumericAddress("2001:db8::46"), 34)));
+ mConfig.dnsServers.addAll(Arrays.asList(
+ parseNumericAddress("192.168.47.47"),
+ parseNumericAddress("2001:db8::47")));
+ // Any added InitialConfiguration field must be included in equals() to be tested properly
+ assertFieldCountEquals(3, InitialConfiguration.class);
+ }
+
+ @Test
+ public void testParcelUnparcelInitialConfiguration() {
+ final InitialConfiguration unparceled =
+ InitialConfiguration.fromStableParcelable(mConfig.toStableParcelable());
+ assertEquals(mConfig, unparceled);
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(mConfig, InitialConfiguration.copy(mConfig));
+
+ assertNotEqualsAfterChange(c -> c.ipAddresses.add(
+ new LinkAddress(parseNumericAddress("192.168.47.47"), 24)));
+ assertNotEqualsAfterChange(c -> c.directlyConnectedRoutes.add(
+ new IpPrefix(parseNumericAddress("192.168.46.46"), 32)));
+ assertNotEqualsAfterChange(c -> c.dnsServers.add(parseNumericAddress("2001:db8::49")));
+ assertFieldCountEquals(3, InitialConfiguration.class);
+ }
+
+ private void assertNotEqualsAfterChange(Consumer<InitialConfiguration> mutator) {
+ final InitialConfiguration newConfig = InitialConfiguration.copy(mConfig);
+ mutator.accept(newConfig);
+ assertNotEquals(mConfig, newConfig);
+ }
+}
diff --git a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java
new file mode 100644
index 0000000..14df392
--- /dev/null
+++ b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.shared;
+
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable;
+import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable;
+import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.DhcpResults;
+import android.net.LinkAddress;
+import android.net.StaticIpConfiguration;
+import android.net.apf.ApfCapabilities;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+
+/**
+ * Tests for {@link IpConfigurationParcelableUtil}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpConfigurationParcelableUtilTest {
+ private StaticIpConfiguration mStaticIpConfiguration;
+ private DhcpResults mDhcpResults;
+
+ @Before
+ public void setUp() {
+ mStaticIpConfiguration = new StaticIpConfiguration();
+ mStaticIpConfiguration.ipAddress = new LinkAddress(parseNumericAddress("2001:db8::42"), 64);
+ mStaticIpConfiguration.gateway = parseNumericAddress("192.168.42.42");
+ mStaticIpConfiguration.dnsServers.add(parseNumericAddress("2001:db8::43"));
+ mStaticIpConfiguration.dnsServers.add(parseNumericAddress("192.168.43.43"));
+ mStaticIpConfiguration.domains = "example.com";
+ // Any added StaticIpConfiguration field must be included in equals() to be tested properly
+ assertFieldCountEquals(4, StaticIpConfiguration.class);
+
+ mDhcpResults = new DhcpResults(mStaticIpConfiguration);
+ mDhcpResults.serverAddress = (Inet4Address) parseNumericAddress("192.168.44.44");
+ mDhcpResults.vendorInfo = "TEST_VENDOR_INFO";
+ mDhcpResults.leaseDuration = 3600;
+ mDhcpResults.mtu = 1450;
+ // Any added DhcpResults field must be included in equals() to be tested properly
+ assertFieldCountEquals(4, DhcpResults.class);
+ }
+
+ @Test
+ public void testParcelUnparcelStaticConfiguration() {
+ doStaticConfigurationParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcelStaticConfiguration_NullIpAddress() {
+ mStaticIpConfiguration.ipAddress = null;
+ doStaticConfigurationParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcelStaticConfiguration_NullGateway() {
+ mStaticIpConfiguration.gateway = null;
+ doStaticConfigurationParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcelStaticConfiguration_NullDomains() {
+ mStaticIpConfiguration.domains = null;
+ doStaticConfigurationParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcelStaticConfiguration_EmptyDomains() {
+ mStaticIpConfiguration.domains = "";
+ doStaticConfigurationParcelUnparcelTest();
+ }
+
+ private void doStaticConfigurationParcelUnparcelTest() {
+ final StaticIpConfiguration unparceled =
+ fromStableParcelable(toStableParcelable(mStaticIpConfiguration));
+ assertEquals(mStaticIpConfiguration, unparceled);
+ }
+
+ @Test
+ public void testParcelUnparcelDhcpResults() {
+ doDhcpResultsParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcelDhcpResults_NullServerAddress() {
+ mDhcpResults.serverAddress = null;
+ doDhcpResultsParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcelDhcpResults_NullVendorInfo() {
+ mDhcpResults.vendorInfo = null;
+ doDhcpResultsParcelUnparcelTest();
+ }
+
+ private void doDhcpResultsParcelUnparcelTest() {
+ final DhcpResults unparceled = fromStableParcelable(toStableParcelable(mDhcpResults));
+ assertEquals(mDhcpResults, unparceled);
+ }
+
+ @Test
+ public void testParcelUnparcelApfCapabilities() {
+ final ApfCapabilities caps = new ApfCapabilities(123, 456, 789);
+ assertEquals(caps, fromStableParcelable(toStableParcelable(caps)));
+ }
+}
diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
index 4cabfc9..6f711c0 100644
--- a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
+++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
@@ -18,6 +18,7 @@
import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable;
+import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
import static org.junit.Assert.assertEquals;
@@ -35,7 +36,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
@@ -100,8 +100,7 @@
// Verify that this test does not miss any new field added later.
// If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals
// must also be updated.
- assertEquals(14, Arrays.stream(LinkProperties.class.getDeclaredFields())
- .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
+ assertFieldCountEquals(14, LinkProperties.class);
return lp;
}
diff --git a/tests/net/java/android/net/shared/ParcelableTestUtil.java b/tests/net/java/android/net/shared/ParcelableTestUtil.java
new file mode 100644
index 0000000..088ea3c
--- /dev/null
+++ b/tests/net/java/android/net/shared/ParcelableTestUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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.shared;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+/**
+ * Utility classes to write tests for stable AIDL parceling/unparceling
+ */
+public final class ParcelableTestUtil {
+
+ /**
+ * Verifies that the number of nonstatic fields in a class equals a given count.
+ *
+ * <p>This assertion serves as a reminder to update test code around it if fields are added
+ * after the test is written.
+ * @param count Expected number of nonstatic fields in the class.
+ * @param clazz Class to test.
+ */
+ public static <T> void assertFieldCountEquals(int count, Class<T> clazz) {
+ assertEquals(count, Arrays.stream(clazz.getDeclaredFields())
+ .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
+ }
+}
diff --git a/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java
new file mode 100644
index 0000000..6ea47d2
--- /dev/null
+++ b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.shared;
+
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
+import static android.net.shared.ProvisioningConfiguration.fromStableParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.net.LinkAddress;
+import android.net.Network;
+import android.net.StaticIpConfiguration;
+import android.net.apf.ApfCapabilities;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link ProvisioningConfiguration}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ProvisioningConfigurationTest {
+ private ProvisioningConfiguration mConfig;
+
+ @Before
+ public void setUp() {
+ mConfig = new ProvisioningConfiguration();
+ mConfig.mEnableIPv4 = true;
+ mConfig.mEnableIPv6 = true;
+ mConfig.mUsingMultinetworkPolicyTracker = true;
+ mConfig.mUsingIpReachabilityMonitor = true;
+ mConfig.mRequestedPreDhcpActionMs = 42;
+ mConfig.mInitialConfig = new InitialConfiguration();
+ mConfig.mInitialConfig.ipAddresses.add(
+ new LinkAddress(parseNumericAddress("192.168.42.42"), 24));
+ mConfig.mStaticIpConfig = new StaticIpConfiguration();
+ mConfig.mStaticIpConfig.ipAddress =
+ new LinkAddress(parseNumericAddress("2001:db8::42"), 90);
+ // Not testing other InitialConfig or StaticIpConfig members: they have their own unit tests
+ mConfig.mApfCapabilities = new ApfCapabilities(1, 2, 3);
+ mConfig.mProvisioningTimeoutMs = 4200;
+ mConfig.mIPv6AddrGenMode = 123;
+ mConfig.mNetwork = new Network(321);
+ mConfig.mDisplayName = "test_config";
+ // Any added field must be included in equals() to be tested properly
+ assertFieldCountEquals(12, ProvisioningConfiguration.class);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ doParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcel_NullInitialConfiguration() {
+ mConfig.mInitialConfig = null;
+ doParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcel_NullStaticConfiguration() {
+ mConfig.mStaticIpConfig = null;
+ doParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcel_NullApfCapabilities() {
+ mConfig.mApfCapabilities = null;
+ doParcelUnparcelTest();
+ }
+
+ @Test
+ public void testParcelUnparcel_NullNetwork() {
+ mConfig.mNetwork = null;
+ doParcelUnparcelTest();
+ }
+
+ private void doParcelUnparcelTest() {
+ final ProvisioningConfiguration unparceled =
+ fromStableParcelable(mConfig.toStableParcelable());
+ assertEquals(mConfig, unparceled);
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(mConfig, new ProvisioningConfiguration(mConfig));
+
+ assertNotEqualsAfterChange(c -> c.mEnableIPv4 = false);
+ assertNotEqualsAfterChange(c -> c.mEnableIPv6 = false);
+ assertNotEqualsAfterChange(c -> c.mUsingMultinetworkPolicyTracker = false);
+ assertNotEqualsAfterChange(c -> c.mUsingIpReachabilityMonitor = false);
+ assertNotEqualsAfterChange(c -> c.mRequestedPreDhcpActionMs++);
+ assertNotEqualsAfterChange(c -> c.mInitialConfig.ipAddresses.add(
+ new LinkAddress(parseNumericAddress("192.168.47.47"), 16)));
+ assertNotEqualsAfterChange(c -> c.mInitialConfig = null);
+ assertNotEqualsAfterChange(c -> c.mStaticIpConfig.ipAddress =
+ new LinkAddress(parseNumericAddress("2001:db8::47"), 64));
+ assertNotEqualsAfterChange(c -> c.mStaticIpConfig = null);
+ assertNotEqualsAfterChange(c -> c.mApfCapabilities = new ApfCapabilities(4, 5, 6));
+ assertNotEqualsAfterChange(c -> c.mApfCapabilities = null);
+ assertNotEqualsAfterChange(c -> c.mProvisioningTimeoutMs++);
+ assertNotEqualsAfterChange(c -> c.mIPv6AddrGenMode++);
+ assertNotEqualsAfterChange(c -> c.mNetwork = new Network(123));
+ assertNotEqualsAfterChange(c -> c.mNetwork = null);
+ assertNotEqualsAfterChange(c -> c.mDisplayName = "other_test");
+ assertNotEqualsAfterChange(c -> c.mDisplayName = null);
+ assertFieldCountEquals(12, ProvisioningConfiguration.class);
+ }
+
+ private void assertNotEqualsAfterChange(Consumer<ProvisioningConfiguration> mutator) {
+ final ProvisioningConfiguration newConfig = new ProvisioningConfiguration(mConfig);
+ mutator.accept(newConfig);
+ assertNotEquals(mConfig, newConfig);
+ }
+}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index e63c3b0..94bcd28 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -16,13 +16,30 @@
package com.android.server.net.ipmemorystore;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import android.content.Context;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.Status;
+import android.net.ipmemorystore.StatusParcelable;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,41 +47,267 @@
import org.mockito.MockitoAnnotations;
import java.io.File;
+import java.lang.reflect.Modifier;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
-/** Unit tests for {@link IpMemoryStoreServiceTest}. */
+/** Unit tests for {@link IpMemoryStoreService}. */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class IpMemoryStoreServiceTest {
+ private static final String TEST_CLIENT_ID = "testClientId";
+ private static final String TEST_DATA_NAME = "testData";
+
@Mock
- Context mMockContext;
+ private Context mMockContext;
+ private File mDbFile;
+
+ private IpMemoryStoreService mService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- doReturn(new File("/tmp/test.db")).when(mMockContext).getDatabasePath(anyString());
+ final Context context = InstrumentationRegistry.getContext();
+ final File dir = context.getFilesDir();
+ mDbFile = new File(dir, "test.db");
+ doReturn(mDbFile).when(mMockContext).getDatabasePath(anyString());
+ mService = new IpMemoryStoreService(mMockContext);
+ }
+
+ @After
+ public void tearDown() {
+ mService.shutdown();
+ mDbFile.delete();
+ }
+
+ /** Helper method to make a vanilla IOnStatusListener */
+ private IOnStatusListener onStatus(Consumer<Status> functor) {
+ return new IOnStatusListener() {
+ @Override
+ public void onComplete(final StatusParcelable statusParcelable) throws RemoteException {
+ functor.accept(new Status(statusParcelable));
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
+ /** Helper method to make an IOnBlobRetrievedListener */
+ private interface OnBlobRetrievedListener {
+ void onBlobRetrieved(Status status, String l2Key, String name, byte[] data);
+ }
+ private IOnBlobRetrievedListener onBlobRetrieved(final OnBlobRetrievedListener functor) {
+ return new IOnBlobRetrievedListener() {
+ @Override
+ public void onBlobRetrieved(final StatusParcelable statusParcelable,
+ final String l2Key, final String name, final Blob blob) throws RemoteException {
+ functor.onBlobRetrieved(new Status(statusParcelable), l2Key, name,
+ null == blob ? null : blob.data);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
+ /** Helper method to make an IOnNetworkAttributesRetrievedListener */
+ private interface OnNetworkAttributesRetrievedListener {
+ void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr);
+ }
+ private IOnNetworkAttributesRetrieved onNetworkAttributesRetrieved(
+ final OnNetworkAttributesRetrievedListener functor) {
+ return new IOnNetworkAttributesRetrieved() {
+ @Override
+ public void onL2KeyResponse(final StatusParcelable status, final String l2Key,
+ final NetworkAttributesParcelable attributes)
+ throws RemoteException {
+ functor.onNetworkAttributesRetrieved(new Status(status), l2Key,
+ null == attributes ? null : new NetworkAttributes(attributes));
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
+ // Helper method to factorize some boilerplate
+ private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ functor.accept(latch);
+ try {
+ latch.await(5000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail(timeoutMessage);
+ }
}
@Test
public void testNetworkAttributes() {
- final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
- // TODO : implement this
+ final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+ try {
+ na.setAssignedV4Address(
+ (Inet4Address) Inet4Address.getByAddress(new byte[]{1, 2, 3, 4}));
+ } catch (UnknownHostException e) { /* Can't happen */ }
+ na.setGroupHint("hint1");
+ na.setMtu(219);
+ final String l2Key = UUID.randomUUID().toString();
+ NetworkAttributes attributes = na.build();
+ doLatched("Did not complete storing attributes", latch ->
+ mService.storeNetworkAttributes(l2Key, attributes.toParcelable(),
+ onStatus(status -> {
+ assertTrue("Store status not successful : " + status.resultCode,
+ status.isSuccess());
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving attributes", latch ->
+ mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(attributes, attr);
+ latch.countDown();
+ })));
+
+ final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder();
+ try {
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
+ } catch (UnknownHostException e) { /* Still can't happen */ }
+ final NetworkAttributes attributes2 = na2.build();
+ doLatched("Did not complete storing attributes 2", latch ->
+ mService.storeNetworkAttributes(l2Key, attributes2.toParcelable(),
+ onStatus(status -> latch.countDown())));
+
+ doLatched("Did not complete retrieving attributes 2", latch ->
+ mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(attributes.assignedV4Address, attr.assignedV4Address);
+ assertEquals(attributes.groupHint, attr.groupHint);
+ assertEquals(attributes.mtu, attr.mtu);
+ assertEquals(attributes2.dnsAddresses, attr.dnsAddresses);
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving attributes 3", latch ->
+ mService.retrieveNetworkAttributes(l2Key + "nonexistent",
+ onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key + "nonexistent", key);
+ assertNull("Retrieved data not stored", attr);
+ latch.countDown();
+ }
+ )));
+
+ // Verify that this test does not miss any new field added later.
+ // If any field is added to NetworkAttributes it must be tested here for storing
+ // and retrieving.
+ assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
+ }
+
+ @Test
+ public void testInvalidAttributes() {
+ doLatched("Did not complete storing bad attributes", latch ->
+ mService.storeNetworkAttributes("key", null, onStatus(status -> {
+ assertFalse("Success storing on a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ latch.countDown();
+ })));
+
+ final NetworkAttributes na = new NetworkAttributes.Builder().setMtu(2).build();
+ doLatched("Did not complete storing bad attributes", latch ->
+ mService.storeNetworkAttributes(null, na.toParcelable(), onStatus(status -> {
+ assertFalse("Success storing null attributes on a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete storing bad attributes", latch ->
+ mService.storeNetworkAttributes(null, null, onStatus(status -> {
+ assertFalse("Success storing null attributes on a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving bad attributes", latch ->
+ mService.retrieveNetworkAttributes(null, onNetworkAttributesRetrieved(
+ (status, key, attr) -> {
+ assertFalse("Success retrieving attributes for a null key",
+ status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ assertNull(key);
+ assertNull(attr);
+ })));
}
@Test
public void testPrivateData() {
- final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
- // TODO : implement this
+ final Blob b = new Blob();
+ b.data = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 };
+ final String l2Key = UUID.randomUUID().toString();
+ doLatched("Did not complete storing private data", latch ->
+ mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
+ onStatus(status -> {
+ assertTrue("Store status not successful : " + status.resultCode,
+ status.isSuccess());
+ latch.countDown();
+ })));
+
+ doLatched("Did not complete retrieving private data", latch ->
+ mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved(
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME);
+ Arrays.equals(b.data, data);
+ latch.countDown();
+ })));
+
+ // Most puzzling error message ever
+ doLatched("Did not complete retrieving nothing", latch ->
+ mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME + "2", onBlobRetrieved(
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME + "2");
+ assertNull(data);
+ latch.countDown();
+ })));
}
@Test
public void testFindL2Key() {
- final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
// TODO : implement this
}
@Test
public void testIsSameNetwork() {
- final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
// TODO : implement this
}
}
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 01728fa1..2a8f695 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -17,6 +17,7 @@
Generate API lists for non-SDK API enforcement.
"""
import argparse
+from collections import defaultdict
import os
import sys
import re
@@ -27,16 +28,20 @@
FLAG_BLACKLIST = "blacklist"
FLAG_GREYLIST_MAX_O = "greylist-max-o"
FLAG_GREYLIST_MAX_P = "greylist-max-p"
+FLAG_CORE_PLATFORM_API = "core-platform-api"
# List of all known flags.
-FLAGS = [
+FLAGS_API_LIST = [
FLAG_WHITELIST,
FLAG_GREYLIST,
FLAG_BLACKLIST,
FLAG_GREYLIST_MAX_O,
FLAG_GREYLIST_MAX_P,
]
-FLAGS_SET = set(FLAGS)
+ALL_FLAGS = FLAGS_API_LIST + [ FLAG_CORE_PLATFORM_API ]
+
+FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
+ALL_FLAGS_SET = set(ALL_FLAGS)
# Suffix used in command line args to express that only known and
# otherwise unassigned entries should be assign the given flag.
@@ -62,7 +67,7 @@
SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
# Predicates to be used with filter_apis.
-IS_UNASSIGNED = lambda api, flags: not flags
+HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
def get_args():
@@ -73,12 +78,10 @@
"""
parser = argparse.ArgumentParser()
parser.add_argument('--output', required=True)
- parser.add_argument('--public', required=True, help='list of all public entries')
- parser.add_argument('--private', required=True, help='list of all private entries')
parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
help='CSV files to be merged into output')
- for flag in FLAGS:
+ for flag in ALL_FLAGS:
ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
help='lists of entries with flag "' + flag + '"')
@@ -118,26 +121,9 @@
f.writelines(lines)
class FlagsDict:
- def __init__(self, public_api, private_api):
- # Bootstrap the entries dictionary.
-
- # Check that the two sets do not overlap.
- public_api_set = set(public_api)
- private_api_set = set(private_api)
- assert public_api_set.isdisjoint(private_api_set), (
- "Lists of public and private API overlap. " +
- "This suggests an issue with the `hiddenapi` build tool.")
-
- # Compute the whole key set
- self._dict_keyset = public_api_set.union(private_api_set)
-
- # Create a dict that creates entries for both public and private API,
- # and assigns public API to the whitelist.
- self._dict = {}
- for api in public_api:
- self._dict[api] = set([ FLAG_WHITELIST ])
- for api in private_api:
- self._dict[api] = set()
+ def __init__(self):
+ self._dict_keyset = set()
+ self._dict = defaultdict(set)
def _check_entries_set(self, keys_subset, source):
assert isinstance(keys_subset, set)
@@ -150,12 +136,12 @@
def _check_flags_set(self, flags_subset, source):
assert isinstance(flags_subset, set)
- assert flags_subset.issubset(FLAGS_SET), (
+ assert flags_subset.issubset(ALL_FLAGS_SET), (
"Error processing: {}\n"
"The following flags were not recognized: \n"
"{}\n"
"Please visit go/hiddenapi for more information.").format(
- source, "\n".join(flags_subset - FLAGS_SET))
+ source, "\n".join(flags_subset - ALL_FLAGS_SET))
def filter_apis(self, filter_fn):
"""Returns APIs which match a given predicate.
@@ -173,7 +159,7 @@
def get_valid_subset_of_unassigned_apis(self, api_subset):
"""Sanitizes a key set input to only include keys which exist in the dictionary
- and have not been assigned any flags.
+ and have not been assigned any API list flags.
Args:
entries_subset (set/list): Key set to be sanitized.
@@ -182,7 +168,7 @@
Sanitized key set.
"""
assert isinstance(api_subset, set)
- return api_subset.intersection(self.filter_apis(IS_UNASSIGNED))
+ return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
def generate_csv(self):
"""Constructs CSV entries from a dictionary.
@@ -203,14 +189,13 @@
source (string): Origin of `csv_lines`. Will be printed in error messages.
Throws:
- AssertionError if parsed API signatures of flags are invalid.
+ AssertionError if parsed flags are invalid.
"""
# Split CSV lines into arrays of values.
csv_values = [ line.split(',') for line in csv_lines ]
- # Check that all entries exist in the dict.
- csv_keys = set([ csv[0] for csv in csv_values ])
- self._check_entries_set(csv_keys, source)
+ # Update the full set of API signatures.
+ self._dict_keyset.update([ csv[0] for csv in csv_values ])
# Check that all flags are known.
csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], []))
@@ -224,7 +209,7 @@
"""Assigns a flag to given subset of entries.
Args:
- flag (string): One of FLAGS.
+ flag (string): One of ALL_FLAGS.
apis (set): Subset of APIs to recieve the flag.
source (string): Origin of `entries_subset`. Will be printed in error messages.
@@ -245,18 +230,23 @@
# Parse arguments.
args = vars(get_args())
- flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"]))
+ # Initialize API->flags dictionary.
+ flags = FlagsDict()
+
+ # Merge input CSV files into the dictionary.
+ # Do this first because CSV files produced by parsing API stubs will
+ # contain the full set of APIs. Subsequent additions from text files
+ # will be able to detect invalid entries, and/or filter all as-yet
+ # unassigned entries.
+ for filename in args["csv"]:
+ flags.parse_and_merge_csv(read_lines(filename), filename)
# Combine inputs which do not require any particular order.
# (1) Assign serialization API to whitelist.
flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION))
- # (2) Merge input CSV files into the dictionary.
- for filename in args["csv"]:
- flags.parse_and_merge_csv(read_lines(filename), filename)
-
- # (3) Merge text files with a known flag into the dictionary.
- for flag in FLAGS:
+ # (2) Merge text files with a known flag into the dictionary.
+ for flag in ALL_FLAGS:
for filename in args[flag]:
flags.assign_flag(flag, read_lines(filename), filename)
@@ -265,13 +255,13 @@
# (a) the entry exists, and
# (b) it has not been assigned any other flag.
# Because of (b), this must run after all strict assignments have been performed.
- for flag in FLAGS:
+ for flag in ALL_FLAGS:
for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
flags.assign_flag(flag, valid_entries, filename)
# Assign all remaining entries to the blacklist.
- flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED))
+ flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
# Write output.
write_lines(args["output"], flags.generate_csv())