Merge remote-tracking branch 'remotes/aosp/tmp_libs_net_move' into libs_net_move_merge
frameworks/libs/net/common -> packages/modules/Connectivity/staticlibs frameworks/libs/net/client-libs -> packages/modules/Connectivity/staticlbs/client-libs Test: TH Bug: 296014682 Change-Id: I5dc78f0c4653e20312ab3d488b1e69262dbb9840
This commit is contained in:
commit
c480039309
552 changed files with 63165 additions and 6 deletions
|
|
@ -37,10 +37,10 @@ java_sdk_library {
|
|||
"//frameworks/base/core/tests/utillib",
|
||||
"//frameworks/base/packages/Connectivity/tests:__subpackages__",
|
||||
"//frameworks/base/tests/vcn",
|
||||
"//frameworks/libs/net/common/testutils",
|
||||
"//frameworks/libs/net/common/tests:__subpackages__",
|
||||
"//frameworks/opt/telephony/tests/telephonytests",
|
||||
"//packages/modules/CaptivePortalLogin/tests",
|
||||
"//packages/modules/Connectivity/staticlibs/testutils",
|
||||
"//packages/modules/Connectivity/staticlibs/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/tests:__subpackages__",
|
||||
"//packages/modules/IPsec/tests/iketests",
|
||||
|
|
|
|||
|
|
@ -175,11 +175,11 @@ java_sdk_library {
|
|||
"//frameworks/base/core/tests/benchmarks",
|
||||
"//frameworks/base/core/tests/utillib",
|
||||
"//frameworks/base/tests/vcn",
|
||||
"//frameworks/libs/net/common/testutils",
|
||||
"//frameworks/libs/net/common/tests:__subpackages__",
|
||||
"//frameworks/opt/net/ethernet/tests:__subpackages__",
|
||||
"//frameworks/opt/telephony/tests/telephonytests",
|
||||
"//packages/modules/CaptivePortalLogin/tests",
|
||||
"//packages/modules/Connectivity/staticlibs/testutils",
|
||||
"//packages/modules/Connectivity/staticlibs/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/tests:__subpackages__",
|
||||
"//packages/modules/IPsec/tests/iketests",
|
||||
|
|
|
|||
|
|
@ -175,11 +175,11 @@ java_sdk_library {
|
|||
"//frameworks/base/core/tests/benchmarks",
|
||||
"//frameworks/base/core/tests/utillib",
|
||||
"//frameworks/base/tests/vcn",
|
||||
"//frameworks/libs/net/common/testutils",
|
||||
"//frameworks/libs/net/common/tests:__subpackages__",
|
||||
"//frameworks/opt/net/ethernet/tests:__subpackages__",
|
||||
"//frameworks/opt/telephony/tests/telephonytests",
|
||||
"//packages/modules/CaptivePortalLogin/tests",
|
||||
"//packages/modules/Connectivity/staticlibs/testutils",
|
||||
"//packages/modules/Connectivity/staticlibs/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/Cronet/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/tests:__subpackages__",
|
||||
|
|
|
|||
434
staticlibs/Android.bp
Normal file
434
staticlibs/Android.bp
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// 1. The "net-utils-framework-common" library is also compiled into the framework and placed on the
|
||||
// boot classpath. It uses jarjar rules so that anything outside the framework can use this
|
||||
// library directly.
|
||||
// 2. The "net-utils-services-common" library is for use by modules and frameworks/base/services.
|
||||
// It does not need to be jarjared because it is not placed on the bootclasspath.
|
||||
// 3. The "net-utils-telephony-common-srcs" filegroup is for use specifically by telephony, which
|
||||
// places many of its classes, even non-API service classes, on the boot classpath. Any file that
|
||||
// is added to this filegroup *must* have a corresponding jarjar rule in the telephony jarjar
|
||||
// rules file. Otherwise, it will end up on the boot classpath and other modules will not be able
|
||||
// to provide their own copy.
|
||||
|
||||
// Note: all filegroups here must have the right path attribute because otherwise, if they are
|
||||
// included in the bootclasspath, they could incorrectly be included in the SDK documentation even
|
||||
// though they are not in the current.txt files.
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/arp/ArpPacket.java",
|
||||
"device/com/android/net/module/util/DeviceConfigUtils.java",
|
||||
"device/com/android/net/module/util/DomainUtils.java",
|
||||
"device/com/android/net/module/util/FdEventsReader.java",
|
||||
"device/com/android/net/module/util/NetworkMonitorUtils.java",
|
||||
"device/com/android/net/module/util/PacketReader.java",
|
||||
"device/com/android/net/module/util/SharedLog.java",
|
||||
"device/com/android/net/module/util/SocketUtils.java",
|
||||
"device/com/android/net/module/util/FeatureVersions.java",
|
||||
// This library is used by system modules, for which the system health impact of Kotlin
|
||||
// has not yet been evaluated. Annotations may need jarjar'ing.
|
||||
// "src_devicecommon/**/*.kt",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
target_sdk_version: "30",
|
||||
apex_available: [
|
||||
"//apex_available:anyapex",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
visibility: [
|
||||
"//frameworks/base/packages/Tethering",
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/Connectivity/framework:__subpackages__",
|
||||
"//frameworks/opt/net/ike",
|
||||
"//frameworks/opt/net/wifi/service",
|
||||
"//packages/modules/Wifi/service",
|
||||
"//frameworks/opt/net/telephony",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
"//packages/modules/CaptivePortalLogin",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-framework-common",
|
||||
],
|
||||
libs: [
|
||||
"androidx.annotation_annotation",
|
||||
"framework-annotations-lib",
|
||||
"framework-configinfrastructure",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_defaults {
|
||||
name: "lib_mockito_extended",
|
||||
static_libs: [
|
||||
"mockito-target-extended-minus-junit4"
|
||||
],
|
||||
jni_libs: [
|
||||
"libdexmakerjvmtiagent",
|
||||
"libstaticjvmtiagent",
|
||||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-dnspacket-common",
|
||||
srcs: [
|
||||
"framework/**/DnsPacket.java",
|
||||
"framework/**/DnsPacketUtils.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
visibility: [
|
||||
"//packages/services/Iwlan:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "net-utils-framework-common-srcs",
|
||||
srcs: ["framework/**/*.java"],
|
||||
path: "framework",
|
||||
visibility: [
|
||||
"//frameworks/base",
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common-bpf",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/BpfBitmap.java",
|
||||
"device/com/android/net/module/util/BpfDump.java",
|
||||
"device/com/android/net/module/util/BpfMap.java",
|
||||
"device/com/android/net/module/util/BpfUtils.java",
|
||||
"device/com/android/net/module/util/IBpfMap.java",
|
||||
"device/com/android/net/module/util/JniUtil.java",
|
||||
"device/com/android/net/module/util/Struct.java",
|
||||
"device/com/android/net/module/util/TcUtils.java",
|
||||
"framework/com/android/net/module/util/HexDump.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"androidx.annotation_annotation",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common-struct",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/Ipv6Utils.java",
|
||||
"device/com/android/net/module/util/PacketBuilder.java",
|
||||
"device/com/android/net/module/util/Struct.java",
|
||||
"device/com/android/net/module/util/structs/*.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-framework-common",
|
||||
],
|
||||
libs: [
|
||||
"androidx.annotation_annotation",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common-netlink",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/netlink/*.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-device-common-struct",
|
||||
],
|
||||
libs: [
|
||||
"androidx.annotation_annotation",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_library {
|
||||
// TODO : this target should probably be folded into net-utils-device-common
|
||||
name: "net-utils-device-common-ip",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/ip/*.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"framework-connectivity",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-device-common",
|
||||
"net-utils-device-common-netlink",
|
||||
"net-utils-framework-common",
|
||||
"netd-client",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-framework-common",
|
||||
srcs: [
|
||||
":net-utils-framework-common-srcs",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
libs: [
|
||||
"androidx.annotation_annotation",
|
||||
"framework-annotations-lib",
|
||||
"framework-connectivity.stubs.module_lib",
|
||||
"framework-connectivity-t.stubs.module_lib",
|
||||
"framework-location.stubs.module_lib",
|
||||
],
|
||||
jarjar_rules: "jarjar-rules-shared.txt",
|
||||
visibility: [
|
||||
"//cts/tests/tests/net",
|
||||
"//cts/tests/tests/wifi",
|
||||
"//packages/modules/Connectivity/tests/cts/net",
|
||||
"//frameworks/base/packages/Tethering",
|
||||
"//packages/modules/Connectivity/Tethering",
|
||||
"//frameworks/base/tests:__subpackages__",
|
||||
"//frameworks/opt/net/ike",
|
||||
"//frameworks/opt/telephony",
|
||||
"//frameworks/base/wifi:__subpackages__",
|
||||
"//frameworks/base/packages/Connectivity:__subpackages__",
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
"//packages/modules/CaptivePortalLogin",
|
||||
"//packages/modules/Wifi/framework/tests:__subpackages__",
|
||||
"//packages/apps/Settings",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
errorprone: {
|
||||
enabled: true,
|
||||
// Error-prone checking only warns of problems when building. To make the build fail with
|
||||
// these errors, list the specific error-prone problems below.
|
||||
javacflags: [
|
||||
"-Xep:NullablePrimitive:ERROR",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-services-common",
|
||||
srcs: [
|
||||
"device/android/net/NetworkFactory.java",
|
||||
"device/android/net/NetworkFactoryImpl.java",
|
||||
"device/android/net/NetworkFactoryLegacyImpl.java",
|
||||
"device/android/net/NetworkFactoryShim.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "30",
|
||||
static_libs: [
|
||||
"modules-utils-build_system",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"framework-connectivity",
|
||||
],
|
||||
// TODO: remove "apex_available:platform".
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.btservices",
|
||||
"com.android.tethering",
|
||||
"com.android.wifi",
|
||||
],
|
||||
visibility: [
|
||||
// TODO: remove after NetworkStatsService moves to the module.
|
||||
"//frameworks/base/services/net",
|
||||
"//packages/modules/Connectivity/service",
|
||||
"//packages/modules/Connectivity/tests:__subpackages__",
|
||||
"//packages/modules/Bluetooth/android/app",
|
||||
"//packages/modules/Wifi/service:__subpackages__",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common-async",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/async/*.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
],
|
||||
static_libs: [
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common-wear",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/wear/*.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-device-common-async",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
lint: { strict_updatability_linting: true },
|
||||
}
|
||||
|
||||
// Limited set of utilities for use by service-connectivity-mdns-standalone-build-test, to make sure
|
||||
// the mDNS code can build with only system APIs.
|
||||
// The mDNS code is platform code so it should use framework-annotations-lib, contrary to apps that
|
||||
// should use sdk_version: "system_current" and only androidx.annotation_annotation. But this build
|
||||
// rule verifies that the mDNS code can be built into apps, if code transformations are applied to
|
||||
// the annotations.
|
||||
// When using "system_current", framework annotations are not available; they would appear as
|
||||
// package-private as they are marked as such in the system_current stubs. So build against
|
||||
// core_platform and add the stubs manually in "libs". See http://b/147773144#comment7.
|
||||
java_library {
|
||||
name: "net-utils-device-common-mdns-standalone-build-test",
|
||||
// Build against core_platform and add the stub libraries manually in "libs", as annotations
|
||||
// are already included in android_system_stubs_current but package-private, so
|
||||
// "framework-annotations-lib" needs to be manually included before
|
||||
// "android_system_stubs_current" (b/272392042)
|
||||
sdk_version: "core_platform",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/FdEventsReader.java",
|
||||
"device/com/android/net/module/util/SharedLog.java",
|
||||
"framework/com/android/net/module/util/ByteUtils.java",
|
||||
"framework/com/android/net/module/util/CollectionUtils.java",
|
||||
"framework/com/android/net/module/util/HexDump.java",
|
||||
"framework/com/android/net/module/util/LinkPropertiesUtils.java",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"android_system_stubs_current",
|
||||
"androidx.annotation_annotation",
|
||||
],
|
||||
visibility: ["//packages/modules/Connectivity/service-t"],
|
||||
}
|
||||
|
||||
// Use a filegroup and not a library for telephony sources, as framework-annotations cannot be
|
||||
// included either (some annotations would be duplicated on the bootclasspath).
|
||||
filegroup {
|
||||
name: "net-utils-telephony-common-srcs",
|
||||
srcs: [
|
||||
// Any class here *must* have a corresponding jarjar rule in the telephony build rules.
|
||||
"device/android/net/NetworkFactory.java",
|
||||
"device/android/net/NetworkFactoryImpl.java",
|
||||
"device/android/net/NetworkFactoryLegacyImpl.java",
|
||||
"device/android/net/NetworkFactoryShim.java",
|
||||
],
|
||||
path: "device",
|
||||
visibility: [
|
||||
"//frameworks/opt/telephony",
|
||||
],
|
||||
}
|
||||
|
||||
// Use a filegroup and not a library for wifi sources, as this needs corresponding jar-jar
|
||||
// rules on the wifi side.
|
||||
// Any class here *must* have a corresponding jarjar rule in the wifi build rules.
|
||||
filegroup {
|
||||
name: "net-utils-framework-wifi-common-srcs",
|
||||
srcs: [
|
||||
"framework/com/android/net/module/util/DnsSdTxtRecord.java",
|
||||
"framework/com/android/net/module/util/Inet4AddressUtils.java",
|
||||
"framework/com/android/net/module/util/InetAddressUtils.java",
|
||||
"framework/com/android/net/module/util/MacAddressUtils.java",
|
||||
"framework/com/android/net/module/util/NetUtils.java",
|
||||
],
|
||||
path: "framework",
|
||||
visibility: [
|
||||
"//frameworks/base",
|
||||
],
|
||||
}
|
||||
|
||||
// Use a filegroup and not a library for wifi sources, as this needs corresponding jar-jar
|
||||
// rules on the wifi side.
|
||||
// Any class here *must* have a corresponding jarjar rule in the wifi build rules.
|
||||
filegroup {
|
||||
name: "net-utils-wifi-service-common-srcs",
|
||||
srcs: [
|
||||
"device/android/net/NetworkFactory.java",
|
||||
"device/android/net/NetworkFactoryImpl.java",
|
||||
"device/android/net/NetworkFactoryLegacyImpl.java",
|
||||
"device/android/net/NetworkFactoryShim.java",
|
||||
],
|
||||
visibility: [
|
||||
"//frameworks/opt/net/wifi/service",
|
||||
"//packages/modules/Wifi/service",
|
||||
],
|
||||
}
|
||||
28
staticlibs/TEST_MAPPING
Normal file
28
staticlibs/TEST_MAPPING
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "netdutils_test"
|
||||
}
|
||||
],
|
||||
"imports": [
|
||||
{
|
||||
"path": "frameworks/base/core/java/android/net"
|
||||
},
|
||||
// Below tests already run the library tests as part of their coverage tests
|
||||
{
|
||||
"path": "packages/modules/NetworkStack"
|
||||
},
|
||||
{
|
||||
"path": "packages/modules/CaptivePortalLogin"
|
||||
},
|
||||
{
|
||||
"path": "frameworks/base/packages/Tethering"
|
||||
},
|
||||
{
|
||||
"path": "packages/modules/Wifi/framework"
|
||||
},
|
||||
{
|
||||
"path": "vendor/xts/gts-tests/hostsidetests/networkstack"
|
||||
}
|
||||
]
|
||||
}
|
||||
26
staticlibs/client-libs/Android.bp
Normal file
26
staticlibs/client-libs/Android.bp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "netd-client",
|
||||
srcs: ["netd/**/*"],
|
||||
sdk_version: "system_current",
|
||||
min_sdk_version: "29",
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.tethering",
|
||||
"com.android.wifi"
|
||||
],
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//frameworks/base/services:__subpackages__",
|
||||
"//frameworks/base/packages:__subpackages__",
|
||||
"//packages/modules/Wifi/service:__subpackages__"
|
||||
],
|
||||
libs: ["androidx.annotation_annotation"],
|
||||
static_libs: [
|
||||
"netd_aidl_interface-lateststable-java",
|
||||
"netd_event_listener_interface-lateststable-java"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.net.module.util;
|
||||
|
||||
import android.net.metrics.INetdEventListener;
|
||||
|
||||
/**
|
||||
* Base {@link INetdEventListener} that provides no-op implementations which can
|
||||
* be overridden.
|
||||
*/
|
||||
public class BaseNetdEventListener extends INetdEventListener.Stub {
|
||||
|
||||
@Override
|
||||
public void onDnsEvent(int netId, int eventType, int returnCode,
|
||||
int latencyMs, String hostname, String[] ipAddresses,
|
||||
int ipAddressesCount, int uid) { }
|
||||
|
||||
@Override
|
||||
public void onPrivateDnsValidationEvent(int netId, String ipAddress,
|
||||
String hostname, boolean validated) { }
|
||||
|
||||
@Override
|
||||
public void onConnectEvent(int netId, int error, int latencyMs,
|
||||
String ipAddr, int port, int uid) { }
|
||||
|
||||
@Override
|
||||
public void onWakeupEvent(String prefix, int uid, int ethertype,
|
||||
int ipNextHeader, byte[] dstHw, String srcIp, String dstIp,
|
||||
int srcPort, int dstPort, long timestampNs) { }
|
||||
|
||||
@Override
|
||||
public void onTcpSocketStatsEvent(int[] networkIds, int[] sentPackets,
|
||||
int[] lostPackets, int[] rttUs, int[] sentAckDiffMs) { }
|
||||
|
||||
@Override
|
||||
public void onNat64PrefixEvent(int netId, boolean added,
|
||||
String prefixString, int prefixLength) { }
|
||||
|
||||
@Override
|
||||
public int getInterfaceVersion() {
|
||||
return INetdEventListener.VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInterfaceHash() {
|
||||
return INetdEventListener.HASH;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.net.module.util;
|
||||
|
||||
import android.net.INetdUnsolicitedEventListener;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be
|
||||
* overridden.
|
||||
*/
|
||||
public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
|
||||
|
||||
@Override
|
||||
public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs,
|
||||
int uid) { }
|
||||
|
||||
@Override
|
||||
public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS,
|
||||
@NonNull String[] servers) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags,
|
||||
int scope) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags,
|
||||
int scope) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceAdded(@NonNull String ifName) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceRemoved(@NonNull String ifName) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceChanged(@NonNull String ifName, boolean up) { }
|
||||
|
||||
@Override
|
||||
public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { }
|
||||
|
||||
@Override
|
||||
public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway,
|
||||
@NonNull String ifName) { }
|
||||
|
||||
@Override
|
||||
public void onStrictCleartextDetected(int uid, @NonNull String hex) { }
|
||||
|
||||
@Override
|
||||
public int getInterfaceVersion() {
|
||||
return INetdUnsolicitedEventListener.VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInterfaceHash() {
|
||||
return INetdUnsolicitedEventListener.HASH;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* 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.net.module.util;
|
||||
|
||||
import static android.net.INetd.IF_STATE_DOWN;
|
||||
import static android.net.INetd.IF_STATE_UP;
|
||||
import static android.net.RouteInfo.RTN_THROW;
|
||||
import static android.net.RouteInfo.RTN_UNICAST;
|
||||
import static android.net.RouteInfo.RTN_UNREACHABLE;
|
||||
import static android.system.OsConstants.EBUSY;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.net.INetd;
|
||||
import android.net.InterfaceConfigurationParcel;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.TetherConfigParcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Collection of utilities for netd.
|
||||
*/
|
||||
public class NetdUtils {
|
||||
private static final String TAG = NetdUtils.class.getSimpleName();
|
||||
|
||||
/** Used to modify the specified route. */
|
||||
public enum ModifyOperation {
|
||||
ADD,
|
||||
REMOVE,
|
||||
}
|
||||
|
||||
/**
|
||||
* Get InterfaceConfigurationParcel from netd.
|
||||
*/
|
||||
public static InterfaceConfigurationParcel getInterfaceConfigParcel(@NonNull INetd netd,
|
||||
@NonNull String iface) {
|
||||
try {
|
||||
return netd.interfaceGetCfg(iface);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateFlag(String flag) {
|
||||
if (flag.indexOf(' ') >= 0) {
|
||||
throw new IllegalArgumentException("flag contains space: " + flag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the InterfaceConfigurationParcel contains the target flag or not.
|
||||
*
|
||||
* @param config The InterfaceConfigurationParcel instance.
|
||||
* @param flag Target flag string to be checked.
|
||||
*/
|
||||
public static boolean hasFlag(@NonNull final InterfaceConfigurationParcel config,
|
||||
@NonNull final String flag) {
|
||||
validateFlag(flag);
|
||||
final Set<String> flagList = new HashSet<String>(Arrays.asList(config.flags));
|
||||
return flagList.contains(flag);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected static String[] removeAndAddFlags(@NonNull String[] flags, @NonNull String remove,
|
||||
@NonNull String add) {
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
try {
|
||||
// Validate the add flag first, so that the for-loop can be ignore once the format of
|
||||
// add flag is invalid.
|
||||
validateFlag(add);
|
||||
for (String flag : flags) {
|
||||
// Simply ignore both of remove and add flags first, then add the add flag after
|
||||
// exiting the loop to prevent adding the duplicate flag.
|
||||
if (remove.equals(flag) || add.equals(flag)) continue;
|
||||
result.add(flag);
|
||||
}
|
||||
result.add(add);
|
||||
return result.toArray(new String[result.size()]);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set interface configuration to netd by passing InterfaceConfigurationParcel.
|
||||
*/
|
||||
public static void setInterfaceConfig(INetd netd, InterfaceConfigurationParcel configParcel) {
|
||||
try {
|
||||
netd.interfaceSetCfg(configParcel);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given interface up.
|
||||
*/
|
||||
public static void setInterfaceUp(INetd netd, String iface) {
|
||||
final InterfaceConfigurationParcel configParcel = getInterfaceConfigParcel(netd, iface);
|
||||
configParcel.flags = removeAndAddFlags(configParcel.flags, IF_STATE_DOWN /* remove */,
|
||||
IF_STATE_UP /* add */);
|
||||
setInterfaceConfig(netd, configParcel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given interface down.
|
||||
*/
|
||||
public static void setInterfaceDown(INetd netd, String iface) {
|
||||
final InterfaceConfigurationParcel configParcel = getInterfaceConfigParcel(netd, iface);
|
||||
configParcel.flags = removeAndAddFlags(configParcel.flags, IF_STATE_UP /* remove */,
|
||||
IF_STATE_DOWN /* add */);
|
||||
setInterfaceConfig(netd, configParcel);
|
||||
}
|
||||
|
||||
/** Start tethering. */
|
||||
public static void tetherStart(final INetd netd, final boolean usingLegacyDnsProxy,
|
||||
final String[] dhcpRange) throws RemoteException, ServiceSpecificException {
|
||||
final TetherConfigParcel config = new TetherConfigParcel();
|
||||
config.usingLegacyDnsProxy = usingLegacyDnsProxy;
|
||||
config.dhcpRanges = dhcpRange;
|
||||
netd.tetherStartWithConfiguration(config);
|
||||
}
|
||||
|
||||
/** Setup interface for tethering. */
|
||||
public static void tetherInterface(final INetd netd, final String iface, final IpPrefix dest)
|
||||
throws RemoteException, ServiceSpecificException {
|
||||
tetherInterface(netd, iface, dest, 20 /* maxAttempts */, 50 /* pollingIntervalMs */);
|
||||
}
|
||||
|
||||
/** Setup interface with configurable retries for tethering. */
|
||||
public static void tetherInterface(final INetd netd, final String iface, final IpPrefix dest,
|
||||
int maxAttempts, int pollingIntervalMs)
|
||||
throws RemoteException, ServiceSpecificException {
|
||||
netd.tetherInterfaceAdd(iface);
|
||||
networkAddInterface(netd, iface, maxAttempts, pollingIntervalMs);
|
||||
List<RouteInfo> routes = new ArrayList<>();
|
||||
routes.add(new RouteInfo(dest, null, iface, RTN_UNICAST));
|
||||
addRoutesToLocalNetwork(netd, iface, routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry Netd#networkAddInterface for EBUSY error code.
|
||||
* If the same interface (e.g., wlan0) is in client mode and then switches to tethered mode.
|
||||
* There can be a race where puts the interface into the local network but interface is still
|
||||
* in use in netd because the ConnectivityService thread hasn't processed the disconnect yet.
|
||||
* See b/158269544 for detail.
|
||||
*/
|
||||
private static void networkAddInterface(final INetd netd, final String iface,
|
||||
int maxAttempts, int pollingIntervalMs)
|
||||
throws ServiceSpecificException, RemoteException {
|
||||
for (int i = 1; i <= maxAttempts; i++) {
|
||||
try {
|
||||
netd.networkAddInterface(INetd.LOCAL_NET_ID, iface);
|
||||
return;
|
||||
} catch (ServiceSpecificException e) {
|
||||
if (e.errorCode == EBUSY && i < maxAttempts) {
|
||||
SystemClock.sleep(pollingIntervalMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.e(TAG, "Retry Netd#networkAddInterface failure: " + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset interface for tethering. */
|
||||
public static void untetherInterface(final INetd netd, String iface)
|
||||
throws RemoteException, ServiceSpecificException {
|
||||
try {
|
||||
netd.tetherInterfaceRemove(iface);
|
||||
} finally {
|
||||
netd.networkRemoveInterface(INetd.LOCAL_NET_ID, iface);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add |routes| to local network. */
|
||||
public static void addRoutesToLocalNetwork(final INetd netd, final String iface,
|
||||
final List<RouteInfo> routes) {
|
||||
|
||||
for (RouteInfo route : routes) {
|
||||
if (!route.isDefaultRoute()) {
|
||||
modifyRoute(netd, ModifyOperation.ADD, INetd.LOCAL_NET_ID, route);
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6 link local should be activated always.
|
||||
modifyRoute(netd, ModifyOperation.ADD, INetd.LOCAL_NET_ID,
|
||||
new RouteInfo(new IpPrefix("fe80::/64"), null, iface, RTN_UNICAST));
|
||||
}
|
||||
|
||||
/** Remove routes from local network. */
|
||||
public static int removeRoutesFromLocalNetwork(final INetd netd, final List<RouteInfo> routes) {
|
||||
int failures = 0;
|
||||
|
||||
for (RouteInfo route : routes) {
|
||||
try {
|
||||
modifyRoute(netd, ModifyOperation.REMOVE, INetd.LOCAL_NET_ID, route);
|
||||
} catch (IllegalStateException e) {
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private static String findNextHop(final RouteInfo route) {
|
||||
final String nextHop;
|
||||
switch (route.getType()) {
|
||||
case RTN_UNICAST:
|
||||
if (route.hasGateway()) {
|
||||
nextHop = route.getGateway().getHostAddress();
|
||||
} else {
|
||||
nextHop = INetd.NEXTHOP_NONE;
|
||||
}
|
||||
break;
|
||||
case RTN_UNREACHABLE:
|
||||
nextHop = INetd.NEXTHOP_UNREACHABLE;
|
||||
break;
|
||||
case RTN_THROW:
|
||||
nextHop = INetd.NEXTHOP_THROW;
|
||||
break;
|
||||
default:
|
||||
nextHop = INetd.NEXTHOP_NONE;
|
||||
break;
|
||||
}
|
||||
return nextHop;
|
||||
}
|
||||
|
||||
/** Add or remove |route|. */
|
||||
public static void modifyRoute(final INetd netd, final ModifyOperation op, final int netId,
|
||||
final RouteInfo route) {
|
||||
final String ifName = route.getInterface();
|
||||
final String dst = route.getDestination().toString();
|
||||
final String nextHop = findNextHop(route);
|
||||
|
||||
try {
|
||||
switch(op) {
|
||||
case ADD:
|
||||
netd.networkAddRoute(netId, ifName, dst, nextHop);
|
||||
break;
|
||||
case REMOVE:
|
||||
netd.networkRemoveRoute(netId, ifName, dst, nextHop);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported modify operation:" + op);
|
||||
}
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
staticlibs/client-libs/tests/unit/Android.bp
Normal file
44
staticlibs/client-libs/tests/unit/Android.bp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "NetdStaticLibTestsLib",
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
],
|
||||
min_sdk_version: "29",
|
||||
static_libs: [
|
||||
"androidx.test.rules",
|
||||
"mockito-target-extended-minus-junit4",
|
||||
"net-tests-utils-host-device-common",
|
||||
"netd-client",
|
||||
],
|
||||
libs: [
|
||||
"android.test.runner",
|
||||
"android.test.base",
|
||||
],
|
||||
visibility: [
|
||||
// Visible for Tethering and NetworkStack integration test and link NetdStaticLibTestsLib
|
||||
// there, so that the tests under client-libs can also be run when running tethering and
|
||||
// NetworkStack MTS.
|
||||
"//packages/modules/Connectivity/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
|
||||
"//packages/modules/NetworkStack/tests/integration",
|
||||
]
|
||||
}
|
||||
|
||||
android_test {
|
||||
name: "NetdStaticLibTests",
|
||||
certificate: "platform",
|
||||
static_libs: [
|
||||
"NetdStaticLibTestsLib",
|
||||
],
|
||||
jni_libs: [
|
||||
// For mockito extended
|
||||
"libdexmakerjvmtiagent",
|
||||
"libstaticjvmtiagent",
|
||||
],
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
28
staticlibs/client-libs/tests/unit/AndroidManifest.xml
Normal file
28
staticlibs/client-libs/tests/unit/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.frameworks.clientlibs.tests">
|
||||
|
||||
<application android:debuggable="true">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.frameworks.clientlibs.tests"
|
||||
android:label="Netd Static Library Tests" />
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* 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.net.module.util;
|
||||
|
||||
import static android.net.INetd.LOCAL_NET_ID;
|
||||
import static android.system.OsConstants.EBUSY;
|
||||
|
||||
import static com.android.testutils.MiscAsserts.assertThrows;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.net.INetd;
|
||||
import android.net.InterfaceConfigurationParcel;
|
||||
import android.net.IpPrefix;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class NetdUtilsTest {
|
||||
@Mock private INetd mNetd;
|
||||
|
||||
private static final String IFACE = "TEST_IFACE";
|
||||
private static final IpPrefix TEST_IPPREFIX = new IpPrefix("192.168.42.1/24");
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
private void setupFlagsForInterfaceConfiguration(String[] flags) throws Exception {
|
||||
final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel();
|
||||
config.flags = flags;
|
||||
when(mNetd.interfaceGetCfg(eq(IFACE))).thenReturn(config);
|
||||
}
|
||||
|
||||
private void verifyMethodsAndArgumentsOfSetInterface(boolean ifaceUp) throws Exception {
|
||||
final String[] flagsContainDownAndUp = new String[] {"flagA", "down", "flagB", "up"};
|
||||
final String[] flagsForInterfaceDown = new String[] {"flagA", "down", "flagB"};
|
||||
final String[] flagsForInterfaceUp = new String[] {"flagA", "up", "flagB"};
|
||||
final String[] expectedFinalFlags;
|
||||
setupFlagsForInterfaceConfiguration(flagsContainDownAndUp);
|
||||
if (ifaceUp) {
|
||||
// "down" flag will be removed from flagsContainDownAndUp when interface is up. Set
|
||||
// expectedFinalFlags to flagsForInterfaceUp.
|
||||
expectedFinalFlags = flagsForInterfaceUp;
|
||||
NetdUtils.setInterfaceUp(mNetd, IFACE);
|
||||
} else {
|
||||
// "up" flag will be removed from flagsContainDownAndUp when interface is down. Set
|
||||
// expectedFinalFlags to flagsForInterfaceDown.
|
||||
expectedFinalFlags = flagsForInterfaceDown;
|
||||
NetdUtils.setInterfaceDown(mNetd, IFACE);
|
||||
}
|
||||
verify(mNetd).interfaceSetCfg(
|
||||
argThat(config ->
|
||||
// Check if actual flags are the same as expected flags.
|
||||
// TODO: Have a function in MiscAsserts to check if two arrays are the same.
|
||||
CollectionUtils.all(Arrays.asList(expectedFinalFlags),
|
||||
flag -> Arrays.asList(config.flags).contains(flag))
|
||||
&& CollectionUtils.all(Arrays.asList(config.flags),
|
||||
flag -> Arrays.asList(expectedFinalFlags).contains(flag))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInterfaceUp() throws Exception {
|
||||
verifyMethodsAndArgumentsOfSetInterface(true /* ifaceUp */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInterfaceDown() throws Exception {
|
||||
verifyMethodsAndArgumentsOfSetInterface(false /* ifaceUp */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndAddFlags() throws Exception {
|
||||
final String[] flags = new String[] {"flagA", "down", "flagB"};
|
||||
// Add an invalid flag and expect to get an IllegalStateException.
|
||||
assertThrows(IllegalStateException.class,
|
||||
() -> NetdUtils.removeAndAddFlags(flags, "down" /* remove */, "u p" /* add */));
|
||||
}
|
||||
|
||||
private void setNetworkAddInterfaceOutcome(final Exception cause, int numLoops)
|
||||
throws Exception {
|
||||
// This cannot be an int because local variables referenced from a lambda expression must
|
||||
// be final or effectively final.
|
||||
final Counter myCounter = new Counter();
|
||||
doAnswer((invocation) -> {
|
||||
myCounter.count();
|
||||
if (myCounter.isCounterReached(numLoops)) {
|
||||
if (cause == null) return null;
|
||||
|
||||
throw cause;
|
||||
}
|
||||
|
||||
throw new ServiceSpecificException(EBUSY);
|
||||
}).when(mNetd).networkAddInterface(LOCAL_NET_ID, IFACE);
|
||||
}
|
||||
|
||||
class Counter {
|
||||
private int mValue = 0;
|
||||
|
||||
private void count() {
|
||||
mValue++;
|
||||
}
|
||||
|
||||
private boolean isCounterReached(int target) {
|
||||
return mValue >= target;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTetherInterfaceSuccessful() throws Exception {
|
||||
// Expect #networkAddInterface successful at first tries.
|
||||
verifyTetherInterfaceSucceeds(1);
|
||||
|
||||
// Expect #networkAddInterface successful after 10 tries.
|
||||
verifyTetherInterfaceSucceeds(10);
|
||||
}
|
||||
|
||||
private void runTetherInterfaceWithServiceSpecificException(int expectedTries,
|
||||
int expectedCode) throws Exception {
|
||||
setNetworkAddInterfaceOutcome(new ServiceSpecificException(expectedCode), expectedTries);
|
||||
|
||||
try {
|
||||
NetdUtils.tetherInterface(mNetd, IFACE, TEST_IPPREFIX, 20, 0);
|
||||
fail("Expect throw ServiceSpecificException");
|
||||
} catch (ServiceSpecificException e) {
|
||||
assertEquals(e.errorCode, expectedCode);
|
||||
}
|
||||
|
||||
verifyNetworkAddInterfaceFails(expectedTries);
|
||||
reset(mNetd);
|
||||
}
|
||||
|
||||
private void runTetherInterfaceWithRemoteException(int expectedTries) throws Exception {
|
||||
setNetworkAddInterfaceOutcome(new RemoteException(), expectedTries);
|
||||
|
||||
try {
|
||||
NetdUtils.tetherInterface(mNetd, IFACE, TEST_IPPREFIX, 20, 0);
|
||||
fail("Expect throw RemoteException");
|
||||
} catch (RemoteException e) { }
|
||||
|
||||
verifyNetworkAddInterfaceFails(expectedTries);
|
||||
reset(mNetd);
|
||||
}
|
||||
|
||||
private void verifyNetworkAddInterfaceFails(int expectedTries) throws Exception {
|
||||
verify(mNetd).tetherInterfaceAdd(IFACE);
|
||||
verify(mNetd, times(expectedTries)).networkAddInterface(LOCAL_NET_ID, IFACE);
|
||||
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||
verifyNoMoreInteractions(mNetd);
|
||||
}
|
||||
|
||||
private void verifyTetherInterfaceSucceeds(int expectedTries) throws Exception {
|
||||
setNetworkAddInterfaceOutcome(null, expectedTries);
|
||||
|
||||
NetdUtils.tetherInterface(mNetd, IFACE, TEST_IPPREFIX);
|
||||
verify(mNetd).tetherInterfaceAdd(IFACE);
|
||||
verify(mNetd, times(expectedTries)).networkAddInterface(LOCAL_NET_ID, IFACE);
|
||||
verify(mNetd, times(2)).networkAddRoute(eq(LOCAL_NET_ID), eq(IFACE), any(), any());
|
||||
verifyNoMoreInteractions(mNetd);
|
||||
reset(mNetd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTetherInterfaceFailOnNetworkAddInterface() throws Exception {
|
||||
// Test throwing ServiceSpecificException with EBUSY failure.
|
||||
runTetherInterfaceWithServiceSpecificException(20, EBUSY);
|
||||
|
||||
// Test throwing ServiceSpecificException with unexpectedError.
|
||||
final int unexpectedError = 999;
|
||||
runTetherInterfaceWithServiceSpecificException(1, unexpectedError);
|
||||
|
||||
// Test throwing ServiceSpecificException with unexpectedError after 7 tries.
|
||||
runTetherInterfaceWithServiceSpecificException(7, unexpectedError);
|
||||
|
||||
// Test throwing RemoteException.
|
||||
runTetherInterfaceWithRemoteException(1);
|
||||
|
||||
// Test throwing RemoteException after 3 tries.
|
||||
runTetherInterfaceWithRemoteException(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetdUtilsHasFlag() throws Exception {
|
||||
final String[] flags = new String[] {"up", "broadcast", "running", "multicast"};
|
||||
setupFlagsForInterfaceConfiguration(flags);
|
||||
|
||||
// Set interface up.
|
||||
NetdUtils.setInterfaceUp(mNetd, IFACE);
|
||||
final ArgumentCaptor<InterfaceConfigurationParcel> arg =
|
||||
ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
|
||||
verify(mNetd, times(1)).interfaceSetCfg(arg.capture());
|
||||
|
||||
final InterfaceConfigurationParcel p = arg.getValue();
|
||||
assertTrue(NetdUtils.hasFlag(p, "up"));
|
||||
assertTrue(NetdUtils.hasFlag(p, "running"));
|
||||
assertTrue(NetdUtils.hasFlag(p, "broadcast"));
|
||||
assertTrue(NetdUtils.hasFlag(p, "multicast"));
|
||||
assertFalse(NetdUtils.hasFlag(p, "down"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetdUtilsHasFlag_flagContainsSpace() throws Exception {
|
||||
final String[] flags = new String[] {"up", "broadcast", "running", "multicast"};
|
||||
setupFlagsForInterfaceConfiguration(flags);
|
||||
|
||||
// Set interface up.
|
||||
NetdUtils.setInterfaceUp(mNetd, IFACE);
|
||||
final ArgumentCaptor<InterfaceConfigurationParcel> arg =
|
||||
ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
|
||||
verify(mNetd, times(1)).interfaceSetCfg(arg.capture());
|
||||
|
||||
final InterfaceConfigurationParcel p = arg.getValue();
|
||||
assertThrows(IllegalArgumentException.class, () -> NetdUtils.hasFlag(p, "up "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetdUtilsHasFlag_UppercaseString() throws Exception {
|
||||
final String[] flags = new String[] {"up", "broadcast", "running", "multicast"};
|
||||
setupFlagsForInterfaceConfiguration(flags);
|
||||
|
||||
// Set interface up.
|
||||
NetdUtils.setInterfaceUp(mNetd, IFACE);
|
||||
final ArgumentCaptor<InterfaceConfigurationParcel> arg =
|
||||
ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
|
||||
verify(mNetd, times(1)).interfaceSetCfg(arg.capture());
|
||||
|
||||
final InterfaceConfigurationParcel p = arg.getValue();
|
||||
assertFalse(NetdUtils.hasFlag(p, "UP"));
|
||||
assertFalse(NetdUtils.hasFlag(p, "BROADCAST"));
|
||||
assertFalse(NetdUtils.hasFlag(p, "RUNNING"));
|
||||
assertFalse(NetdUtils.hasFlag(p, "MULTICAST"));
|
||||
}
|
||||
}
|
||||
215
staticlibs/device/android/net/NetworkFactory.java
Normal file
215
staticlibs/device/android/net/NetworkFactory.java
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A NetworkFactory is an entity that creates NetworkAgent objects.
|
||||
* The bearers register with ConnectivityService using {@link #register} and
|
||||
* their factory will start receiving scored NetworkRequests. NetworkRequests
|
||||
* can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
|
||||
* overridden function. All of these can be dynamic - changing NetworkCapabilities
|
||||
* or score forces re-evaluation of all current requests.
|
||||
*
|
||||
* If any requests pass the filter some overrideable functions will be called.
|
||||
* If the bearer only cares about very simple start/stopNetwork callbacks, those
|
||||
* functions can be overridden. If the bearer needs more interaction, it can
|
||||
* override addNetworkRequest and removeNetworkRequest which will give it each
|
||||
* request that passes their current filters.
|
||||
*
|
||||
* This class is mostly a shim which delegates to one of two implementations depending
|
||||
* on the SDK level of the device it's running on.
|
||||
*
|
||||
* @hide
|
||||
**/
|
||||
public class NetworkFactory {
|
||||
static final boolean DBG = true;
|
||||
static final boolean VDBG = false;
|
||||
|
||||
final NetworkFactoryShim mImpl;
|
||||
|
||||
private final String LOG_TAG;
|
||||
|
||||
// Ideally the filter argument would be non-null, but null has historically meant to see
|
||||
// no requests and telephony passes null.
|
||||
public NetworkFactory(Looper looper, Context context, String logTag,
|
||||
@Nullable final NetworkCapabilities filter) {
|
||||
LOG_TAG = logTag;
|
||||
if (isAtLeastS()) {
|
||||
mImpl = new NetworkFactoryImpl(this, looper, context, filter);
|
||||
} else {
|
||||
mImpl = new NetworkFactoryLegacyImpl(this, looper, context, filter);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : these two constants and the method are only used by telephony tests. Replace it in
|
||||
// the tests and remove them and the associated code.
|
||||
public static final int CMD_REQUEST_NETWORK = 1;
|
||||
public static final int CMD_CANCEL_REQUEST = 2;
|
||||
/** Like Handler#obtainMessage */
|
||||
@VisibleForTesting
|
||||
public Message obtainMessage(final int what, final int arg1, final int arg2,
|
||||
final @Nullable Object obj) {
|
||||
return mImpl.obtainMessage(what, arg1, arg2, obj);
|
||||
}
|
||||
|
||||
// Called by BluetoothNetworkFactory
|
||||
public final Looper getLooper() {
|
||||
return mImpl.getLooper();
|
||||
}
|
||||
|
||||
// Refcount for simple mode requests
|
||||
private int mRefCount = 0;
|
||||
|
||||
/* Registers this NetworkFactory with the system. May only be called once per factory. */
|
||||
public void register() {
|
||||
mImpl.register(LOG_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this NetworkFactory with the system ignoring the score filter. This will let
|
||||
* the factory always see all network requests matching its capabilities filter.
|
||||
* May only be called once per factory.
|
||||
*/
|
||||
public void registerIgnoringScore() {
|
||||
mImpl.registerIgnoringScore(LOG_TAG);
|
||||
}
|
||||
|
||||
/** Unregisters this NetworkFactory. After this call, the object can no longer be used. */
|
||||
public void terminate() {
|
||||
mImpl.terminate();
|
||||
}
|
||||
|
||||
protected final void reevaluateAllRequests() {
|
||||
mImpl.reevaluateAllRequests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable function to provide complex filtering.
|
||||
* Called for every request every time a new NetworkRequest is seen
|
||||
* and whenever the filterScore or filterNetworkCapabilities change.
|
||||
*
|
||||
* acceptRequest can be overridden to provide complex filter behavior
|
||||
* for the incoming requests
|
||||
*
|
||||
* For output, this class will call {@link #needNetworkFor} and
|
||||
* {@link #releaseNetworkFor} for every request that passes the filters.
|
||||
* If you don't need to see every request, you can leave the base
|
||||
* implementations of those two functions and instead override
|
||||
* {@link #startNetwork} and {@link #stopNetwork}.
|
||||
*
|
||||
* If you want to see every score fluctuation on every request, set
|
||||
* your score filter to a very high number and watch {@link #needNetworkFor}.
|
||||
*
|
||||
* @return {@code true} to accept the request.
|
||||
*/
|
||||
public boolean acceptRequest(@NonNull final NetworkRequest request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called by a factory to release a request as unfulfillable: the request will be
|
||||
* removed, and the caller will get a
|
||||
* {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function
|
||||
* returns.
|
||||
*
|
||||
* Note: this should only be called by factory which KNOWS that it is the ONLY factory which
|
||||
* is able to fulfill this request!
|
||||
*/
|
||||
protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) {
|
||||
mImpl.releaseRequestAsUnfulfillableByAnyFactory(r);
|
||||
}
|
||||
|
||||
// override to do simple mode (request independent)
|
||||
protected void startNetwork() { }
|
||||
protected void stopNetwork() { }
|
||||
|
||||
// override to do fancier stuff
|
||||
protected void needNetworkFor(@NonNull final NetworkRequest networkRequest) {
|
||||
if (++mRefCount == 1) startNetwork();
|
||||
}
|
||||
|
||||
protected void releaseNetworkFor(@NonNull final NetworkRequest networkRequest) {
|
||||
if (--mRefCount == 0) stopNetwork();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated this method was never part of the API (system or public) and is only added
|
||||
* for migration of existing clients.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setScoreFilter(final int score) {
|
||||
mImpl.setScoreFilter(score);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a score filter for this factory.
|
||||
*
|
||||
* This should include the transports the factory knows its networks will have, and
|
||||
* an optimistic view of the attributes it may have. This does not commit the factory
|
||||
* to being able to bring up such a network ; it only lets it avoid hearing about
|
||||
* requests that it has no chance of fulfilling.
|
||||
*
|
||||
* @param score the filter
|
||||
*/
|
||||
public void setScoreFilter(@NonNull final NetworkScore score) {
|
||||
mImpl.setScoreFilter(score);
|
||||
}
|
||||
|
||||
public void setCapabilityFilter(NetworkCapabilities netCap) {
|
||||
mImpl.setCapabilityFilter(netCap);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected int getRequestCount() {
|
||||
return mImpl.getRequestCount();
|
||||
}
|
||||
|
||||
public int getSerialNumber() {
|
||||
return mImpl.getSerialNumber();
|
||||
}
|
||||
|
||||
public NetworkProvider getProvider() {
|
||||
return mImpl.getProvider();
|
||||
}
|
||||
|
||||
protected void log(String s) {
|
||||
Log.d(LOG_TAG, s);
|
||||
}
|
||||
|
||||
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
mImpl.dump(fd, writer, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + LOG_TAG + " " + mImpl.toString() + "}";
|
||||
}
|
||||
}
|
||||
322
staticlibs/device/android/net/NetworkFactoryImpl.java
Normal file
322
staticlibs/device/android/net/NetworkFactoryImpl.java
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* 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 android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.net.NetworkProvider.NetworkOfferCallback;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A NetworkFactory is an entity that creates NetworkAgent objects.
|
||||
* The bearers register with ConnectivityService using {@link #register} and
|
||||
* their factory will start receiving scored NetworkRequests. NetworkRequests
|
||||
* can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
|
||||
* overridden function. All of these can be dynamic - changing NetworkCapabilities
|
||||
* or score forces re-evaluation of all current requests.
|
||||
*
|
||||
* If any requests pass the filter some overrideable functions will be called.
|
||||
* If the bearer only cares about very simple start/stopNetwork callbacks, those
|
||||
* functions can be overridden. If the bearer needs more interaction, it can
|
||||
* override addNetworkRequest and removeNetworkRequest which will give it each
|
||||
* request that passes their current filters.
|
||||
* @hide
|
||||
**/
|
||||
// TODO(b/187083878): factor out common code between this and NetworkFactoryLegacyImpl
|
||||
class NetworkFactoryImpl extends NetworkFactoryLegacyImpl {
|
||||
private static final boolean DBG = NetworkFactory.DBG;
|
||||
private static final boolean VDBG = NetworkFactory.VDBG;
|
||||
|
||||
// A score that will win against everything, so that score filtering will let all requests
|
||||
// through
|
||||
// TODO : remove this and replace with an API to listen to all requests.
|
||||
@NonNull
|
||||
private static final NetworkScore INVINCIBLE_SCORE =
|
||||
new NetworkScore.Builder().setLegacyInt(1000).build();
|
||||
|
||||
// TODO(b/187082970): Replace CMD_* with Handler.post(() -> { ... }) since all the CMDs do is to
|
||||
// run the tasks asynchronously on the Handler thread.
|
||||
|
||||
/**
|
||||
* Pass a network request to the bearer. If the bearer believes it can
|
||||
* satisfy the request it should connect to the network and create a
|
||||
* NetworkAgent. Once the NetworkAgent is fully functional it will
|
||||
* register itself with ConnectivityService using registerNetworkAgent.
|
||||
* If the bearer cannot immediately satisfy the request (no network,
|
||||
* user disabled the radio, lower-scored network) it should remember
|
||||
* any NetworkRequests it may be able to satisfy in the future. It may
|
||||
* disregard any that it will never be able to service, for example
|
||||
* those requiring a different bearer.
|
||||
* msg.obj = NetworkRequest
|
||||
*/
|
||||
// TODO : this and CANCEL_REQUEST are only used by telephony tests. Replace it in the tests
|
||||
// and remove them and the associated code.
|
||||
private static final int CMD_REQUEST_NETWORK = NetworkFactory.CMD_REQUEST_NETWORK;
|
||||
|
||||
/**
|
||||
* Cancel a network request
|
||||
* msg.obj = NetworkRequest
|
||||
*/
|
||||
private static final int CMD_CANCEL_REQUEST = NetworkFactory.CMD_CANCEL_REQUEST;
|
||||
|
||||
/**
|
||||
* Internally used to set our best-guess score.
|
||||
* msg.obj = new score
|
||||
*/
|
||||
private static final int CMD_SET_SCORE = 3;
|
||||
|
||||
/**
|
||||
* Internally used to set our current filter for coarse bandwidth changes with
|
||||
* technology changes.
|
||||
* msg.obj = new filter
|
||||
*/
|
||||
private static final int CMD_SET_FILTER = 4;
|
||||
|
||||
/**
|
||||
* Internally used to send the network offer associated with this factory.
|
||||
* No arguments, will read from members
|
||||
*/
|
||||
private static final int CMD_OFFER_NETWORK = 5;
|
||||
|
||||
/**
|
||||
* Internally used to send the request to listen to all requests.
|
||||
* No arguments, will read from members
|
||||
*/
|
||||
private static final int CMD_LISTEN_TO_ALL_REQUESTS = 6;
|
||||
|
||||
private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
|
||||
new LinkedHashMap<>();
|
||||
|
||||
@NonNull private NetworkScore mScore = new NetworkScore.Builder().setLegacyInt(0).build();
|
||||
|
||||
private final NetworkOfferCallback mRequestCallback = new NetworkOfferCallback() {
|
||||
@Override
|
||||
public void onNetworkNeeded(@NonNull final NetworkRequest request) {
|
||||
handleAddRequest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkUnneeded(@NonNull final NetworkRequest request) {
|
||||
handleRemoveRequest(request);
|
||||
}
|
||||
};
|
||||
@NonNull private final Executor mExecutor = command -> post(command);
|
||||
|
||||
|
||||
// Ideally the filter argument would be non-null, but null has historically meant to see
|
||||
// no requests and telephony passes null.
|
||||
NetworkFactoryImpl(NetworkFactory parent, Looper looper, Context context,
|
||||
@Nullable final NetworkCapabilities filter) {
|
||||
super(parent, looper, context,
|
||||
null != filter ? filter :
|
||||
NetworkCapabilities.Builder.withoutDefaultCapabilities().build());
|
||||
}
|
||||
|
||||
/* Registers this NetworkFactory with the system. May only be called once per factory. */
|
||||
@Override public void register(final String logTag) {
|
||||
register(logTag, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this NetworkFactory with the system ignoring the score filter. This will let
|
||||
* the factory always see all network requests matching its capabilities filter.
|
||||
* May only be called once per factory.
|
||||
*/
|
||||
@Override public void registerIgnoringScore(final String logTag) {
|
||||
register(logTag, true);
|
||||
}
|
||||
|
||||
private void register(final String logTag, final boolean listenToAllRequests) {
|
||||
if (mProvider != null) {
|
||||
throw new IllegalStateException("A NetworkFactory must only be registered once");
|
||||
}
|
||||
if (DBG) mParent.log("Registering NetworkFactory");
|
||||
|
||||
mProvider = new NetworkProvider(mContext, NetworkFactoryImpl.this.getLooper(), logTag) {
|
||||
@Override
|
||||
public void onNetworkRequested(@NonNull NetworkRequest request, int score,
|
||||
int servingProviderId) {
|
||||
handleAddRequest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
|
||||
handleRemoveRequest(request);
|
||||
}
|
||||
};
|
||||
|
||||
((ConnectivityManager) mContext.getSystemService(
|
||||
Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider);
|
||||
|
||||
// The mScore and mCapabilityFilter members can only be accessed on the handler thread.
|
||||
// TODO : offer a separate API to listen to all requests instead
|
||||
if (listenToAllRequests) {
|
||||
sendMessage(obtainMessage(CMD_LISTEN_TO_ALL_REQUESTS));
|
||||
} else {
|
||||
sendMessage(obtainMessage(CMD_OFFER_NETWORK));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOfferNetwork(@NonNull final NetworkScore score) {
|
||||
mProvider.registerNetworkOffer(score, mCapabilityFilter, mExecutor, mRequestCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case CMD_REQUEST_NETWORK: {
|
||||
handleAddRequest((NetworkRequest) msg.obj);
|
||||
break;
|
||||
}
|
||||
case CMD_CANCEL_REQUEST: {
|
||||
handleRemoveRequest((NetworkRequest) msg.obj);
|
||||
break;
|
||||
}
|
||||
case CMD_SET_SCORE: {
|
||||
handleSetScore((NetworkScore) msg.obj);
|
||||
break;
|
||||
}
|
||||
case CMD_SET_FILTER: {
|
||||
handleSetFilter((NetworkCapabilities) msg.obj);
|
||||
break;
|
||||
}
|
||||
case CMD_OFFER_NETWORK: {
|
||||
handleOfferNetwork(mScore);
|
||||
break;
|
||||
}
|
||||
case CMD_LISTEN_TO_ALL_REQUESTS: {
|
||||
handleOfferNetwork(INVINCIBLE_SCORE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NetworkRequestInfo {
|
||||
@NonNull public final NetworkRequest request;
|
||||
public boolean requested; // do we have a request outstanding, limited by score
|
||||
|
||||
NetworkRequestInfo(@NonNull final NetworkRequest request) {
|
||||
this.request = request;
|
||||
this.requested = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + request + ", requested=" + requested + "}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a NetworkRequest that the bearer may want to attempt to satisfy.
|
||||
* @see #CMD_REQUEST_NETWORK
|
||||
*
|
||||
* @param request the request to handle.
|
||||
*/
|
||||
private void handleAddRequest(@NonNull final NetworkRequest request) {
|
||||
NetworkRequestInfo n = mNetworkRequests.get(request);
|
||||
if (n == null) {
|
||||
if (DBG) mParent.log("got request " + request);
|
||||
n = new NetworkRequestInfo(request);
|
||||
mNetworkRequests.put(n.request, n);
|
||||
} else {
|
||||
if (VDBG) mParent.log("handle existing request " + request);
|
||||
}
|
||||
if (VDBG) mParent.log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
|
||||
|
||||
if (mParent.acceptRequest(request)) {
|
||||
n.requested = true;
|
||||
mParent.needNetworkFor(request);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRemoveRequest(NetworkRequest request) {
|
||||
NetworkRequestInfo n = mNetworkRequests.get(request);
|
||||
if (n != null) {
|
||||
mNetworkRequests.remove(request);
|
||||
if (n.requested) mParent.releaseNetworkFor(n.request);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetScore(@NonNull final NetworkScore score) {
|
||||
if (mScore.equals(score)) return;
|
||||
mScore = score;
|
||||
mParent.reevaluateAllRequests();
|
||||
}
|
||||
|
||||
private void handleSetFilter(@NonNull final NetworkCapabilities netCap) {
|
||||
if (netCap.equals(mCapabilityFilter)) return;
|
||||
mCapabilityFilter = netCap;
|
||||
mParent.reevaluateAllRequests();
|
||||
}
|
||||
|
||||
@Override public final void reevaluateAllRequests() {
|
||||
if (mProvider == null) return;
|
||||
mProvider.registerNetworkOffer(mScore, mCapabilityFilter, mExecutor, mRequestCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated this method was never part of the API (system or public) and is only added
|
||||
* for migration of existing clients.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setScoreFilter(final int score) {
|
||||
setScoreFilter(new NetworkScore.Builder().setLegacyInt(score).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a score filter for this factory.
|
||||
*
|
||||
* This should include the transports the factory knows its networks will have, and
|
||||
* an optimistic view of the attributes it may have. This does not commit the factory
|
||||
* to being able to bring up such a network ; it only lets it avoid hearing about
|
||||
* requests that it has no chance of fulfilling.
|
||||
*
|
||||
* @param score the filter
|
||||
*/
|
||||
@Override public void setScoreFilter(@NonNull final NetworkScore score) {
|
||||
sendMessage(obtainMessage(CMD_SET_SCORE, score));
|
||||
}
|
||||
|
||||
@Override public void setCapabilityFilter(NetworkCapabilities netCap) {
|
||||
sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
|
||||
}
|
||||
|
||||
@Override public int getRequestCount() {
|
||||
return mNetworkRequests.size();
|
||||
}
|
||||
|
||||
@Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
writer.println(toString());
|
||||
for (NetworkRequestInfo n : mNetworkRequests.values()) {
|
||||
writer.println(" " + n);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "providerId=" + (mProvider != null ? mProvider.getProviderId() : "null")
|
||||
+ ", ScoreFilter=" + mScore + ", Filter=" + mCapabilityFilter
|
||||
+ ", requests=" + mNetworkRequests.size();
|
||||
}
|
||||
}
|
||||
397
staticlibs/device/android/net/NetworkFactoryLegacyImpl.java
Normal file
397
staticlibs/device/android/net/NetworkFactoryLegacyImpl.java
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* 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 android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A NetworkFactory is an entity that creates NetworkAgent objects.
|
||||
* The bearers register with ConnectivityService using {@link #register} and
|
||||
* their factory will start receiving scored NetworkRequests. NetworkRequests
|
||||
* can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
|
||||
* overridden function. All of these can be dynamic - changing NetworkCapabilities
|
||||
* or score forces re-evaluation of all current requests.
|
||||
*
|
||||
* If any requests pass the filter some overrideable functions will be called.
|
||||
* If the bearer only cares about very simple start/stopNetwork callbacks, those
|
||||
* functions can be overridden. If the bearer needs more interaction, it can
|
||||
* override addNetworkRequest and removeNetworkRequest which will give it each
|
||||
* request that passes their current filters.
|
||||
* @hide
|
||||
**/
|
||||
// TODO(b/187083878): factor out common code between this and NetworkFactoryImpl
|
||||
class NetworkFactoryLegacyImpl extends Handler
|
||||
implements NetworkFactoryShim {
|
||||
private static final boolean DBG = NetworkFactory.DBG;
|
||||
private static final boolean VDBG = NetworkFactory.VDBG;
|
||||
|
||||
// TODO(b/187082970): Replace CMD_* with Handler.post(() -> { ... }) since all the CMDs do is to
|
||||
// run the tasks asynchronously on the Handler thread.
|
||||
|
||||
/**
|
||||
* Pass a network request to the bearer. If the bearer believes it can
|
||||
* satisfy the request it should connect to the network and create a
|
||||
* NetworkAgent. Once the NetworkAgent is fully functional it will
|
||||
* register itself with ConnectivityService using registerNetworkAgent.
|
||||
* If the bearer cannot immediately satisfy the request (no network,
|
||||
* user disabled the radio, lower-scored network) it should remember
|
||||
* any NetworkRequests it may be able to satisfy in the future. It may
|
||||
* disregard any that it will never be able to service, for example
|
||||
* those requiring a different bearer.
|
||||
* msg.obj = NetworkRequest
|
||||
* msg.arg1 = score - the score of the network currently satisfying this
|
||||
* request. If this bearer knows in advance it cannot
|
||||
* exceed this score it should not try to connect, holding the request
|
||||
* for the future.
|
||||
* Note that subsequent events may give a different (lower
|
||||
* or higher) score for this request, transmitted to each
|
||||
* NetworkFactory through additional CMD_REQUEST_NETWORK msgs
|
||||
* with the same NetworkRequest but an updated score.
|
||||
* Also, network conditions may change for this bearer
|
||||
* allowing for a better score in the future.
|
||||
* msg.arg2 = the ID of the NetworkProvider currently responsible for the
|
||||
* NetworkAgent handling this request, or NetworkProvider.ID_NONE if none.
|
||||
*/
|
||||
public static final int CMD_REQUEST_NETWORK = 1;
|
||||
|
||||
/**
|
||||
* Cancel a network request
|
||||
* msg.obj = NetworkRequest
|
||||
*/
|
||||
public static final int CMD_CANCEL_REQUEST = 2;
|
||||
|
||||
/**
|
||||
* Internally used to set our best-guess score.
|
||||
* msg.arg1 = new score
|
||||
*/
|
||||
private static final int CMD_SET_SCORE = 3;
|
||||
|
||||
/**
|
||||
* Internally used to set our current filter for coarse bandwidth changes with
|
||||
* technology changes.
|
||||
* msg.obj = new filter
|
||||
*/
|
||||
private static final int CMD_SET_FILTER = 4;
|
||||
|
||||
final Context mContext;
|
||||
final NetworkFactory mParent;
|
||||
|
||||
private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
|
||||
new LinkedHashMap<>();
|
||||
|
||||
private int mScore;
|
||||
NetworkCapabilities mCapabilityFilter;
|
||||
|
||||
NetworkProvider mProvider = null;
|
||||
|
||||
NetworkFactoryLegacyImpl(NetworkFactory parent, Looper looper, Context context,
|
||||
NetworkCapabilities filter) {
|
||||
super(looper);
|
||||
mParent = parent;
|
||||
mContext = context;
|
||||
mCapabilityFilter = filter;
|
||||
}
|
||||
|
||||
/* Registers this NetworkFactory with the system. May only be called once per factory. */
|
||||
@Override public void register(final String logTag) {
|
||||
if (mProvider != null) {
|
||||
throw new IllegalStateException("A NetworkFactory must only be registered once");
|
||||
}
|
||||
if (DBG) mParent.log("Registering NetworkFactory");
|
||||
|
||||
mProvider = new NetworkProvider(mContext, NetworkFactoryLegacyImpl.this.getLooper(),
|
||||
logTag) {
|
||||
@Override
|
||||
public void onNetworkRequested(@NonNull NetworkRequest request, int score,
|
||||
int servingProviderId) {
|
||||
handleAddRequest(request, score, servingProviderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
|
||||
handleRemoveRequest(request);
|
||||
}
|
||||
};
|
||||
|
||||
((ConnectivityManager) mContext.getSystemService(
|
||||
Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider);
|
||||
}
|
||||
|
||||
/** Unregisters this NetworkFactory. After this call, the object can no longer be used. */
|
||||
@Override public void terminate() {
|
||||
if (mProvider == null) {
|
||||
throw new IllegalStateException("This NetworkFactory was never registered");
|
||||
}
|
||||
if (DBG) mParent.log("Unregistering NetworkFactory");
|
||||
|
||||
((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
|
||||
.unregisterNetworkProvider(mProvider);
|
||||
|
||||
// Remove all pending messages, since this object cannot be reused. Any message currently
|
||||
// being processed will continue to run.
|
||||
removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case CMD_REQUEST_NETWORK: {
|
||||
handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
|
||||
break;
|
||||
}
|
||||
case CMD_CANCEL_REQUEST: {
|
||||
handleRemoveRequest((NetworkRequest) msg.obj);
|
||||
break;
|
||||
}
|
||||
case CMD_SET_SCORE: {
|
||||
handleSetScore(msg.arg1);
|
||||
break;
|
||||
}
|
||||
case CMD_SET_FILTER: {
|
||||
handleSetFilter((NetworkCapabilities) msg.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NetworkRequestInfo {
|
||||
public final NetworkRequest request;
|
||||
public int score;
|
||||
public boolean requested; // do we have a request outstanding, limited by score
|
||||
public int providerId;
|
||||
|
||||
NetworkRequestInfo(NetworkRequest request, int score, int providerId) {
|
||||
this.request = request;
|
||||
this.score = score;
|
||||
this.requested = false;
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + request + ", score=" + score + ", requested=" + requested + "}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a NetworkRequest that the bearer may want to attempt to satisfy.
|
||||
* @see #CMD_REQUEST_NETWORK
|
||||
*
|
||||
* @param request the request to handle.
|
||||
* @param score the score of the NetworkAgent currently satisfying this request.
|
||||
* @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent
|
||||
* currently satisfying this request.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) {
|
||||
NetworkRequestInfo n = mNetworkRequests.get(request);
|
||||
if (n == null) {
|
||||
if (DBG) {
|
||||
mParent.log("got request " + request + " with score " + score
|
||||
+ " and providerId " + servingProviderId);
|
||||
}
|
||||
n = new NetworkRequestInfo(request, score, servingProviderId);
|
||||
mNetworkRequests.put(n.request, n);
|
||||
} else {
|
||||
if (VDBG) {
|
||||
mParent.log("new score " + score + " for existing request " + request
|
||||
+ " and providerId " + servingProviderId);
|
||||
}
|
||||
n.score = score;
|
||||
n.providerId = servingProviderId;
|
||||
}
|
||||
if (VDBG) mParent.log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
|
||||
|
||||
evalRequest(n);
|
||||
}
|
||||
|
||||
private void handleRemoveRequest(NetworkRequest request) {
|
||||
NetworkRequestInfo n = mNetworkRequests.get(request);
|
||||
if (n != null) {
|
||||
mNetworkRequests.remove(request);
|
||||
if (n.requested) mParent.releaseNetworkFor(n.request);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetScore(int score) {
|
||||
mScore = score;
|
||||
evalRequests();
|
||||
}
|
||||
|
||||
private void handleSetFilter(NetworkCapabilities netCap) {
|
||||
mCapabilityFilter = netCap;
|
||||
evalRequests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable function to provide complex filtering.
|
||||
* Called for every request every time a new NetworkRequest is seen
|
||||
* and whenever the filterScore or filterNetworkCapabilities change.
|
||||
*
|
||||
* acceptRequest can be overridden to provide complex filter behavior
|
||||
* for the incoming requests
|
||||
*
|
||||
* For output, this class will call {@link NetworkFactory#needNetworkFor} and
|
||||
* {@link NetworkFactory#releaseNetworkFor} for every request that passes the filters.
|
||||
* If you don't need to see every request, you can leave the base
|
||||
* implementations of those two functions and instead override
|
||||
* {@link NetworkFactory#startNetwork} and {@link NetworkFactory#stopNetwork}.
|
||||
*
|
||||
* If you want to see every score fluctuation on every request, set
|
||||
* your score filter to a very high number and watch {@link NetworkFactory#needNetworkFor}.
|
||||
*
|
||||
* @return {@code true} to accept the request.
|
||||
*/
|
||||
public boolean acceptRequest(NetworkRequest request) {
|
||||
return mParent.acceptRequest(request);
|
||||
}
|
||||
|
||||
private void evalRequest(NetworkRequestInfo n) {
|
||||
if (VDBG) {
|
||||
mParent.log("evalRequest");
|
||||
mParent.log(" n.requests = " + n.requested);
|
||||
mParent.log(" n.score = " + n.score);
|
||||
mParent.log(" mScore = " + mScore);
|
||||
mParent.log(" request.providerId = " + n.providerId);
|
||||
mParent.log(" mProvider.id = " + mProvider.getProviderId());
|
||||
}
|
||||
if (shouldNeedNetworkFor(n)) {
|
||||
if (VDBG) mParent.log(" needNetworkFor");
|
||||
mParent.needNetworkFor(n.request);
|
||||
n.requested = true;
|
||||
} else if (shouldReleaseNetworkFor(n)) {
|
||||
if (VDBG) mParent.log(" releaseNetworkFor");
|
||||
mParent.releaseNetworkFor(n.request);
|
||||
n.requested = false;
|
||||
} else {
|
||||
if (VDBG) mParent.log(" done");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldNeedNetworkFor(NetworkRequestInfo n) {
|
||||
// If this request is already tracked, it doesn't qualify for need
|
||||
return !n.requested
|
||||
// If the score of this request is higher or equal to that of this factory and some
|
||||
// other factory is responsible for it, then this factory should not track the request
|
||||
// because it has no hope of satisfying it.
|
||||
&& (n.score < mScore || n.providerId == mProvider.getProviderId())
|
||||
// If this factory can't satisfy the capability needs of this request, then it
|
||||
// should not be tracked.
|
||||
&& n.request.canBeSatisfiedBy(mCapabilityFilter)
|
||||
// Finally if the concrete implementation of the factory rejects the request, then
|
||||
// don't track it.
|
||||
&& acceptRequest(n.request);
|
||||
}
|
||||
|
||||
private boolean shouldReleaseNetworkFor(NetworkRequestInfo n) {
|
||||
// Don't release a request that's not tracked.
|
||||
return n.requested
|
||||
// The request should be released if it can't be satisfied by this factory. That
|
||||
// means either of the following conditions are met :
|
||||
// - Its score is too high to be satisfied by this factory and it's not already
|
||||
// assigned to the factory
|
||||
// - This factory can't satisfy the capability needs of the request
|
||||
// - The concrete implementation of the factory rejects the request
|
||||
&& ((n.score > mScore && n.providerId != mProvider.getProviderId())
|
||||
|| !n.request.canBeSatisfiedBy(mCapabilityFilter)
|
||||
|| !acceptRequest(n.request));
|
||||
}
|
||||
|
||||
private void evalRequests() {
|
||||
for (NetworkRequestInfo n : mNetworkRequests.values()) {
|
||||
evalRequest(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a command, on this NetworkFactory Handler, to re-evaluate all
|
||||
* outstanding requests. Can be called from a factory implementation.
|
||||
*/
|
||||
@Override public void reevaluateAllRequests() {
|
||||
post(this::evalRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called by a factory to release a request as unfulfillable: the request will be
|
||||
* removed, and the caller will get a
|
||||
* {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function
|
||||
* returns.
|
||||
*
|
||||
* Note: this should only be called by factory which KNOWS that it is the ONLY factory which
|
||||
* is able to fulfill this request!
|
||||
*/
|
||||
@Override public void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) {
|
||||
post(() -> {
|
||||
if (DBG) mParent.log("releaseRequestAsUnfulfillableByAnyFactory: " + r);
|
||||
final NetworkProvider provider = mProvider;
|
||||
if (provider == null) {
|
||||
mParent.log("Ignoring attempt to release unregistered request as unfulfillable");
|
||||
return;
|
||||
}
|
||||
provider.declareNetworkRequestUnfulfillable(r);
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void setScoreFilter(int score) {
|
||||
sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
|
||||
}
|
||||
|
||||
@Override public void setScoreFilter(@NonNull final NetworkScore score) {
|
||||
setScoreFilter(score.getLegacyInt());
|
||||
}
|
||||
|
||||
@Override public void setCapabilityFilter(NetworkCapabilities netCap) {
|
||||
sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
|
||||
}
|
||||
|
||||
@Override public int getRequestCount() {
|
||||
return mNetworkRequests.size();
|
||||
}
|
||||
|
||||
/* TODO: delete when all callers have migrated to NetworkProvider IDs. */
|
||||
@Override public int getSerialNumber() {
|
||||
return mProvider.getProviderId();
|
||||
}
|
||||
|
||||
@Override public NetworkProvider getProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
|
||||
@Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
writer.println(toString());
|
||||
for (NetworkRequestInfo n : mNetworkRequests.values()) {
|
||||
writer.println(" " + n);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "providerId=" + (mProvider != null ? mProvider.getProviderId() : "null")
|
||||
+ ", ScoreFilter=" + mScore + ", Filter=" + mCapabilityFilter
|
||||
+ ", requests=" + mNetworkRequests.size();
|
||||
}
|
||||
}
|
||||
71
staticlibs/device/android/net/NetworkFactoryShim.java
Normal file
71
staticlibs/device/android/net/NetworkFactoryShim.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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 android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Extract an interface for multiple implementation of {@link NetworkFactory}, depending on the SDK
|
||||
* version.
|
||||
*
|
||||
* Known implementations:
|
||||
* - {@link NetworkFactoryImpl}: For Android S+
|
||||
* - {@link NetworkFactoryLegacyImpl}: For Android R-
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface NetworkFactoryShim {
|
||||
void register(String logTag);
|
||||
|
||||
default void registerIgnoringScore(String logTag) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
void terminate();
|
||||
|
||||
void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r);
|
||||
|
||||
void reevaluateAllRequests();
|
||||
|
||||
void setScoreFilter(int score);
|
||||
|
||||
void setScoreFilter(@NonNull NetworkScore score);
|
||||
|
||||
void setCapabilityFilter(NetworkCapabilities netCap);
|
||||
|
||||
int getRequestCount();
|
||||
|
||||
int getSerialNumber();
|
||||
|
||||
NetworkProvider getProvider();
|
||||
|
||||
void dump(FileDescriptor fd, PrintWriter writer, String[] args);
|
||||
|
||||
// All impls inherit Handler
|
||||
@VisibleForTesting
|
||||
Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj);
|
||||
|
||||
Looper getLooper();
|
||||
}
|
||||
126
staticlibs/device/com/android/net/module/util/BpfBitmap.java
Normal file
126
staticlibs/device/com/android/net/module/util/BpfBitmap.java
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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.net.module.util;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
*
|
||||
* Generic bitmap class for use with BPF programs. Corresponds to a BpfMap
|
||||
* array type with key->int and value->uint64_t defined in the bpf program.
|
||||
*
|
||||
*/
|
||||
public class BpfBitmap {
|
||||
private BpfMap<Struct.S32, Struct.S64> mBpfMap;
|
||||
|
||||
/**
|
||||
* Create a BpfBitmap map wrapper with "path" of filesystem.
|
||||
*
|
||||
* @param path The path of the BPF map.
|
||||
*/
|
||||
public BpfBitmap(@NonNull String path) throws ErrnoException {
|
||||
mBpfMap = new BpfMap<Struct.S32, Struct.S64>(path, BpfMap.BPF_F_RDWR,
|
||||
Struct.S32.class, Struct.S64.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value from BpfMap for the given key.
|
||||
*
|
||||
* @param key The key in the map corresponding to the value to return.
|
||||
*/
|
||||
private long getBpfMapValue(Struct.S32 key) throws ErrnoException {
|
||||
Struct.S64 curVal = mBpfMap.getValue(key);
|
||||
if (curVal != null) {
|
||||
return curVal.val;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the bit for the given index in the bitmap.
|
||||
*
|
||||
* @param index Position in bitmap.
|
||||
*/
|
||||
public boolean get(int index) throws ErrnoException {
|
||||
if (index < 0) return false;
|
||||
|
||||
Struct.S32 key = new Struct.S32(index >> 6);
|
||||
return ((getBpfMapValue(key) >>> (index & 63)) & 1L) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specified index in the bitmap.
|
||||
*
|
||||
* @param index Position to set in bitmap.
|
||||
*/
|
||||
public void set(int index) throws ErrnoException {
|
||||
set(index, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the specified index in the bitmap.
|
||||
*
|
||||
* @param index Position to unset in bitmap.
|
||||
*/
|
||||
public void unset(int index) throws ErrnoException {
|
||||
set(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the specified index in the bitmap to set value.
|
||||
*
|
||||
* @param index Position to unset in bitmap.
|
||||
* @param set Boolean indicating to set or unset index.
|
||||
*/
|
||||
public void set(int index, boolean set) throws ErrnoException {
|
||||
if (index < 0) throw new IllegalArgumentException("Index out of bounds.");
|
||||
|
||||
Struct.S32 key = new Struct.S32(index >> 6);
|
||||
long mask = (1L << (index & 63));
|
||||
long val = getBpfMapValue(key);
|
||||
if (set) val |= mask; else val &= ~mask;
|
||||
mBpfMap.updateEntry(key, new Struct.S64(val));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the map. The map may already be empty.
|
||||
*
|
||||
* @throws ErrnoException if updating entry to 0 fails.
|
||||
*/
|
||||
public void clear() throws ErrnoException {
|
||||
mBpfMap.forEach((key, value) -> {
|
||||
mBpfMap.updateEntry(key, new Struct.S64(0));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all bitmap values are 0.
|
||||
*/
|
||||
public boolean isEmpty() throws ErrnoException {
|
||||
Struct.S32 key = mBpfMap.getFirstKey();
|
||||
while (key != null) {
|
||||
if (getBpfMapValue(key) != 0) {
|
||||
return false;
|
||||
}
|
||||
key = mBpfMap.getNextKey(key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
136
staticlibs/device/com/android/net/module/util/BpfDump.java
Normal file
136
staticlibs/device/com/android/net/module/util/BpfDump.java
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.net.module.util;
|
||||
|
||||
import static android.system.OsConstants.R_OK;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Base64;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* The classes and the methods for BPF dump utilization.
|
||||
*/
|
||||
public class BpfDump {
|
||||
// Using "," as a separator between base64 encoded key and value is safe because base64
|
||||
// characters are [0-9a-zA-Z/=+].
|
||||
private static final String BASE64_DELIMITER = ",";
|
||||
|
||||
/**
|
||||
* Encode BPF key and value into a base64 format string which uses the delimiter ',':
|
||||
* <base64 encoded key>,<base64 encoded value>
|
||||
*/
|
||||
public static <K extends Struct, V extends Struct> String toBase64EncodedString(
|
||||
@NonNull final K key, @NonNull final V value) {
|
||||
final byte[] keyBytes = key.writeToBytes();
|
||||
final String keyBase64Str = Base64.encodeToString(keyBytes, Base64.DEFAULT)
|
||||
.replace("\n", "");
|
||||
final byte[] valueBytes = value.writeToBytes();
|
||||
final String valueBase64Str = Base64.encodeToString(valueBytes, Base64.DEFAULT)
|
||||
.replace("\n", "");
|
||||
|
||||
return keyBase64Str + BASE64_DELIMITER + valueBase64Str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode Struct from a base64 format string
|
||||
*/
|
||||
private static <T extends Struct> T parseStruct(
|
||||
Class<T> structClass, @NonNull String base64Str) {
|
||||
final byte[] bytes = Base64.decode(base64Str, Base64.DEFAULT);
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
return Struct.parse(structClass, byteBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode BPF key and value from a base64 format string which uses the delimiter ',':
|
||||
* <base64 encoded key>,<base64 encoded value>
|
||||
*/
|
||||
public static <K extends Struct, V extends Struct> Pair<K, V> fromBase64EncodedString(
|
||||
Class<K> keyClass, Class<V> valueClass, @NonNull String base64Str) {
|
||||
String[] keyValueStrs = base64Str.split(BASE64_DELIMITER);
|
||||
if (keyValueStrs.length != 2 /* key + value */) {
|
||||
throw new IllegalArgumentException("Invalid base64Str (" + base64Str + "), base64Str"
|
||||
+ " must contain exactly one delimiter '" + BASE64_DELIMITER + "'");
|
||||
}
|
||||
final K k = parseStruct(keyClass, keyValueStrs[0]);
|
||||
final V v = parseStruct(valueClass, keyValueStrs[1]);
|
||||
return new Pair<>(k, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the BpfMap name and entries
|
||||
*/
|
||||
public static <K extends Struct, V extends Struct> void dumpMap(IBpfMap<K, V> map,
|
||||
PrintWriter pw, String mapName, BiFunction<K, V, String> entryToString) {
|
||||
dumpMap(map, pw, mapName, "" /* header */, entryToString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the BpfMap name, header, and entries
|
||||
*/
|
||||
public static <K extends Struct, V extends Struct> void dumpMap(IBpfMap<K, V> map,
|
||||
PrintWriter pw, String mapName, String header, BiFunction<K, V, String> entryToString) {
|
||||
pw.println(mapName + ":");
|
||||
if (!header.isEmpty()) {
|
||||
pw.println(" " + header);
|
||||
}
|
||||
try {
|
||||
map.forEach((key, value) -> {
|
||||
// Value could be null if there is a concurrent entry deletion.
|
||||
// http://b/220084230.
|
||||
if (value != null) {
|
||||
pw.println(" " + entryToString.apply(key, value));
|
||||
} else {
|
||||
pw.println("Entry is deleted while dumping, iterating from first entry");
|
||||
}
|
||||
});
|
||||
} catch (ErrnoException e) {
|
||||
pw.println("Map dump end with error: " + Os.strerror(e.errno));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the BpfMap status
|
||||
*/
|
||||
public static <K extends Struct, V extends Struct> void dumpMapStatus(IBpfMap<K, V> map,
|
||||
PrintWriter pw, String mapName, String path) {
|
||||
if (map != null) {
|
||||
pw.println(mapName + ": OK");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Os.access(path, R_OK);
|
||||
pw.println(mapName + ": NULL(map is pinned to " + path + ")");
|
||||
} catch (ErrnoException e) {
|
||||
pw.println(mapName + ": NULL(map is not pinned to " + path + ": "
|
||||
+ Os.strerror(e.errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add a helper to dump bpf map content with the map name, the header line
|
||||
// (ex: "BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif"), a lambda that
|
||||
// knows how to dump each line, and the PrintWriter.
|
||||
}
|
||||
312
staticlibs/device/com/android/net/module/util/BpfMap.java
Normal file
312
staticlibs/device/com/android/net/module/util/BpfMap.java
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.net.module.util;
|
||||
|
||||
import static android.system.OsConstants.EEXIST;
|
||||
import static android.system.OsConstants.ENOENT;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.system.ErrnoException;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* BpfMap is a key -> value mapping structure that is designed to maintained the bpf map entries.
|
||||
* This is a wrapper class of in-kernel data structure. The in-kernel data can be read/written by
|
||||
* passing syscalls with map file descriptor.
|
||||
*
|
||||
* @param <K> the key of the map.
|
||||
* @param <V> the value of the map.
|
||||
*/
|
||||
public class BpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V> {
|
||||
static {
|
||||
System.loadLibrary(JniUtil.getJniLibraryName(BpfMap.class.getPackage()));
|
||||
}
|
||||
|
||||
// Following definitions from kernel include/uapi/linux/bpf.h
|
||||
public static final int BPF_F_RDWR = 0;
|
||||
public static final int BPF_F_RDONLY = 1 << 3;
|
||||
public static final int BPF_F_WRONLY = 1 << 4;
|
||||
|
||||
public static final int BPF_MAP_TYPE_HASH = 1;
|
||||
|
||||
private static final int BPF_F_NO_PREALLOC = 1;
|
||||
|
||||
private static final int BPF_ANY = 0;
|
||||
private static final int BPF_NOEXIST = 1;
|
||||
private static final int BPF_EXIST = 2;
|
||||
|
||||
private final ParcelFileDescriptor mMapFd;
|
||||
private final Class<K> mKeyClass;
|
||||
private final Class<V> mValueClass;
|
||||
private final int mKeySize;
|
||||
private final int mValueSize;
|
||||
|
||||
private static ConcurrentHashMap<Pair<String, Integer>, ParcelFileDescriptor> sFdCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
private static ParcelFileDescriptor cachedBpfFdGet(String path, int mode,
|
||||
int keySize, int valueSize)
|
||||
throws ErrnoException, NullPointerException {
|
||||
// Supports up to 1023 byte key and 65535 byte values
|
||||
// Creating a BpfMap with larger keys/values seems like a bad idea any way...
|
||||
keySize &= 1023; // 10-bits
|
||||
valueSize &= 65535; // 16-bits
|
||||
var key = Pair.create(path, (mode << 26) ^ (keySize << 16) ^ valueSize);
|
||||
// unlocked fetch is safe: map is concurrent read capable, and only inserted into
|
||||
ParcelFileDescriptor fd = sFdCache.get(key);
|
||||
if (fd != null) return fd;
|
||||
// ok, no cached fd present, need to grab a lock
|
||||
synchronized (BpfMap.class) {
|
||||
// need to redo the check
|
||||
fd = sFdCache.get(key);
|
||||
if (fd != null) return fd;
|
||||
// okay, we really haven't opened this before...
|
||||
fd = ParcelFileDescriptor.adoptFd(nativeBpfFdGet(path, mode, keySize, valueSize));
|
||||
sFdCache.put(key, fd);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BpfMap map wrapper with "path" of filesystem.
|
||||
*
|
||||
* @param flag the access mode, one of BPF_F_RDWR, BPF_F_RDONLY, or BPF_F_WRONLY.
|
||||
* @throws ErrnoException if the BPF map associated with {@code path} cannot be retrieved.
|
||||
* @throws NullPointerException if {@code path} is null.
|
||||
*/
|
||||
public BpfMap(@NonNull final String path, final int flag, final Class<K> key,
|
||||
final Class<V> value) throws ErrnoException, NullPointerException {
|
||||
mKeyClass = key;
|
||||
mValueClass = value;
|
||||
mKeySize = Struct.getSize(key);
|
||||
mValueSize = Struct.getSize(value);
|
||||
mMapFd = cachedBpfFdGet(path, flag, mKeySize, mValueSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing or create a new key -> value entry in an eBbpf map.
|
||||
* (use insertOrReplaceEntry() if you need to know whether insert or replace happened)
|
||||
*/
|
||||
@Override
|
||||
public void updateEntry(K key, V value) throws ErrnoException {
|
||||
nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_ANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the key does not exist in the map, insert key -> value entry into eBpf map.
|
||||
* Otherwise IllegalStateException will be thrown.
|
||||
*/
|
||||
@Override
|
||||
public void insertEntry(K key, V value)
|
||||
throws ErrnoException, IllegalStateException {
|
||||
try {
|
||||
nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(),
|
||||
BPF_NOEXIST);
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno == EEXIST) throw new IllegalStateException(key + " already exists");
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the key already exists in the map, replace its value. Otherwise NoSuchElementException
|
||||
* will be thrown.
|
||||
*/
|
||||
@Override
|
||||
public void replaceEntry(K key, V value)
|
||||
throws ErrnoException, NoSuchElementException {
|
||||
try {
|
||||
nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(),
|
||||
BPF_EXIST);
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno == ENOENT) throw new NoSuchElementException(key + " not found");
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing or create a new key -> value entry in an eBbpf map.
|
||||
* Returns true if inserted, false if replaced.
|
||||
* (use updateEntry() if you don't care whether insert or replace happened)
|
||||
* Note: see inline comment below if running concurrently with delete operations.
|
||||
*/
|
||||
@Override
|
||||
public boolean insertOrReplaceEntry(K key, V value)
|
||||
throws ErrnoException {
|
||||
try {
|
||||
nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(),
|
||||
BPF_NOEXIST);
|
||||
return true; /* insert succeeded */
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno != EEXIST) throw e;
|
||||
}
|
||||
try {
|
||||
nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(),
|
||||
BPF_EXIST);
|
||||
return false; /* replace succeeded */
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno != ENOENT) throw e;
|
||||
}
|
||||
/* If we reach here somebody deleted after our insert attempt and before our replace:
|
||||
* this implies a race happened. The kernel bpf delete interface only takes a key,
|
||||
* and not the value, so we can safely pretend the replace actually succeeded and
|
||||
* was immediately followed by the other thread's delete, since the delete cannot
|
||||
* observe the potential change to the value.
|
||||
*/
|
||||
return false; /* pretend replace succeeded */
|
||||
}
|
||||
|
||||
/** Remove existing key from eBpf map. Return false if map was not modified. */
|
||||
@Override
|
||||
public boolean deleteEntry(K key) throws ErrnoException {
|
||||
return nativeDeleteMapEntry(mMapFd.getFd(), key.writeToBytes());
|
||||
}
|
||||
|
||||
/** Returns {@code true} if this map contains no elements. */
|
||||
@Override
|
||||
public boolean isEmpty() throws ErrnoException {
|
||||
return getFirstKey() == null;
|
||||
}
|
||||
|
||||
private K getNextKeyInternal(@Nullable K key) throws ErrnoException {
|
||||
byte[] rawKey = new byte[mKeySize];
|
||||
|
||||
if (!nativeGetNextMapKey(mMapFd.getFd(),
|
||||
key == null ? null : key.writeToBytes(),
|
||||
rawKey)) return null;
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(rawKey);
|
||||
buffer.order(ByteOrder.nativeOrder());
|
||||
return Struct.parse(mKeyClass, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next key of the passed-in key. If the passed-in key is not found, return the first
|
||||
* key. If the passed-in key is the last one, return null.
|
||||
*
|
||||
* TODO: consider allowing null passed-in key.
|
||||
*/
|
||||
@Override
|
||||
public K getNextKey(@NonNull K key) throws ErrnoException {
|
||||
Objects.requireNonNull(key);
|
||||
return getNextKeyInternal(key);
|
||||
}
|
||||
|
||||
/** Get the first key of eBpf map. */
|
||||
@Override
|
||||
public K getFirstKey() throws ErrnoException {
|
||||
return getNextKeyInternal(null);
|
||||
}
|
||||
|
||||
/** Check whether a key exists in the map. */
|
||||
@Override
|
||||
public boolean containsKey(@NonNull K key) throws ErrnoException {
|
||||
Objects.requireNonNull(key);
|
||||
|
||||
byte[] rawValue = new byte[mValueSize];
|
||||
return nativeFindMapEntry(mMapFd.getFd(), key.writeToBytes(), rawValue);
|
||||
}
|
||||
|
||||
/** Retrieve a value from the map. Return null if there is no such key. */
|
||||
@Override
|
||||
public V getValue(@NonNull K key) throws ErrnoException {
|
||||
Objects.requireNonNull(key);
|
||||
|
||||
byte[] rawValue = new byte[mValueSize];
|
||||
if (!nativeFindMapEntry(mMapFd.getFd(), key.writeToBytes(), rawValue)) return null;
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(rawValue);
|
||||
buffer.order(ByteOrder.nativeOrder());
|
||||
return Struct.parse(mValueClass, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through the map and handle each key -> value retrieved base on the given BiConsumer.
|
||||
* The given BiConsumer may to delete the passed-in entry, but is not allowed to perform any
|
||||
* other structural modifications to the map, such as adding entries or deleting other entries.
|
||||
* Otherwise, iteration will result in undefined behaviour.
|
||||
*/
|
||||
@Override
|
||||
public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException {
|
||||
@Nullable K nextKey = getFirstKey();
|
||||
|
||||
while (nextKey != null) {
|
||||
@NonNull final K curKey = nextKey;
|
||||
@NonNull final V value = getValue(curKey);
|
||||
|
||||
nextKey = getNextKey(curKey);
|
||||
action.accept(curKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Empty implementation to implement AutoCloseable, so we can use BpfMaps
|
||||
* with try with resources, but due to persistent FD cache, there is no actual
|
||||
* need to close anything. File descriptors will actually be closed when we
|
||||
* unlock the BpfMap class and destroy the ParcelFileDescriptor objects.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the map. The map may already be empty.
|
||||
*
|
||||
* @throws ErrnoException if the map is already closed, if an error occurred during iteration,
|
||||
* or if a non-ENOENT error occurred when deleting a key.
|
||||
*/
|
||||
@Override
|
||||
public void clear() throws ErrnoException {
|
||||
K key = getFirstKey();
|
||||
while (key != null) {
|
||||
deleteEntry(key); // ignores ENOENT.
|
||||
key = getFirstKey();
|
||||
}
|
||||
}
|
||||
|
||||
private static native int nativeBpfFdGet(String path, int mode, int keySize, int valueSize)
|
||||
throws ErrnoException, NullPointerException;
|
||||
|
||||
// Note: the following methods appear to not require the object by virtue of taking the
|
||||
// fd as an int argument, but the hidden reference to this is actually what prevents
|
||||
// the object from being garbage collected (and thus potentially maps closed) prior
|
||||
// to the native code actually running (with a possibly already closed fd).
|
||||
|
||||
private native void nativeWriteToMapEntry(int fd, byte[] key, byte[] value, int flags)
|
||||
throws ErrnoException;
|
||||
|
||||
private native boolean nativeDeleteMapEntry(int fd, byte[] key) throws ErrnoException;
|
||||
|
||||
// If key is found, the operation returns true and the nextKey would reference to the next
|
||||
// element. If key is not found, the operation returns true and the nextKey would reference to
|
||||
// the first element. If key is the last element, false is returned.
|
||||
private native boolean nativeGetNextMapKey(int fd, byte[] key, byte[] nextKey)
|
||||
throws ErrnoException;
|
||||
|
||||
private native boolean nativeFindMapEntry(int fd, byte[] key, byte[] value)
|
||||
throws ErrnoException;
|
||||
}
|
||||
69
staticlibs/device/com/android/net/module/util/BpfUtils.java
Normal file
69
staticlibs/device/com/android/net/module/util/BpfUtils.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.net.module.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The classes and the methods for BPF utilization.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public class BpfUtils {
|
||||
static {
|
||||
System.loadLibrary(JniUtil.getJniLibraryName(BpfUtils.class.getPackage()));
|
||||
}
|
||||
|
||||
// Defined in include/uapi/linux/bpf.h. Only adding the CGROUPS currently being used for now.
|
||||
public static final int BPF_CGROUP_INET_INGRESS = 0;
|
||||
public static final int BPF_CGROUP_INET_EGRESS = 1;
|
||||
public static final int BPF_CGROUP_INET4_BIND = 8;
|
||||
public static final int BPF_CGROUP_INET6_BIND = 9;
|
||||
|
||||
|
||||
/**
|
||||
* Attach BPF program to CGROUP
|
||||
*/
|
||||
public static void attachProgram(int type, @NonNull String programPath,
|
||||
@NonNull String cgroupPath, int flags) throws IOException {
|
||||
native_attachProgramToCgroup(type, programPath, cgroupPath, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach BPF program from CGROUP
|
||||
*/
|
||||
public static void detachProgram(int type, @NonNull String cgroupPath)
|
||||
throws IOException {
|
||||
native_detachProgramFromCgroup(type, cgroupPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach single BPF program from CGROUP
|
||||
*/
|
||||
public static void detachSingleProgram(int type, @NonNull String programPath,
|
||||
@NonNull String cgroupPath) throws IOException {
|
||||
native_detachSingleProgramFromCgroup(type, programPath, cgroupPath);
|
||||
}
|
||||
|
||||
private static native boolean native_attachProgramToCgroup(int type, String programPath,
|
||||
String cgroupPath, int flags) throws IOException;
|
||||
private static native boolean native_detachProgramFromCgroup(int type, String cgroupPath)
|
||||
throws IOException;
|
||||
private static native boolean native_detachSingleProgramFromCgroup(int type,
|
||||
String programPath, String cgroupPath) throws IOException;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue