Merge fixed history of Connectivity
BUG: 189375701 TEST: TH Ignore-AOSP-First: per-branch merges Merged-In: Ib95d84b91a455d1a5f10cbf3f8c08c0459bc1c7c Change-Id: Ie3a058b904cb2e3704c3a0725c8f720f81bb235e
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Eclipse project
|
||||||
|
**/.classpath
|
||||||
|
**/.project
|
||||||
|
|
||||||
|
# IntelliJ project
|
||||||
|
**/.idea
|
||||||
|
**/*.iml
|
||||||
|
**/*.ipr
|
||||||
13
OWNERS
@@ -1,3 +1,10 @@
|
|||||||
# Placing an OWNERS block to perform migration
|
# Owners block: ongoing migration
|
||||||
# detailed in b/186628461
|
reminv@google.com
|
||||||
baligh@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
@@ -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}
|
||||||
49
TEST_MAPPING
@@ -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": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "frameworks/base/core/java/android/net"
|
"path": "frameworks/base/core/java/android/net"
|
||||||
@@ -16,4 +63,4 @@
|
|||||||
"path": "packages/modules/Connectivity/Tethering"
|
"path": "packages/modules/Connectivity/Tethering"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
164
Tethering/Android.bp
Normal 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",
|
||||||
|
],
|
||||||
|
}
|
||||||
57
Tethering/AndroidManifest.xml
Normal 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>
|
||||||
29
Tethering/AndroidManifestBase.xml
Normal 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>
|
||||||
34
Tethering/AndroidManifest_InProcess.xml
Normal 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
@@ -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
@@ -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",
|
||||||
|
],
|
||||||
|
}
|
||||||
29
Tethering/apex/AndroidManifest.xml
Normal 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>
|
||||||
BIN
Tethering/apex/com.android.tethering.avbpubkey
Normal file
51
Tethering/apex/com.android.tethering.pem
Normal 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-----
|
||||||
BIN
Tethering/apex/com.android.tethering.pk8
Normal file
35
Tethering/apex/com.android.tethering.x509.pem
Normal 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-----
|
||||||
4
Tethering/apex/manifest.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "com.android.tethering",
|
||||||
|
"version": 309999900
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
68
Tethering/bpf_progs/Android.bp
Normal 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
|
||||||
|
],
|
||||||
|
}
|
||||||
217
Tethering/bpf_progs/bpf_tethering.h
Normal 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
|
||||||
838
Tethering/bpf_progs/offload.c
Normal 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");
|
||||||
47
Tethering/bpf_progs/test.c
Normal 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");
|
||||||
63
Tethering/common/TetheringLib/Android.bp
Normal 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"
|
||||||
|
}
|
||||||
1
Tethering/common/TetheringLib/api/current.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// Signature format: 2.0
|
||||||
41
Tethering/common/TetheringLib/api/module-lib-current.txt
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1
Tethering/common/TetheringLib/api/module-lib-removed.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// Signature format: 2.0
|
||||||
1
Tethering/common/TetheringLib/api/removed.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// Signature format: 2.0
|
||||||
117
Tethering/common/TetheringLib/api/system-current.txt
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1
Tethering/common/TetheringLib/api/system-removed.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// Signature format: 2.0
|
||||||
1
Tethering/common/TetheringLib/jarjar-rules.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# jarjar rules for the bootclasspath tethering framework library here
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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 + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
1541
Tethering/common/TetheringLib/src/android/net/TetheringManager.java
Normal 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;
|
||||||
|
}
|
||||||
14
Tethering/jarjar-rules.txt
Normal 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
|
||||||
184
Tethering/jni/android_net_util_TetheringUtils.cpp
Normal 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
|
||||||
@@ -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
|
||||||
175
Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
Normal 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
|
||||||
352
Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
Normal 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
@@ -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
@@ -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_*;
|
||||||
|
}
|
||||||
BIN
Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
Normal file
|
After Width: | Height: | Size: 891 B |
BIN
Tethering/res/drawable-hdpi/stat_sys_tether_general.png
Normal file
|
After Width: | Height: | Size: 963 B |
BIN
Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
Normal file
|
After Width: | Height: | Size: 909 B |
BIN
Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
Normal file
|
After Width: | Height: | Size: 534 B |
BIN
Tethering/res/drawable-ldpi/stat_sys_tether_general.png
Normal file
|
After Width: | Height: | Size: 554 B |
BIN
Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
Normal file
|
After Width: | Height: | Size: 518 B |
BIN
Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
Normal file
|
After Width: | Height: | Size: 675 B |
BIN
Tethering/res/drawable-mdpi/stat_sys_tether_general.png
Normal file
|
After Width: | Height: | Size: 659 B |
BIN
Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
Normal file
|
After Width: | Height: | Size: 698 B |
BIN
Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
Normal file
|
After Width: | Height: | Size: 1002 B |
29
Tethering/res/values-af/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-am/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-ar/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-as/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-az/strings.xml
Normal 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 & 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>
|
||||||
29
Tethering/res/values-b+sr+Latn/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-be/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-bg/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-bn/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-bs/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-ca/strings.xml
Normal 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 Wi‑Fi 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 Wi‑Fi 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>
|
||||||
29
Tethering/res/values-cs/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-da/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-de/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-el/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-en-rAU/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-en-rCA/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-en-rGB/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-en-rIN/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-en-rXC/strings.xml
Normal 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 & 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>
|
||||||
29
Tethering/res/values-es-rUS/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-es/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-et/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-eu/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-fa/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-fi/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-fr-rCA/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-fr/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-gl/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-gu/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-hi/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-hr/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-hu/strings.xml
Normal 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>
|
||||||
29
Tethering/res/values-hy/strings.xml
Normal 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>
|
||||||