Merge fixed history of Connectivity

BUG: 189375701
TEST: TH
Ignore-AOSP-First: per-branch merges

Merged-In: Ib95d84b91a455d1a5f10cbf3f8c08c0459bc1c7c
Change-Id: Ie3a058b904cb2e3704c3a0725c8f720f81bb235e
This commit is contained in:
Remi NGUYEN VAN
2021-05-27 16:58:08 +09:00
654 changed files with 70030 additions and 195 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
# Eclipse project
**/.classpath
**/.project
# IntelliJ project
**/.idea
**/*.iml
**/*.ipr

13
OWNERS
View File

@@ -1,3 +1,10 @@
# Placing an OWNERS block to perform migration
# detailed in b/186628461
baligh@google.com
# Owners block: ongoing migration
reminv@google.com
#codewiz@google.com
#jchalard@google.com
#junyulai@google.com
#lorenzo@google.com
#maze@google.com
#reminv@google.com
#satk@google.com

4
PREUPLOAD.cfg Normal file
View File

@@ -0,0 +1,4 @@
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}

View File

@@ -1,4 +1,51 @@
{
"presubmit": [
// Run in addition to mainline-presubmit as mainline-presubmit is not
// supported in every branch.
// CtsNetTestCasesLatestSdk uses stable API shims, so does not exercise
// some latest APIs. Run CtsNetTestCases to get coverage of newer APIs.
{
"name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
}
]
},
{
"name": "TetheringTests"
},
{
"name": "TetheringIntegrationTests"
}
],
"mainline-presubmit": [
{
// TODO: add back the tethering modules when updatable in this branch
"name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
}
]
}
],
"mainline-postsubmit": [
// Tests on physical devices with SIM cards: postsubmit only for capacity constraints
{
// TODO: add back the tethering module when updatable in this branch
"name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex]",
"keywords": ["sim"]
},
{
"name": "TetheringCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
}
],
"imports": [
{
"path": "packages/modules/NetworkStack"
}
],
"imports": [
{
"path": "frameworks/base/core/java/android/net"

164
Tethering/Android.bp Normal file
View File

@@ -0,0 +1,164 @@
//
// 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 {
default_applicable_licenses: ["Android-Apache-2.0"],
}
java_defaults {
name: "TetheringAndroidLibraryDefaults",
sdk_version: "module_current",
min_sdk_version: "30",
srcs: [
"apishim/**/*.java",
"src/**/*.java",
":framework-connectivity-shared-srcs",
":tethering-module-utils-srcs",
":services-tethering-shared-srcs",
],
static_libs: [
"NetworkStackApiStableShims",
"androidx.annotation_annotation",
"modules-utils-build",
"netlink-client",
"networkstack-client",
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.tetheroffload.control-V1.1-java",
"net-utils-framework-common",
"net-utils-device-common",
"netd-client",
"NetworkStackApiCurrentShims",
],
libs: [
"framework-connectivity",
"framework-statsd.stubs.module_lib",
"framework-tethering.impl",
"framework-wifi",
"unsupportedappusage",
],
plugins: ["java_api_finder"],
manifest: "AndroidManifestBase.xml",
}
// Build tethering static library, used to compile both variants of the tethering.
android_library {
name: "TetheringApiCurrentLib",
defaults: ["TetheringAndroidLibraryDefaults"],
}
// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
cc_library {
name: "libtetherutilsjni",
sdk_version: "current",
apex_available: [
"//apex_available:platform", // Used by InProcessTethering
"com.android.tethering",
],
min_sdk_version: "30",
header_libs: [
"bpf_syscall_wrappers",
"bpf_tethering_headers",
],
srcs: [
"jni/*.cpp",
],
shared_libs: [
"liblog",
"libnativehelper_compat_libc++",
],
static_libs: [
"libnetjniutils",
],
// We cannot use plain "libc++" here to link libc++ dynamically because it results in:
// java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
// even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
// build because soong complains of:
// module Tethering missing dependencies: libc++_shared
//
// So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
// we depend on do not dynamically link libc++. This is currently the case, because liblog is
// C-only and libnativehelper_compat_libc also uses stl: "c++_static".
stl: "c++_static",
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
],
ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
}
// Common defaults for compiling the actual APK.
java_defaults {
name: "TetheringAppDefaults",
sdk_version: "module_current",
privileged: true,
jni_libs: [
"libtetherutilsjni",
],
resource_dirs: [
"res",
],
libs: [
"framework-tethering",
"framework-wifi",
],
jarjar_rules: "jarjar-rules.txt",
optimize: {
proguard_flags_files: ["proguard.flags"],
},
}
// Non-updatable tethering running in the system server process for devices not using the module
android_app {
name: "InProcessTethering",
defaults: ["TetheringAppDefaults"],
static_libs: ["TetheringApiCurrentLib"],
certificate: "platform",
manifest: "AndroidManifest_InProcess.xml",
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
apex_available: ["com.android.tethering"],
min_sdk_version: "30",
}
// Updatable tethering packaged as an application
android_app {
name: "Tethering",
defaults: ["TetheringAppDefaults"],
static_libs: ["TetheringApiCurrentLib"],
certificate: "networkstack",
manifest: "AndroidManifest.xml",
use_embedded_native_libs: true,
// The permission configuration *must* be included to ensure security of the device
required: [
"NetworkPermissionConfig",
"privapp_whitelist_com.android.networkstack.tethering",
],
apex_available: ["com.android.tethering"],
min_sdk_version: "30",
}
sdk {
name: "tethering-module-sdk",
java_sdk_libs: [
"framework-tethering",
],
}

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* 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.
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering"
android:sharedUserId="android.uid.networkstack">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
<!-- Permissions must be defined here, and not in the base manifest, as the tethering
running in the system server process does not need any permission, and having
privileged permissions added would cause crashes on startup unless they are also
added to the privileged permissions allowlist for that package. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
<application
android:process="com.android.networkstack.process"
android:extractNativeLibs="false"
android:persistent="true">
<service android:name="com.android.networkstack.tethering.TetheringService"
android:permission="android.permission.MAINLINE_NETWORK_STACK"
android:exported="true">
<intent-filter>
<action android:name="android.net.ITetheringConnector"/>
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* 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.
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering"
android:versionCode="1"
android:versionName="R-initial">
<application
android:label="Tethering"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
android:usesCleartextTraffic="true">
</application>
</manifest>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* 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.
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering.inprocess"
android:sharedUserId="android.uid.system"
android:process="system">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
<application>
<service android:name="com.android.networkstack.tethering.TetheringService"
android:process="system"
android:permission="android.permission.MAINLINE_NETWORK_STACK"
android:exported="true">
<intent-filter>
<action android:name="android.net.ITetheringConnector.InProcess"/>
</intent-filter>
</service>
</application>
</manifest>

5
Tethering/OWNERS Normal file
View File

@@ -0,0 +1,5 @@
# include platform/packages/modules/NetworkStack/:/OWNERS
# Owners block: ongoing migration
# markchien@google.com
reminv@google.com

87
Tethering/apex/Android.bp Normal file
View File

@@ -0,0 +1,87 @@
//
// 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 {
default_applicable_licenses: ["Android-Apache-2.0"],
}
apex {
name: "com.android.tethering",
compile_multilib: "both",
updatable: true,
min_sdk_version: "30",
bootclasspath_fragments: [
"com.android.tethering-bootclasspath-fragment",
],
java_libs: [
"service-connectivity",
],
multilib: {
first: {
jni_libs: ["libservice-connectivity"]
},
both: {
jni_libs: ["libframework-connectivity-jni"],
}
},
bpfs: [
"offload.o",
"test.o",
],
apps: [
"ServiceConnectivityResources",
"Tethering",
],
prebuilts: ["current_sdkinfo"],
manifest: "manifest.json",
key: "com.android.tethering.key",
// Indicates that pre-installed version of this apex can be compressed.
// Whether it actually will be compressed is controlled on per-device basis.
compressible: true,
androidManifest: "AndroidManifest.xml",
}
apex_key {
name: "com.android.tethering.key",
public_key: "com.android.tethering.avbpubkey",
private_key: "com.android.tethering.pem",
}
android_app_certificate {
name: "com.android.tethering.certificate",
certificate: "com.android.tethering",
}
// Encapsulate the contributions made by the com.android.tethering to the bootclasspath.
bootclasspath_fragment {
name: "com.android.tethering-bootclasspath-fragment",
contents: [
"framework-connectivity",
"framework-tethering",
],
apex_available: ["com.android.tethering"],
}
override_apex {
name: "com.android.tethering.inprocess",
base: "com.android.tethering",
package_name: "com.android.tethering.inprocess",
apps: [
"ServiceConnectivityResources",
"InProcessTethering",
],
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tethering">
<!-- APEX does not have classes.dex -->
<application android:hasCode="false" />
<!-- b/145383354: Current minSdk is locked to Q for development cycle, lock it to next version
before ship. -->
<!-- TODO: Uncomment this when the R API level is fixed. b/148281152 -->
<!--uses-sdk
android:minSdkVersion="29"
android:targetSdkVersion="29"
/>
-->
</manifest>

Binary file not shown.

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEA+AWTp03PBRMGt4mVNLt5PDoFFSfmFOVTM7jt5AJXnQMIDsAM
1cyWGWRridGIpoHAaCALVgW5aRySgi8yV5xP4w0YHcKbfh9M6I9oz4RUo4GQBZfX
+lFIGaLjb6I3tEJxPuxps4sW26Io63ihwTnKeGyADHdHGWDUs9WU0Ml+QTvKrdjy
qC03M0dehYXILGiA9m+UXwKoKxhWgfDUhWLhDBUtLJLPL4WeqKc9sG9h+zzVqE+8
LzJsfrodKhTTrLpWOXi6YLRTk8dzsuPz/Nu98sJd1w3fHd20DrmkqsxVhgN1h+nk
zcPpxyGYIP6qYVZCmIXCwZZNtPeb7y/tOs967VHoZ4Qj7p2tE0CAWFMZFGjA/pcZ
7fi6CsIuMOYBbj4+wRlJwpG1g5zSJBCjzhv7dZp8S5oXmLShNYOMYEdsPfaZbm08
3pVY+k8DVf7idcANXNw1lM+sPbE2hp5VuEuVpK+ca5x8hIMpTqJ84wDAjnC1kCwm
X2xfNvYPKNF58SvqlNCPN8X7hQjoeaEb7w24vCdZMRqeGBmu1GNQvCyzbBO0huQm
f5CQPrZjPcnoImlP879VPxY4YB6tAjsA/ZLiub9VdT108lCjb5r8criMzpMAA/AQ
NqQLWFI3M43xPemGBTiIguTYgpRgGcdRZf7XuTgTY5qzQZZuZMVuwaqSD2cCAwEA
AQKCAgEA0jMvw3BPTrakT7Lb8JgelKt7mUV6WyVMUZ6eh0pw5JIoJxAfEKfWYmjY
NzKNRMjcv6LA2MP7MplTld/YI6ZHkl+Lm9VOISL39HVuV8mIThbFb+gT1INEvu1t
IjRyT2SsQ67rmo377mLNmVtgg7mt3kfecjI44MpPGqad/CF4zmKVUKd4aI4BpYUM
F8+dKf3bpoBEWA2RZwy2bGQmSXHW132vDoLR8y2knL04rCqJ+PrC/WWuULXEe9bS
VtLV3yMBZq3qD4Fk/+7fILLPGvNFVdPi4htQiChYrM4rP9HzfaO63VieYMF0hR70
pqoOznXj9Q4QVC9FZmUgFCQjQ1+KhqJw3OldIo0SnvpsLdTO/inKkhQWKC5HlPyh
/rqvro2j3pTHWPAziuBr+oQPcdVCOlCBZ+B99L1tO7aGktVPEIVQG7G7jlFMBiJ1
j/kRGk2RTX8RaPQJTnwUqp8mWUV2fwxHiXNadjejA5ZU3eQT2eAOhXl1w6Lv2jEl
0wMOwPMJGcF77CcqnnWHON8fkxCbAfyy5Uo6Pm9g/Zzecn+ji2sabG7Ge5t0gzdL
LKRcGoyakN2CrbQ8pxlCTgE4HX5oPY+VuqOf8L3AIWIJBsyLbXHVkL1mqQ/Ed2uz
zaaSFYUZw81+m/5bl8JLPaIFNPyikZrXTD0YRer3V06XiyP/kYECggEBAP033xeF
OhgRwkRTjd68hwRJpyHsZDWxHiUqQf6l6yFv5mEE355G2IGI7cZmR2+tUDjQdxLv
tAZIszTK4PFCdVTeWfGVFbVF84eNWLB124pHDMM79GN/AMcuHnQPR756a8IO1hIy
4KxIUE1a1PKN5b9IgE5Lu4TZM96HDpFcUAmCT5urdYDmg3++IWT9PYQlGS7Hhiar
r+Hh646waM8Qx619CwXBqy+Y37+WHVbYqJClr6AcpVMrGA+6cgpskFpZAPLsoy7G
RSsVfyV8pH2JKm/hzk7XCwIpczxeWQSfpJWZ+oOPFHu+zM60Cdj2UrQyKrNHwew8
+WYe9eCA+MiNBcECggEBAPq/F1vdqROiLv9uzhKb8ybgdL7CmREELiqwK+MvNE9t
W7lQz7lcWzav+b2n0M+VJBxUWB3XClgoIvA/AllgTgsYXfKAxNakhKLSBoMmvKCW
HtWcGr/D3RcmacK+DTMWlVS/LuueAFLuH6UmBIUFKc+qA5x7oQecAFALBFupE3G4
LtAspLBI6P8gRtRav5p2whs9H8qjYcyf2f6liWpkmFITcXvPvAxFHicR6ZJdwZ/S
PiX2LJQnOpT7L3+2PWnYwzFStb4MkMGlFKcscU9CvS53JcP/J4Asjk0I4zDB2gri
xzFHPlVzCr2IVVGptKCQ3sdYiMIzQKzEXQHCU8h37ycCggEBAJu8aC48Fz3Edlm1
ldS+2L9vWSaJEBzhoSu0cMBgZVu8SdGzwKDE69XHVI4oS5lI28UFmaaA3JTc07MN
cAmSGT2oP2NQkPhbXGsrKLfm1K6YAiZ1Ulp7OwxFth8lYreo7Wt92nV46yuqkhDx
Y3UGhp39xkPhWiRbvgYHxJLsVqFyjumsK2mq3IeNdVZ6VgJXGsTlnAFeqJ7hZxHs
N5natSRjeosA0PtGJ57agZLvT8Ue0gREef3LzFGoFwmIOcQHZ4kAt2BGOzZDU17H
6Rb4bKxBEbT1l2St/5zKXi90zDHicOvG7Q8qiyY6HrBc1wLSs+ZtpLxZx/3h3tFE
IT6fVUECggEBAMSAQm8Ey76OJ+SXUjk1K50442SnHcs/Cmr7urkEQitImUwl71Pk
87pst/uP6szypOTqmE9yOTIS6iZ6Sn3+QcriIqWrkhZfwW3Tx7S6A7KZUrq15iSH
+thsiw9JXxC9TvOmC8AsBzb2U6hZncsc28JZCxFztSNAduJDb/vhCVLiMxWDFuDr
kmR1R+yc3XDQRpeQFDz6QudYEj9EPOc6xD/16sZLaqP2+oVFvVSt0tJLsdaQECle
gMNGAdhE2eX8MCOUHMc+E6cdlozYAEhMFfO2/cqWR79jq3TlVR3dnOFRDScqHMhc
KnuTvsELjHkUbvGsCSiff7yk+fop7vy4OJsCggEAPemJdItO2rhib8EofrZdY72I
oifX1jhPZ1BWD2GKgcx+eVyJGbONBbJVexvvskTfZBvCcAegmgp+sngP6MO6yZkr
cHMfAJeApYZnshsgXksHGMDtSB50/w1JLrc/nqpxdpy/aTazt0Eu1pLWpze1HFZ/
Xyu4PcmrU+4P1vN7c396slHMktEvly6QqOn4nfBbGDJ17Ow6X1XFvGjAxQPIDTB+
6loV14AHymwmqwMrGn84O72rzqyw+41GxW5+oXhOZ4MeXF3u89TBLWvXDpPy/YQU
EiKpodN0YeEn6Ghzplan8rUha+7TP7AYnS5pCszsCHKd03Py0lMLkF+uAfVsDA==
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGKTCCBBGgAwIBAgIUNiSs5EMqxCZ31gWWCcRJVp9HffAwDQYJKoZIhvcNAQEL
BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC50ZXRoZXJpbmcxIjAgBgkqhkiG9w0B
CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjE4MDcwMDQ4WhgPNDc1NzEx
MTMwNzAwNDhaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE
CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQudGV0aGVyaW5nMSIwIAYJ
KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAxvTUA4seblYjZLfTVNwZuJH914QVNFTj+vD94pWmt5Aq
sH1DVTpBvpXXegc/P5HI2XF/71poSBib1WaQSuXG0fU5K75T18bOGL0qF+fhMtBO
wUyvulcjO0h4XE/xf0txY54exUjAA4JS9ERGJOgb4GOwSbPyzekfmzIyCZ2Yawwu
+oGwD2ZNzZRaPOoWxjwohBWQ6mySuvF9RRRb300qmxxUGFM9Ki3aqrWlYlHEOwOC
M+gIXxYFO7S+yUzf6/gMZLOz2YqfcTOup4hAxtExR7niutxJSsRLPBL237exAJoz
OupoXjtWAlPK4ZwZ/Nl1jdTWauJ+Kv3WqzhHGEb2gn3ZpeO3IdOjJhDgFJ6m1OT/
kjRbW1LCuKGrKaoqsEDT2X3a7Izfripn65hSNTfR5gNLtgELaI3/vXi8Fmzw1AfH
+qi6ulElZvSwx0qm+S0QiPyGFlxrsdnHoGJl1tzjJW8KdNZRvzRLUQtbphPp+VkL
5i0bNKum+AwbfdUkLkNLfw9XdbujgBkZTZDQbZGsNjgrvyXcPO2KiJee0hVCZRs0
rhDi5Pfm7BnN/I2vaTRz/W4mdct9H2RWMuqlSH90JvmKtWcND8ahmOJ3sggrvzfO
QNs3k4JTRecamMzqIkylhlnEC4FjWc6Bx4wsEpwBMZOkF/tGGMZYf2C09a8tpP0C
AwEAAaNTMFEwHQYDVR0OBBYEFNP5gIpNWmq0xa411M1GaRPbEijvMB8GA1UdIwQY
MBaAFNP5gIpNWmq0xa411M1GaRPbEijvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggIBADJGmU3QP4EGbt6eBhVPeo/efsqrHsuB2fvFzvIobJbfkSob
cmvjbzIikOlPAgFWj8lT5SDcIWRorFf1u2JylClJ0nSDcqJMHVKmT7wseV/KtX//
1yUyJFRQVzmjC89dp8OIc00GmItivKLer3NbJdkR3rTUjg7+bNUO27Qp3AFREmiJ
P+M7ouvcQRvByUWbp/LOrJpMdJLysRBO562RwrtwTjltdvufyYswbBZOKEiUh1Jc
Ged+3+SJdhwq3Wy+R3Uj7YE7mUMu1QNbANIMrwF8W93EA53eoL2+cKmuaVU6ZURL
xgSJaY6TrunnSI9XTROLtjsFlJorYWy2tvG7Q5Hw3OkO2Xdz/mm85VTkiusg9DMB
WWTv607YtsIO0FhKmcV4bp3q/EkRj3t/zLvL9uFJrWDGkuShZq6fQvqbCvaokOPY
+M0ZRIwgwa9UpEE0BMklVWqR6BGyap614gOgcOjYM70WRNl59Qne+g128ZN7g9nz
61F70i7kUngV0ZUz1/Fu/NCG+6wGF85ZbFmQl60YHPDw1FtjVUuKyBblaDzdJunx
yQr2t9RUokzFBFK0lGW3+yf0WDQ5fqTMs5h8bz1FCq8/HzWmpdOfqePLe4zsld3b
1nFuSohaIfbn/HDdTNtTBGQPgz8ZswQ6ejJJqTLz9D/odbqn9LeIhDZXcQTf
-----END CERTIFICATE-----

View File

@@ -0,0 +1,4 @@
{
"name": "com.android.tethering",
"version": 309999900
}

View File

@@ -0,0 +1,197 @@
/*
* 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.networkstack.tethering.apishim.api30;
import android.net.INetd;
import android.net.MacAddress;
import android.net.TetherStatsParcel;
import android.net.util.SharedLog;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
import com.android.networkstack.tethering.TetherStatsValue;
/**
* Bpf coordinator class for API shims.
*/
public class BpfCoordinatorShimImpl
extends com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim {
private static final String TAG = "api30.BpfCoordinatorShimImpl";
@NonNull
private final SharedLog mLog;
@NonNull
private final INetd mNetd;
public BpfCoordinatorShimImpl(@NonNull final Dependencies deps) {
mLog = deps.getSharedLog().forSubComponent(TAG);
mNetd = deps.getNetd();
}
@Override
public boolean isInitialized() {
return true;
};
@Override
public boolean tetherOffloadRuleAdd(@NonNull final Ipv6ForwardingRule rule) {
try {
mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Could not add IPv6 forwarding rule: ", e);
return false;
}
return true;
};
@Override
public boolean tetherOffloadRuleRemove(@NonNull final Ipv6ForwardingRule rule) {
try {
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Could not remove IPv6 forwarding rule: ", e);
return false;
}
return true;
}
@Override
public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
@NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
@NonNull MacAddress outDstMac, int mtu) {
return true;
}
@Override
public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex,
int upstreamIfindex, @NonNull MacAddress inDstMac) {
return true;
}
@Override
@Nullable
public SparseArray<TetherStatsValue> tetherOffloadGetStats() {
final TetherStatsParcel[] tetherStatsList;
try {
// The reported tether stats are total data usage for all currently-active upstream
// interfaces since tethering start. There will only ever be one entry for a given
// interface index.
tetherStatsList = mNetd.tetherOffloadGetStats();
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Fail to fetch tethering stats from netd: " + e);
return null;
}
return toTetherStatsValueSparseArray(tetherStatsList);
}
@Override
public boolean tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes) {
try {
mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Exception when updating quota " + quotaBytes + ": ", e);
return false;
}
return true;
}
@NonNull
private SparseArray<TetherStatsValue> toTetherStatsValueSparseArray(
@NonNull final TetherStatsParcel[] parcels) {
final SparseArray<TetherStatsValue> tetherStatsList = new SparseArray<TetherStatsValue>();
for (TetherStatsParcel p : parcels) {
tetherStatsList.put(p.ifIndex, new TetherStatsValue(p.rxPackets, p.rxBytes,
0 /* rxErrors */, p.txPackets, p.txBytes, 0 /* txErrors */));
}
return tetherStatsList;
}
@Override
@Nullable
public TetherStatsValue tetherOffloadGetAndClearStats(int ifIndex) {
try {
final TetherStatsParcel stats =
mNetd.tetherOffloadGetAndClearStats(ifIndex);
return new TetherStatsValue(stats.rxPackets, stats.rxBytes, 0 /* rxErrors */,
stats.txPackets, stats.txBytes, 0 /* txErrors */);
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Exception when cleanup tether stats for upstream index "
+ ifIndex + ": ", e);
return null;
}
}
@Override
public boolean tetherOffloadRuleAdd(boolean downstream, @NonNull Tether4Key key,
@NonNull Tether4Value value) {
/* no op */
return true;
}
@Override
public boolean tetherOffloadRuleRemove(boolean downstream, @NonNull Tether4Key key) {
/* no op */
return true;
}
@Override
public boolean attachProgram(String iface, boolean downstream) {
/* no op */
return true;
}
@Override
public boolean detachProgram(String iface) {
/* no op */
return true;
}
@Override
public boolean isAnyIpv4RuleOnUpstream(int ifIndex) {
/* no op */
return false;
}
@Override
public boolean addDevMap(int ifIndex) {
/* no op */
return false;
}
@Override
public boolean removeDevMap(int ifIndex) {
/* no op */
return false;
}
@Override
public String toString() {
return "Netd used";
}
}

View File

@@ -0,0 +1,522 @@
/*
* 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.networkstack.tethering.apishim.api31;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import android.net.MacAddress;
import android.net.util.SharedLog;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.BpfMap;
import com.android.networkstack.tethering.BpfUtils;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
import com.android.networkstack.tethering.Tether6Value;
import com.android.networkstack.tethering.TetherDevKey;
import com.android.networkstack.tethering.TetherDevValue;
import com.android.networkstack.tethering.TetherDownstream6Key;
import com.android.networkstack.tethering.TetherLimitKey;
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherStatsKey;
import com.android.networkstack.tethering.TetherStatsValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* Bpf coordinator class for API shims.
*/
public class BpfCoordinatorShimImpl
extends com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim {
private static final String TAG = "api31.BpfCoordinatorShimImpl";
// AF_KEY socket type. See include/linux/socket.h.
private static final int AF_KEY = 15;
// PFKEYv2 constants. See include/uapi/linux/pfkeyv2.h.
private static final int PF_KEY_V2 = 2;
@NonNull
private final SharedLog mLog;
// BPF map for downstream IPv4 forwarding.
@Nullable
private final BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map;
// BPF map for upstream IPv4 forwarding.
@Nullable
private final BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map;
// BPF map for downstream IPv6 forwarding.
@Nullable
private final BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
// BPF map for upstream IPv6 forwarding.
@Nullable
private final BpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
// BPF map of tethering statistics of the upstream interface since tethering startup.
@Nullable
private final BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
// BPF map of per-interface quota for tethering offload.
@Nullable
private final BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
// BPF map of interface index mapping for XDP.
@Nullable
private final BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
// Tracking IPv4 rule count while any rule is using the given upstream interfaces. Used for
// reducing the BPF map iteration query. The count is increased or decreased when the rule is
// added or removed successfully on mBpfDownstream4Map. Counting the rules on downstream4 map
// is because tetherOffloadRuleRemove can't get upstream interface index from upstream key,
// unless pass upstream value which is not required for deleting map entry. The upstream
// interface index is the same in Upstream4Value.oif and Downstream4Key.iif. For now, it is
// okay to count on Downstream4Key. See BpfConntrackEventConsumer#accept.
// Note that except the constructor, any calls to mBpfDownstream4Map.clear() need to clear
// this counter as well.
// TODO: Count the rule on upstream if multi-upstream is supported and the
// packet needs to be sent and responded on different upstream interfaces.
// TODO: Add IPv6 rule count.
private final SparseArray<Integer> mRule4CountOnUpstream = new SparseArray<>();
public BpfCoordinatorShimImpl(@NonNull final Dependencies deps) {
mLog = deps.getSharedLog().forSubComponent(TAG);
mBpfDownstream4Map = deps.getBpfDownstream4Map();
mBpfUpstream4Map = deps.getBpfUpstream4Map();
mBpfDownstream6Map = deps.getBpfDownstream6Map();
mBpfUpstream6Map = deps.getBpfUpstream6Map();
mBpfStatsMap = deps.getBpfStatsMap();
mBpfLimitMap = deps.getBpfLimitMap();
mBpfDevMap = deps.getBpfDevMap();
// Clear the stubs of the maps for handling the system service crash if any.
// Doesn't throw the exception and clear the stubs as many as possible.
try {
if (mBpfDownstream4Map != null) mBpfDownstream4Map.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfDownstream4Map: " + e);
}
try {
if (mBpfUpstream4Map != null) mBpfUpstream4Map.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfUpstream4Map: " + e);
}
try {
if (mBpfDownstream6Map != null) mBpfDownstream6Map.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfDownstream6Map: " + e);
}
try {
if (mBpfUpstream6Map != null) mBpfUpstream6Map.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfUpstream6Map: " + e);
}
try {
if (mBpfStatsMap != null) mBpfStatsMap.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfStatsMap: " + e);
}
try {
if (mBpfLimitMap != null) mBpfLimitMap.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfLimitMap: " + e);
}
try {
if (mBpfDevMap != null) mBpfDevMap.clear();
} catch (ErrnoException e) {
mLog.e("Could not clear mBpfDevMap: " + e);
}
}
@Override
public boolean isInitialized() {
return mBpfDownstream4Map != null && mBpfUpstream4Map != null && mBpfDownstream6Map != null
&& mBpfUpstream6Map != null && mBpfStatsMap != null && mBpfLimitMap != null
&& mBpfDevMap != null;
}
@Override
public boolean tetherOffloadRuleAdd(@NonNull final Ipv6ForwardingRule rule) {
if (!isInitialized()) return false;
final TetherDownstream6Key key = rule.makeTetherDownstream6Key();
final Tether6Value value = rule.makeTether6Value();
try {
mBpfDownstream6Map.updateEntry(key, value);
} catch (ErrnoException e) {
mLog.e("Could not update entry: ", e);
return false;
}
return true;
}
@Override
public boolean tetherOffloadRuleRemove(@NonNull final Ipv6ForwardingRule rule) {
if (!isInitialized()) return false;
try {
mBpfDownstream6Map.deleteEntry(rule.makeTetherDownstream6Key());
} catch (ErrnoException e) {
// Silent if the rule did not exist.
if (e.errno != OsConstants.ENOENT) {
mLog.e("Could not update entry: ", e);
return false;
}
}
return true;
}
@Override
public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
@NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
@NonNull MacAddress outDstMac, int mtu) {
if (!isInitialized()) return false;
final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex, inDstMac);
final Tether6Value value = new Tether6Value(upstreamIfindex, outSrcMac,
outDstMac, OsConstants.ETH_P_IPV6, mtu);
try {
mBpfUpstream6Map.insertEntry(key, value);
} catch (ErrnoException | IllegalStateException e) {
mLog.e("Could not insert upstream6 entry: " + e);
return false;
}
return true;
}
@Override
public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
@NonNull MacAddress inDstMac) {
if (!isInitialized()) return false;
final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex, inDstMac);
try {
mBpfUpstream6Map.deleteEntry(key);
} catch (ErrnoException e) {
mLog.e("Could not delete upstream IPv6 entry: " + e);
return false;
}
return true;
}
@Override
@Nullable
public SparseArray<TetherStatsValue> tetherOffloadGetStats() {
if (!isInitialized()) return null;
final SparseArray<TetherStatsValue> tetherStatsList = new SparseArray<TetherStatsValue>();
try {
// The reported tether stats are total data usage for all currently-active upstream
// interfaces since tethering start.
mBpfStatsMap.forEach((key, value) -> tetherStatsList.put((int) key.ifindex, value));
} catch (ErrnoException e) {
mLog.e("Fail to fetch tethering stats from BPF map: ", e);
return null;
}
return tetherStatsList;
}
@Override
public boolean tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes) {
if (!isInitialized()) return false;
// The common case is an update, where the stats already exist,
// hence we read first, even though writing with BPF_NOEXIST
// first would make the code simpler.
long rxBytes, txBytes;
TetherStatsValue statsValue = null;
try {
statsValue = mBpfStatsMap.getValue(new TetherStatsKey(ifIndex));
} catch (ErrnoException e) {
// The BpfMap#getValue doesn't throw an errno ENOENT exception. Catch other error
// while trying to get stats entry.
mLog.e("Could not get stats entry of interface index " + ifIndex + ": ", e);
return false;
}
if (statsValue != null) {
// Ok, there was a stats entry.
rxBytes = statsValue.rxBytes;
txBytes = statsValue.txBytes;
} else {
// No stats entry - create one with zeroes.
try {
// This function is the *only* thing that can create entries.
// BpfMap#insertEntry use BPF_NOEXIST to create the entry. The entry is created
// if and only if it doesn't exist.
mBpfStatsMap.insertEntry(new TetherStatsKey(ifIndex), new TetherStatsValue(
0 /* rxPackets */, 0 /* rxBytes */, 0 /* rxErrors */, 0 /* txPackets */,
0 /* txBytes */, 0 /* txErrors */));
} catch (ErrnoException | IllegalArgumentException e) {
mLog.e("Could not create stats entry: ", e);
return false;
}
rxBytes = 0;
txBytes = 0;
}
// rxBytes + txBytes won't overflow even at 5gbps for ~936 years.
long newLimit = rxBytes + txBytes + quotaBytes;
// if adding limit (e.g., if limit is QUOTA_UNLIMITED) caused overflow: clamp to 'infinity'
if (newLimit < rxBytes + txBytes) newLimit = QUOTA_UNLIMITED;
try {
mBpfLimitMap.updateEntry(new TetherLimitKey(ifIndex), new TetherLimitValue(newLimit));
} catch (ErrnoException e) {
mLog.e("Fail to set quota " + quotaBytes + " for interface index " + ifIndex + ": ", e);
return false;
}
return true;
}
@Override
@Nullable
public TetherStatsValue tetherOffloadGetAndClearStats(int ifIndex) {
if (!isInitialized()) return null;
// getAndClearTetherOffloadStats is called after all offload rules have already been
// deleted for the given upstream interface. Before starting to do cleanup stuff in this
// function, use synchronizeKernelRCU to make sure that all the current running eBPF
// programs are finished on all CPUs, especially the unfinished packet processing. After
// synchronizeKernelRCU returned, we can safely read or delete on the stats map or the
// limit map.
final int res = synchronizeKernelRCU();
if (res != 0) {
// Error log but don't return. Do as much cleanup as possible.
mLog.e("synchronize_rcu() failed: " + res);
}
TetherStatsValue statsValue = null;
try {
statsValue = mBpfStatsMap.getValue(new TetherStatsKey(ifIndex));
} catch (ErrnoException e) {
mLog.e("Could not get stats entry for interface index " + ifIndex + ": ", e);
return null;
}
if (statsValue == null) {
mLog.e("Could not get stats entry for interface index " + ifIndex);
return null;
}
try {
mBpfStatsMap.deleteEntry(new TetherStatsKey(ifIndex));
} catch (ErrnoException e) {
mLog.e("Could not delete stats entry for interface index " + ifIndex + ": ", e);
return null;
}
try {
mBpfLimitMap.deleteEntry(new TetherLimitKey(ifIndex));
} catch (ErrnoException e) {
mLog.e("Could not delete limit for interface index " + ifIndex + ": ", e);
return null;
}
return statsValue;
}
@Override
public boolean tetherOffloadRuleAdd(boolean downstream, @NonNull Tether4Key key,
@NonNull Tether4Value value) {
if (!isInitialized()) return false;
try {
if (downstream) {
mBpfDownstream4Map.insertEntry(key, value);
// Increase the rule count while a adding rule is using a given upstream interface.
final int upstreamIfindex = (int) key.iif;
int count = mRule4CountOnUpstream.get(upstreamIfindex, 0 /* default */);
mRule4CountOnUpstream.put(upstreamIfindex, ++count);
} else {
mBpfUpstream4Map.insertEntry(key, value);
}
} catch (ErrnoException e) {
mLog.e("Could not insert entry (" + key + ", " + value + "): " + e);
return false;
} catch (IllegalStateException e) {
// Silent if the rule already exists. Note that the errno EEXIST was rethrown as
// IllegalStateException. See BpfMap#insertEntry.
}
return true;
}
@Override
public boolean tetherOffloadRuleRemove(boolean downstream, @NonNull Tether4Key key) {
if (!isInitialized()) return false;
try {
if (downstream) {
if (!mBpfDownstream4Map.deleteEntry(key)) {
mLog.e("Could not delete entry (key: " + key + ")");
return false;
}
// Decrease the rule count while a deleting rule is not using a given upstream
// interface anymore.
final int upstreamIfindex = (int) key.iif;
Integer count = mRule4CountOnUpstream.get(upstreamIfindex);
if (count == null) {
Log.wtf(TAG, "Could not delete count for interface " + upstreamIfindex);
return false;
}
if (--count == 0) {
// Remove the entry if the count decreases to zero.
mRule4CountOnUpstream.remove(upstreamIfindex);
} else {
mRule4CountOnUpstream.put(upstreamIfindex, count);
}
} else {
mBpfUpstream4Map.deleteEntry(key);
}
} catch (ErrnoException e) {
// Silent if the rule did not exist.
if (e.errno != OsConstants.ENOENT) {
mLog.e("Could not delete entry: ", e);
return false;
}
}
return true;
}
@Override
public boolean attachProgram(String iface, boolean downstream) {
if (!isInitialized()) return false;
try {
BpfUtils.attachProgram(iface, downstream);
} catch (IOException e) {
mLog.e("Could not attach program: " + e);
return false;
}
return true;
}
@Override
public boolean detachProgram(String iface) {
if (!isInitialized()) return false;
try {
BpfUtils.detachProgram(iface);
} catch (IOException e) {
mLog.e("Could not detach program: " + e);
return false;
}
return true;
}
@Override
public boolean isAnyIpv4RuleOnUpstream(int ifIndex) {
// No entry means no rule for the given interface because 0 has never been stored.
return mRule4CountOnUpstream.get(ifIndex) != null;
}
@Override
public boolean addDevMap(int ifIndex) {
if (!isInitialized()) return false;
try {
mBpfDevMap.updateEntry(new TetherDevKey(ifIndex), new TetherDevValue(ifIndex));
} catch (ErrnoException e) {
mLog.e("Could not add interface " + ifIndex + ": " + e);
return false;
}
return true;
}
@Override
public boolean removeDevMap(int ifIndex) {
if (!isInitialized()) return false;
try {
mBpfDevMap.deleteEntry(new TetherDevKey(ifIndex));
} catch (ErrnoException e) {
mLog.e("Could not delete interface " + ifIndex + ": " + e);
return false;
}
return true;
}
private String mapStatus(BpfMap m, String name) {
return name + "{" + (m != null ? "OK" : "ERROR") + "}";
}
@Override
public String toString() {
return String.join(", ", new String[] {
mapStatus(mBpfDownstream6Map, "mBpfDownstream6Map"),
mapStatus(mBpfUpstream6Map, "mBpfUpstream6Map"),
mapStatus(mBpfDownstream4Map, "mBpfDownstream4Map"),
mapStatus(mBpfUpstream4Map, "mBpfUpstream4Map"),
mapStatus(mBpfStatsMap, "mBpfStatsMap"),
mapStatus(mBpfLimitMap, "mBpfLimitMap"),
mapStatus(mBpfDevMap, "mBpfDevMap")
});
}
/**
* Call synchronize_rcu() to block until all existing RCU read-side critical sections have
* been completed.
* Note that BpfCoordinatorTest have no permissions to create or close pf_key socket. It is
* okay for now because the caller #bpfGetAndClearStats doesn't care the result of this
* function. The tests don't be broken.
* TODO: Wrap this function into Dependencies for mocking in tests.
*/
private int synchronizeKernelRCU() {
// This is a temporary hack for network stats map swap on devices running
// 4.9 kernels. The kernel code of socket release on pf_key socket will
// explicitly call synchronize_rcu() which is exactly what we need.
FileDescriptor pfSocket;
try {
pfSocket = Os.socket(AF_KEY, OsConstants.SOCK_RAW | OsConstants.SOCK_CLOEXEC,
PF_KEY_V2);
} catch (ErrnoException e) {
mLog.e("create PF_KEY socket failed: ", e);
return e.errno;
}
// When closing socket, synchronize_rcu() gets called in sock_release().
try {
Os.close(pfSocket);
} catch (ErrnoException e) {
mLog.e("failed to close the PF_KEY socket: ", e);
return e.errno;
}
return 0;
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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.networkstack.tethering.apishim.common;
import android.net.MacAddress;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
import com.android.networkstack.tethering.TetherStatsValue;
/**
* Bpf coordinator class for API shims.
*/
public abstract class BpfCoordinatorShim {
/**
* Get BpfCoordinatorShim object by OS build version.
*/
@NonNull
public static BpfCoordinatorShim getBpfCoordinatorShim(@NonNull final Dependencies deps) {
if (deps.isAtLeastS()) {
return new com.android.networkstack.tethering.apishim.api31.BpfCoordinatorShimImpl(
deps);
} else {
return new com.android.networkstack.tethering.apishim.api30.BpfCoordinatorShimImpl(
deps);
}
}
/**
* Return true if this class has been initialized, otherwise return false.
*/
public abstract boolean isInitialized();
/**
* Adds a tethering offload rule to BPF map, or updates it if it already exists.
*
* Currently, only downstream /128 IPv6 entries are supported. An existing rule will be updated
* if the input interface and destination prefix match. Otherwise, a new rule will be created.
* Note that this can be only called on handler thread.
*
* @param rule The rule to add or update.
*/
public abstract boolean tetherOffloadRuleAdd(@NonNull Ipv6ForwardingRule rule);
/**
* Deletes a tethering offload rule from the BPF map.
*
* Currently, only downstream /128 IPv6 entries are supported. An existing rule will be deleted
* if the destination IP address and the source interface match. It is not an error if there is
* no matching rule to delete.
*
* @param rule The rule to delete.
*/
public abstract boolean tetherOffloadRuleRemove(@NonNull Ipv6ForwardingRule rule);
/**
* Starts IPv6 forwarding between the specified interfaces.
* @param downstreamIfindex the downstream interface index
* @param upstreamIfindex the upstream interface index
* @param inDstMac the destination MAC address to use for XDP
* @param outSrcMac the source MAC address to use for packets
* @param outDstMac the destination MAC address to use for packets
* @return true if operation succeeded or was a no-op, false otherwise
*/
public abstract boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
@NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
@NonNull MacAddress outDstMac, int mtu);
/**
* Stops IPv6 forwarding between the specified interfaces.
* @param downstreamIfindex the downstream interface index
* @param upstreamIfindex the upstream interface index
* @param inDstMac the destination MAC address to use for XDP
* @return true if operation succeeded or was a no-op, false otherwise
*/
public abstract boolean stopUpstreamIpv6Forwarding(int downstreamIfindex,
int upstreamIfindex, @NonNull MacAddress inDstMac);
/**
* Return BPF tethering offload statistics.
*
* @return an array of TetherStatsValue's, where each entry contains the upstream interface
* index and its tethering statistics since tethering was first started.
* There will only ever be one entry for a given interface index.
*/
@Nullable
public abstract SparseArray<TetherStatsValue> tetherOffloadGetStats();
/**
* Set a per-interface quota for tethering offload.
*
* @param ifIndex Index of upstream interface
* @param quotaBytes The quota defined as the number of bytes, starting from zero and counting
* from *now*. A value of QUOTA_UNLIMITED (-1) indicates there is no limit.
*/
@Nullable
public abstract boolean tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
/**
* Return BPF tethering offload statistics and clear the stats for a given upstream.
*
* Must only be called once all offload rules have already been deleted for the given upstream
* interface. The existing stats will be fetched and returned. The stats and the limit for the
* given upstream interface will be deleted as well.
*
* The stats and limit for a given upstream interface must be initialized (using
* tetherOffloadSetInterfaceQuota) before any offload will occur on that interface.
*
* Note that this can be only called while the BPF maps were initialized.
*
* @param ifIndex Index of upstream interface.
* @return TetherStatsValue, which contains the given upstream interface's tethering statistics
* since tethering was first started on that upstream interface.
*/
@Nullable
public abstract TetherStatsValue tetherOffloadGetAndClearStats(int ifIndex);
/**
* Adds a tethering IPv4 offload rule to appropriate BPF map.
*/
public abstract boolean tetherOffloadRuleAdd(boolean downstream, @NonNull Tether4Key key,
@NonNull Tether4Value value);
/**
* Deletes a tethering IPv4 offload rule from the appropriate BPF map.
*/
public abstract boolean tetherOffloadRuleRemove(boolean downstream, @NonNull Tether4Key key);
/**
* Whether there is currently any IPv4 rule on the specified upstream.
*/
public abstract boolean isAnyIpv4RuleOnUpstream(int ifIndex);
/**
* Attach BPF program.
*
* TODO: consider using InterfaceParams to replace interface name.
*/
public abstract boolean attachProgram(@NonNull String iface, boolean downstream);
/**
* Detach BPF program.
*
* TODO: consider using InterfaceParams to replace interface name.
*/
public abstract boolean detachProgram(@NonNull String iface);
/**
* Add interface index mapping.
*/
public abstract boolean addDevMap(int ifIndex);
/**
* Remove interface index mapping.
*/
public abstract boolean removeDevMap(int ifIndex);
}

View File

@@ -0,0 +1,68 @@
//
// 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.
//
//
// struct definitions shared with JNI
//
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_library_headers {
name: "bpf_tethering_headers",
vendor_available: false,
host_supported: false,
export_include_dirs: ["."],
cflags: [
"-Wall",
"-Werror",
],
sdk_version: "30",
min_sdk_version: "30",
apex_available: ["com.android.tethering"],
visibility: [
"//packages/modules/Connectivity/Tethering",
],
}
//
// bpf kernel programs
//
bpf {
name: "offload.o",
srcs: ["offload.c"],
cflags: [
"-Wall",
"-Werror",
],
include_dirs: [
// TODO: get rid of system/netd.
"system/netd/bpf_progs", // for bpf_net_helpers.h
],
}
bpf {
name: "test.o",
srcs: ["test.c"],
cflags: [
"-Wall",
"-Werror",
],
include_dirs: [
// TODO: get rid of system/netd.
"system/netd/bpf_progs", // for bpf_net_helpers.h
],
}

View File

@@ -0,0 +1,217 @@
/*
* 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.
*/
#pragma once
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/in6.h>
// Common definitions for BPF code in the tethering mainline module.
// These definitions are available to:
// - The BPF programs in Tethering/bpf_progs/
// - JNI code that depends on the bpf_tethering_headers library.
#define BPF_TETHER_ERRORS \
ERR(INVALID_IP_VERSION) \
ERR(LOW_TTL) \
ERR(INVALID_TCP_HEADER) \
ERR(TCP_CONTROL_PACKET) \
ERR(NON_GLOBAL_SRC) \
ERR(NON_GLOBAL_DST) \
ERR(LOCAL_SRC_DST) \
ERR(NO_STATS_ENTRY) \
ERR(NO_LIMIT_ENTRY) \
ERR(BELOW_IPV4_MTU) \
ERR(BELOW_IPV6_MTU) \
ERR(LIMIT_REACHED) \
ERR(CHANGE_HEAD_FAILED) \
ERR(TOO_SHORT) \
ERR(HAS_IP_OPTIONS) \
ERR(IS_IP_FRAG) \
ERR(CHECKSUM) \
ERR(NON_TCP_UDP) \
ERR(NON_TCP) \
ERR(SHORT_L4_HEADER) \
ERR(SHORT_TCP_HEADER) \
ERR(SHORT_UDP_HEADER) \
ERR(UDP_CSUM_ZERO) \
ERR(TRUNCATED_IPV4) \
ERR(_MAX)
#define ERR(x) BPF_TETHER_ERR_ ##x,
enum {
BPF_TETHER_ERRORS
};
#undef ERR
#define ERR(x) #x,
static const char *bpf_tether_errors[] = {
BPF_TETHER_ERRORS
};
#undef ERR
// This header file is shared by eBPF kernel programs (C) and netd (C++) and
// some of the maps are also accessed directly from Java mainline module code.
//
// Hence: explicitly pad all relevant structures and assert that their size
// is the sum of the sizes of their fields.
#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
#define BPF_PATH_TETHER BPF_PATH "tethering/"
#define TETHER_STATS_MAP_PATH BPF_PATH_TETHER "map_offload_tether_stats_map"
typedef uint32_t TetherStatsKey; // upstream ifindex
typedef struct {
uint64_t rxPackets;
uint64_t rxBytes;
uint64_t rxErrors;
uint64_t txPackets;
uint64_t txBytes;
uint64_t txErrors;
} TetherStatsValue;
STRUCT_SIZE(TetherStatsValue, 6 * 8); // 48
#define TETHER_LIMIT_MAP_PATH BPF_PATH_TETHER "map_offload_tether_limit_map"
typedef uint32_t TetherLimitKey; // upstream ifindex
typedef uint64_t TetherLimitValue; // in bytes
#define TETHER_DOWNSTREAM6_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_downstream6_rawip"
#define TETHER_DOWNSTREAM6_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_downstream6_ether"
#define TETHER_DOWNSTREAM6_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM6_TC_PROG_RAWIP_NAME
#define TETHER_DOWNSTREAM6_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM6_TC_PROG_ETHER_NAME
#define TETHER_DOWNSTREAM6_MAP_PATH BPF_PATH_TETHER "map_offload_tether_downstream6_map"
// For now tethering offload only needs to support downstreams that use 6-byte MAC addresses,
// because all downstream types that are currently supported (WiFi, USB, Bluetooth and
// Ethernet) have 6-byte MAC addresses.
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
uint8_t zero[2]; // zero pad for 8 byte alignment
struct in6_addr neigh6; // The destination IPv6 address
} TetherDownstream6Key;
STRUCT_SIZE(TetherDownstream6Key, 4 + 6 + 2 + 16); // 28
typedef struct {
uint32_t oif; // The output interface to redirect to
struct ethhdr macHeader; // includes dst/src mac and ethertype (zeroed iff rawip egress)
uint16_t pmtu; // The maximum L3 output path/route mtu
} Tether6Value;
STRUCT_SIZE(Tether6Value, 4 + 14 + 2); // 20
#define TETHER_DOWNSTREAM64_MAP_PATH BPF_PATH_TETHER "map_offload_tether_downstream64_map"
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
uint16_t l4Proto; // IPPROTO_TCP/UDP/...
struct in6_addr src6; // source &
struct in6_addr dst6; // destination IPv6 addresses
__be16 srcPort; // source &
__be16 dstPort; // destination tcp/udp/... ports
} TetherDownstream64Key;
STRUCT_SIZE(TetherDownstream64Key, 4 + 6 + 2 + 16 + 16 + 2 + 2); // 48
typedef struct {
uint32_t oif; // The output interface to redirect to
struct ethhdr macHeader; // includes dst/src mac and ethertype (zeroed iff rawip egress)
uint16_t pmtu; // The maximum L3 output path/route mtu
struct in_addr src4; // source &
struct in_addr dst4; // destination IPv4 addresses
__be16 srcPort; // source &
__be16 outPort; // destination tcp/udp/... ports
uint64_t lastUsed; // Kernel updates on each use with bpf_ktime_get_boot_ns()
} TetherDownstream64Value;
STRUCT_SIZE(TetherDownstream64Value, 4 + 14 + 2 + 4 + 4 + 2 + 2 + 8); // 40
#define TETHER_UPSTREAM6_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_upstream6_rawip"
#define TETHER_UPSTREAM6_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_upstream6_ether"
#define TETHER_UPSTREAM6_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_UPSTREAM6_TC_PROG_RAWIP_NAME
#define TETHER_UPSTREAM6_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_UPSTREAM6_TC_PROG_ETHER_NAME
#define TETHER_UPSTREAM6_MAP_PATH BPF_PATH_TETHER "map_offload_tether_upstream6_map"
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
uint8_t zero[2]; // zero pad for 8 byte alignment
// TODO: extend this to include src ip /64 subnet
} TetherUpstream6Key;
STRUCT_SIZE(TetherUpstream6Key, 12);
#define TETHER_DOWNSTREAM4_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_downstream4_rawip"
#define TETHER_DOWNSTREAM4_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_downstream4_ether"
#define TETHER_DOWNSTREAM4_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM4_TC_PROG_RAWIP_NAME
#define TETHER_DOWNSTREAM4_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM4_TC_PROG_ETHER_NAME
#define TETHER_DOWNSTREAM4_MAP_PATH BPF_PATH_TETHER "map_offload_tether_downstream4_map"
#define TETHER_UPSTREAM4_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_upstream4_rawip"
#define TETHER_UPSTREAM4_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_upstream4_ether"
#define TETHER_UPSTREAM4_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_UPSTREAM4_TC_PROG_RAWIP_NAME
#define TETHER_UPSTREAM4_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_UPSTREAM4_TC_PROG_ETHER_NAME
#define TETHER_UPSTREAM4_MAP_PATH BPF_PATH_TETHER "map_offload_tether_upstream4_map"
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
uint16_t l4Proto; // IPPROTO_TCP/UDP/...
struct in_addr src4; // source &
struct in_addr dst4; // destination IPv4 addresses
__be16 srcPort; // source &
__be16 dstPort; // destination TCP/UDP/... ports
} Tether4Key;
STRUCT_SIZE(Tether4Key, 4 + 6 + 2 + 4 + 4 + 2 + 2); // 24
typedef struct {
uint32_t oif; // The output interface to redirect to
struct ethhdr macHeader; // includes dst/src mac and ethertype (zeroed iff rawip egress)
uint16_t pmtu; // Maximum L3 output path/route mtu
struct in6_addr src46; // source & (always IPv4 mapped for downstream)
struct in6_addr dst46; // destination IP addresses (may be IPv4 mapped or IPv6 for upstream)
__be16 srcPort; // source &
__be16 dstPort; // destination tcp/udp/... ports
uint64_t last_used; // Kernel updates on each use with bpf_ktime_get_boot_ns()
} Tether4Value;
STRUCT_SIZE(Tether4Value, 4 + 14 + 2 + 16 + 16 + 2 + 2 + 8); // 64
#define TETHER_DOWNSTREAM_XDP_PROG_RAWIP_NAME "prog_offload_xdp_tether_downstream_rawip"
#define TETHER_DOWNSTREAM_XDP_PROG_ETHER_NAME "prog_offload_xdp_tether_downstream_ether"
#define TETHER_DOWNSTREAM_XDP_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM_XDP_PROG_RAWIP_NAME
#define TETHER_DOWNSTREAM_XDP_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM_XDP_PROG_ETHER_NAME
#define TETHER_UPSTREAM_XDP_PROG_RAWIP_NAME "prog_offload_xdp_tether_upstream_rawip"
#define TETHER_UPSTREAM_XDP_PROG_ETHER_NAME "prog_offload_xdp_tether_upstream_ether"
#define TETHER_UPSTREAM_XDP_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_UPSTREAM_XDP_PROG_RAWIP_NAME
#define TETHER_UPSTREAM_XDP_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_UPSTREAM_XDP_PROG_ETHER_NAME
#undef STRUCT_SIZE

View File

@@ -0,0 +1,838 @@
/*
* 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.
*/
#include <linux/if.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <linux/tcp.h>
// bionic kernel uapi linux/udp.h header is munged...
#define __kernel_udphdr udphdr
#include <linux/udp.h>
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_tethering.h"
// From kernel:include/net/ip.h
#define IP_DF 0x4000 // Flag: "Don't Fragment"
// ----- Helper functions for offsets to fields -----
// They all assume simple IP packets:
// - no VLAN ethernet tags
// - no IPv4 options (see IPV4_HLEN/TCP4_OFFSET/UDP4_OFFSET)
// - no IPv6 extension headers
// - no TCP options (see TCP_HLEN)
//#define ETH_HLEN sizeof(struct ethhdr)
#define IP4_HLEN sizeof(struct iphdr)
#define IP6_HLEN sizeof(struct ipv6hdr)
#define TCP_HLEN sizeof(struct tcphdr)
#define UDP_HLEN sizeof(struct udphdr)
// Offsets from beginning of L4 (TCP/UDP) header
#define TCP_OFFSET(field) offsetof(struct tcphdr, field)
#define UDP_OFFSET(field) offsetof(struct udphdr, field)
// Offsets from beginning of L3 (IPv4) header
#define IP4_OFFSET(field) offsetof(struct iphdr, field)
#define IP4_TCP_OFFSET(field) (IP4_HLEN + TCP_OFFSET(field))
#define IP4_UDP_OFFSET(field) (IP4_HLEN + UDP_OFFSET(field))
// Offsets from beginning of L3 (IPv6) header
#define IP6_OFFSET(field) offsetof(struct ipv6hdr, field)
#define IP6_TCP_OFFSET(field) (IP6_HLEN + TCP_OFFSET(field))
#define IP6_UDP_OFFSET(field) (IP6_HLEN + UDP_OFFSET(field))
// Offsets from beginning of L2 (ie. Ethernet) header (which must be present)
#define ETH_IP4_OFFSET(field) (ETH_HLEN + IP4_OFFSET(field))
#define ETH_IP4_TCP_OFFSET(field) (ETH_HLEN + IP4_TCP_OFFSET(field))
#define ETH_IP4_UDP_OFFSET(field) (ETH_HLEN + IP4_UDP_OFFSET(field))
#define ETH_IP6_OFFSET(field) (ETH_HLEN + IP6_OFFSET(field))
#define ETH_IP6_TCP_OFFSET(field) (ETH_HLEN + IP6_TCP_OFFSET(field))
#define ETH_IP6_UDP_OFFSET(field) (ETH_HLEN + IP6_UDP_OFFSET(field))
// ----- Tethering Error Counters -----
DEFINE_BPF_MAP_GRW(tether_error_map, ARRAY, uint32_t, uint32_t, BPF_TETHER_ERR__MAX,
AID_NETWORK_STACK)
#define COUNT_AND_RETURN(counter, ret) do { \
uint32_t code = BPF_TETHER_ERR_ ## counter; \
uint32_t *count = bpf_tether_error_map_lookup_elem(&code); \
if (count) __sync_fetch_and_add(count, 1); \
return ret; \
} while(0)
#define TC_DROP(counter) COUNT_AND_RETURN(counter, TC_ACT_SHOT)
#define TC_PUNT(counter) COUNT_AND_RETURN(counter, TC_ACT_OK)
#define XDP_DROP(counter) COUNT_AND_RETURN(counter, XDP_DROP)
#define XDP_PUNT(counter) COUNT_AND_RETURN(counter, XDP_PASS)
// ----- Tethering Data Stats and Limits -----
// Tethering stats, indexed by upstream interface.
DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, TetherStatsKey, TetherStatsValue, 16, AID_NETWORK_STACK)
// Tethering data limit, indexed by upstream interface.
// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif])
DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, TetherLimitKey, TetherLimitValue, 16, AID_NETWORK_STACK)
// ----- IPv6 Support -----
DEFINE_BPF_MAP_GRW(tether_downstream6_map, HASH, TetherDownstream6Key, Tether6Value, 64,
AID_NETWORK_STACK)
DEFINE_BPF_MAP_GRW(tether_downstream64_map, HASH, TetherDownstream64Key, TetherDownstream64Value,
1024, AID_NETWORK_STACK)
DEFINE_BPF_MAP_GRW(tether_upstream6_map, HASH, TetherUpstream6Key, Tether6Value, 64,
AID_NETWORK_STACK)
static inline __always_inline int do_forward6(struct __sk_buff* skb, const bool is_ethernet,
const bool downstream) {
// Must be meta-ethernet IPv6 frame
if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
// Require ethernet dst mac address to be our unicast address.
if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_OK;
const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
// Since the program never writes via DPA (direct packet access) auto-pull/unclone logic does
// not trigger and thus we need to manually make sure we can read packet headers via DPA.
// Note: this is a blind best effort pull, which may fail or pull less - this doesn't matter.
// It has to be done early cause it will invalidate any skb->data/data_end derived pointers.
try_make_readable(skb, l2_header_size + IP6_HLEN + TCP_HLEN);
void* data = (void*)(long)skb->data;
const void* data_end = (void*)(long)skb->data_end;
struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data;
// Must have (ethernet and) ipv6 header
if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK;
// Ethertype - if present - must be IPv6
if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK;
// IP version must be 6
if (ip6->version != 6) TC_PUNT(INVALID_IP_VERSION);
// Cannot decrement during forward if already zero or would be zero,
// Let the kernel's stack handle these cases and generate appropriate ICMP errors.
if (ip6->hop_limit <= 1) TC_PUNT(LOW_TTL);
// If hardware offload is running and programming flows based on conntrack entries,
// try not to interfere with it.
if (ip6->nexthdr == IPPROTO_TCP) {
struct tcphdr* tcph = (void*)(ip6 + 1);
// Make sure we can get at the tcp header
if (data + l2_header_size + sizeof(*ip6) + sizeof(*tcph) > data_end)
TC_PUNT(INVALID_TCP_HEADER);
// Do not offload TCP packets with any one of the SYN/FIN/RST flags
if (tcph->syn || tcph->fin || tcph->rst) TC_PUNT(TCP_CONTROL_PACKET);
}
// Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness.
__be32 src32 = ip6->saddr.s6_addr32[0];
if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP
(src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast
TC_PUNT(NON_GLOBAL_SRC);
// Protect against forwarding packets destined to ::1 or fe80::/64 or other weirdness.
__be32 dst32 = ip6->daddr.s6_addr32[0];
if (dst32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP
(dst32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast
TC_PUNT(NON_GLOBAL_DST);
// In the upstream direction do not forward traffic within the same /64 subnet.
if (!downstream && (src32 == dst32) && (ip6->saddr.s6_addr32[1] == ip6->daddr.s6_addr32[1]))
TC_PUNT(LOCAL_SRC_DST);
TetherDownstream6Key kd = {
.iif = skb->ifindex,
.neigh6 = ip6->daddr,
};
TetherUpstream6Key ku = {
.iif = skb->ifindex,
};
if (is_ethernet) __builtin_memcpy(downstream ? kd.dstMac : ku.dstMac, eth->h_dest, ETH_ALEN);
Tether6Value* v = downstream ? bpf_tether_downstream6_map_lookup_elem(&kd)
: bpf_tether_upstream6_map_lookup_elem(&ku);
// If we don't find any offload information then simply let the core stack handle it...
if (!v) return TC_ACT_OK;
uint32_t stat_and_limit_k = downstream ? skb->ifindex : v->oif;
TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
// If we don't have anywhere to put stats, then abort...
if (!stat_v) TC_PUNT(NO_STATS_ENTRY);
uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k);
// If we don't have a limit, then abort...
if (!limit_v) TC_PUNT(NO_LIMIT_ENTRY);
// Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort...
if (v->pmtu < IPV6_MIN_MTU) TC_PUNT(BELOW_IPV6_MTU);
// Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default
// outbound path mtu of 1500 is not necessarily correct, but worst case we simply
// undercount, which is still better then not accounting for this overhead at all.
// Note: this really shouldn't be device/path mtu at all, but rather should be
// derived from this particular connection's mss (ie. from gro segment size).
// This would require a much newer kernel with newer ebpf accessors.
// (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
uint64_t packets = 1;
uint64_t bytes = skb->len;
if (bytes > v->pmtu) {
const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
const int mss = v->pmtu - tcp_overhead;
const uint64_t payload = bytes - tcp_overhead;
packets = (payload + mss - 1) / mss;
bytes = tcp_overhead * packets + payload;
}
// Are we past the limit? If so, then abort...
// Note: will not overflow since u64 is 936 years even at 5Gbps.
// Do not drop here. Offload is just that, whenever we fail to handle
// a packet we let the core stack deal with things.
// (The core stack needs to handle limits correctly anyway,
// since we don't offload all traffic in both directions)
if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) TC_PUNT(LIMIT_REACHED);
if (!is_ethernet) {
// Try to inject an ethernet header, and simply return if we fail.
// We do this even if TX interface is RAWIP and thus does not need an ethernet header,
// because this is easier and the kernel will strip extraneous ethernet header.
if (bpf_skb_change_head(skb, sizeof(struct ethhdr), /*flags*/ 0)) {
__sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_PUNT(CHANGE_HEAD_FAILED);
}
// bpf_skb_change_head() invalidates all pointers - reload them
data = (void*)(long)skb->data;
data_end = (void*)(long)skb->data_end;
eth = data;
ip6 = (void*)(eth + 1);
// I do not believe this can ever happen, but keep the verifier happy...
if (data + sizeof(struct ethhdr) + sizeof(*ip6) > data_end) {
__sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_DROP(TOO_SHORT);
}
};
// At this point we always have an ethernet header - which will get stripped by the
// kernel during transmit through a rawip interface. ie. 'eth' pointer is valid.
// Additionally note that 'is_ethernet' and 'l2_header_size' are no longer correct.
// CHECKSUM_COMPLETE is a 16-bit one's complement sum,
// thus corrections for it need to be done in 16-byte chunks at even offsets.
// IPv6 nexthdr is at offset 6, while hop limit is at offset 7
uint8_t old_hl = ip6->hop_limit;
--ip6->hop_limit;
uint8_t new_hl = ip6->hop_limit;
// bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
// (-ENOTSUPP) if it isn't.
bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
__sync_fetch_and_add(downstream ? &stat_v->rxPackets : &stat_v->txPackets, packets);
__sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, bytes);
// Overwrite any mac header with the new one
// For a rawip tx interface it will simply be a bunch of zeroes and later stripped.
*eth = v->macHeader;
// Redirect to forwarded interface.
//
// Note that bpf_redirect() cannot fail unless you pass invalid flags.
// The redirect actually happens after the ebpf program has already terminated,
// and can fail for example for mtu reasons at that point in time, but there's nothing
// we can do about it here.
return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
}
DEFINE_BPF_PROG("schedcls/tether_downstream6_ether", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream6_ether)
(struct __sk_buff* skb) {
return do_forward6(skb, /* is_ethernet */ true, /* downstream */ true);
}
DEFINE_BPF_PROG("schedcls/tether_upstream6_ether", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream6_ether)
(struct __sk_buff* skb) {
return do_forward6(skb, /* is_ethernet */ true, /* downstream */ false);
}
// Note: section names must be unique to prevent programs from appending to each other,
// so instead the bpf loader will strip everything past the final $ symbol when actually
// pinning the program into the filesystem.
//
// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed:
// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head
// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
// (the first of those has already been upstreamed)
//
// 5.4 kernel support was only added to Android Common Kernel in R,
// and thus a 5.4 kernel always supports this.
//
// Hence, these mandatory (must load successfully) implementations for 5.4+ kernels:
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream6_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream6_rawip_5_4, KVER(5, 4, 0))
(struct __sk_buff* skb) {
return do_forward6(skb, /* is_ethernet */ false, /* downstream */ true);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream6_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream6_rawip_5_4, KVER(5, 4, 0))
(struct __sk_buff* skb) {
return do_forward6(skb, /* is_ethernet */ false, /* downstream */ false);
}
// and these identical optional (may fail to load) implementations for [4.14..5.4) patched kernels:
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$4_14",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream6_rawip_4_14,
KVER(4, 14, 0), KVER(5, 4, 0))
(struct __sk_buff* skb) {
return do_forward6(skb, /* is_ethernet */ false, /* downstream */ true);
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$4_14",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream6_rawip_4_14,
KVER(4, 14, 0), KVER(5, 4, 0))
(struct __sk_buff* skb) {
return do_forward6(skb, /* is_ethernet */ false, /* downstream */ false);
}
// and define no-op stubs for [4.9,4.14) and unpatched [4.14,5.4) kernels.
// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned
// it at the same location this one would be pinned at and will thus skip loading this stub)
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream6_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
return TC_ACT_OK;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream6_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
return TC_ACT_OK;
}
// ----- IPv4 Support -----
DEFINE_BPF_MAP_GRW(tether_downstream4_map, HASH, Tether4Key, Tether4Value, 1024, AID_NETWORK_STACK)
DEFINE_BPF_MAP_GRW(tether_upstream4_map, HASH, Tether4Key, Tether4Value, 1024, AID_NETWORK_STACK)
static inline __always_inline int do_forward4(struct __sk_buff* skb, const bool is_ethernet,
const bool downstream, const bool updatetime) {
// Require ethernet dst mac address to be our unicast address.
if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_OK;
// Must be meta-ethernet IPv4 frame
if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_OK;
const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
// Since the program never writes via DPA (direct packet access) auto-pull/unclone logic does
// not trigger and thus we need to manually make sure we can read packet headers via DPA.
// Note: this is a blind best effort pull, which may fail or pull less - this doesn't matter.
// It has to be done early cause it will invalidate any skb->data/data_end derived pointers.
try_make_readable(skb, l2_header_size + IP4_HLEN + TCP_HLEN);
void* data = (void*)(long)skb->data;
const void* data_end = (void*)(long)skb->data_end;
struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
struct iphdr* ip = is_ethernet ? (void*)(eth + 1) : data;
// Must have (ethernet and) ipv4 header
if (data + l2_header_size + sizeof(*ip) > data_end) return TC_ACT_OK;
// Ethertype - if present - must be IPv4
if (is_ethernet && (eth->h_proto != htons(ETH_P_IP))) return TC_ACT_OK;
// IP version must be 4
if (ip->version != 4) TC_PUNT(INVALID_IP_VERSION);
// We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
if (ip->ihl != 5) TC_PUNT(HAS_IP_OPTIONS);
// Calculate the IPv4 one's complement checksum of the IPv4 header.
__wsum sum4 = 0;
for (int i = 0; i < sizeof(*ip) / sizeof(__u16); ++i) {
sum4 += ((__u16*)ip)[i];
}
// Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
// for a correct checksum we should get *a* zero, but sum4 must be positive, ie 0xFFFF
if (sum4 != 0xFFFF) TC_PUNT(CHECKSUM);
// Minimum IPv4 total length is the size of the header
if (ntohs(ip->tot_len) < sizeof(*ip)) TC_PUNT(TRUNCATED_IPV4);
// We are incapable of dealing with IPv4 fragments
if (ip->frag_off & ~htons(IP_DF)) TC_PUNT(IS_IP_FRAG);
// Cannot decrement during forward if already zero or would be zero,
// Let the kernel's stack handle these cases and generate appropriate ICMP errors.
if (ip->ttl <= 1) TC_PUNT(LOW_TTL);
// If we cannot update the 'last_used' field due to lack of bpf_ktime_get_boot_ns() helper,
// then it is not safe to offload UDP due to the small conntrack timeouts, as such,
// in such a situation we can only support TCP. This also has the added nice benefit of
// using a separate error counter, and thus making it obvious which version of the program
// is loaded.
if (!updatetime && ip->protocol != IPPROTO_TCP) TC_PUNT(NON_TCP);
// We do not support offloading anything besides IPv4 TCP and UDP, due to need for NAT,
// but no need to check this if !updatetime due to check immediately above.
if (updatetime && (ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP))
TC_PUNT(NON_TCP_UDP);
// We want to make sure that the compiler will, in the !updatetime case, entirely optimize
// out all the non-tcp logic. Also note that at this point is_udp === !is_tcp.
const bool is_tcp = !updatetime || (ip->protocol == IPPROTO_TCP);
// This is a bit of a hack to make things easier on the bpf verifier.
// (In particular I believe the Linux 4.14 kernel's verifier can get confused later on about
// what offsets into the packet are valid and can spuriously reject the program, this is
// because it fails to realize that is_tcp && !is_tcp is impossible)
//
// For both TCP & UDP we'll need to read and modify the src/dst ports, which so happen to
// always be in the first 4 bytes of the L4 header. Additionally for UDP we'll need access
// to the checksum field which is in bytes 7 and 8. While for TCP we'll need to read the
// TCP flags (at offset 13) and access to the checksum field (2 bytes at offset 16).
// As such we *always* need access to at least 8 bytes.
if (data + l2_header_size + sizeof(*ip) + 8 > data_end) TC_PUNT(SHORT_L4_HEADER);
struct tcphdr* tcph = is_tcp ? (void*)(ip + 1) : NULL;
struct udphdr* udph = is_tcp ? NULL : (void*)(ip + 1);
if (is_tcp) {
// Make sure we can get at the tcp header
if (data + l2_header_size + sizeof(*ip) + sizeof(*tcph) > data_end)
TC_PUNT(SHORT_TCP_HEADER);
// If hardware offload is running and programming flows based on conntrack entries, try not
// to interfere with it, so do not offload TCP packets with any one of the SYN/FIN/RST flags
if (tcph->syn || tcph->fin || tcph->rst) TC_PUNT(TCP_CONTROL_PACKET);
} else { // UDP
// Make sure we can get at the udp header
if (data + l2_header_size + sizeof(*ip) + sizeof(*udph) > data_end)
TC_PUNT(SHORT_UDP_HEADER);
// Skip handling of CHECKSUM_COMPLETE packets with udp checksum zero due to need for
// additional updating of skb->csum (this could be fixed up manually with more effort).
//
// Note that the in-kernel implementation of 'int64_t bpf_csum_update(skb, u32 csum)' is:
// if (skb->ip_summed == CHECKSUM_COMPLETE)
// return (skb->csum = csum_add(skb->csum, csum));
// else
// return -ENOTSUPP;
//
// So this will punt any CHECKSUM_COMPLETE packet with a zero UDP checksum,
// and leave all other packets unaffected (since it just at most adds zero to skb->csum).
//
// In practice this should almost never trigger because most nics do not generate
// CHECKSUM_COMPLETE packets on receive - especially so for nics/drivers on a phone.
//
// Additionally since we're forwarding, in most cases the value of the skb->csum field
// shouldn't matter (it's not used by physical nic egress).
//
// It only matters if we're ingressing through a CHECKSUM_COMPLETE capable nic
// and egressing through a virtual interface looping back to the kernel itself
// (ie. something like veth) where the CHECKSUM_COMPLETE/skb->csum can get reused
// on ingress.
//
// If we were in the kernel we'd simply probably call
// void skb_checksum_complete_unset(struct sk_buff *skb) {
// if (skb->ip_summed == CHECKSUM_COMPLETE) skb->ip_summed = CHECKSUM_NONE;
// }
// here instead. Perhaps there should be a bpf helper for that?
if (!udph->check && (bpf_csum_update(skb, 0) >= 0)) TC_PUNT(UDP_CSUM_ZERO);
}
Tether4Key k = {
.iif = skb->ifindex,
.l4Proto = ip->protocol,
.src4.s_addr = ip->saddr,
.dst4.s_addr = ip->daddr,
.srcPort = is_tcp ? tcph->source : udph->source,
.dstPort = is_tcp ? tcph->dest : udph->dest,
};
if (is_ethernet) __builtin_memcpy(k.dstMac, eth->h_dest, ETH_ALEN);
Tether4Value* v = downstream ? bpf_tether_downstream4_map_lookup_elem(&k)
: bpf_tether_upstream4_map_lookup_elem(&k);
// If we don't find any offload information then simply let the core stack handle it...
if (!v) return TC_ACT_OK;
uint32_t stat_and_limit_k = downstream ? skb->ifindex : v->oif;
TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
// If we don't have anywhere to put stats, then abort...
if (!stat_v) TC_PUNT(NO_STATS_ENTRY);
uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k);
// If we don't have a limit, then abort...
if (!limit_v) TC_PUNT(NO_LIMIT_ENTRY);
// Required IPv4 minimum mtu is 68, below that not clear what we should do, abort...
if (v->pmtu < 68) TC_PUNT(BELOW_IPV4_MTU);
// Approximate handling of TCP/IPv4 overhead for incoming LRO/GRO packets: default
// outbound path mtu of 1500 is not necessarily correct, but worst case we simply
// undercount, which is still better then not accounting for this overhead at all.
// Note: this really shouldn't be device/path mtu at all, but rather should be
// derived from this particular connection's mss (ie. from gro segment size).
// This would require a much newer kernel with newer ebpf accessors.
// (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
uint64_t packets = 1;
uint64_t bytes = skb->len;
if (bytes > v->pmtu) {
const int tcp_overhead = sizeof(struct iphdr) + sizeof(struct tcphdr) + 12;
const int mss = v->pmtu - tcp_overhead;
const uint64_t payload = bytes - tcp_overhead;
packets = (payload + mss - 1) / mss;
bytes = tcp_overhead * packets + payload;
}
// Are we past the limit? If so, then abort...
// Note: will not overflow since u64 is 936 years even at 5Gbps.
// Do not drop here. Offload is just that, whenever we fail to handle
// a packet we let the core stack deal with things.
// (The core stack needs to handle limits correctly anyway,
// since we don't offload all traffic in both directions)
if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) TC_PUNT(LIMIT_REACHED);
if (!is_ethernet) {
// Try to inject an ethernet header, and simply return if we fail.
// We do this even if TX interface is RAWIP and thus does not need an ethernet header,
// because this is easier and the kernel will strip extraneous ethernet header.
if (bpf_skb_change_head(skb, sizeof(struct ethhdr), /*flags*/ 0)) {
__sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_PUNT(CHANGE_HEAD_FAILED);
}
// bpf_skb_change_head() invalidates all pointers - reload them
data = (void*)(long)skb->data;
data_end = (void*)(long)skb->data_end;
eth = data;
ip = (void*)(eth + 1);
tcph = is_tcp ? (void*)(ip + 1) : NULL;
udph = is_tcp ? NULL : (void*)(ip + 1);
// I do not believe this can ever happen, but keep the verifier happy...
if (data + sizeof(struct ethhdr) + sizeof(*ip) + (is_tcp ? sizeof(*tcph) : sizeof(*udph)) > data_end) {
__sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_DROP(TOO_SHORT);
}
};
// At this point we always have an ethernet header - which will get stripped by the
// kernel during transmit through a rawip interface. ie. 'eth' pointer is valid.
// Additionally note that 'is_ethernet' and 'l2_header_size' are no longer correct.
// Overwrite any mac header with the new one
// For a rawip tx interface it will simply be a bunch of zeroes and later stripped.
*eth = v->macHeader;
const int l4_offs_csum = is_tcp ? ETH_IP4_TCP_OFFSET(check) : ETH_IP4_UDP_OFFSET(check);
const int sz4 = sizeof(__be32);
// UDP 0 is special and stored as FFFF (this flag also causes a csum of 0 to be unmodified)
const int l4_flags = is_tcp ? 0 : BPF_F_MARK_MANGLED_0;
const __be32 old_daddr = k.dst4.s_addr;
const __be32 old_saddr = k.src4.s_addr;
const __be32 new_daddr = v->dst46.s6_addr32[3];
const __be32 new_saddr = v->src46.s6_addr32[3];
bpf_l4_csum_replace(skb, l4_offs_csum, old_daddr, new_daddr, sz4 | BPF_F_PSEUDO_HDR | l4_flags);
bpf_l3_csum_replace(skb, ETH_IP4_OFFSET(check), old_daddr, new_daddr, sz4);
bpf_skb_store_bytes(skb, ETH_IP4_OFFSET(daddr), &new_daddr, sz4, 0);
bpf_l4_csum_replace(skb, l4_offs_csum, old_saddr, new_saddr, sz4 | BPF_F_PSEUDO_HDR | l4_flags);
bpf_l3_csum_replace(skb, ETH_IP4_OFFSET(check), old_saddr, new_saddr, sz4);
bpf_skb_store_bytes(skb, ETH_IP4_OFFSET(saddr), &new_saddr, sz4, 0);
const int sz2 = sizeof(__be16);
// The offsets for TCP and UDP ports: source (u16 @ L4 offset 0) & dest (u16 @ L4 offset 2) are
// actually the same, so the compiler should just optimize them both down to a constant.
bpf_l4_csum_replace(skb, l4_offs_csum, k.srcPort, v->srcPort, sz2 | l4_flags);
bpf_skb_store_bytes(skb, is_tcp ? ETH_IP4_TCP_OFFSET(source) : ETH_IP4_UDP_OFFSET(source),
&v->srcPort, sz2, 0);
bpf_l4_csum_replace(skb, l4_offs_csum, k.dstPort, v->dstPort, sz2 | l4_flags);
bpf_skb_store_bytes(skb, is_tcp ? ETH_IP4_TCP_OFFSET(dest) : ETH_IP4_UDP_OFFSET(dest),
&v->dstPort, sz2, 0);
// TEMP HACK: lack of TTL decrement
// This requires the bpf_ktime_get_boot_ns() helper which was added in 5.8,
// and backported to all Android Common Kernel 4.14+ trees.
if (updatetime) v->last_used = bpf_ktime_get_boot_ns();
__sync_fetch_and_add(downstream ? &stat_v->rxPackets : &stat_v->txPackets, packets);
__sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, bytes);
// Redirect to forwarded interface.
//
// Note that bpf_redirect() cannot fail unless you pass invalid flags.
// The redirect actually happens after the ebpf program has already terminated,
// and can fail for example for mtu reasons at that point in time, but there's nothing
// we can do about it here.
return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
}
// Full featured (required) implementations for 5.8+ kernels (these are S+ by definition)
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_rawip$5_8", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ true);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_rawip$5_8", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ true);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_ether$5_8", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_ether_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ true, /* downstream */ true, /* updatetime */ true);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_ether$5_8", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_ether_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ true, /* downstream */ false, /* updatetime */ true);
}
// Full featured (optional) implementations for 4.14-S, 4.19-S & 5.4-S kernels
// (optional, because we need to be able to fallback for 4.14/4.19/5.4 pre-S kernels)
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ true);
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ true);
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_ether_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ true, /* downstream */ true, /* updatetime */ true);
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_ether_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ true, /* downstream */ false, /* updatetime */ true);
}
// Partial (TCP-only: will not update 'last_used' field) implementations for 4.14+ kernels.
// These will be loaded only if the above optional ones failed (loading of *these* must succeed
// for 5.4+, since that is always an R patched kernel).
//
// [Note: as a result TCP connections will not have their conntrack timeout refreshed, however,
// since /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established defaults to 432000 (seconds),
// this in practice means they'll break only after 5 days. This seems an acceptable trade-off.
//
// Additionally kernel/tests change "net-test: add bpf_ktime_get_ns / bpf_ktime_get_boot_ns tests"
// which enforces and documents the required kernel cherrypicks will make it pretty unlikely that
// many devices upgrading to S will end up relying on these fallback programs.
// RAWIP: Required for 5.4-R kernels -- which always support bpf_skb_change_head().
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_5_4, KVER(5, 4, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ false);
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_5_4, KVER(5, 4, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ false);
}
// RAWIP: Optional for 4.14/4.19 (R) kernels -- which support bpf_skb_change_head().
// [Note: fallback for 4.14/4.19 (P/Q) kernels is below in stub section]
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$4_14",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_4_14,
KVER(4, 14, 0), KVER(5, 4, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ false);
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$4_14",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_4_14,
KVER(4, 14, 0), KVER(5, 4, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ false);
}
// ETHER: Required for 4.14-Q/R, 4.19-Q/R & 5.4-R kernels.
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$4_14", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_ether_4_14, KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ true, /* downstream */ true, /* updatetime */ false);
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$4_14", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_ether_4_14, KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
return do_forward4(skb, /* is_ethernet */ true, /* downstream */ false, /* updatetime */ false);
}
// Placeholder (no-op) implementations for older Q kernels
// RAWIP: 4.9-P/Q, 4.14-P/Q & 4.19-Q kernels -- without bpf_skb_change_head() for tc programs
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
return TC_ACT_OK;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
return TC_ACT_OK;
}
// ETHER: 4.9-P/Q kernel
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_ether_stub, KVER_NONE, KVER(4, 14, 0))
(struct __sk_buff* skb) {
return TC_ACT_OK;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_ether_stub, KVER_NONE, KVER(4, 14, 0))
(struct __sk_buff* skb) {
return TC_ACT_OK;
}
// ----- XDP Support -----
DEFINE_BPF_MAP_GRW(tether_dev_map, DEVMAP_HASH, uint32_t, uint32_t, 64, AID_NETWORK_STACK)
static inline __always_inline int do_xdp_forward6(struct xdp_md *ctx, const bool is_ethernet,
const bool downstream) {
return XDP_PASS;
}
static inline __always_inline int do_xdp_forward4(struct xdp_md *ctx, const bool is_ethernet,
const bool downstream) {
return XDP_PASS;
}
static inline __always_inline int do_xdp_forward_ether(struct xdp_md *ctx, const bool downstream) {
const void* data = (void*)(long)ctx->data;
const void* data_end = (void*)(long)ctx->data_end;
const struct ethhdr* eth = data;
// Make sure we actually have an ethernet header
if ((void*)(eth + 1) > data_end) return XDP_PASS;
if (eth->h_proto == htons(ETH_P_IPV6))
return do_xdp_forward6(ctx, /* is_ethernet */ true, downstream);
if (eth->h_proto == htons(ETH_P_IP))
return do_xdp_forward4(ctx, /* is_ethernet */ true, downstream);
// Anything else we don't know how to handle...
return XDP_PASS;
}
static inline __always_inline int do_xdp_forward_rawip(struct xdp_md *ctx, const bool downstream) {
const void* data = (void*)(long)ctx->data;
const void* data_end = (void*)(long)ctx->data_end;
// The top nibble of both IPv4 and IPv6 headers is the IP version.
if (data_end - data < 1) return XDP_PASS;
const uint8_t v = (*(uint8_t*)data) >> 4;
if (v == 6) return do_xdp_forward6(ctx, /* is_ethernet */ false, downstream);
if (v == 4) return do_xdp_forward4(ctx, /* is_ethernet */ false, downstream);
// Anything else we don't know how to handle...
return XDP_PASS;
}
#define DEFINE_XDP_PROG(str, func) \
DEFINE_BPF_PROG_KVER(str, AID_ROOT, AID_NETWORK_STACK, func, KVER(5, 9, 0))(struct xdp_md *ctx)
DEFINE_XDP_PROG("xdp/tether_downstream_ether",
xdp_tether_downstream_ether) {
return do_xdp_forward_ether(ctx, /* downstream */ true);
}
DEFINE_XDP_PROG("xdp/tether_downstream_rawip",
xdp_tether_downstream_rawip) {
return do_xdp_forward_rawip(ctx, /* downstream */ true);
}
DEFINE_XDP_PROG("xdp/tether_upstream_ether",
xdp_tether_upstream_ether) {
return do_xdp_forward_ether(ctx, /* downstream */ false);
}
DEFINE_XDP_PROG("xdp/tether_upstream_rawip",
xdp_tether_upstream_rawip) {
return do_xdp_forward_rawip(ctx, /* downstream */ false);
}
LICENSE("Apache 2.0");
CRITICAL("tethering");

View File

@@ -0,0 +1,47 @@
/*
* 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.
*/
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_tethering.h"
// Used only by TetheringPrivilegedTests, not by production code.
DEFINE_BPF_MAP_GRW(tether_downstream6_map, HASH, TetherDownstream6Key, Tether6Value, 16,
AID_NETWORK_STACK)
DEFINE_BPF_PROG_KVER("xdp/drop_ipv4_udp_ether", AID_ROOT, AID_NETWORK_STACK,
xdp_test, KVER(5, 9, 0))
(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
int hsize = sizeof(*eth);
struct iphdr *ip = data + hsize;
hsize += sizeof(struct iphdr);
if (data + hsize > data_end) return XDP_PASS;
if (eth->h_proto != htons(ETH_P_IP)) return XDP_PASS;
if (ip->protocol == IPPROTO_UDP) return XDP_DROP;
return XDP_PASS;
}
LICENSE("Apache 2.0");

View File

@@ -0,0 +1,63 @@
//
// 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 {
default_applicable_licenses: ["Android-Apache-2.0"],
}
java_sdk_library {
name: "framework-tethering",
defaults: ["framework-module-defaults"],
impl_library_visibility: [
"//packages/modules/Connectivity/Tethering:__subpackages__",
],
srcs: [":framework-tethering-srcs"],
libs: ["framework-connectivity.stubs.module_lib"],
stub_only_libs: ["framework-connectivity.stubs.module_lib"],
aidl: {
include_dirs: [
"packages/modules/Connectivity/framework/aidl-export",
],
},
jarjar_rules: "jarjar-rules.txt",
installable: true,
hostdex: true, // for hiddenapi check
apex_available: ["com.android.tethering"],
permitted_packages: ["android.net"],
min_sdk_version: "30",
}
filegroup {
name: "framework-tethering-srcs",
srcs: [
"src/android/net/TetheredClient.aidl",
"src/android/net/TetheredClient.java",
"src/android/net/TetheringManager.java",
"src/android/net/TetheringConstants.java",
"src/android/net/IIntResultListener.aidl",
"src/android/net/ITetheringEventCallback.aidl",
"src/android/net/ITetheringConnector.aidl",
"src/android/net/TetheringCallbackStartedParcel.aidl",
"src/android/net/TetheringConfigurationParcel.aidl",
"src/android/net/TetheringRequestParcel.aidl",
"src/android/net/TetherStatesParcel.aidl",
"src/android/net/TetheringInterface.aidl",
"src/android/net/TetheringInterface.java",
],
path: "src"
}

View File

@@ -0,0 +1 @@
// Signature format: 2.0

View File

@@ -0,0 +1,41 @@
// Signature format: 2.0
package android.net {
public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
}
public class TetheringManager {
ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
method public int getLastTetherError(@NonNull String);
method @NonNull public String[] getTetherableBluetoothRegexs();
method @NonNull public String[] getTetherableIfaces();
method @NonNull public String[] getTetherableUsbRegexs();
method @NonNull public String[] getTetherableWifiRegexs();
method @NonNull public String[] getTetheredIfaces();
method @NonNull public String[] getTetheringErroredIfaces();
method public boolean isTetheringSupported();
method public boolean isTetheringSupported(@NonNull String);
method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
method @Deprecated public int setUsbTethering(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
method @Deprecated public int tether(@NonNull String);
method @Deprecated public int untether(@NonNull String);
}
public static interface TetheringManager.TetheringEventCallback {
method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
}
@Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
}

View File

@@ -0,0 +1 @@
// Signature format: 2.0

View File

@@ -0,0 +1 @@
// Signature format: 2.0

View File

@@ -0,0 +1,117 @@
// Signature format: 2.0
package android.net {
public final class TetheredClient implements android.os.Parcelable {
ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
method public int describeContents();
method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
method @NonNull public android.net.MacAddress getMacAddress();
method public int getTetheringType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
}
public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.net.LinkAddress getAddress();
method @Nullable public String getHostname();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
}
public final class TetheringInterface implements android.os.Parcelable {
ctor public TetheringInterface(int, @NonNull String);
method public int describeContents();
method @NonNull public String getInterface();
method public int getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheringInterface> CREATOR;
}
public class TetheringManager {
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
field @Deprecated public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
field public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; // 0x1
field public static final int CONNECTIVITY_SCOPE_LOCAL = 2; // 0x2
field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_ETHERNET = 5; // 0x5
field public static final int TETHERING_INVALID = -1; // 0xffffffff
field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3
field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
}
public static interface TetheringManager.OnTetheringEntitlementResultListener {
method public void onTetheringEntitlementResult(int);
}
public static interface TetheringManager.StartTetheringCallback {
method public default void onTetheringFailed(int);
method public default void onTetheringStarted();
}
public static interface TetheringManager.TetheringEventCallback {
method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
method public default void onError(@NonNull String, int);
method public default void onError(@NonNull android.net.TetheringInterface, int);
method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List<java.lang.String>);
method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
method public default void onTetherableInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
method public default void onTetheredInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
method public default void onTetheringSupported(boolean);
method public default void onUpstreamChanged(@Nullable android.net.Network);
}
public static class TetheringManager.TetheringRequest {
method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
method public int getConnectivityScope();
method @Nullable public android.net.LinkAddress getLocalIpv4Address();
method public boolean getShouldShowEntitlementUi();
method public int getTetheringType();
method public boolean isExemptFromEntitlementCheck();
}
public static class TetheringManager.TetheringRequest.Builder {
ctor public TetheringManager.TetheringRequest.Builder(int);
method @NonNull public android.net.TetheringManager.TetheringRequest build();
method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setConnectivityScope(int);
method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
}
}

View File

@@ -0,0 +1 @@
// Signature format: 2.0

View File

@@ -0,0 +1 @@
# jarjar rules for the bootclasspath tethering framework library here

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/**
* Listener interface allowing objects to listen to various module event.
* {@hide}
*/
oneway interface IIntResultListener {
void onResult(int resultCode);
}

View File

@@ -0,0 +1,52 @@
/*
* 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 android.net;
import android.net.IIntResultListener;
import android.net.ITetheringEventCallback;
import android.net.TetheringRequestParcel;
import android.os.ResultReceiver;
/** @hide */
oneway interface ITetheringConnector {
void tether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
void untether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
void setUsbTethering(boolean enable, String callerPkg,
String callingAttributionTag, IIntResultListener receiver);
void startTethering(in TetheringRequestParcel request, String callerPkg,
String callingAttributionTag, IIntResultListener receiver);
void stopTethering(int type, String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
boolean showEntitlementUi, String callerPkg, String callingAttributionTag);
void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
void isTetheringSupported(String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
void stopAllTethering(String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.net.Network;
import android.net.TetheredClient;
import android.net.TetheringConfigurationParcel;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetherStatesParcel;
/**
* Callback class for receiving tethering changed events.
* @hide
*/
oneway interface ITetheringEventCallback
{
/** Called immediately after the callbacks are registered */
void onCallbackStarted(in TetheringCallbackStartedParcel parcel);
void onCallbackStopped(int errorCode);
void onUpstreamChanged(in Network network);
void onConfigurationChanged(in TetheringConfigurationParcel config);
void onTetherStatesChanged(in TetherStatesParcel states);
void onTetherClientsChanged(in List<TetheredClient> clients);
void onOffloadStatusChanged(int status);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.net.TetheringInterface;
/**
* Status details for tethering downstream interfaces.
* {@hide}
*/
parcelable TetherStatesParcel {
TetheringInterface[] availableList;
TetheringInterface[] tetheredList;
TetheringInterface[] localOnlyList;
TetheringInterface[] erroredIfaceList;
// List of Last error code corresponding to each errored iface in erroredIfaceList. */
// TODO: Improve this as b/143122247.
int[] lastErrorList;
}

View File

@@ -0,0 +1,18 @@
/**
* 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 android.net;
@JavaOnlyStableParcelable parcelable TetheredClient;

View File

@@ -0,0 +1,239 @@
/*
* 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 android.net;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
/**
* Information on a tethered downstream client.
* @hide
*/
@SystemApi
public final class TetheredClient implements Parcelable {
@NonNull
private final MacAddress mMacAddress;
@NonNull
private final List<AddressInfo> mAddresses;
// TODO: use an @IntDef here
private final int mTetheringType;
public TetheredClient(@NonNull MacAddress macAddress,
@NonNull Collection<AddressInfo> addresses, int tetheringType) {
mMacAddress = macAddress;
mAddresses = new ArrayList<>(addresses);
mTetheringType = tetheringType;
}
private TetheredClient(@NonNull Parcel in) {
this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt());
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mMacAddress, flags);
dest.writeTypedList(mAddresses);
dest.writeInt(mTetheringType);
}
/**
* Get the MAC address used to identify the client.
*/
@NonNull
public MacAddress getMacAddress() {
return mMacAddress;
}
/**
* Get information on the list of addresses that are associated with the client.
*/
@NonNull
public List<AddressInfo> getAddresses() {
return new ArrayList<>(mAddresses);
}
/**
* Get the type of tethering used by the client.
* @return one of the {@code TetheringManager#TETHERING_*} constants.
*/
public int getTetheringType() {
return mTetheringType;
}
/**
* Return a new {@link TetheredClient} that has all the attributes of this instance, plus the
* {@link AddressInfo} of the provided {@link TetheredClient}.
*
* <p>Duplicate addresses are removed.
* @hide
*/
public TetheredClient addAddresses(@NonNull TetheredClient other) {
final LinkedHashSet<AddressInfo> newAddresses = new LinkedHashSet<>(
mAddresses.size() + other.mAddresses.size());
newAddresses.addAll(mAddresses);
newAddresses.addAll(other.mAddresses);
return new TetheredClient(mMacAddress, newAddresses, mTetheringType);
}
@Override
public int hashCode() {
return Objects.hash(mMacAddress, mAddresses, mTetheringType);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof TetheredClient)) return false;
final TetheredClient other = (TetheredClient) obj;
return mMacAddress.equals(other.mMacAddress)
&& mAddresses.equals(other.mAddresses)
&& mTetheringType == other.mTetheringType;
}
/**
* Information on an lease assigned to a tethered client.
*/
public static final class AddressInfo implements Parcelable {
@NonNull
private final LinkAddress mAddress;
@Nullable
private final String mHostname;
/** @hide */
public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
this.mAddress = address;
this.mHostname = hostname;
}
private AddressInfo(Parcel in) {
this(in.readParcelable(null), in.readString());
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mAddress, flags);
dest.writeString(mHostname);
}
/**
* Get the link address (including prefix length and lifetime) used by the client.
*
* This may be an IPv4 or IPv6 address.
*/
@NonNull
public LinkAddress getAddress() {
return mAddress;
}
/**
* Get the hostname that was advertised by the client when obtaining its address, if any.
*/
@Nullable
public String getHostname() {
return mHostname;
}
/**
* Get the expiration time of the address assigned to the client.
* @hide
*/
public long getExpirationTime() {
return mAddress.getExpirationTime();
}
@Override
public int describeContents() {
return 0;
}
@Override
public int hashCode() {
return Objects.hash(mAddress, mHostname);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof AddressInfo)) return false;
final AddressInfo other = (AddressInfo) obj;
// Use .equals() for addresses as all changes, including address expiry changes,
// should be included.
return other.mAddress.equals(mAddress)
&& Objects.equals(mHostname, other.mHostname);
}
@NonNull
public static final Creator<AddressInfo> CREATOR = new Creator<AddressInfo>() {
@NonNull
@Override
public AddressInfo createFromParcel(@NonNull Parcel in) {
return new AddressInfo(in);
}
@NonNull
@Override
public AddressInfo[] newArray(int size) {
return new AddressInfo[size];
}
};
@NonNull
@Override
public String toString() {
return "AddressInfo {"
+ mAddress
+ (mHostname != null ? ", hostname " + mHostname : "")
+ "}";
}
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public static final Creator<TetheredClient> CREATOR = new Creator<TetheredClient>() {
@NonNull
@Override
public TetheredClient createFromParcel(@NonNull Parcel in) {
return new TetheredClient(in);
}
@NonNull
@Override
public TetheredClient[] newArray(int size) {
return new TetheredClient[size];
}
};
@NonNull
@Override
public String toString() {
return "TetheredClient {hwAddr " + mMacAddress
+ ", addresses " + mAddresses
+ ", tetheringType " + mTetheringType
+ "}";
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 android.net;
import android.net.Network;
import android.net.TetheredClient;
import android.net.TetheringConfigurationParcel;
import android.net.TetherStatesParcel;
/**
* Initial information reported by tethering upon callback registration.
* @hide
*/
parcelable TetheringCallbackStartedParcel {
boolean tetheringSupported;
Network upstreamNetwork;
TetheringConfigurationParcel config;
TetherStatesParcel states;
List<TetheredClient> tetheredClients;
int offloadStatus;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/**
* Configuration details for tethering.
* @hide
*/
parcelable TetheringConfigurationParcel {
int subId;
String[] tetherableUsbRegexs;
String[] tetherableWifiRegexs;
String[] tetherableBluetoothRegexs;
boolean isDunRequired;
boolean chooseUpstreamAutomatically;
int[] preferredUpstreamIfaceTypes;
String[] legacyDhcpRanges;
String[] defaultIPv4DNS;
boolean enableLegacyDhcpServer;
String[] provisioningApp;
String provisioningAppNoUi;
int provisioningCheckPeriod;
}

View File

@@ -0,0 +1,93 @@
/*
* 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 android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.SystemApi;
import android.os.ResultReceiver;
/**
* Collections of constants for internal tethering usage.
*
* <p>These hidden constants are not in TetheringManager as they are not part of the API stubs
* generated for TetheringManager, which prevents the tethering module from linking them at
* build time.
* TODO: investigate changing the tethering build rules so that Tethering can reference hidden
* symbols from framework-tethering even when they are in a non-hidden class.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class TetheringConstants {
/** An explicit private class to avoid exposing constructor.*/
private TetheringConstants() { }
/**
* Extra used for communicating with the TetherService and TetherProvisioningActivity.
* Includes the type of tethering to enable if any.
*/
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
/**
* Extra used for communicating with the TetherService. Includes the type of tethering for
* which to cancel provisioning.
*/
public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
/**
* Extra used for communicating with the TetherService. True to schedule a recheck of tether
* provisioning.
*/
public static final String EXTRA_SET_ALARM = "extraSetAlarm";
/**
* Tells the TetherService to run a provision check now.
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
/**
* Extra used for communicating with the TetherService and TetherProvisioningActivity.
* Contains the {@link ResultReceiver} which will receive provisioning results.
* Can not be empty.
*/
public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
/**
* Extra used for communicating with the TetherService and TetherProvisioningActivity.
* Contains the subId of current active cellular upstream.
* @hide
*/
public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
/**
* Extra used for telling TetherProvisioningActivity the entitlement package name and class
* name to start UI entitlement check.
* @hide
*/
public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME =
"android.net.extra.TETHER_UI_PROVISIONING_APP_NAME";
/**
* Extra used for telling TetherService the intent action to start silent entitlement check.
* @hide
*/
public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION =
"android.net.extra.TETHER_SILENT_PROVISIONING_ACTION";
/**
* Extra used for TetherService to receive the response of provisioning check.
* @hide
*/
public static final String EXTRA_TETHER_PROVISIONING_RESPONSE =
"android.net.extra.TETHER_PROVISIONING_RESPONSE";
}

View File

@@ -0,0 +1,18 @@
/*
* 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;
@JavaOnlyStableParcelable parcelable TetheringInterface;

View File

@@ -0,0 +1,102 @@
/*
* 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.annotation.SystemApi;
import android.net.TetheringManager.TetheringType;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* The mapping of tethering interface and type.
* @hide
*/
@SystemApi
public final class TetheringInterface implements Parcelable {
private final int mType;
private final String mInterface;
public TetheringInterface(@TetheringType int type, @NonNull String iface) {
Objects.requireNonNull(iface);
mType = type;
mInterface = iface;
}
private TetheringInterface(@NonNull Parcel in) {
this(in.readInt(), in.readString());
}
/** Get tethering type. */
public int getType() {
return mType;
}
/** Get tethering interface. */
@NonNull
public String getInterface() {
return mInterface;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeString(mInterface);
}
@Override
public int hashCode() {
return Objects.hash(mType, mInterface);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof TetheringInterface)) return false;
final TetheringInterface other = (TetheringInterface) obj;
return mType == other.mType && mInterface.equals(other.mInterface);
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public static final Creator<TetheringInterface> CREATOR = new Creator<TetheringInterface>() {
@NonNull
@Override
public TetheringInterface createFromParcel(@NonNull Parcel in) {
return new TetheringInterface(in);
}
@NonNull
@Override
public TetheringInterface[] newArray(int size) {
return new TetheringInterface[size];
}
};
@NonNull
@Override
public String toString() {
return "TetheringInterface {mType=" + mType
+ ", mInterface=" + mInterface + "}";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
/*
* 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 android.net;
import android.net.LinkAddress;
/**
* Configuration details for requesting tethering.
* @hide
*/
parcelable TetheringRequestParcel {
int tetheringType;
LinkAddress localIPv4Address;
LinkAddress staticClientAddress;
boolean exemptFromEntitlementCheck;
boolean showProvisioningUi;
int connectivityScope;
}

View File

@@ -0,0 +1,14 @@
# These must be kept in sync with the framework-connectivity-shared-srcs filegroup.
# Classes from the framework-connectivity-shared-srcs filegroup.
# If there are files in that filegroup that are not covered below, the classes in the
# module will be overwritten by the ones in the framework.
rule com.android.internal.util.** com.android.networkstack.tethering.util.@1
rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
# Classes from net-utils-framework-common
rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1
# Classes from net-utils-device-common
rule com.android.net.module.util.Struct* com.android.networkstack.tethering.util.Struct@1

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <error.h>
#include <jni.h>
#include <linux/filter.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <netjniutils/netjniutils.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <sys/socket.h>
#include <stdio.h>
namespace android {
static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
sock_filter filter_code[] = {
// Check header is ICMPv6.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
// Check ICMPv6 type.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
// Accept or reject.
BPF_STMT(BPF_RET | BPF_K, 0xffff),
BPF_STMT(BPF_RET | BPF_K, 0)
};
const sock_fprog filter = {
sizeof(filter_code) / sizeof(filter_code[0]),
filter_code,
};
int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
}
}
static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
{
android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
}
static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd)
{
android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
}
static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
jint ifIndex)
{
static const int kLinkLocalHopLimit = 255;
int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
// Set an ICMPv6 filter that only passes Router Solicitations.
struct icmp6_filter rs_only;
ICMP6_FILTER_SETBLOCKALL(&rs_only);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
socklen_t len = sizeof(rs_only);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(ICMP6_FILTER): %s", strerror(errno));
return;
}
// Most/all of the rest of these options can be set via Java code, but
// because we're here on account of setting an icmp6_filter go ahead
// and do it all natively for now.
// Set the multicast hoplimit to 255 (link-local only).
int hops = kLinkLocalHopLimit;
len = sizeof(hops);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
return;
}
// Set the unicast hoplimit to 255 (link-local only).
hops = kLinkLocalHopLimit;
len = sizeof(hops);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
return;
}
// Explicitly disable multicast loopback.
int off = 0;
len = sizeof(off);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
return;
}
// Specify the IPv6 interface to use for outbound multicast.
len = sizeof(ifIndex);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
return;
}
// Additional options to be considered:
// - IPV6_TCLASS
// - IPV6_RECVPKTINFO
// - IPV6_RECVHOPLIMIT
// Bind to [::].
const struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_port = 0,
.sin6_flowinfo = 0,
.sin6_addr = IN6ADDR_ANY_INIT,
.sin6_scope_id = 0,
};
auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
len = sizeof(sin6);
if (bind(fd, sa, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"bind(IN6ADDR_ANY): %s", strerror(errno));
return;
}
// Join the all-routers multicast group, ff02::2%index.
struct ipv6_mreq all_rtrs = {
.ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
.ipv6mr_interface = ifIndex,
};
len = sizeof(all_rtrs);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
return;
}
}
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
(void*) android_net_util_setupNaSocket },
{ "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
(void*) android_net_util_setupNsSocket },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
(void*) android_net_util_setupRaSocket },
};
int register_android_net_util_TetheringUtils(JNIEnv* env) {
return jniRegisterNativeMethods(env,
"android/net/util/TetheringUtils",
gMethods, NELEM(gMethods));
}
}; // namespace android

View File

@@ -0,0 +1,47 @@
/*
* 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.
*/
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include "bpf_tethering.h"
namespace android {
static jobjectArray getBpfCounterNames(JNIEnv *env) {
size_t size = BPF_TETHER_ERR__MAX;
jobjectArray ret = env->NewObjectArray(size, env->FindClass("java/lang/String"), nullptr);
for (int i = 0; i < size; i++) {
env->SetObjectArrayElement(ret, i, env->NewStringUTF(bpf_tether_errors[i]));
}
return ret;
}
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "getBpfCounterNames", "()[Ljava/lang/String;", (void*) getBpfCounterNames },
};
int register_com_android_networkstack_tethering_BpfCoordinator(JNIEnv* env) {
return jniRegisterNativeMethods(env,
"com/android/networkstack/tethering/BpfCoordinator",
gMethods, NELEM(gMethods));
}
}; // namespace android

View File

@@ -0,0 +1,175 @@
/*
* 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.
*/
#include <errno.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include "nativehelper/scoped_primitive_array.h"
#include "nativehelper/scoped_utf_chars.h"
#define BPF_FD_JUST_USE_INT
#include "BpfSyscallWrappers.h"
namespace android {
static jclass sErrnoExceptionClass;
static jmethodID sErrnoExceptionCtor2;
static jmethodID sErrnoExceptionCtor3;
static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
if (sErrnoExceptionClass == nullptr || sErrnoExceptionClass == nullptr) return;
jthrowable cause = nullptr;
if (env->ExceptionCheck()) {
cause = env->ExceptionOccurred();
env->ExceptionClear();
}
ScopedLocalRef<jstring> msg(env, env->NewStringUTF(functionName));
// Not really much we can do here if msg is null, let's try to stumble on...
if (msg.get() == nullptr) env->ExceptionClear();
jobject errnoException;
if (cause != nullptr) {
errnoException = env->NewObject(sErrnoExceptionClass, sErrnoExceptionCtor3, msg.get(),
error, cause);
} else {
errnoException = env->NewObject(sErrnoExceptionClass, sErrnoExceptionCtor2, msg.get(),
error);
}
env->Throw(static_cast<jthrowable>(errnoException));
}
static jint com_android_networkstack_tethering_BpfMap_closeMap(JNIEnv *env, jobject clazz,
jint fd) {
int ret = close(fd);
if (ret) throwErrnoException(env, "closeMap", errno);
return ret;
}
static jint com_android_networkstack_tethering_BpfMap_bpfFdGet(JNIEnv *env, jobject clazz,
jstring path, jint mode) {
ScopedUtfChars pathname(env, path);
jint fd = bpf::bpfFdGet(pathname.c_str(), static_cast<unsigned>(mode));
return fd;
}
static void com_android_networkstack_tethering_BpfMap_writeToMapEntry(JNIEnv *env, jobject clazz,
jint fd, jbyteArray key, jbyteArray value, jint flags) {
ScopedByteArrayRO keyRO(env, key);
ScopedByteArrayRO valueRO(env, value);
int ret = bpf::writeToMapEntry(static_cast<int>(fd), keyRO.get(), valueRO.get(),
static_cast<int>(flags));
if (ret) throwErrnoException(env, "writeToMapEntry", errno);
}
static jboolean throwIfNotEnoent(JNIEnv *env, const char* functionName, int ret, int err) {
if (ret == 0) return true;
if (err != ENOENT) throwErrnoException(env, functionName, err);
return false;
}
static jboolean com_android_networkstack_tethering_BpfMap_deleteMapEntry(JNIEnv *env, jobject clazz,
jint fd, jbyteArray key) {
ScopedByteArrayRO keyRO(env, key);
// On success, zero is returned. If the element is not found, -1 is returned and errno is set
// to ENOENT.
int ret = bpf::deleteMapEntry(static_cast<int>(fd), keyRO.get());
return throwIfNotEnoent(env, "deleteMapEntry", ret, errno);
}
static jboolean com_android_networkstack_tethering_BpfMap_getNextMapKey(JNIEnv *env, jobject clazz,
jint fd, jbyteArray key, jbyteArray nextKey) {
// If key is found, the operation returns zero and sets the next key pointer to the key of the
// next element. If key is not found, the operation returns zero and sets the next key pointer
// to the key of the first element. If key is the last element, -1 is returned and errno is
// set to ENOENT. Other possible errno values are ENOMEM, EFAULT, EPERM, and EINVAL.
ScopedByteArrayRW nextKeyRW(env, nextKey);
int ret;
if (key == nullptr) {
// Called by getFirstKey. Find the first key in the map.
ret = bpf::getNextMapKey(static_cast<int>(fd), nullptr, nextKeyRW.get());
} else {
ScopedByteArrayRO keyRO(env, key);
ret = bpf::getNextMapKey(static_cast<int>(fd), keyRO.get(), nextKeyRW.get());
}
return throwIfNotEnoent(env, "getNextMapKey", ret, errno);
}
static jboolean com_android_networkstack_tethering_BpfMap_findMapEntry(JNIEnv *env, jobject clazz,
jint fd, jbyteArray key, jbyteArray value) {
ScopedByteArrayRO keyRO(env, key);
ScopedByteArrayRW valueRW(env, value);
// If an element is found, the operation returns zero and stores the element's value into
// "value". If no element is found, the operation returns -1 and sets errno to ENOENT.
int ret = bpf::findMapEntry(static_cast<int>(fd), keyRO.get(), valueRW.get());
return throwIfNotEnoent(env, "findMapEntry", ret, errno);
}
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "closeMap", "(I)I",
(void*) com_android_networkstack_tethering_BpfMap_closeMap },
{ "bpfFdGet", "(Ljava/lang/String;I)I",
(void*) com_android_networkstack_tethering_BpfMap_bpfFdGet },
{ "writeToMapEntry", "(I[B[BI)V",
(void*) com_android_networkstack_tethering_BpfMap_writeToMapEntry },
{ "deleteMapEntry", "(I[B)Z",
(void*) com_android_networkstack_tethering_BpfMap_deleteMapEntry },
{ "getNextMapKey", "(I[B[B)Z",
(void*) com_android_networkstack_tethering_BpfMap_getNextMapKey },
{ "findMapEntry", "(I[B[B)Z",
(void*) com_android_networkstack_tethering_BpfMap_findMapEntry },
};
int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env) {
sErrnoExceptionClass = static_cast<jclass>(env->NewGlobalRef(
env->FindClass("android/system/ErrnoException")));
if (sErrnoExceptionClass == nullptr) return JNI_ERR;
sErrnoExceptionCtor2 = env->GetMethodID(sErrnoExceptionClass, "<init>",
"(Ljava/lang/String;I)V");
if (sErrnoExceptionCtor2 == nullptr) return JNI_ERR;
sErrnoExceptionCtor3 = env->GetMethodID(sErrnoExceptionClass, "<init>",
"(Ljava/lang/String;ILjava/lang/Throwable;)V");
if (sErrnoExceptionCtor3 == nullptr) return JNI_ERR;
return jniRegisterNativeMethods(env,
"com/android/networkstack/tethering/BpfMap",
gMethods, NELEM(gMethods));
}
}; // namespace android

View File

@@ -0,0 +1,352 @@
/*
* 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.
*/
#include <arpa/inet.h>
#include <jni.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/pkt_cls.h>
#include <linux/pkt_sched.h>
#include <linux/rtnetlink.h>
#include <nativehelper/JNIHelp.h>
#include <net/if.h>
#include <stdio.h>
#include <sys/socket.h>
// TODO: use unique_fd.
#define BPF_FD_JUST_USE_INT
#include "BpfSyscallWrappers.h"
#include "bpf_tethering.h"
#include "nativehelper/scoped_utf_chars.h"
// The maximum length of TCA_BPF_NAME. Sync from net/sched/cls_bpf.c.
#define CLS_BPF_NAME_LEN 256
// Classifier name. See cls_bpf_ops in net/sched/cls_bpf.c.
#define CLS_BPF_KIND_NAME "bpf"
namespace android {
// Sync from system/netd/server/NetlinkCommands.h
const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
// TODO: move to frameworks/libs/net/common/native for sharing with
// system/netd/server/OffloadUtils.{c, h}.
static void sendAndProcessNetlinkResponse(JNIEnv* env, const void* req, int len) {
int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); // TODO: use unique_fd
if (fd == -1) {
jniThrowExceptionFmt(env, "java/io/IOException",
"socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE): %s",
strerror(errno));
return;
}
static constexpr int on = 1;
if (setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on))) {
jniThrowExceptionFmt(env, "java/io/IOException",
"setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, %d)", on);
close(fd);
return;
}
// this is needed to get valid strace netlink parsing, it allocates the pid
if (bind(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR))) {
jniThrowExceptionFmt(env, "java/io/IOException", "bind(fd, {AF_NETLINK, 0, 0}): %s",
strerror(errno));
close(fd);
return;
}
// we do not want to receive messages from anyone besides the kernel
if (connect(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR))) {
jniThrowExceptionFmt(env, "java/io/IOException", "connect(fd, {AF_NETLINK, 0, 0}): %s",
strerror(errno));
close(fd);
return;
}
int rv = send(fd, req, len, 0);
if (rv == -1) {
jniThrowExceptionFmt(env, "java/io/IOException", "send(fd, req, len, 0): %s",
strerror(errno));
close(fd);
return;
}
if (rv != len) {
jniThrowExceptionFmt(env, "java/io/IOException", "send(fd, req, len, 0): %s",
strerror(EMSGSIZE));
close(fd);
return;
}
struct {
nlmsghdr h;
nlmsgerr e;
char buf[256];
} resp = {};
rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
if (rv == -1) {
jniThrowExceptionFmt(env, "java/io/IOException", "recv() failed: %s", strerror(errno));
close(fd);
return;
}
if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
jniThrowExceptionFmt(env, "java/io/IOException", "recv() returned short packet: %d", rv);
close(fd);
return;
}
if (resp.h.nlmsg_len != (unsigned)rv) {
jniThrowExceptionFmt(env, "java/io/IOException",
"recv() returned invalid header length: %d != %d", resp.h.nlmsg_len,
rv);
close(fd);
return;
}
if (resp.h.nlmsg_type != NLMSG_ERROR) {
jniThrowExceptionFmt(env, "java/io/IOException",
"recv() did not return NLMSG_ERROR message: %d", resp.h.nlmsg_type);
close(fd);
return;
}
if (resp.e.error) { // returns 0 on success
jniThrowExceptionFmt(env, "java/io/IOException", "NLMSG_ERROR message return error: %s",
strerror(-resp.e.error));
}
close(fd);
return;
}
static int hardwareAddressType(const char* interface) {
int fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (fd < 0) return -errno;
struct ifreq ifr = {};
// We use strncpy() instead of strlcpy() since kernel has to be able
// to handle non-zero terminated junk passed in by userspace anyway,
// and this way too long interface names (more than IFNAMSIZ-1 = 15
// characters plus terminating NULL) will not get truncated to 15
// characters and zero-terminated and thus potentially erroneously
// match a truncated interface if one were to exist.
strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
int rv;
if (ioctl(fd, SIOCGIFHWADDR, &ifr, sizeof(ifr))) {
rv = -errno;
} else {
rv = ifr.ifr_hwaddr.sa_family;
}
close(fd);
return rv;
}
static jboolean com_android_networkstack_tethering_BpfUtils_isEthernet(JNIEnv* env, jobject clazz,
jstring iface) {
ScopedUtfChars interface(env, iface);
int rv = hardwareAddressType(interface.c_str());
if (rv < 0) {
jniThrowExceptionFmt(env, "java/io/IOException",
"Get hardware address type of interface %s failed: %s",
interface.c_str(), strerror(-rv));
return false;
}
switch (rv) {
case ARPHRD_ETHER:
return true;
case ARPHRD_NONE:
case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
return false;
default:
jniThrowExceptionFmt(env, "java/io/IOException",
"Unknown hardware address type %d on interface %s", rv,
interface.c_str());
return false;
}
}
// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
// direct-action
static void com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf(
JNIEnv* env, jobject clazz, jint ifIndex, jboolean ingress, jshort prio, jshort proto,
jstring bpfProgPath) {
ScopedUtfChars pathname(env, bpfProgPath);
const int bpfFd = bpf::retrieveProgram(pathname.c_str());
if (bpfFd == -1) {
jniThrowExceptionFmt(env, "java/io/IOException", "retrieveProgram failed %s",
strerror(errno));
return;
}
struct {
nlmsghdr n;
tcmsg t;
struct {
nlattr attr;
// The maximum classifier name length is defined in
// tcf_proto_ops in include/net/sch_generic.h.
char str[NLMSG_ALIGN(sizeof(CLS_BPF_KIND_NAME))];
} kind;
struct {
nlattr attr;
struct {
nlattr attr;
__u32 u32;
} fd;
struct {
nlattr attr;
char str[NLMSG_ALIGN(CLS_BPF_NAME_LEN)];
} name;
struct {
nlattr attr;
__u32 u32;
} flags;
} options;
} req = {
.n =
{
.nlmsg_len = sizeof(req),
.nlmsg_type = RTM_NEWTFILTER,
.nlmsg_flags = NETLINK_REQUEST_FLAGS | NLM_F_EXCL | NLM_F_CREATE,
},
.t =
{
.tcm_family = AF_UNSPEC,
.tcm_ifindex = ifIndex,
.tcm_handle = TC_H_UNSPEC,
.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
.tcm_info = static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
htons(static_cast<uint16_t>(proto))),
},
.kind =
{
.attr =
{
.nla_len = sizeof(req.kind),
.nla_type = TCA_KIND,
},
.str = CLS_BPF_KIND_NAME,
},
.options =
{
.attr =
{
.nla_len = sizeof(req.options),
.nla_type = NLA_F_NESTED | TCA_OPTIONS,
},
.fd =
{
.attr =
{
.nla_len = sizeof(req.options.fd),
.nla_type = TCA_BPF_FD,
},
.u32 = static_cast<__u32>(bpfFd),
},
.name =
{
.attr =
{
.nla_len = sizeof(req.options.name),
.nla_type = TCA_BPF_NAME,
},
// Visible via 'tc filter show', but
// is overwritten by strncpy below
.str = "placeholder",
},
.flags =
{
.attr =
{
.nla_len = sizeof(req.options.flags),
.nla_type = TCA_BPF_FLAGS,
},
.u32 = TCA_BPF_FLAG_ACT_DIRECT,
},
},
};
snprintf(req.options.name.str, sizeof(req.options.name.str), "%s:[*fsobj]",
basename(pathname.c_str()));
// The exception may be thrown from sendAndProcessNetlinkResponse. Close the file descriptor of
// BPF program before returning the function in any case.
sendAndProcessNetlinkResponse(env, &req, sizeof(req));
close(bpfFd);
}
// tc filter del dev .. in/egress prio .. protocol ..
static void com_android_networkstack_tethering_BpfUtils_tcFilterDelDev(JNIEnv* env, jobject clazz,
jint ifIndex,
jboolean ingress,
jshort prio, jshort proto) {
const struct {
nlmsghdr n;
tcmsg t;
} req = {
.n =
{
.nlmsg_len = sizeof(req),
.nlmsg_type = RTM_DELTFILTER,
.nlmsg_flags = NETLINK_REQUEST_FLAGS,
},
.t =
{
.tcm_family = AF_UNSPEC,
.tcm_ifindex = ifIndex,
.tcm_handle = TC_H_UNSPEC,
.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
.tcm_info = static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
htons(static_cast<uint16_t>(proto))),
},
};
sendAndProcessNetlinkResponse(env, &req, sizeof(req));
}
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"isEthernet", "(Ljava/lang/String;)Z",
(void*)com_android_networkstack_tethering_BpfUtils_isEthernet},
{"tcFilterAddDevBpf", "(IZSSLjava/lang/String;)V",
(void*)com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf},
{"tcFilterDelDev", "(IZSS)V",
(void*)com_android_networkstack_tethering_BpfUtils_tcFilterDelDev},
};
int register_com_android_networkstack_tethering_BpfUtils(JNIEnv* env) {
return jniRegisterNativeMethods(env, "com/android/networkstack/tethering/BpfUtils", gMethods,
NELEM(gMethods));
}
}; // namespace android

48
Tethering/jni/onload.cpp Normal file
View File

@@ -0,0 +1,48 @@
/*
* 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.
*/
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#define LOG_TAG "TetheringJni"
#include <android/log.h>
namespace android {
int register_android_net_util_TetheringUtils(JNIEnv* env);
int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env);
int register_com_android_networkstack_tethering_BpfCoordinator(JNIEnv* env);
int register_com_android_networkstack_tethering_BpfUtils(JNIEnv* env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
__android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "ERROR: GetEnv failed");
return JNI_ERR;
}
if (register_android_net_util_TetheringUtils(env) < 0) return JNI_ERR;
if (register_com_android_networkstack_tethering_BpfMap(env) < 0) return JNI_ERR;
if (register_com_android_networkstack_tethering_BpfCoordinator(env) < 0) return JNI_ERR;
if (register_com_android_networkstack_tethering_BpfUtils(env) < 0) return JNI_ERR;
return JNI_VERSION_1_6;
}
}; // namespace android

17
Tethering/proguard.flags Normal file
View File

@@ -0,0 +1,17 @@
# Keep class's integer static field for MessageUtils to parsing their name.
-keep class com.android.networkstack.tethering.Tethering$TetherMainSM {
static final int CMD_*;
static final int EVENT_*;
}
-keep class com.android.networkstack.tethering.BpfMap {
native <methods>;
}
-keepclassmembers public class * extends com.android.networkstack.tethering.util.Struct {
*;
}
-keepclassmembers class android.net.ip.IpServer {
static final int CMD_*;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Verbinding of warmkol is aktief"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tik om op te stel."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Verbinding is gedeaktiveer"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontak jou administrateur vir besonderhede"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Warmkol- en verbindingstatus"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"ለማዋቀር መታ ያድርጉ።"</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"النطاق نشط أو نقطة الاتصال نشطة"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"انقر للإعداد."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"التوصيل متوقف."</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"تواصَل مع المشرف للحصول على التفاصيل."</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"حالة نقطة الاتصال والتوصيل"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"ছেট আপ কৰিবলৈ টিপক।"</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Birləşmə və ya hotspot aktivdir"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamaq üçün toxunun."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Birləşmə deaktivdir"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Detallar üçün adminlə əlaqə saxlayın"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot &amp; birləşmə statusu"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Privezivanje ili hotspot je aktivan"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste podesili."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Privezivanje je onemogućeno"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Potražite detalje od administratora"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspota i privezivanja"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Мадэм або хот-спот актыўныя"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Дакраніцеся, каб наладзіць."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Рэжым мадэма выключаны"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Стан \"Хот-спот і мадэм\""</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Има активна споделена връзка или точка за достъп"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Докоснете, за да настроите."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Функцията за тетъринг е деактивирана"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Свържете се с администратора си за подробности"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Състояние на функцията за точка за достъп и тетъринг"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"টিথারিং বা হটস্পট চালু আছে"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"সেট-আপ করতে ট্যাপ করুন।"</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"টিথারিং বন্ধ করা আছে"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"হটস্পট ও টিথারিং স্ট্যাটাস"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Aktivno je povezivanje putem mobitela ili pristupna tačka"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da postavite."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezivanje putem mobitela je onemogućeno"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontaktirajte svog administratora za detalje"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status pristupne tačke i povezivanja putem mobitela"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Compartició de xarxa o punt d\'accés WiFi actius"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Toca per configurar."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"La compartició de xarxa està desactivada"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta amb el teu administrador per obtenir més informació"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estat del punt d\'accés WiFi i de la compartició de xarxa"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering nebo hotspot je aktivní"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím zahájíte nastavení."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je zakázán"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požádejte administrátora"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Netdeling eller hotspot er aktivt"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tryk for at konfigurere."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Netdeling er deaktiveret"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakt din administrator for at få oplysninger"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for hotspot og netdeling"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering oder Hotspot aktiv"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Zum Einrichten tippen."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering ist deaktiviert"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Bitte wende dich für weitere Informationen an den Administrator"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot- und Tethering-Status"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Πατήστε για ρύθμιση."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Η σύνδεση είναι απενεργοποιημένη"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot &amp; tethering status"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Conexión a red o hotspot conectados"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Presiona para configurar esta opción."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Para obtener más información, comunícate con el administrador"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del hotspot y la conexión mediante dispositivo portátil"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida o punto de acceso activos"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"La conexión compartida está inhabilitada"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Solicita más información a tu administrador"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del punto de acceso y de la conexión compartida"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Jagamine või kuumkoht on aktiivne"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Puudutage seadistamiseks."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Jagamine on keelatud"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Kuumkoha ja jagamise olek"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Konexioa partekatzea edo wifi-gunea aktibo dago"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Sakatu konfiguratzeko."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Desgaituta dago konexioa partekatzeko aukera"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"اشتراک‌گذاری اینترنت یا نقطه اتصال فعال"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"برای راه‌اندازی ضربه بزنید."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"اشتراک‌گذاری اینترنت غیرفعال است"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"وضعیت نقطه اتصال و اشتراک‌گذاری اینترنت"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Yhteyden jakaminen tai hotspot käytössä"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Ota käyttöön napauttamalla."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Yhteyden jakaminen on poistettu käytöstä"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Pyydä lisätietoja järjestelmänvalvojalta"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspotin ja yhteyden jakamisen tila"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès sans fil activé"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Touchez pour configurer."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Point d\'accès et partage de connexion"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès activé"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Appuyez pour effectuer la configuration."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Pour en savoir plus, contactez votre administrateur"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"État du point d\'accès et du partage de connexion"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida ou zona wifi activada"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"A conexión compartida está desactivada"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta co administrador para obter información"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona wifi e da conexión compartida"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"સેટઅપ કરવા માટે ટૅપ કરો."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग या हॉटस्पॉट चालू है"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"सेट अप करने के लिए टैप करें."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद है"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट और टेदरिंग की स्थिति"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Modemsko povezivanje ili žarišna točka aktivni"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste postavili."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Modemsko je povezivanje onemogućeno"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Obratite se administratoru da biste saznali pojedinosti"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status žarišne točke i modemskog povezivanja"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Megosztás vagy aktív hotspot"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Koppintson a beállításhoz."</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Az internetmegosztás le van tiltva"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"A részletekért forduljon rendszergazdájához"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot és internetmegosztás állapota"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="tethered_notification_title" msgid="6426563586025792944">"Մոդեմի ռեժիմը միացված է"</string>
<string name="tethered_notification_message" msgid="64800879503420696">"Հպեք՝ կարգավորելու համար։"</string>
<string name="disable_tether_notification_title" msgid="3004509127903564191">"Մոդեմի ռեժիմն անջատված է"</string>
<string name="disable_tether_notification_message" msgid="6717523799293901476">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
<string name="notification_channel_tethering_status" msgid="2663463891530932727">"Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը"</string>
<string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
<string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
<string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
<string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
<string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
</resources>

Some files were not shown because too many files have changed in this diff Show More