Merge "Use SocketUtils from shared lib"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 829e66a..d2f6d15 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -34,6 +34,7 @@
// Libraries not including Tethering's own framework-tethering (different flavors of that one
// are needed depending on the build rule)
libs: [
+ "connectivity-internal-api-util",
"framework-configinfrastructure",
"framework-connectivity.stubs.module_lib",
"framework-connectivity-t.stubs.module_lib",
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index e59c8e4..8840394 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -199,6 +199,7 @@
"android.net.connectivity",
"android.net.netstats.provider",
"android.net.nsd",
+ "android.net.wear",
],
},
}
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 9ca3f14..481557b 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -22,6 +22,10 @@
defaults: ["framework-module-defaults"],
impl_library_visibility: [
"//packages/modules/Connectivity/Tethering:__subpackages__",
+ "//packages/modules/Connectivity/framework",
+ "//packages/modules/Connectivity/framework-t",
+ "//packages/modules/Connectivity/service",
+ "//packages/modules/Connectivity/service-t",
// Using for test only
"//cts/tests/netlegacy22.api",
@@ -64,19 +68,8 @@
filegroup {
name: "framework-tethering-srcs",
srcs: [
- "src/android/net/TetheredClient.aidl",
- "src/android/net/TetheredClient.java",
- "src/android/net/TetheringManager.java",
- "src/android/net/TetheringConstants.java",
- "src/android/net/IIntResultListener.aidl",
- "src/android/net/ITetheringEventCallback.aidl",
- "src/android/net/ITetheringConnector.aidl",
- "src/android/net/TetheringCallbackStartedParcel.aidl",
- "src/android/net/TetheringConfigurationParcel.aidl",
- "src/android/net/TetheringRequestParcel.aidl",
- "src/android/net/TetherStatesParcel.aidl",
- "src/android/net/TetheringInterface.aidl",
- "src/android/net/TetheringInterface.java",
+ "src/**/*.aidl",
+ "src/**/*.java",
],
path: "src"
}
diff --git a/Tethering/common/TetheringLib/src/android/net/wear/ICompanionDeviceManagerProxy.aidl b/Tethering/common/TetheringLib/src/android/net/wear/ICompanionDeviceManagerProxy.aidl
new file mode 100644
index 0000000..f8c39bf
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/wear/ICompanionDeviceManagerProxy.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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.wear;
+
+import android.companion.AssociationInfo;
+
+/** @hide */
+interface ICompanionDeviceManagerProxy {
+ List<AssociationInfo> getAllAssociations();
+}
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 8cf13d3..65ea8e5 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -17,6 +17,13 @@
package android.net.ip;
import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
@@ -310,7 +317,7 @@
mDeps = deps;
mTetheringMetrics = tetheringMetrics;
resetLinkProperties();
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
+ mLastError = TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE;
mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
@@ -475,7 +482,7 @@
}
private void handleError() {
- mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ mLastError = TETHER_ERROR_DHCPSERVER_ERROR;
transitionTo(mInitialState);
}
}
@@ -583,7 +590,7 @@
public void callback(int statusCode) {
if (statusCode != STATUS_SUCCESS) {
mLog.e("Error stopping DHCP server: " + statusCode);
- mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ mLastError = TETHER_ERROR_DHCPSERVER_ERROR;
// Not much more we can do here
}
mDhcpLeases.clear();
@@ -1134,7 +1141,7 @@
maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
+ mLastError = TETHER_ERROR_NO_ERROR;
switch (message.arg1) {
case STATE_LOCAL_ONLY:
maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
@@ -1172,7 +1179,7 @@
startConntrackMonitoring();
if (!startIPv4()) {
- mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+ mLastError = TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
@@ -1180,7 +1187,7 @@
NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
mLog.e("Error Tethering", e);
- mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+ mLastError = TETHER_ERROR_TETHER_IFACE_ERROR;
return;
}
@@ -1201,7 +1208,7 @@
try {
NetdUtils.untetherInterface(mNetd, mIfaceName);
} catch (RemoteException | ServiceSpecificException e) {
- mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+ mLastError = TETHER_ERROR_UNTETHER_IFACE_ERROR;
mLog.e("Failed to untether interface: " + e);
}
@@ -1234,7 +1241,7 @@
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
- mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
+ mLastError = TETHER_ERROR_INTERNAL_ERROR;
transitionTo(mInitialState);
break;
case CMD_NEW_PREFIX_REQUEST:
@@ -1261,7 +1268,7 @@
@Override
public void enter() {
super.enter();
- if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
+ if (mLastError != TETHER_ERROR_NO_ERROR) {
transitionTo(mInitialState);
}
@@ -1297,7 +1304,7 @@
@Override
public void enter() {
super.enter();
- if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
+ if (mLastError != TETHER_ERROR_NO_ERROR) {
transitionTo(mInitialState);
}
@@ -1405,7 +1412,7 @@
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Exception enabling NAT: " + e.toString());
cleanupUpstream();
- mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
+ mLastError = TETHER_ERROR_ENABLE_FORWARDING_ERROR;
transitionTo(mInitialState);
return true;
}
@@ -1454,7 +1461,7 @@
@Override
public void enter() {
mIpNeighborMonitor.stop();
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
+ mLastError = TETHER_ERROR_NO_ERROR;
sendInterfaceState(STATE_UNAVAILABLE);
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/wear/CompanionDeviceManagerProxy.java b/Tethering/src/com/android/networkstack/tethering/wear/CompanionDeviceManagerProxy.java
new file mode 100644
index 0000000..e94febb
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/wear/CompanionDeviceManagerProxy.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering.wear;
+
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceManager;
+import android.content.Context;
+import android.net.connectivity.TiramisuConnectivityInternalApiUtil;
+import android.net.wear.ICompanionDeviceManagerProxy;
+import android.os.Build;
+import android.os.RemoteException;
+
+import androidx.annotation.RequiresApi;
+
+import java.util.List;
+
+/**
+ * A proxy for {@link android.companion.CompanionDeviceManager}, allowing Tethering to call it with
+ * a different set of permissions.
+ * @hide
+ */
+public class CompanionDeviceManagerProxy {
+ private final ICompanionDeviceManagerProxy mService;
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public CompanionDeviceManagerProxy(Context context) {
+ mService = ICompanionDeviceManagerProxy.Stub.asInterface(
+ TiramisuConnectivityInternalApiUtil.getCompanionDeviceManagerProxyService(context));
+ }
+
+ /**
+ * @see CompanionDeviceManager#getAllAssociations()
+ */
+ public List<AssociationInfo> getAllAssociations() {
+ try {
+ return mService.getAllAssociations();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 8e99b8d..3eb4e02 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -31,7 +31,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "bpf_net_helpers.h"
-#include "bpf_shared.h"
+#include "netd.h"
// This is defined for cgroup bpf filter only.
static const int DROP = 0;
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/netd.h
similarity index 100%
rename from bpf_progs/bpf_shared.h
rename to bpf_progs/netd.h
diff --git a/framework/Android.bp b/framework/Android.bp
index 485961c..3950dba 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -80,7 +80,9 @@
"framework-connectivity-t.stubs.module_lib",
],
impl_only_libs: [
- "framework-tethering.stubs.module_lib",
+ // TODO: figure out why just using "framework-tethering" uses the stubs, even though both
+ // framework-connectivity and framework-tethering are in the same APEX.
+ "framework-tethering.impl",
"framework-wifi.stubs.module_lib",
"net-utils-device-common",
],
@@ -109,9 +111,9 @@
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
// to generate the connectivity stubs. That would create a circular dependency
- // because the tethering stubs depend on the connectivity stubs (e.g.,
+ // because the tethering impl depend on the connectivity stubs (e.g.,
// TetheringRequest depends on LinkAddress).
- "framework-tethering.stubs.module_lib",
+ "framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
visibility: ["//packages/modules/Connectivity:__subpackages__"]
@@ -243,3 +245,24 @@
"//packages/modules/Connectivity/service",
],
}
+
+// Library providing limited APIs within the connectivity module, so that R+ components like
+// Tethering have a controlled way to depend on newer components like framework-connectivity that
+// are not loaded on R.
+java_library {
+ name: "connectivity-internal-api-util",
+ sdk_version: "module_current",
+ libs: [
+ "androidx.annotation_annotation",
+ "framework-connectivity.impl",
+ ],
+ jarjar_rules: ":framework-connectivity-jarjar-rules",
+ srcs: [
+ // Files listed here MUST all be annotated with @RequiresApi(Build.VERSION_CODES.TIRAMISU),
+ // so that API checks are enforced for R+ users of this library
+ "src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java",
+ ],
+ visibility: [
+ "//packages/modules/Connectivity/Tethering:__subpackages__",
+ ],
+}
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 0b0f2bb..dd3404c 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -515,8 +515,8 @@
ctor public VpnTransportInfo(int, @Nullable String, boolean, boolean);
method public boolean areLongLivedTcpConnectionsExpensive();
method public int describeContents();
- method public boolean getBypassable();
method public int getType();
+ method public boolean isBypassable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 39c5af2..1b4b42f 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -5992,4 +5992,13 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /** @hide */
+ public IBinder getCompanionDeviceManagerProxyService() {
+ try {
+ return mService.getCompanionDeviceManagerProxyService();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 29fea00..43d2f07 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -247,4 +247,6 @@
boolean getFirewallChainEnabled(int chain);
void replaceFirewallChain(int chain, in int[] uids);
+
+ IBinder getCompanionDeviceManagerProxyService();
}
diff --git a/framework/src/android/net/VpnTransportInfo.java b/framework/src/android/net/VpnTransportInfo.java
index e335c0f..6bb00c8 100644
--- a/framework/src/android/net/VpnTransportInfo.java
+++ b/framework/src/android/net/VpnTransportInfo.java
@@ -86,7 +86,7 @@
// When the module runs on older SDKs, |bypassable| will always be false since the old Vpn
// code will call this constructor. For Settings VPNs, this is always correct as they are
// never bypassable. For VpnManager and VpnService types, this may be wrong since both of
- // them have a choice. However, on these SDKs VpnTransportInfo#getBypassable is not
+ // them have a choice. However, on these SDKs VpnTransportInfo#isBypassable is not
// available anyway, so this should be harmless. False is a better choice than true here
// regardless because it is the default value for both VpnManager and VpnService if the app
// does not do anything about it.
@@ -111,7 +111,7 @@
* {@code UnsupportedOperationException} if called.
*/
@RequiresApi(UPSIDE_DOWN_CAKE)
- public boolean getBypassable() {
+ public boolean isBypassable() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException("Not supported before U");
}
@@ -134,7 +134,7 @@
* VPNs can be bypassable or not. When the VPN is not bypassable, the user has
* expressed explicit intent to have no connection outside of the VPN, so even
* privileged apps with permission to bypass non-bypassable VPNs should not do
- * so. See {@link #getBypassable()}.
+ * so. See {@link #isBypassable()}.
* For bypassable VPNs however, the user expects apps choose reasonable tradeoffs
* about whether they use the VPN.
*
diff --git a/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java b/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java
new file mode 100644
index 0000000..d65858f
--- /dev/null
+++ b/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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.connectivity;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.Build;
+import android.os.IBinder;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * Utility providing limited access to module-internal APIs which are only available on Android T+,
+ * as this class is only in the bootclasspath on T+ as part of framework-connectivity.
+ *
+ * R+ module components like Tethering cannot depend on all hidden symbols from
+ * framework-connectivity. They only have access to stable API stubs where newer APIs can be
+ * accessed after an API level check (enforced by the linter), or to limited hidden symbols in this
+ * class which is also annotated with @RequiresApi (so API level checks are also enforced by the
+ * linter).
+ * @hide
+ */
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+public class TiramisuConnectivityInternalApiUtil {
+
+ /**
+ * Get a service binder token for
+ * {@link com.android.server.connectivity.wear.CompanionDeviceManagerProxyService}.
+ */
+ public static IBinder getCompanionDeviceManagerProxyService(Context ctx) {
+ final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
+ return cm.getCompanionDeviceManagerProxyService();
+ }
+}
diff --git a/netd/BpfHandler.h b/netd/BpfHandler.h
index 925a725..a6da4eb 100644
--- a/netd/BpfHandler.h
+++ b/netd/BpfHandler.h
@@ -18,7 +18,7 @@
#include <netdutils/Status.h>
#include "bpf/BpfMap.h"
-#include "bpf_shared.h"
+#include "netd.h"
using android::bpf::BpfMap;
using android::bpf::BpfMapRO;
diff --git a/service-t/Android.bp b/service-t/Android.bp
index 2e7a4f3..d876166 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -50,7 +50,8 @@
"framework-configinfrastructure",
"framework-connectivity-pre-jarjar",
"framework-connectivity-t-pre-jarjar",
- "framework-tethering",
+ // TODO: use framework-tethering-pre-jarjar when it is separated from framework-tethering
+ "framework-tethering.impl",
"service-connectivity-pre-jarjar",
"service-nearby-pre-jarjar",
"ServiceConnectivityResources",
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 28de881..122c2d4 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -26,7 +26,7 @@
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
#include "bpf/BpfMap.h"
-#include "bpf_shared.h"
+#include "netd.h"
#include "netdbpf/BpfNetworkStats.h"
#ifdef LOG_TAG
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
index 6f9c8c2..ff62c0b 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
@@ -326,7 +326,8 @@
.txPackets = TEST_PACKET1,
.txBytes = TEST_BYTES1,
};
- populateFakeStats(0, 0, 0, OVERFLOW_COUNTERSET, value1, mFakeStatsMap);
+ // next stats entry will be ignored due to ifindex 0 not being present in mFakeIfaceIndexNameMap
+ populateFakeStats(0, 0, 0, TEST_COUNTERSET1, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET1, value1, mFakeStatsMap);
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
index 8ab7e25..03a1a44 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
@@ -18,7 +18,7 @@
#define _BPF_NETWORKSTATS_H
#include <bpf/BpfMap.h>
-#include "bpf_shared.h"
+#include "netd.h"
namespace android {
namespace bpf {
diff --git a/service/Android.bp b/service/Android.bp
index 98bbbac..224fa19 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -157,7 +157,9 @@
// against the implementation because the two libraries are in the same
// APEX.
"framework-connectivity-t.stubs.module_lib",
- "framework-tethering",
+ // TODO: figure out why just using "framework-tethering" uses the stubs, even though both
+ // service-connectivity and framework-tethering are in the same APEX.
+ "framework-tethering.impl",
"framework-wifi",
"unsupportedappusage",
"ServiceConnectivityResources",
@@ -166,6 +168,7 @@
static_libs: [
// Do not add libs here if they are already included
// in framework-connectivity
+ "androidx.annotation_annotation",
"connectivity-net-module-utils-bpf",
"connectivity_native_aidl_interface-lateststable-java",
"dnsresolver_aidl_interface-V9-java",
@@ -258,7 +261,7 @@
"framework-annotations-lib",
"framework-connectivity.impl",
"framework-connectivity-t.impl",
- "framework-tethering",
+ "framework-tethering.impl",
"framework-wifi",
"libprotobuf-java-nano",
],
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 799ac5c..05f50b0 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -18,7 +18,8 @@
#include "TrafficController.h"
-#include <bpf_shared.h>
+#include "netd.h"
+
#include <jni.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 5cd6e5d..18d2311 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -30,7 +30,6 @@
#include <bpf/BpfMap.h>
#include <bpf/BpfUtils.h>
-#include <bpf_shared.h>
#include <netjniutils/netjniutils.h>
#include <private/android_filesystem_config.h>
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java b/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java
new file mode 100644
index 0000000..91e08a8
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Looper;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.SocketAddress;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Sends mDns announcements when a service registration changes and at regular intervals.
+ *
+ * This allows maintaining other hosts' caches up-to-date. See RFC6762 8.3.
+ */
+public class MdnsAnnouncer extends MdnsPacketRepeater<MdnsAnnouncer.AnnouncementInfo> {
+ private static final long ANNOUNCEMENT_INITIAL_DELAY_MS = 1000L;
+ @VisibleForTesting
+ static final int ANNOUNCEMENT_COUNT = 8;
+
+ @NonNull
+ private final String mLogTag;
+
+ static class AnnouncementInfo implements MdnsPacketRepeater.Request {
+ @NonNull
+ private final MdnsPacket mPacket;
+ @NonNull
+ private final Supplier<Iterable<SocketAddress>> mDestinationsSupplier;
+
+ AnnouncementInfo(List<MdnsRecord> announcedRecords, List<MdnsRecord> additionalRecords,
+ Supplier<Iterable<SocketAddress>> destinationsSupplier) {
+ // Records to announce (as answers)
+ // Records to place in the "Additional records", with NSEC negative responses
+ // to mark records that have been verified unique
+ final int flags = 0x8400; // Response, authoritative (rfc6762 18.4)
+ mPacket = new MdnsPacket(flags,
+ Collections.emptyList() /* questions */,
+ announcedRecords,
+ Collections.emptyList() /* authorityRecords */,
+ additionalRecords);
+ mDestinationsSupplier = destinationsSupplier;
+ }
+
+ @Override
+ public MdnsPacket getPacket(int index) {
+ return mPacket;
+ }
+
+ @Override
+ public Iterable<SocketAddress> getDestinations(int index) {
+ return mDestinationsSupplier.get();
+ }
+
+ @Override
+ public long getDelayMs(int nextIndex) {
+ // Delay is doubled for each announcement
+ return ANNOUNCEMENT_INITIAL_DELAY_MS << (nextIndex - 1);
+ }
+
+ @Override
+ public int getNumSends() {
+ return ANNOUNCEMENT_COUNT;
+ }
+ }
+
+ public MdnsAnnouncer(@NonNull String interfaceTag, @NonNull Looper looper,
+ @NonNull MdnsReplySender replySender,
+ @Nullable PacketRepeaterCallback<AnnouncementInfo> cb) {
+ super(looper, replySender, cb);
+ mLogTag = MdnsAnnouncer.class.getSimpleName() + "/" + interfaceTag;
+ }
+
+ @Override
+ protected String getTag() {
+ return mLogTag;
+ }
+
+ // TODO: Notify MdnsRecordRepository that the records were announced for that service ID,
+ // so it can update the last advertised timestamp of the associated records.
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java
index 57c3c03..06fdd5e 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java
@@ -96,8 +96,9 @@
@Override
protected void writeData(MdnsPacketWriter writer) throws IOException {
- // No compression as per RFC3845 2.1.1
- writer.writeLabelsNoCompression(mNextDomain);
+ // Standard NSEC records should use no compression for the Next Domain Name field as per
+ // RFC3845 2.1.1, but for mDNS RFC6762 18.14 specifies that compression should be used.
+ writer.writeLabels(mNextDomain);
// type bitmaps: RFC3845 2.1.2
int typesBlockStart = 0;
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java
index 1f22fa9..c0f9b8b 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java
@@ -192,22 +192,31 @@
}
}
+ final int[] offsets;
if (suffixLength > 0) {
- for (int i = 0; i < (labels.length - suffixLength); ++i) {
- writeString(labels[i]);
- }
+ offsets = writePartialLabelsNoCompression(labels, labels.length - suffixLength);
writePointer(suffixPointer);
} else {
- int[] offsets = writeLabelsNoCompression(labels);
-
- // Add entries to the label dictionary for each suffix of the label list, including
- // the whole list itself.
- for (int i = 0, len = labels.length; i < labels.length; ++i, --len) {
- String[] value = new String[len];
- System.arraycopy(labels, i, value, 0, len);
- labelDictionary.put(offsets[i], value);
- }
+ offsets = writeLabelsNoCompression(labels);
}
+
+ // Add entries to the label dictionary for each suffix of the label list, including
+ // the whole list itself.
+ // Do not replace the last suffixLength suffixes that already have dictionary entries.
+ for (int i = 0, len = labels.length; i < labels.length - suffixLength; ++i, --len) {
+ String[] value = new String[len];
+ System.arraycopy(labels, i, value, 0, len);
+ labelDictionary.put(offsets[i], value);
+ }
+ }
+
+ private int[] writePartialLabelsNoCompression(String[] labels, int count) throws IOException {
+ int[] offsets = new int[count];
+ for (int i = 0; i < count; ++i) {
+ offsets[i] = getWritePosition();
+ writeString(labels[i]);
+ }
+ return offsets;
}
/**
@@ -216,11 +225,7 @@
* @return The offsets where each label was written to.
*/
public int[] writeLabelsNoCompression(String[] labels) throws IOException {
- int[] offsets = new int[labels.length];
- for (int i = 0; i < labels.length; ++i) {
- offsets[i] = getWritePosition();
- writeString(labels[i]);
- }
+ final int[] offsets = writePartialLabelsNoCompression(labels, labels.length);
writeUInt8(0); // NUL terminator
return offsets;
}
@@ -246,4 +251,4 @@
public DatagramPacket getPacket(SocketAddress destAddress) throws IOException {
return new DatagramPacket(data, pos, destAddress);
}
-}
\ No newline at end of file
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java b/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java
new file mode 100644
index 0000000..db7049e
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.os.Looper;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.CollectionUtils;
+
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Sends mDns probe requests to verify service records are unique on the network.
+ *
+ * TODO: implement receiving replies and handling conflicts.
+ */
+public class MdnsProber extends MdnsPacketRepeater<MdnsProber.ProbingInfo> {
+ @NonNull
+ private final String mLogTag;
+
+ public MdnsProber(@NonNull String interfaceTag, @NonNull Looper looper,
+ @NonNull MdnsReplySender replySender,
+ @NonNull PacketRepeaterCallback<ProbingInfo> cb) {
+ // 3 packets as per https://datatracker.ietf.org/doc/html/rfc6762#section-8.1
+ super(looper, replySender, cb);
+ mLogTag = MdnsProber.class.getSimpleName() + "/" + interfaceTag;
+ }
+
+ static class ProbingInfo implements Request {
+
+ private final int mServiceId;
+ @NonNull
+ private final MdnsPacket mPacket;
+ @NonNull
+ private final Supplier<Iterable<SocketAddress>> mDestinationsSupplier;
+
+ /**
+ * Create a new ProbingInfo
+ * @param serviceId Service to probe for.
+ * @param probeRecords Records to be probed for uniqueness.
+ * @param destinationsSupplier Supplier for the probe destinations. Will be called on the
+ * probe handler thread for each probe.
+ */
+ ProbingInfo(int serviceId, @NonNull List<MdnsRecord> probeRecords,
+ @NonNull Supplier<Iterable<SocketAddress>> destinationsSupplier) {
+ mServiceId = serviceId;
+ mPacket = makePacket(probeRecords);
+ mDestinationsSupplier = destinationsSupplier;
+ }
+
+ public int getServiceId() {
+ return mServiceId;
+ }
+
+ @NonNull
+ @Override
+ public MdnsPacket getPacket(int index) {
+ return mPacket;
+ }
+
+ @NonNull
+ @Override
+ public Iterable<SocketAddress> getDestinations(int index) {
+ return mDestinationsSupplier.get();
+ }
+
+ @Override
+ public long getDelayMs(int nextIndex) {
+ // As per https://datatracker.ietf.org/doc/html/rfc6762#section-8.1
+ return 250L;
+ }
+
+ @Override
+ public int getNumSends() {
+ // 3 packets as per https://datatracker.ietf.org/doc/html/rfc6762#section-8.1
+ return 3;
+ }
+
+ private static MdnsPacket makePacket(@NonNull List<MdnsRecord> records) {
+ final ArrayList<MdnsRecord> questions = new ArrayList<>(records.size());
+ for (final MdnsRecord record : records) {
+ if (containsName(questions, record.getName())) {
+ // Already added this name
+ continue;
+ }
+
+ // TODO: legacy Android mDNS used to send the first probe (only) as unicast, even
+ // though https://datatracker.ietf.org/doc/html/rfc6762#section-8.1 says they
+ // SHOULD all be. rfc6762 15.1 says that if the port is shared with another
+ // responder unicast questions should not be used, and the legacy mdnsresponder may
+ // be running, so not using unicast at all may be better. Consider using legacy
+ // behavior if this causes problems.
+ questions.add(new MdnsAnyRecord(record.getName(), false /* unicast */));
+ }
+
+ return new MdnsPacket(
+ MdnsConstants.FLAGS_QUERY,
+ questions,
+ Collections.emptyList() /* answers */,
+ records /* authorityRecords */,
+ Collections.emptyList() /* additionalRecords */);
+ }
+
+ /**
+ * Return whether the specified name is present in the list of records.
+ */
+ private static boolean containsName(@NonNull List<MdnsRecord> records,
+ @NonNull String[] name) {
+ return CollectionUtils.any(records, r -> Arrays.equals(name, r.getName()));
+ }
+ }
+
+ @NonNull
+ @Override
+ protected String getTag() {
+ return mLogTag;
+ }
+
+ @VisibleForTesting
+ protected long getInitialDelay() {
+ // First wait for a random time in 0-250ms
+ // as per https://datatracker.ietf.org/doc/html/rfc6762#section-8.1
+ return (long) (Math.random() * 250);
+ }
+
+ /**
+ * Start sending packets for probing.
+ */
+ public void startProbing(@NonNull ProbingInfo info) {
+ startProbing(info, getInitialDelay());
+ }
+
+ private void startProbing(@NonNull ProbingInfo info, long delay) {
+ startSending(info.getServiceId(), info, delay);
+ }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java
index 10b8825..00871ea 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -201,6 +201,17 @@
protected abstract void readData(MdnsPacketReader reader) throws IOException;
/**
+ * Write the first fields of the record, which are common fields for questions and answers.
+ *
+ * @param writer The writer to use.
+ */
+ public final void writeHeaderFields(MdnsPacketWriter writer) throws IOException {
+ writer.writeLabels(name);
+ writer.writeUInt16(type);
+ writer.writeUInt16(cls);
+ }
+
+ /**
* Writes the record to a packet.
*
* @param writer The writer to use.
@@ -208,9 +219,7 @@
*/
@VisibleForTesting
public final void write(MdnsPacketWriter writer, long now) throws IOException {
- writer.writeLabels(name);
- writer.writeUInt16(type);
- writer.writeUInt16(cls);
+ writeHeaderFields(writer);
writer.writeUInt32(MILLISECONDS.toSeconds(getRemainingTTL(now)));
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java b/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
index 2acd789..1fdbc5c 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
@@ -67,7 +67,8 @@
writer.writeUInt16(packet.additionalRecords.size()); // additional records count
for (MdnsRecord record : packet.questions) {
- record.write(writer, 0L);
+ // Questions do not have TTL or data
+ record.writeHeaderFields(writer);
}
for (MdnsRecord record : packet.answers) {
record.write(writer, 0L);
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
index b44d795..cb6c836 100644
--- a/service/native/include/TrafficController.h
+++ b/service/native/include/TrafficController.h
@@ -21,7 +21,7 @@
#include "android-base/thread_annotations.h"
#include "bpf/BpfMap.h"
-#include "bpf_shared.h"
+#include "netd.h"
#include "netdutils/DumpWriter.h"
#include "netdutils/NetlinkListener.h"
#include "netdutils/StatusOr.h"
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index b8a8fb4..bce9f53 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -139,7 +139,7 @@
@VisibleForTesting public static final long OEM_DENY_1_MATCH = (1 << 9);
@VisibleForTesting public static final long OEM_DENY_2_MATCH = (1 << 10);
@VisibleForTesting public static final long OEM_DENY_3_MATCH = (1 << 11);
- // LINT.ThenChange(packages/modules/Connectivity/bpf_progs/bpf_shared.h)
+ // LINT.ThenChange(packages/modules/Connectivity/bpf_progs/netd.h)
private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index a44494c..e3e12fd 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -279,12 +279,14 @@
import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.NetworkOffer;
+import com.android.server.connectivity.NetworkPreferenceList;
import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
-import com.android.server.connectivity.ProfileNetworkPreferenceList;
+import com.android.server.connectivity.ProfileNetworkPreferenceInfo;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.UidRangeUtils;
+import com.android.server.connectivity.wear.CompanionDeviceManagerProxyService;
import libcore.io.IoUtils;
@@ -434,6 +436,7 @@
*/
@GuardedBy("mTNSLock")
private TestNetworkService mTNS;
+ private final CompanionDeviceManagerProxyService mCdmps;
private final Object mTNSLock = new Object();
@@ -1585,6 +1588,12 @@
mIngressRateLimit = ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(
mContext);
+
+ if (SdkLevel.isAtLeastT()) {
+ mCdmps = new CompanionDeviceManagerProxyService(context);
+ } else {
+ mCdmps = null;
+ }
}
/**
@@ -3413,7 +3422,7 @@
if (!mProfileNetworkPreferences.isEmpty()) {
pw.println("Profile preferences:");
pw.increaseIndent();
- pw.println(mProfileNetworkPreferences.preferences);
+ pw.println(mProfileNetworkPreferences);
pw.decreaseIndent();
}
if (!mOemNetworkPreferences.isEmpty()) {
@@ -5498,10 +5507,8 @@
break;
}
case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
- final Pair<List<ProfileNetworkPreferenceList.Preference>,
- IOnCompleteListener> arg =
- (Pair<List<ProfileNetworkPreferenceList.Preference>,
- IOnCompleteListener>) msg.obj;
+ final Pair<List<ProfileNetworkPreferenceInfo>, IOnCompleteListener> arg =
+ (Pair<List<ProfileNetworkPreferenceInfo>, IOnCompleteListener>) msg.obj;
handleSetProfileNetworkPreference(arg.first, arg.second);
break;
}
@@ -6142,7 +6149,7 @@
private void onUserRemoved(@NonNull final UserHandle user) {
// If there was a network preference for this user, remove it.
handleSetProfileNetworkPreference(
- List.of(new ProfileNetworkPreferenceList.Preference(user, null, true)),
+ List.of(new ProfileNetworkPreferenceInfo(user, null, true)),
null /* listener */);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
@@ -7112,8 +7119,8 @@
// Current per-profile network preferences. This object follows the same threading rules as
// the OEM network preferences above.
@NonNull
- private ProfileNetworkPreferenceList mProfileNetworkPreferences =
- new ProfileNetworkPreferenceList();
+ private NetworkPreferenceList<UserHandle, ProfileNetworkPreferenceInfo>
+ mProfileNetworkPreferences = new NetworkPreferenceList<>();
// A set of UIDs that should use mobile data preferentially if available. This object follows
// the same threading rules as the OEM network preferences above.
@@ -10816,7 +10823,7 @@
+ "or the device owner must be set. ");
}
- final List<ProfileNetworkPreferenceList.Preference> preferenceList = new ArrayList<>();
+ final List<ProfileNetworkPreferenceInfo> preferenceList = new ArrayList<>();
boolean hasDefaultPreference = false;
for (final ProfileNetworkPreference preference : preferences) {
final NetworkCapabilities nc;
@@ -10863,8 +10870,7 @@
throw new IllegalArgumentException(
"Invalid preference in setProfileNetworkPreferences");
}
- preferenceList.add(new ProfileNetworkPreferenceList.Preference(
- profile, nc, allowFallback));
+ preferenceList.add(new ProfileNetworkPreferenceInfo(profile, nc, allowFallback));
if (hasDefaultPreference && preferenceList.size() > 1) {
throw new IllegalArgumentException(
"Default profile preference should not be set along with other preference");
@@ -10913,9 +10919,9 @@
}
private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
- @NonNull final ProfileNetworkPreferenceList prefs) {
+ @NonNull final NetworkPreferenceList<UserHandle, ProfileNetworkPreferenceInfo> prefs) {
final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
- for (final ProfileNetworkPreferenceList.Preference pref : prefs.preferences) {
+ for (final ProfileNetworkPreferenceInfo pref : prefs) {
// The NRI for a user should contain the request for capabilities.
// If fallback to default network is needed then NRI should include
// the request for the default network. Create an image of it to
@@ -10945,12 +10951,12 @@
*
*/
private boolean isRangeAlreadyInPreferenceList(
- @NonNull List<ProfileNetworkPreferenceList.Preference> preferenceList,
+ @NonNull List<ProfileNetworkPreferenceInfo> preferenceList,
@NonNull Set<UidRange> uidRangeSet) {
if (uidRangeSet.size() == 0 || preferenceList.size() == 0) {
return false;
}
- for (ProfileNetworkPreferenceList.Preference pref : preferenceList) {
+ for (ProfileNetworkPreferenceInfo pref : preferenceList) {
if (UidRangeUtils.doesRangeSetOverlap(
UidRange.fromIntRanges(pref.capabilities.getUids()), uidRangeSet)) {
return true;
@@ -10960,7 +10966,7 @@
}
private void handleSetProfileNetworkPreference(
- @NonNull final List<ProfileNetworkPreferenceList.Preference> preferenceList,
+ @NonNull final List<ProfileNetworkPreferenceInfo> preferenceList,
@Nullable final IOnCompleteListener listener) {
/*
* handleSetProfileNetworkPreference is always called for single user.
@@ -10969,9 +10975,8 @@
* Clear all the existing preferences for the user before applying new preferences.
*
*/
- mProfileNetworkPreferences = mProfileNetworkPreferences.withoutUser(
- preferenceList.get(0).user);
- for (final ProfileNetworkPreferenceList.Preference preference : preferenceList) {
+ mProfileNetworkPreferences = mProfileNetworkPreferences.minus(preferenceList.get(0).user);
+ for (final ProfileNetworkPreferenceInfo preference : preferenceList) {
mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
}
@@ -11494,4 +11499,10 @@
mBpfNetMaps.replaceUidChain(chain, uids);
}
+
+ @Override
+ public IBinder getCompanionDeviceManagerProxyService() {
+ enforceNetworkStackPermission(mContext);
+ return mCdmps;
+ }
}
diff --git a/service/src/com/android/server/connectivity/NetworkPreferenceList.java b/service/src/com/android/server/connectivity/NetworkPreferenceList.java
new file mode 100644
index 0000000..fa6d157
--- /dev/null
+++ b/service/src/com/android/server/connectivity/NetworkPreferenceList.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A generic data class containing network preferences.
+ * @param <K> The type of key in T
+ * @param <T> The type of preference stored in this preference list
+ */
+public class NetworkPreferenceList<K, T extends NetworkPreferenceList.NetworkPreference<K>>
+ implements Iterable<T> {
+ /**
+ * A network preference
+ * @param <K> the type of key by which this preference is indexed. A NetworkPreferenceList
+ * can have multiple preferences associated with this key, but has methods to
+ * work on the keys.
+ */
+ public interface NetworkPreference<K> {
+ /**
+ * Whether this preference codes for cancelling the preference for this key
+ *
+ * A preference that codes for cancelling is removed from the list of preferences, since
+ * it means the behavior should be the same as if there was no preference for this key.
+ */
+ boolean isCancel();
+ /** The key */
+ K getKey();
+ }
+
+ @NonNull private final List<T> mPreferences;
+
+ public NetworkPreferenceList() {
+ mPreferences = Collections.EMPTY_LIST;
+ }
+
+ private NetworkPreferenceList(@NonNull final List<T> list) {
+ mPreferences = Collections.unmodifiableList(list);
+ }
+
+ /**
+ * Returns a new object consisting of this object plus the passed preference.
+ *
+ * If the passed preference is a cancel preference (see {@link NetworkPreference#isCancel()}
+ * then it is not added.
+ */
+ public NetworkPreferenceList<K, T> plus(@NonNull final T pref) {
+ final ArrayList<T> newPrefs = new ArrayList<>(mPreferences);
+ if (!pref.isCancel()) {
+ newPrefs.add(pref);
+ }
+ return new NetworkPreferenceList<>(newPrefs);
+ }
+
+ /**
+ * Remove all preferences corresponding to a key.
+ */
+ public NetworkPreferenceList<K, T> minus(@NonNull final K key) {
+ final ArrayList<T> newPrefs = new ArrayList<>();
+ for (final T existingPref : mPreferences) {
+ if (!existingPref.getKey().equals(key)) {
+ newPrefs.add(existingPref);
+ }
+ }
+ return new NetworkPreferenceList<>(newPrefs);
+ }
+
+ public boolean isEmpty() {
+ return mPreferences.isEmpty();
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return mPreferences.iterator();
+ }
+
+ @Override public String toString() {
+ return "NetworkPreferenceList : " + mPreferences;
+ }
+}
diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceInfo.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceInfo.java
new file mode 100644
index 0000000..10f3886
--- /dev/null
+++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceInfo.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.UserHandle;
+
+/**
+ * A single profile preference, as it applies to a given user profile.
+ */
+public class ProfileNetworkPreferenceInfo
+ implements NetworkPreferenceList.NetworkPreference<UserHandle> {
+ @NonNull
+ public final UserHandle user;
+ // Capabilities are only null when sending an object to remove the setting for a user
+ @Nullable
+ public final NetworkCapabilities capabilities;
+ public final boolean allowFallback;
+
+ public ProfileNetworkPreferenceInfo(@NonNull final UserHandle user,
+ @Nullable final NetworkCapabilities capabilities,
+ final boolean allowFallback) {
+ this.user = user;
+ this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
+ this.allowFallback = allowFallback;
+ }
+
+ @Override
+ public boolean isCancel() {
+ return null == capabilities;
+ }
+
+ @Override
+ @NonNull
+ public UserHandle getKey() {
+ return user;
+ }
+
+ /** toString */
+ public String toString() {
+ return "[ProfileNetworkPreference user=" + user
+ + " caps=" + capabilities
+ + " allowFallback=" + allowFallback
+ + "]";
+ }
+}
diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
deleted file mode 100644
index 5bafef9..0000000
--- a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.NetworkCapabilities;
-import android.os.UserHandle;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A data class containing all the per-profile network preferences.
- *
- * A given profile can only have one preference.
- */
-public class ProfileNetworkPreferenceList {
- /**
- * A single preference, as it applies to a given user profile.
- */
- public static class Preference {
- @NonNull public final UserHandle user;
- // Capabilities are only null when sending an object to remove the setting for a user
- @Nullable public final NetworkCapabilities capabilities;
- public final boolean allowFallback;
-
- public Preference(@NonNull final UserHandle user,
- @Nullable final NetworkCapabilities capabilities,
- final boolean allowFallback) {
- this.user = user;
- this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
- this.allowFallback = allowFallback;
- }
-
- /** toString */
- public String toString() {
- return "[ProfileNetworkPreference user=" + user
- + " caps=" + capabilities
- + " allowFallback=" + allowFallback
- + "]";
- }
- }
-
- @NonNull public final List<Preference> preferences;
-
- public ProfileNetworkPreferenceList() {
- preferences = Collections.EMPTY_LIST;
- }
-
- private ProfileNetworkPreferenceList(@NonNull final List<Preference> list) {
- preferences = Collections.unmodifiableList(list);
- }
-
- /**
- * Returns a new object consisting of this object plus the passed preference.
- *
- * It is not expected that unwanted preference already exists for the same user.
- * All preferences for the user that were previously configured should be cleared before
- * adding a new preference.
- * Passing a Preference object containing a null capabilities object is equivalent
- * to removing the preference for this user.
- */
- public ProfileNetworkPreferenceList plus(@NonNull final Preference pref) {
- final ArrayList<Preference> newPrefs = new ArrayList<>(preferences);
- if (null != pref.capabilities) {
- newPrefs.add(pref);
- }
- return new ProfileNetworkPreferenceList(newPrefs);
- }
-
- /**
- * Remove all preferences corresponding to a user.
- */
- public ProfileNetworkPreferenceList withoutUser(UserHandle user) {
- final ArrayList<Preference> newPrefs = new ArrayList<>();
- for (final Preference existingPref : preferences) {
- if (!existingPref.user.equals(user)) {
- newPrefs.add(existingPref);
- }
- }
- return new ProfileNetworkPreferenceList(newPrefs);
- }
-
- public boolean isEmpty() {
- return preferences.isEmpty();
- }
-}
diff --git a/service/src/com/android/server/connectivity/wear/CompanionDeviceManagerProxyService.java b/service/src/com/android/server/connectivity/wear/CompanionDeviceManagerProxyService.java
new file mode 100644
index 0000000..7e1cf5c
--- /dev/null
+++ b/service/src/com/android/server/connectivity/wear/CompanionDeviceManagerProxyService.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.wear;
+
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceManager;
+import android.content.Context;
+import android.net.wear.ICompanionDeviceManagerProxy;
+import android.os.Binder;
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.net.module.util.PermissionUtils;
+
+import java.util.List;
+
+/**
+ * A proxy for {@link CompanionDeviceManager}, for use by Tethering with NetworkStack permissions.
+ */
+public class CompanionDeviceManagerProxyService extends ICompanionDeviceManagerProxy.Stub {
+ private final Context mContext;
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public CompanionDeviceManagerProxyService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public List<AssociationInfo> getAllAssociations() {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final CompanionDeviceManager cdm = mContext.getSystemService(
+ CompanionDeviceManager.class);
+ return cdm.getAllAssociations();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/tests/common/java/android/net/VpnTransportInfoTest.java b/tests/common/java/android/net/VpnTransportInfoTest.java
index f32ab8b..2d01df7 100644
--- a/tests/common/java/android/net/VpnTransportInfoTest.java
+++ b/tests/common/java/android/net/VpnTransportInfoTest.java
@@ -94,20 +94,20 @@
@DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
@Test
- public void testGetBypassable_beforeU() {
+ public void testIsBypassable_beforeU() {
final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
- assertThrows(UnsupportedOperationException.class, () -> v.getBypassable());
+ assertThrows(UnsupportedOperationException.class, () -> v.isBypassable());
}
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
@Test
- public void testGetBypassable_afterU() {
+ public void testIsBypassable_afterU() {
final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
- assertFalse(v.getBypassable());
+ assertFalse(v.isBypassable());
final VpnTransportInfo v2 =
new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345", true, false);
- assertTrue(v2.getBypassable());
+ assertTrue(v2.isBypassable());
}
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 96acac3..7624f9c 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2408,6 +2408,15 @@
}
private void doTestBlockedStatusCallback() throws Exception {
+ // The test will need a stable active network that is persistent during the test.
+ // Try to connect to a wifi network and wait for it becomes the default network before
+ // starting the test to prevent from sudden active network change caused by previous
+ // executed tests.
+ if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ final Network expectedDefaultNetwork = mCtsNetUtils.ensureWifiConnected();
+ mCtsNetUtils.expectNetworkIsSystemDefault(expectedDefaultNetwork);
+ }
+
final DetailedBlockedStatusCallback myUidCallback = new DetailedBlockedStatusCallback();
final DetailedBlockedStatusCallback otherUidCallback = new DetailedBlockedStatusCallback();
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index 3f2ff9d..df3a4aa 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -170,7 +170,7 @@
}
}
- private Network expectNetworkIsSystemDefault(Network network)
+ public Network expectNetworkIsSystemDefault(Network network)
throws Exception {
final CompletableFuture<Network> future = new CompletableFuture();
final NetworkCallback cb = new NetworkCallback() {
diff --git a/tests/native/connectivity_native_test/bpf_base_test.cpp b/tests/native/connectivity_native_test/bpf_base_test.cpp
new file mode 100644
index 0000000..f164b2f
--- /dev/null
+++ b/tests/native/connectivity_native_test/bpf_base_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <linux/inet_diag.h>
+#include <linux/sock_diag.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <cutils/qtaguid.h>
+#include <processgroup/processgroup.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <netdutils/NetNativeTestBase.h>
+
+#include "bpf/BpfMap.h"
+#include "bpf/BpfUtils.h"
+#include "bpf_shared.h"
+
+using android::base::Result;
+
+namespace android {
+namespace bpf {
+
+// Use the upper limit of uid to avoid conflict with real app uids. We can't use UID_MAX because
+// it's -1, which is INVALID_UID.
+constexpr uid_t TEST_UID = UID_MAX - 1;
+constexpr uint32_t TEST_TAG = 42;
+
+class BpfBasicTest : public NetNativeTestBase {
+ protected:
+ BpfBasicTest() {}
+};
+
+TEST_F(BpfBasicTest, TestCgroupMounted) {
+ std::string cg2_path;
+ ASSERT_EQ(true, CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cg2_path));
+ ASSERT_EQ(0, access(cg2_path.c_str(), R_OK));
+ ASSERT_EQ(0, access((cg2_path + "/cgroup.controllers").c_str(), R_OK));
+}
+
+TEST_F(BpfBasicTest, TestTrafficControllerSetUp) {
+ ASSERT_EQ(0, access(BPF_EGRESS_PROG_PATH, R_OK));
+ ASSERT_EQ(0, access(BPF_INGRESS_PROG_PATH, R_OK));
+ ASSERT_EQ(0, access(XT_BPF_INGRESS_PROG_PATH, R_OK));
+ ASSERT_EQ(0, access(XT_BPF_EGRESS_PROG_PATH, R_OK));
+ ASSERT_EQ(0, access(COOKIE_TAG_MAP_PATH, R_OK));
+ ASSERT_EQ(0, access(UID_COUNTERSET_MAP_PATH, R_OK));
+ ASSERT_EQ(0, access(STATS_MAP_A_PATH, R_OK));
+ ASSERT_EQ(0, access(STATS_MAP_B_PATH, R_OK));
+ ASSERT_EQ(0, access(IFACE_INDEX_NAME_MAP_PATH, R_OK));
+ ASSERT_EQ(0, access(IFACE_STATS_MAP_PATH, R_OK));
+ ASSERT_EQ(0, access(CONFIGURATION_MAP_PATH, R_OK));
+ ASSERT_EQ(0, access(UID_OWNER_MAP_PATH, R_OK));
+}
+
+TEST_F(BpfBasicTest, TestSocketFilterSetUp) {
+ ASSERT_EQ(0, access(CGROUP_SOCKET_PROG_PATH, R_OK));
+ ASSERT_EQ(0, access(UID_PERMISSION_MAP_PATH, R_OK));
+}
+
+TEST_F(BpfBasicTest, TestTagSocket) {
+ BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
+ ASSERT_TRUE(cookieTagMap.isValid());
+ int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_LE(0, sock);
+ uint64_t cookie = getSocketCookie(sock);
+ ASSERT_NE(NONEXISTENT_COOKIE, cookie);
+ ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID));
+ Result<UidTagValue> tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_RESULT_OK(tagResult);
+ ASSERT_EQ(TEST_UID, tagResult.value().uid);
+ ASSERT_EQ(TEST_TAG, tagResult.value().tag);
+ ASSERT_EQ(0, qtaguid_untagSocket(sock));
+ tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_FALSE(tagResult.ok());
+ ASSERT_EQ(ENOENT, tagResult.error().code());
+}
+
+TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) {
+ BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
+ ASSERT_TRUE(cookieTagMap.isValid());
+ int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_LE(0, sock);
+ uint64_t cookie = getSocketCookie(sock);
+ ASSERT_NE(NONEXISTENT_COOKIE, cookie);
+ ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID));
+ Result<UidTagValue> tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_RESULT_OK(tagResult);
+ ASSERT_EQ(TEST_UID, tagResult.value().uid);
+ ASSERT_EQ(TEST_TAG, tagResult.value().tag);
+ ASSERT_EQ(0, close(sock));
+ // Check map periodically until sk destroy handler have done its job.
+ for (int i = 0; i < 10; i++) {
+ usleep(5000); // 5ms
+ tagResult = cookieTagMap.readValue(cookie);
+ if (!tagResult.ok()) {
+ ASSERT_EQ(ENOENT, tagResult.error().code());
+ return;
+ }
+ }
+ FAIL() << "socket tag still exist after 50ms";
+}
+
+}
+}
diff --git a/tests/native/utilities/firewall.h b/tests/native/utilities/firewall.h
index 185559b..1e7e987 100644
--- a/tests/native/utilities/firewall.h
+++ b/tests/native/utilities/firewall.h
@@ -19,7 +19,7 @@
#include <android-base/thread_annotations.h>
#include <bpf/BpfMap.h>
-#include <bpf_shared.h>
+#include "netd.h"
using android::base::Result;
using android::bpf::BpfMap;
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 8ed735a..209430a 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -74,6 +74,7 @@
"java/com/android/server/connectivity/VpnTest.java",
"java/com/android/server/net/ipmemorystore/*.java",
"java/com/android/server/connectivity/mdns/**/*.java",
+ "java/com/android/server/connectivity/mdns/**/*.kt",
]
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 677e7b6..3f87ffd 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -1893,7 +1893,7 @@
// Check if allowBypass is set or not.
assertTrue(nacCaptor.getValue().isBypassableVpn());
- assertTrue(((VpnTransportInfo) ncCaptor.getValue().getTransportInfo()).getBypassable());
+ assertTrue(((VpnTransportInfo) ncCaptor.getValue().getTransportInfo()).isBypassable());
return new PlatformVpnSnapshot(vpn, nwCb, ikeCb, childCb);
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
new file mode 100644
index 0000000..e9325d5
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.net.InetAddresses.parseNumericAddress
+import android.os.Build
+import android.os.HandlerThread
+import android.os.SystemClock
+import com.android.internal.util.HexDump
+import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import java.net.DatagramPacket
+import java.net.Inet6Address
+import java.net.InetAddress
+import java.net.InetSocketAddress
+import java.net.MulticastSocket
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+
+private const val FIRST_ANNOUNCES_DELAY = 100L
+private const val FIRST_ANNOUNCES_COUNT = 2
+private const val NEXT_ANNOUNCES_DELAY = 1L
+private const val TEST_TIMEOUT_MS = 1000L
+
+private val destinationsSupplier = {
+ listOf(InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT)) }
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsAnnouncerTest {
+
+ private val thread = HandlerThread(MdnsAnnouncerTest::class.simpleName)
+ private val socket = mock(MulticastSocket::class.java)
+ private val buffer = ByteArray(1500)
+
+ @Before
+ fun setUp() {
+ thread.start()
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ private class TestAnnouncementInfo(
+ announcedRecords: List<MdnsRecord>,
+ additionalRecords: List<MdnsRecord>
+ )
+ : AnnouncementInfo(announcedRecords, additionalRecords, destinationsSupplier) {
+ override fun getDelayMs(nextIndex: Int) =
+ if (nextIndex < FIRST_ANNOUNCES_COUNT) {
+ FIRST_ANNOUNCES_DELAY
+ } else {
+ NEXT_ANNOUNCES_DELAY
+ }
+ }
+
+ @Test
+ fun testAnnounce() {
+ val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ @Suppress("UNCHECKED_CAST")
+ val cb = mock(MdnsPacketRepeater.PacketRepeaterCallback::class.java)
+ as MdnsPacketRepeater.PacketRepeaterCallback<AnnouncementInfo>
+ val announcer = MdnsAnnouncer("testiface", thread.looper, replySender, cb)
+ /*
+ The expected packet replicates records announced when registering a service, as observed in
+ the legacy mDNS implementation (some ordering differs to be more readable).
+ Obtained with scapy 2.5.0 RC3 (2.4.5 does not compress TLDs like .arpa properly) with:
+ scapy.raw(scapy.dns_compress(scapy.DNS(rd=0, qr=1, aa=1,
+ qd = None,
+ an =
+ scapy.DNSRR(type='PTR', rrname='123.0.2.192.in-addr.arpa.', rdata='Android.local',
+ rclass=0x8001, ttl=120) /
+ scapy.DNSRR(type='PTR',
+ rrname='3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
+ rdata='Android.local', rclass=0x8001, ttl=120) /
+ scapy.DNSRR(type='PTR',
+ rrname='6.5.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
+ rdata='Android.local', rclass=0x8001, ttl=120) /
+ scapy.DNSRR(type='PTR', rrname='_testtype._tcp.local',
+ rdata='testservice._testtype._tcp.local', rclass='IN', ttl=4500) /
+ scapy.DNSRRSRV(rrname='testservice._testtype._tcp.local', rclass=0x8001, port=31234,
+ target='Android.local', ttl=120) /
+ scapy.DNSRR(type='TXT', rrname='testservice._testtype._tcp.local', rclass=0x8001, rdata='',
+ ttl=4500) /
+ scapy.DNSRR(type='A', rrname='Android.local', rclass=0x8001, rdata='192.0.2.123', ttl=120) /
+ scapy.DNSRR(type='AAAA', rrname='Android.local', rclass=0x8001, rdata='2001:db8::123',
+ ttl=120) /
+ scapy.DNSRR(type='AAAA', rrname='Android.local', rclass=0x8001, rdata='2001:db8::456',
+ ttl=120),
+ ar =
+ scapy.DNSRRNSEC(rrname='123.0.2.192.in-addr.arpa.', rclass=0x8001, ttl=120,
+ nextname='123.0.2.192.in-addr.arpa.', typebitmaps=[12]) /
+ scapy.DNSRRNSEC(
+ rrname='3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
+ rclass=0x8001, ttl=120,
+ nextname='3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
+ typebitmaps=[12]) /
+ scapy.DNSRRNSEC(
+ rrname='6.5.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
+ rclass=0x8001, ttl=120,
+ nextname='6.5.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
+ typebitmaps=[12]) /
+ scapy.DNSRRNSEC(
+ rrname='testservice._testtype._tcp.local', rclass=0x8001, ttl=4500,
+ nextname='testservice._testtype._tcp.local', typebitmaps=[16, 33]) /
+ scapy.DNSRRNSEC(
+ rrname='Android.local', rclass=0x8001, ttl=120, nextname='Android.local',
+ typebitmaps=[1, 28]))
+ )).hex().upper()
+ */
+ val expected = "00008400000000090000000503313233013001320331393207696E2D61646472046172706" +
+ "100000C800100000078000F07416E64726F6964056C6F63616C00013301320131013001300130013" +
+ "00130013001300130013001300130013001300130013001300130013001300130013001380142014" +
+ "40130013101300130013203697036C020000C8001000000780002C030013601350134C045000C800" +
+ "1000000780002C030095F7465737474797065045F746370C038000C000100001194000E0B7465737" +
+ "473657276696365C0A5C0C000218001000000780008000000007A02C030C0C000108001000011940" +
+ "000C03000018001000000780004C000027BC030001C800100000078001020010DB80000000000000" +
+ "00000000123C030001C800100000078001020010DB8000000000000000000000456C00C002F80010" +
+ "00000780006C00C00020008C03F002F8001000000780006C03F00020008C091002F8001000000780" +
+ "006C09100020008C0C0002F8001000011940009C0C000050000800040C030002F800100000078000" +
+ "8C030000440000008"
+
+ val hostname = arrayOf("Android", "local")
+ val serviceType = arrayOf("_testtype", "_tcp", "local")
+ val serviceName = arrayOf("testservice", "_testtype", "_tcp", "local")
+ val v4Addr = parseNumericAddress("192.0.2.123")
+ val v6Addr1 = parseNumericAddress("2001:DB8::123")
+ val v6Addr2 = parseNumericAddress("2001:DB8::456")
+ val v4AddrRev = arrayOf("123", "0", "2", "192", "in-addr", "arpa")
+ val v6Addr1Rev = getReverseV6AddressName(v6Addr1)
+ val v6Addr2Rev = getReverseV6AddressName(v6Addr2)
+
+ val announcedRecords = listOf(
+ // Reverse address records
+ MdnsPointerRecord(v4AddrRev,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ hostname),
+ MdnsPointerRecord(v6Addr1Rev,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ hostname),
+ MdnsPointerRecord(v6Addr2Rev,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ hostname),
+ // Service registration records (RFC6763)
+ MdnsPointerRecord(
+ serviceType,
+ 0L /* receiptTimeMillis */,
+ // Not a unique name owned by the announcer, so cacheFlush=false
+ false /* cacheFlush */,
+ 4500000L /* ttlMillis */,
+ serviceName),
+ MdnsServiceRecord(
+ serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ 31234 /* servicePort */,
+ hostname),
+ MdnsTextRecord(
+ serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 4500000L /* ttlMillis */,
+ emptyList() /* entries */),
+ // Address records for the hostname
+ MdnsInetAddressRecord(hostname,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ v4Addr),
+ MdnsInetAddressRecord(hostname,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ v6Addr1),
+ MdnsInetAddressRecord(hostname,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ v6Addr2))
+ // Negative responses (RFC6762 6.1)
+ val additionalRecords = listOf(
+ MdnsNsecRecord(v4AddrRev,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ v4AddrRev,
+ intArrayOf(MdnsRecord.TYPE_PTR)),
+ MdnsNsecRecord(v6Addr1Rev,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ v6Addr1Rev,
+ intArrayOf(MdnsRecord.TYPE_PTR)),
+ MdnsNsecRecord(v6Addr2Rev,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ v6Addr2Rev,
+ intArrayOf(MdnsRecord.TYPE_PTR)),
+ MdnsNsecRecord(serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 4500000L /* ttlMillis */,
+ serviceName,
+ intArrayOf(MdnsRecord.TYPE_TXT, MdnsRecord.TYPE_SRV)),
+ MdnsNsecRecord(hostname,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 120000L /* ttlMillis */,
+ hostname,
+ intArrayOf(MdnsRecord.TYPE_A, MdnsRecord.TYPE_AAAA)))
+ val request = TestAnnouncementInfo(announcedRecords, additionalRecords)
+
+ val timeStart = SystemClock.elapsedRealtime()
+ val startDelay = 50L
+ val sendId = 1
+ announcer.startSending(sendId, request, startDelay)
+
+ val captor = ArgumentCaptor.forClass(DatagramPacket::class.java)
+ repeat(FIRST_ANNOUNCES_COUNT) { i ->
+ verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, request)
+ verify(socket, atLeast(i + 1)).send(any())
+ val now = SystemClock.elapsedRealtime()
+ assertTrue(now > timeStart + startDelay + i * FIRST_ANNOUNCES_DELAY)
+ assertTrue(now < timeStart + startDelay + (i + 1) * FIRST_ANNOUNCES_DELAY)
+ }
+
+ // Subsequent announces should happen quickly (NEXT_ANNOUNCES_DELAY)
+ verify(socket, timeout(TEST_TIMEOUT_MS).times(MdnsAnnouncer.ANNOUNCEMENT_COUNT))
+ .send(captor.capture())
+ verify(cb, timeout(TEST_TIMEOUT_MS)).onFinished(request)
+
+ captor.allValues.forEach {
+ assertEquals(expected, HexDump.toHexString(it.data))
+ }
+ }
+}
+
+/**
+ * Compute 2001:db8::1 --> 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.1.0.0.2.ip6.arpa
+ */
+private fun getReverseV6AddressName(addr: InetAddress): Array<String> {
+ assertTrue(addr is Inet6Address)
+ return addr.address.flatMapTo(mutableListOf("arpa", "ip6")) {
+ HexDump.toHexString(it).toCharArray().map(Char::toString)
+ }.reversed().toTypedArray()
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
new file mode 100644
index 0000000..5c9c294
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.net.InetAddresses
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import java.net.InetSocketAddress
+import kotlin.test.assertContentEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsPacketWriterTest {
+ @Test
+ fun testNameCompression() {
+ val writer = MdnsPacketWriter(ByteArray(1000))
+ writer.writeLabels(arrayOf("my", "first", "name"))
+ writer.writeLabels(arrayOf("my", "second", "name"))
+ writer.writeLabels(arrayOf("other", "first", "name"))
+ writer.writeLabels(arrayOf("my", "second", "name"))
+ writer.writeLabels(arrayOf("unrelated"))
+
+ val packet = writer.getPacket(
+ InetSocketAddress(InetAddresses.parseNumericAddress("2001:db8::123"), 123))
+
+ // Each label takes length + 1. So "first.name" offset = 3, "name" offset = 9
+ val expected = "my".label() + "first".label() + "name".label() + 0x00.toByte() +
+ // "my.second.name" offset = 15
+ "my".label() + "second".label() + byteArrayOf(0xC0.toByte(), 9) +
+ "other".label() + byteArrayOf(0xC0.toByte(), 3) +
+ byteArrayOf(0xC0.toByte(), 15) +
+ "unrelated".label() + 0x00.toByte()
+
+ assertContentEquals(expected, packet.data.copyOfRange(0, packet.length))
+ }
+}
+
+private fun String.label() = byteArrayOf(length.toByte()) + encodeToByteArray()
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
new file mode 100644
index 0000000..419121c
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import com.android.internal.util.HexDump
+import com.android.server.connectivity.mdns.MdnsProber.ProbingInfo
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import java.net.DatagramPacket
+import java.net.InetSocketAddress
+import java.net.MulticastSocket
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+private val destinationsSupplier = {
+ listOf(InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT)) }
+
+private const val TEST_TIMEOUT_MS = 10_000L
+private const val SHORT_TIMEOUT_MS = 200L
+
+private val TEST_SERVICE_NAME_1 = arrayOf("testservice", "_nmt", "_tcp", "local")
+private val TEST_SERVICE_NAME_2 = arrayOf("testservice2", "_nmt", "_tcp", "local")
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsProberTest {
+ private val thread = HandlerThread(MdnsProberTest::class.simpleName)
+ private val socket = mock(MulticastSocket::class.java)
+ @Suppress("UNCHECKED_CAST")
+ private val cb = mock(MdnsPacketRepeater.PacketRepeaterCallback::class.java)
+ as MdnsPacketRepeater.PacketRepeaterCallback<ProbingInfo>
+ private val buffer = ByteArray(1500)
+
+ @Before
+ fun setUp() {
+ thread.start()
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ private class TestProbeInfo(probeRecords: List<MdnsRecord>, private val delayMs: Long = 1L) :
+ ProbingInfo(1 /* serviceId */, probeRecords, destinationsSupplier) {
+ // Just send the packets quickly. Timing-related tests for MdnsPacketRepeater are already
+ // done in MdnsAnnouncerTest.
+ override fun getDelayMs(nextIndex: Int) = delayMs
+ }
+
+ private class TestProber(
+ looper: Looper,
+ replySender: MdnsReplySender,
+ cb: PacketRepeaterCallback<ProbingInfo>
+ ) : MdnsProber("testiface", looper, replySender, cb) {
+ override fun getInitialDelay() = 0L
+ }
+
+ private fun assertProbesSent(probeInfo: TestProbeInfo, expectedHex: String) {
+ repeat(probeInfo.numSends) { i ->
+ verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, probeInfo)
+ // If the probe interval is short, more than (i+1) probes may have been sent already
+ verify(socket, atLeast(i + 1)).send(any())
+ }
+
+ val captor = ArgumentCaptor.forClass(DatagramPacket::class.java)
+ // There should be exactly numSends probes sent at the end
+ verify(socket, times(probeInfo.numSends)).send(captor.capture())
+
+ captor.allValues.forEach {
+ assertEquals(expectedHex, HexDump.toHexString(it.data))
+ }
+ verify(cb, timeout(TEST_TIMEOUT_MS)).onFinished(probeInfo)
+ }
+
+ private fun makeServiceRecord(name: Array<String>, port: Int) = MdnsServiceRecord(
+ name,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 120_000L /* ttlMillis */,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ port,
+ arrayOf("myhostname", "local"))
+
+ @Test
+ fun testProbe() {
+ val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val prober = TestProber(thread.looper, replySender, cb)
+ val probeInfo = TestProbeInfo(
+ listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890)))
+ prober.startProbing(probeInfo)
+
+ // Inspect with python3:
+ // import scapy.all as scapy; scapy.DNS(bytes.fromhex('[bytes]')).show2()
+ val expected = "0000000000010000000100000B7465737473657276696365045F6E6D74045F746370056C" +
+ "6F63616C0000FF0001C00C002100010000007800130000000094020A6D79686F73746E616D65C022"
+ assertProbesSent(probeInfo, expected)
+ }
+
+ @Test
+ fun testProbeMultipleRecords() {
+ val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val prober = TestProber(thread.looper, replySender, cb)
+ val probeInfo = TestProbeInfo(listOf(
+ makeServiceRecord(TEST_SERVICE_NAME_1, 37890),
+ makeServiceRecord(TEST_SERVICE_NAME_2, 37891),
+ MdnsTextRecord(
+ // Same name as the first record; there should not be 2 duplicated questions
+ TEST_SERVICE_NAME_1,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 120_000L /* ttlMillis */,
+ listOf(MdnsServiceInfo.TextEntry("testKey", "testValue")))))
+ prober.startProbing(probeInfo)
+
+ /*
+ Expected data obtained with:
+ scapy.raw(scapy.dns_compress(scapy.DNS(rd=0,
+ qd =
+ scapy.DNSQR(qname='testservice._nmt._tcp.local.', qtype='ALL') /
+ scapy.DNSQR(qname='testservice2._nmt._tcp.local.', qtype='ALL'),
+ ns=
+ scapy.DNSRRSRV(rrname='testservice._nmt._tcp.local.', type='SRV', ttl=120,
+ port=37890, target='myhostname.local.') /
+ scapy.DNSRRSRV(rrname='testservice2._nmt._tcp.local.', type='SRV', ttl=120,
+ port=37891, target='myhostname.local.') /
+ scapy.DNSRR(type='TXT', ttl=120, rrname='testservice._nmt._tcp.local.',
+ rdata='testKey=testValue'))
+ )).hex().upper()
+ */
+ val expected = "0000000000020000000300000B7465737473657276696365045F6E6D74045F746370056C6" +
+ "F63616C0000FF00010C746573747365727669636532C01800FF0001C00C002100010000007800130" +
+ "000000094020A6D79686F73746E616D65C022C02D00210001000000780008000000009403C052C00" +
+ "C0010000100000078001211746573744B65793D7465737456616C7565"
+ assertProbesSent(probeInfo, expected)
+ }
+
+ @Test
+ fun testStopProbing() {
+ val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val prober = TestProber(thread.looper, replySender, cb)
+ val probeInfo = TestProbeInfo(
+ listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890)),
+ // delayMs is the delay between each probe, so does not apply to the first one
+ delayMs = SHORT_TIMEOUT_MS)
+ prober.startProbing(probeInfo)
+
+ // Expect the initial probe
+ verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(0, probeInfo)
+
+ // Stop probing
+ val stopResult = CompletableFuture<Boolean>()
+ Handler(thread.looper).post { stopResult.complete(prober.stop(probeInfo.serviceId)) }
+ assertTrue(stopResult.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS),
+ "stop should return true when probing was in progress")
+
+ // Wait for a bit (more than the probe delay) to ensure no more probes were sent
+ Thread.sleep(SHORT_TIMEOUT_MS * 2)
+ verify(cb, never()).onSent(1, probeInfo)
+ verify(cb, never()).onFinished(probeInfo)
+
+ // Only one sent packet
+ verify(socket, times(1)).send(any())
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
index 7d800d8..55c2846 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
@@ -281,9 +281,9 @@
// TTL 0x0000003c (60 secs)
+ "0000003C"
// Data length
- + "003C"
- // nextdomain.android.com
- + "0A6E657874646F6D61696E07616E64726F696403636F6D00"
+ + "0031"
+ // nextdomain.android.com, with compression for android.com
+ + "0A6E657874646F6D61696EC007"
// Type bitmaps: window block 0x00, bitmap length 0x05,
// bits 16 (TXT) and 33 (SRV) set: 0x0000800040
+ "00050000800040"
diff --git a/tools/gn2bp/Android.bp.swp b/tools/gn2bp/Android.bp.swp
index 3e521bf..78ae260 100644
--- a/tools/gn2bp/Android.bp.swp
+++ b/tools/gn2bp/Android.bp.swp
@@ -5759,6 +5759,7 @@
"base/android/java/src/org/chromium/base/RadioUtils.java",
"base/android/java/src/org/chromium/base/StreamUtil.java",
"base/android/java/src/org/chromium/base/StrictModeContext.java",
+ "base/android/java/src/org/chromium/base/SysUtils.java",
"base/android/java/src/org/chromium/base/ThreadUtils.java",
"base/android/java/src/org/chromium/base/TimeUtils.java",
"base/android/java/src/org/chromium/base/TimezoneUtils.java",
@@ -5801,6 +5802,8 @@
"base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
"base/android/java/src/org/chromium/base/jank_tracker/JankTrackerImpl.java",
"base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+ "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+ "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
"base/android/java/src/org/chromium/base/library_loader/Linker.java",
"base/android/java/src/org/chromium/base/library_loader/LinkerJni.java",
"base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
@@ -5831,6 +5834,7 @@
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
+ "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
@@ -6068,6 +6072,7 @@
"base/android/java/src/org/chromium/base/RadioUtils.java",
"base/android/java/src/org/chromium/base/StreamUtil.java",
"base/android/java/src/org/chromium/base/StrictModeContext.java",
+ "base/android/java/src/org/chromium/base/SysUtils.java",
"base/android/java/src/org/chromium/base/ThreadUtils.java",
"base/android/java/src/org/chromium/base/TimeUtils.java",
"base/android/java/src/org/chromium/base/TimezoneUtils.java",
@@ -6110,6 +6115,8 @@
"base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
"base/android/java/src/org/chromium/base/jank_tracker/JankTrackerImpl.java",
"base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+ "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+ "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
"base/android/java/src/org/chromium/base/library_loader/Linker.java",
"base/android/java/src/org/chromium/base/library_loader/LinkerJni.java",
"base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
@@ -6140,6 +6147,7 @@
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
+ "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
@@ -6377,6 +6385,7 @@
"base/android/java/src/org/chromium/base/RadioUtils.java",
"base/android/java/src/org/chromium/base/StreamUtil.java",
"base/android/java/src/org/chromium/base/StrictModeContext.java",
+ "base/android/java/src/org/chromium/base/SysUtils.java",
"base/android/java/src/org/chromium/base/ThreadUtils.java",
"base/android/java/src/org/chromium/base/TimeUtils.java",
"base/android/java/src/org/chromium/base/TimezoneUtils.java",
@@ -6419,6 +6428,8 @@
"base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
"base/android/java/src/org/chromium/base/jank_tracker/JankTrackerImpl.java",
"base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+ "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+ "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
"base/android/java/src/org/chromium/base/library_loader/Linker.java",
"base/android/java/src/org/chromium/base/library_loader/LinkerJni.java",
"base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
@@ -6449,6 +6460,7 @@
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
+ "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
"base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
index 4103270..cf8f1dd 100755
--- a/tools/gn2bp/gen_android_bp
+++ b/tools/gn2bp/gen_android_bp
@@ -96,6 +96,11 @@
local_include_dirs_denylist = [
]
+android_include_dirs_denylist = [
+ 'third_party/brotli/include/',
+ 'third_party/zlib/',
+]
+
# Name of the module which settings such as compiler flags for all other
# modules.
defaults_module = module_prefix + 'defaults'
@@ -140,15 +145,56 @@
],
}
+def enable_brotli(module, arch):
+ # Requires crrev/c/4111690
+ if arch is None:
+ module.static_libs.add('libbrotli')
+ else:
+ module.arch[arch].static_libs.add('libbrotli')
+
+def enable_modp_b64(module, arch):
+ # Requires crrev/c/4112845
+ # Requires aosp/2359455
+ # Requires aosp/2359456
+ if not module.is_compiled():
+ return
+ if arch is None:
+ module.static_libs.add('libmodpb64')
+ else:
+ module.arch[arch].static_libs.add('libmodpb64')
+
+def enable_zlib(module, arch):
+ # Requires crrev/c/4109079
+ if arch is None:
+ module.shared_libs.add('libz')
+ else:
+ module.arch[arch].shared_libs.add('libz')
+
# Android equivalents for third-party libraries that the upstream project
# depends on.
builtin_deps = {
'//buildtools/third_party/libunwind:libunwind':
- lambda x: None, # disable libunwind
+ lambda m, a: None, # disable libunwind
'//net/tools/root_store_tool:root_store_tool':
- lambda x: None,
+ lambda m, a: None,
}
+android_deps = {
+ '//third_party/brotli:common':
+ enable_brotli,
+ '//third_party/brotli:dec':
+ enable_brotli,
+ '//third_party/modp_b64:modp_b64':
+ enable_modp_b64,
+ '//third_party/zlib:zlib':
+ enable_zlib,
+}
+
+# Uncomment the following lines to use Android deps rather than their Chromium
+# equivalent:
+#builtin_deps.update(android_deps)
+#local_include_dirs_denylist.extend(android_include_dirs_denylist)
+
# Name of tethering apex module
tethering_apex = "com.android.tethering"
@@ -1086,6 +1132,10 @@
module.local_include_dirs.update([gn_utils.label_to_path(d)
for d in include_dirs
if not re.match('^//out/.*', d)])
+ # Remove prohibited include directories
+ module.local_include_dirs = [d for d in module.local_include_dirs
+ if d not in local_include_dirs_denylist]
+
def create_modules_from_target(blueprint, gn, gn_target_name):
"""Generate module(s) for a given GN target.
@@ -1192,10 +1242,6 @@
if lib in static_library_allowlist:
module.add_android_static_lib(android_lib)
- # Remove prohibited include directories
- module.local_include_dirs = [d for d in module.local_include_dirs
- if d not in local_include_dirs_denylist]
-
# If the module is a static library, export all the generated headers.
if module.type == 'cc_library_static':
module.export_generated_headers = module.generated_headers
@@ -1216,7 +1262,7 @@
# |builtin_deps| override GN deps with Android-specific ones. See the
# config in the top of this file.
if dep_name in builtin_deps:
- builtin_deps[dep_name](module)
+ builtin_deps[dep_name](module, None)
continue
dep_module = create_modules_from_target(blueprint, gn, dep_name)
@@ -1269,6 +1315,11 @@
for arch_name, arch in target.arch.items():
for dep_name in arch.deps:
+ # |builtin_deps| override GN deps with Android-specific ones. See the
+ # config in the top of this file.
+ if dep_name in builtin_deps:
+ builtin_deps[dep_name](module, arch_name)
+ continue
dep_module = create_modules_from_target(blueprint, gn, dep_name)
# Arch-specific dependencies currently only include cc_library_static.
# Revisit this approach once we need to support more target types.
@@ -1283,19 +1334,11 @@
if module.type not in ["cc_object"]:
module.target[arch_name].export_generated_headers.update(
dep_module.genrule_headers)
- else:
- raise Error('Unsupported arch-specific dependency %s of target %s with type %s' %
- (dep_module.name, target.name, dep_module.type))
- for dep_name in arch.source_set_deps:
- dep_module = create_modules_from_target(blueprint, gn, dep_name)
- if dep_module.type == 'cc_object':
- if module.type != 'cc_object':
- # We only want to bubble up cc_objects for modules that are not cc_objects
- # otherwise they'd be recompiled and that would cause multiple symbol redefinitions.
- if dep_module.has_input_files():
- # Only add it as part of srcs if the dep_module has input files otherwise
- # this would throw an error.
- module.target[arch_name].srcs.add(":" + dep_module.name)
+ elif dep_module.type == 'cc_object':
+ if dep_module.has_input_files():
+ # Only add it as part of srcs if the dep_module has input files otherwise
+ # this would throw an error.
+ module.target[arch_name].srcs.add(":" + dep_module.name)
else:
raise Error('Unsupported arch-specific dependency %s of target %s with type %s' %
(dep_module.name, target.name, dep_module.type))
diff --git a/tools/gn2bp/gn_utils.py b/tools/gn2bp/gn_utils.py
index b3f51c9..919b3e3 100644
--- a/tools/gn2bp/gn_utils.py
+++ b/tools/gn2bp/gn_utils.py
@@ -182,6 +182,9 @@
def device_supported(self):
return any([name.startswith('android') for name in self.arch.keys()])
+ def is_linker_unit_type(self):
+ return self.type in LINKER_UNIT_TYPES
+
def __lt__(self, other):
if isinstance(other, self.__class__):
return self.name < other.name
@@ -322,9 +325,8 @@
return target # Target already processed.
if target.name in self.builtin_deps:
- # return early, no need to dive into the modules deps as the module is a
- # builtin.
- return None
+ # return early, no need to parse any further as the module is a builtin.
+ return target
target.testonly = desc.get('testonly', False)
@@ -344,7 +346,7 @@
elif target.type == 'source_set':
self.source_sets[gn_target_name] = target
target.arch[arch].sources.update(desc.get('sources', []))
- elif target.type in LINKER_UNIT_TYPES:
+ elif target.is_linker_unit_type():
self.linker_units[gn_target_name] = target
target.arch[arch].sources.update(desc.get('sources', []))
elif (desc.get("script", "") in JAVA_BANNED_SCRIPTS
@@ -390,9 +392,7 @@
# Recurse in dependencies.
for gn_dep_name in desc.get('deps', []):
dep = self.parse_gn_desc(gn_desc, gn_dep_name, is_java_target)
- if dep is None:
- continue
- elif dep.type == 'proto_library':
+ if dep.type == 'proto_library':
target.proto_deps.add(dep.name)
target.transitive_proto_deps.add(dep.name)
target.proto_paths.update(dep.proto_paths)
@@ -400,12 +400,15 @@
elif dep.type == 'source_set':
target.arch[arch].source_set_deps.add(dep.name)
target.arch[arch].source_set_deps.update(dep.arch[arch].source_set_deps)
+ # flatten source_set deps
+ if target.is_linker_unit_type():
+ target.arch[arch].deps.update(target.arch[arch].source_set_deps)
elif dep.type == 'group':
target.update(dep, arch) # Bubble up groups's cflags/ldflags etc.
elif dep.type in ['action', 'action_foreach', 'copy']:
if proto_target_type is None:
target.arch[arch].deps.add(dep.name)
- elif dep.type in LINKER_UNIT_TYPES:
+ elif dep.is_linker_unit_type():
target.arch[arch].deps.add(dep.name)
elif dep.type == 'java_group':
# Explicitly break dependency chain when a java_group is added.