Merge "Disable Bluetooth if BLE was disabled while enabling"
diff --git a/Android.bp b/Android.bp
index e784546..aeaaca3d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,22 +35,96 @@
}
// These are subset of framework-core-sources that are needed by the
-// android.test.mock library. Ideally, the library should use public APIs only,
-// but unfortunately its API signature has some references to these private APIs.
+// android.test.mock library. The implementation of android.test.mock references
+// private members of various components to allow mocking of classes that cannot
+// be mocked without access to those internal implementation details.
filegroup {
name: "framework-core-sources-for-test-mock",
srcs: [
+ "core/java/android/accounts/AccountManagerCallback.java",
+ "core/java/android/accounts/AccountManagerFuture.java",
+ "core/java/android/accounts/AccountManager.java",
+ "core/java/android/accounts/AccountsException.java",
+ "core/java/android/accounts/AuthenticatorException.java",
+ "core/java/android/accounts/OperationCanceledException.java",
+ "core/java/android/annotation/AnimatorRes.java",
+ "core/java/android/annotation/AnimRes.java",
+ "core/java/android/annotation/AnyRes.java",
+ "core/java/android/annotation/ArrayRes.java",
+ "core/java/android/annotation/AttrRes.java",
+ "core/java/android/annotation/BoolRes.java",
+ "core/java/android/annotation/BroadcastBehavior.java",
+ "core/java/android/annotation/CallbackExecutor.java",
+ "core/java/android/annotation/CallSuper.java",
+ "core/java/android/annotation/CheckResult.java",
+ "core/java/android/annotation/ColorInt.java",
+ "core/java/android/annotation/ColorRes.java",
+ "core/java/android/annotation/DimenRes.java",
+ "core/java/android/annotation/DrawableRes.java",
+ "core/java/android/annotation/FontRes.java",
+ "core/java/android/annotation/FractionRes.java",
+ "core/java/android/annotation/IntDef.java",
+ "core/java/android/annotation/IntegerRes.java",
+ "core/java/android/annotation/IntRange.java",
+ "core/java/android/annotation/LayoutRes.java",
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/Nullable.java",
+ "core/java/android/annotation/PluralsRes.java",
+ "core/java/android/annotation/RawRes.java",
+ "core/java/android/annotation/RequiresPermission.java",
+ "core/java/android/annotation/SdkConstant.java",
+ "core/java/android/annotation/Size.java",
+ "core/java/android/annotation/StringDef.java",
+ "core/java/android/annotation/StringRes.java",
+ "core/java/android/annotation/StyleableRes.java",
+ "core/java/android/annotation/StyleRes.java",
+ "core/java/android/annotation/SuppressLint.java",
+ "core/java/android/annotation/SystemApi.java",
+ "core/java/android/annotation/SystemService.java",
+ "core/java/android/annotation/TestApi.java",
+ "core/java/android/annotation/UserIdInt.java",
+ "core/java/android/annotation/XmlRes.java",
+ "core/java/android/app/Application.java",
"core/java/android/app/IApplicationThread.aidl",
"core/java/android/app/IServiceConnection.aidl",
+ "core/java/android/app/PackageDeleteObserver.java",
+ "core/java/android/content/ComponentCallbacks2.java",
+ "core/java/android/content/ComponentCallbacks.java",
+ "core/java/android/content/ContentInterface.java",
+ "core/java/android/content/ContentProvider.java",
+ "core/java/android/content/ContentProviderNative.java",
+ "core/java/android/content/ContentResolver.java",
+ "core/java/android/content/Context.java",
+ "core/java/android/content/ContextWrapper.java",
+ "core/java/android/content/DialogInterface.java",
"core/java/android/content/IContentProvider.java",
- "core/java/android/content/pm/IPackageDataObserver.aidl",
+ "core/java/android/content/Intent.java",
+ "core/java/android/content/IntentSender.java",
+ "core/java/android/content/OperationApplicationException.java",
+ "core/java/android/content/pm/ActivityInfo.java",
+ "core/java/android/content/pm/ApplicationInfo.java",
"core/java/android/content/pm/InstantAppInfo.java",
+ "core/java/android/content/pm/IPackageDataObserver.aidl",
"core/java/android/content/pm/KeySet.java",
"core/java/android/content/pm/PackageManager.java",
"core/java/android/content/pm/VerifierDeviceIdentity.java",
"core/java/android/content/res/Resources.java",
+ "core/java/android/database/CrossProcessCursor.java",
+ "core/java/android/database/CrossProcessCursorWrapper.java",
+ "core/java/android/database/Cursor.java",
+ "core/java/android/database/CursorWrapper.java",
+ "core/java/android/os/Binder.java",
+ "core/java/android/os/Bundle.java",
+ "core/java/android/os/IBinder.java",
+ "core/java/android/os/IInterface.java",
+ "core/java/android/os/Parcelable.java",
+ "core/java/android/os/ParcelFileDescriptor.java",
+ "core/java/android/os/RemoteException.java",
"core/java/android/os/storage/VolumeInfo.java",
+ "core/java/android/util/AndroidException.java",
"core/java/android/view/DisplayAdjustments.java",
+ "core/java/android/view/ViewDebug.java",
+ "core/java/com/android/internal/annotations/VisibleForTesting.java",
],
path: "core/java",
visibility: ["//frameworks/base/test-mock"],
@@ -298,7 +372,7 @@
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
"framework-sdkextensions-stubs-module_libs_api",
- "framework-tethering-stubs-module_libs_api",
+ "framework-tethering.stubs.module_lib",
"updatable_media_stubs",
],
sdk_version: "module_current",
@@ -613,7 +687,7 @@
filegroup {
name: "framework-ike-shared-srcs",
- visibility: ["//frameworks/opt/net/ike"],
+ visibility: ["//packages/modules/IPsec"],
srcs: [
"core/java/android/annotation/StringDef.java",
"core/java/android/net/annotations/PolicyDirection.java",
@@ -1058,6 +1132,7 @@
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+ "--error NoSettingsProvider " +
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
"--api-lint-ignore-prefix android.icu. " +
"--api-lint-ignore-prefix java. " +
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 60f56de..029699e 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -66,7 +66,7 @@
":opt-telephony-srcs",
":opt-net-voip-srcs",
":art-module-public-api-stubs-source",
- ":conscrypt.module.public.api.stubs.source",
+ ":conscrypt.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"test-mock/src/**/*.java",
"test-runner/src/**/*.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 4a77463..c80d134 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -49,6 +49,7 @@
":opt-net-voip-srcs",
":art-module-public-api-stubs-source",
":android_icu4j_public_api_files",
+ "**/package.html",
],
libs: ["framework-internal-utils"],
installable: false,
@@ -65,7 +66,7 @@
name: "metalava-full-api-stubs-default",
defaults: ["metalava-base-api-stubs-default"],
srcs: [
- ":conscrypt.module.public.api.stubs.source",
+ ":conscrypt.module.public.api{.public.stubs.source}",
":framework-updatable-sources",
],
sdk_version: "core_platform",
diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp
index 4c0f2aa..d160d48 100644
--- a/apct-tests/perftests/core/jni/Android.bp
+++ b/apct-tests/perftests/core/jni/Android.bp
@@ -10,4 +10,5 @@
"-Wunused",
"-Wunreachable-code",
],
+ header_libs: ["jni_headers"],
}
diff --git a/apex/Android.bp b/apex/Android.bp
index c381c0f..380b4c6 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -63,6 +63,72 @@
"--hide-annotation android.annotation.Hide " +
"--hide InternalClasses " // com.android.* classes are okay in this interface
+// Defaults for mainline module provided java_sdk_library instances.
+java_defaults {
+ name: "framework-module-defaults",
+
+ // Additional annotations used for compiling both the implementation and the
+ // stubs libraries.
+ libs: ["framework-annotations-lib"],
+
+ // Framework modules are not generally shared libraries, i.e. they are not
+ // intended, and must not be allowed, to be used in a <uses-library> manifest
+ // entry.
+ shared_library: false,
+
+ // Prevent dependencies that do not specify an sdk_version from accessing the
+ // implementation library by default and force them to use stubs instead.
+ default_to_stubs: true,
+
+ // Enable api lint. This will eventually become the default for java_sdk_library
+ // but it cannot yet be turned on because some usages have not been cleaned up.
+ // TODO(b/156126315) - Remove when no longer needed.
+ api_lint: {
+ enabled: true,
+ },
+
+ // The API scope specific properties.
+ public: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+
+ // Configure framework module specific metalava options.
+ droiddoc_options: [mainline_stubs_args],
+
+ // The stub libraries must be visible to frameworks/base so they can be combined
+ // into API specific libraries.
+ stubs_library_visibility: [
+ "//frameworks/base", // Framework
+ ],
+
+ // Set the visibility of the modules creating the stubs source.
+ stubs_source_visibility: [
+ // Ignore any visibility rules specified on the java_sdk_library when
+ // setting the visibility of the stubs source modules.
+ "//visibility:override",
+
+ // Currently, the stub source is not required for anything other than building
+ // the stubs library so is private to avoid misuse.
+ "//visibility:private",
+ ],
+
+ // Collates API usages from each module for further analysis.
+ plugins: ["java_api_finder"],
+
+ // Mainline modules should only rely on 'module_lib' APIs provided by other modules
+ // and the non updatable parts of the platform.
+ sdk_version: "module_current",
+}
+
stubs_defaults {
name: "framework-module-stubs-defaults-publicapi",
args: mainline_framework_stubs_args,
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
deleted file mode 100644
index dbb5bd3d..0000000
--- a/apex/sdkextensions/Android.bp
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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_visibility: [":__subpackages__"],
-}
-
-apex {
- name: "com.android.sdkext",
- defaults: [ "com.android.sdkext-defaults" ],
- binaries: [ "derive_sdk" ],
- prebuilts: [ "cur_sdkinfo" ],
- manifest: "manifest.json",
- min_sdk_version: "current",
-}
-
-apex_defaults {
- name: "com.android.sdkext-defaults",
- updatable: true,
- min_sdk_version: "R",
- java_libs: [ "framework-sdkextensions" ],
- prebuilts: [
- "derive_sdk.rc",
- ],
- key: "com.android.sdkext.key",
- certificate: ":com.android.sdkext.certificate",
-}
-
-sdk {
- name: "sdkextensions-sdk",
- java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ],
-}
-
-apex_key {
- name: "com.android.sdkext.key",
- public_key: "com.android.sdkext.avbpubkey",
- private_key: "com.android.sdkext.pem",
-}
-
-android_app_certificate {
- name: "com.android.sdkext.certificate",
- certificate: "com.android.sdkext",
-}
-
-python_binary_host {
- name: "gen_sdkinfo",
- srcs: [
- "sdk.proto",
- "gen_sdkinfo.py",
- ],
- proto: {
- canonical_path_from_root: false,
- },
- version: {
- py3: {
- embedded_launcher: true,
- },
- },
-}
-
-gensrcs {
- name: "cur_sdkinfo_src",
- srcs: [""],
- tools: [ "gen_sdkinfo" ],
- cmd: "$(location) -v 0 -o $(out)",
-}
-
-prebuilt_etc {
- name: "cur_sdkinfo",
- src: ":cur_sdkinfo_src",
- filename: "sdkinfo.binarypb",
- installable: false,
-}
diff --git a/apex/sdkextensions/OWNERS b/apex/sdkextensions/OWNERS
deleted file mode 100644
index a6e5522..0000000
--- a/apex/sdkextensions/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-dariofreni@google.com
-hansson@google.com
diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING
deleted file mode 100644
index 3dc1b9f..0000000
--- a/apex/sdkextensions/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsSdkExtensionsTestCases"
- },
- {
- "name": "sdkextensions_e2e_tests"
- }
- ]
-}
diff --git a/apex/sdkextensions/com.android.sdkext.avbpubkey b/apex/sdkextensions/com.android.sdkext.avbpubkey
deleted file mode 100644
index 8f47741..0000000
--- a/apex/sdkextensions/com.android.sdkext.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/sdkextensions/com.android.sdkext.pem b/apex/sdkextensions/com.android.sdkext.pem
deleted file mode 100644
index 8164601..0000000
--- a/apex/sdkextensions/com.android.sdkext.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAr72pTSavrziDP54AtQZlRclDxJf9HXRZwFRbYx9hWZ4z7ZtO
-pNBDPvPJCiAOVUsILgCQhBUolz2dyLob25Fd0PVp0n9ibIPEQYjTfHjOK40qb57N
-LhEp2ceGiAfsywPSi0TH1PQ6JgbCe/RM4TefI/sj3gYJPka3ksMvylhMIgUVLgME
-kYizhzwHqlLMspB858SioREZdGwcdZrMMIajGFU69Q9ZRDBzhPvxyKhYoObcOtk1
-uVaiE/fNoi3wKGJz2l2vhUuNrQW7MWlVMag+Qes4YACUTk9LZrOVFEJFjWc8xGUi
-ABtfKGs5JgNr/sWnhvifLn8lJuf0/BJmjD+L5QwXYs2cS7gcZJtTM12J94r0Twgw
-wF2lNmIxAE9sYqj5Rh3dIlTPE5vMUECmQEGjIBB/hzT65VxVqSjU/IlS506JTg3p
-IOQtZ15cUzTBpda4jrvqcq6RNVvgBCu2bV5D8Z4z9FUlPyvD+Zq/6lcoJfLtznAs
-G2463hyPAHTGBIcZ5p5bTuGxoAb6ivyqo4b9Qi4yYA6je9HJmuy8T3Mn5JROoeu9
-BH1K54r/mpT4TQPwuKUvRRtBAV2OPHjo+zp0Gd4Y6rxDYxEIdfEae7pQr/QExSPB
-q/QCr9RhixR1mO373LHuja+MxdAxIxugb2HTS61PQo+PbYrhJMcVuxTwJOECAwEA
-AQKCAgAH7ToRrMkH0ji5SdsmTx+KQkW4PFLCXVke/68PjX7KmAQnl3W4oVwnHr/W
-oROEbVn1GTlre7jU+YaAY0SWZrwgjLE1OWGrG1ZizlUbrCdAd6GOX09J4KROml1L
-DXB0x7tbZMLOrCVjSbLD/ITrM6MN8Gnxvbv0/yOQjxU8vzbP4gLOjHxMRCo001RV
-Ll7lPvcjTQ84zJilU6sE8vJ6zdfVZSK/ou2X0cekG+kP7+fvefo8/UcbEPlGhUrV
-IdVPPQGUu90K2hmN0FBdLi8Vik0klAN68Qu/bHwuKbNzsnmIoztucFFUR+fG3u84
-87aPS0L/J3+mjT2Tv6qhJANUGBmrK/h7MkelpKXlRTCITJLX9xP7hfSbJ4f6aLVq
-ZYPPciGxSBbUDgAwvPtOlMDzccg7YsYyiBBO28wh8MN97rePmc0z6nGmjeXhcbCC
-QktG50VYFCyqp5muKgqQmRfRjHFHLWs8GEqgxMeEL3U3HjYfCYr+6E8Sr5OnOBeH
-3buCi1+zgnNYCvbamgY/OJmW7f9h5O31hxmTplc2E1ZuxUGQZthabt1rN3bmNkyf
-KUmPwnIYkDkWBSV5lzyQExfS3/EVvj0EnHhx8faamimNrGo8xCcfnLT3c0WEFVmo
-yIyVRX3EpXJFM2JkeJ21/IEZXTzHSoNxk12CBG8i8lLSflWSMQKCAQEA2ZqVnOuV
-SZfLCUYUUh8Hvhc5zONstfq7ma1Zsttsdaj9t68nLRiBDvLOGnMjDkYZzoSd4fyl
-oy+YqWGBqcqa5kg1NOCH0I46p9d8RcWAfDnB4sqbLgWh70qsvni6igRijmsMDvkA
-U9HeEdPaLCjQ4UXw7GQvN5rRxuRt+OSqV3tV/Pk9JYyYjz7faC8dmbKDrWHHuOvm
-/9y6Xy+L5IgftykNlUeddSCIoMOAadM7BiRjsrHnOYBQ8xBcn0OYafpIswItrgVi
-IrsPJaBFidx8QYK4MVibyka6U0cm28OocDSPtSk/4jrnCEEhLjFUnWwuMXuBGlrd
-W7wP/muoJqb1VwKCAQEAzsAT90kkOCvAcrfGRE3KkUjwWAsQyP8u2+27JIQPqrpW
-GfWAzJXFt80TSp0Zf/Lrq3/SQ9n4AaL4K9dcMoreedoQN9C9JI7zPtZAWNrJVUcV
-dq2gZjBQ78+oK7uQgvFNWxga5D+Nh+Y+9Tp537fc5HIh0Y13PgsxxPk2OnZJTvLX
-HM5H7Aua9ssmqChsrKihuUsDSPozfBz+H7FNHEdKMqVLqJJSK6m0uMxuLovdVfka
-5S7iBMjEGZc46Iz3ckE0pdOiQLooNqfEQqFe5Uou/KZxxKI1NW25rEEBTVyQWt+2
-BNUCfKP7noZ45u5sUY3eJrgI7BrPEDbNS81WYaLchwKCAQA8Q4mHyd6wYO+EA/qA
-u8NDK9+AFMP4qhXme5HJ7Obetwx9IG7zGEQ1xZy6yoQ84cEn5qZq/bNJvFbFIhHs
-2gWIHRtPJ5e1dI5eCVmLYSUyQjSmAIJ1fm3YfY/VuE3BB3HcC11tkBw9GnQr78YO
-UMd4fAw7C4vgFGpgcMbcFUfvrmKkCsqaaZOeqETq75F9DWlWTSwo1HxHA/RBhENz
-6RcPfLkcTJcY5wevrjUUGcHQ86cAyDBHRngkuLVODkRZpU0Y9lN8TFVfVPre6sIX
-ag6nffJRCD8tB+V2RtBGMKunV4ctHt1oY/Oz34W260aJynoIjjG1ANEpJK4xQdNx
-0O9FAoIBAQCz2AGGKemHswdEwveEkuaSWpA3Bekj7lYkmTchHH9EU7JyAkx3qhDD
-QXB2hxGXawf1tsqAmypQwiJ+gGeCz6mW9UkGRF1DX9XX4yc2I5rew2a4RXAxc/Xz
-pP70i8O5I43Wn7FEusOyY2aAis1Y/eb4EQ+56QTAw5wXa3DwidRbCIJ2XDnT6oRy
-CWUnAYMG7ek/9TB2Wq5OWCn2B5S79IdmZsLZb+5qbMT3u1xcwO1Xy8jJc27IGpv6
-ZsDqCTV1/aJ+XQnWpBg28tiV3Sle6pjUzTRJh5AhWcEZRbKMSOiJI/CBY4k2Qq6t
-xuuEdgFjL7T+mTepqehUglcyiPuLEtAhAoIBAQDDQ5pTFOlunfYzm+CIvvImAgy7
-vrEabJYABATytAbXRMMbrKoEdU2ApEDyEW7PgysDnYLAZ+icObnJlBTYvNdlWFly
-XiviGVfpjFWDT9U/gUwFQu2lfEjoiivoGS92USHUy4UMVHiiznnm20VLLkgd3xna
-HUNSDdHIEgzOTmDsKJwMfA9zGckx23JJimPR5ekv6vr6QllYtECs4lTC1gVQAp2f
-5daxHRbkmO6gw1RgQADLkAnYz3aI1jNuHm5VyAZGt/d3JCtZ3Wwwejll8uJ4J09G
-oEtqyY9RVeHK50bLO4lyAXFiE+J6qqXjsGC20cpxeZYW5llMY/dhA6WV4YXV
------END RSA PRIVATE KEY-----
diff --git a/apex/sdkextensions/com.android.sdkext.pk8 b/apex/sdkextensions/com.android.sdkext.pk8
deleted file mode 100644
index ccc0bf4..0000000
--- a/apex/sdkextensions/com.android.sdkext.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/sdkextensions/com.android.sdkext.x509.pem b/apex/sdkextensions/com.android.sdkext.x509.pem
deleted file mode 100644
index 45d2ade..0000000
--- a/apex/sdkextensions/com.android.sdkext.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGIzCCBAugAwIBAgIUXuDL7QvzQh7S6rihWz2KRvCFVT0wDQYJKoZIhvcNAQEL
-BQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMRswGQYDVQQDDBJjb20uYW5kcm9pZC5zZGtleHQxIjAgBgkqhkiG9w0BCQEW
-E2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjAyMTQyNDM0WhgPNDc1NzEwMjgx
-NDI0MzRaMIGfMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
-A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
-QW5kcm9pZDEbMBkGA1UEAwwSY29tLmFuZHJvaWQuc2RrZXh0MSIwIAYJKoZIhvcN
-AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
-MIICCgKCAgEAxFvZZ6ES1oqAu1K74/ZxnC3SOhHnLISLBgJEe7DqtdpuNFAwvdVO
-RL/HULhDbjYlOhpU2x3SavDIZZ2lRfiS9Q+M25WftxTRHVjBcpgwbV77TVxPKlAa
-tVN2lUVOY+s4QAVMNIXjC4kCKK/pCQtacH715EtdV47fWdg/Nx4iP/Aord8k3KGI
-9iI2ZOUjaugTRxu5lKRNDrv0bw5rEzyYmDyMud+kR/iS3/5oog57wPE0ffAkZXWE
-p3L2Cejre3ekCizsvVh6EmH6ForKLtL6f0z5Zir1f4R9+YcENspTlJR3pDhg7y3I
-uTQT/iDCtV0l+g2PjGZPEeAQHND3+kDQR7Sno/WC1Nhws6vcu1MdrC+kIh1ewx4y
-8moy/yqb5M98PJDzTSi/AOTB/OiqLXo/T8rjLBmirs9y3fTT6gJ6qXxOWgt8dle9
-7TBfa84Xi8uVY61c+A+YI0nLal7QDPsP3RPv5sJSQ9x9YnweVfD9Q0EOi52sSNu+
-QuN/kcUrMgPgha20VhfH/CkkPDyIp6aZyHHM69MIl+cYEm8vPa5uy3dosuRomT0f
-I4HOBjULDIuj+rIi+Rg3qHvmpuejwZXI/FBNWIhLEUG3ytgksjMaBoYAYflGdrcj
-BQexuF3EO+j4uo7JGjNcaT7wRoCH9gt29VHckDg2qz6VWXrlpmME4UkCAwEAAaNT
-MFEwHQYDVR0OBBYEFISN2nmUHllgPZMZ62U7mU3ZxzlXMB8GA1UdIwQYMBaAFISN
-2nmUHllgPZMZ62U7mU3ZxzlXMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
-BQADggIBAFHIwyBNIVyHXUsDUdcjxfojXQsF/BCL9ehE3pgdkvDfQanaIREWn0nc
-oCFDFkYMRqaXOGC5TKq4OCjXOLsdfODt8HQ3F9J1B0ghQ5tfOdw7xDugNAszqP/Q
-h7kpvqLTycjrqOeZ5KjxEEYtP/KlUmALgOKcTcSH+XhWyxhjF4j24T9F2yJRr3/A
-r1NGU/djH953bHKC8OpJ2teUpDLA4TxVp/EhslH2eVigF80c/w74QPLEWkD9zv/4
-YeRg/R5N83zHs99NtlWMIeHfK6fUbzMyaSZtvm+jK20tkByQb/OQRed+drk25MtL
-68IRvxqri367qRScdpTZbu0ByLO4X8gFdubRUWr+tcO4pZX+DJRVriExbOkU2xhS
-Vtslq23V/hwTuUNm1CXjR70mPS13BTmHrIQDqLoIw/WTQlGh+vxnlAFRIHM3KB2c
-OdzUBu+NcB4aZEd0KKtct600A0DKPr1MQPb5jDq9wEtPSQYwMF0nRFNnXltbrXMd
-4hhwnhKr74fVMUmb+7eQP56XE/Nk4D+npMO54vv1pav+DI2/nxCND9BOLBweY38p
-Tvd2RjesMok0zXuVXiCIu4GEpwo7WkSnv25xrb0Ey2M8QWnGNnCcX7Kv6ip3RdWy
-HiN0G8RJrs/yNEVSDRx8ZhtwTpXVPQxbARbmhNF4/fnolElkmrMP
------END CERTIFICATE-----
diff --git a/apex/sdkextensions/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp
deleted file mode 100644
index 41eae09..0000000
--- a/apex/sdkextensions/derive_sdk/Android.bp
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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.
-
-cc_defaults {
- name: "derive_sdk-defaults",
- srcs: [
- "derive_sdk.cpp",
- "sdk.proto",
- ],
- proto: {
- type: "lite",
- static: true,
- },
- min_sdk_version: "current",
- shared_libs: ["liblog"],
- // static c++/libbase for smaller size
- stl: "c++_static",
- static_libs: ["libbase"],
-}
-
-cc_binary {
- name: "derive_sdk",
- defaults: [ "derive_sdk-defaults" ],
- apex_available: [ "com.android.sdkext" ],
- visibility: [ "//frameworks/base/apex/sdkextensions" ]
-}
-
-// Work around testing using a 64-bit test suite on 32-bit test device by
-// using a prefer32 version of derive_sdk in testing.
-cc_binary {
- name: "derive_sdk_prefer32",
- defaults: [ "derive_sdk-defaults" ],
- compile_multilib: "prefer32",
- stem: "derive_sdk",
- apex_available: [ "test_com.android.sdkext" ],
- visibility: [ "//frameworks/base/apex/sdkextensions/testing" ],
- installable: false,
-}
-
-prebuilt_etc {
- name: "derive_sdk.rc",
- src: "derive_sdk.rc",
- installable: false,
-}
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
deleted file mode 100644
index 900193a..0000000
--- a/apex/sdkextensions/derive_sdk/derive_sdk.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "derive_sdk"
-
-#include <algorithm>
-#include <dirent.h>
-#include <iostream>
-#include <sys/stat.h>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "frameworks/base/apex/sdkextensions/derive_sdk/sdk.pb.h"
-
-using com::android::sdkext::proto::SdkVersion;
-
-int main(int, char**) {
- std::unique_ptr<DIR, decltype(&closedir)> apex(opendir("/apex"), closedir);
- if (!apex) {
- LOG(ERROR) << "Could not read /apex";
- return EXIT_FAILURE;
- }
- struct dirent* de;
- std::vector<std::string> paths;
- while ((de = readdir(apex.get()))) {
- std::string name = de->d_name;
- if (name[0] == '.' || name.find('@') != std::string::npos) {
- // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
- continue;
- }
- std::string path = "/apex/" + name + "/etc/sdkinfo.binarypb";
- struct stat statbuf;
- if (stat(path.c_str(), &statbuf) == 0) {
- paths.push_back(path);
- }
- }
-
- std::vector<int> versions;
- for (const auto& path : paths) {
- std::string contents;
- if (!android::base::ReadFileToString(path, &contents, true)) {
- LOG(ERROR) << "failed to read " << path;
- continue;
- }
- SdkVersion sdk_version;
- if (!sdk_version.ParseFromString(contents)) {
- LOG(ERROR) << "failed to parse " << path;
- continue;
- }
- LOG(INFO) << "Read version " << sdk_version.version() << " from " << path;
- versions.push_back(sdk_version.version());
- }
- auto itr = std::min_element(versions.begin(), versions.end());
- std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
-
- if (!android::base::SetProperty("build.version.extensions.r", prop_value)) {
- LOG(ERROR) << "failed to set sdk_info prop";
- return EXIT_FAILURE;
- }
-
- LOG(INFO) << "R extension version is " << prop_value;
- return EXIT_SUCCESS;
-}
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.rc b/apex/sdkextensions/derive_sdk/derive_sdk.rc
deleted file mode 100644
index 18f021c..0000000
--- a/apex/sdkextensions/derive_sdk/derive_sdk.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service derive_sdk /apex/com.android.sdkext/bin/derive_sdk
- user nobody
- group nobody
- oneshot
- disabled
diff --git a/apex/sdkextensions/derive_sdk/sdk.proto b/apex/sdkextensions/derive_sdk/sdk.proto
deleted file mode 100644
index d15b935..0000000
--- a/apex/sdkextensions/derive_sdk/sdk.proto
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto3";
-package com.android.sdkext.proto;
-
-option java_outer_classname = "SdkProto";
-option optimize_for = LITE_RUNTIME;
-
-message SdkVersion {
- int32 version = 1;
-}
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
deleted file mode 100644
index 6a78711..0000000
--- a/apex/sdkextensions/framework/Android.bp
+++ /dev/null
@@ -1,117 +0,0 @@
-// 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_visibility: [ ":__pkg__" ]
-}
-
-filegroup {
- name: "framework-sdkextensions-sources",
- srcs: [
- "java/**/*.java",
- ],
- path: "java",
- visibility: [ "//frameworks/base" ] // For the "global" stubs.
-}
-
-java_library {
- name: "framework-sdkextensions",
- srcs: [ ":framework-sdkextensions-sources" ],
- sdk_version: "system_current",
- libs: [ "framework-annotations-lib" ],
- permitted_packages: [ "android.os.ext" ],
- installable: true,
- plugins: ["java_api_finder"],
- visibility: [
- "//frameworks/base/apex/sdkextensions",
- "//frameworks/base/apex/sdkextensions/testing",
- ],
- hostdex: true, // for hiddenapi check
- apex_available: [
- "com.android.sdkext",
- "test_com.android.sdkext",
- ],
-}
-
-stubs_defaults {
- name: "framework-sdkextensions-stubs-defaults",
- srcs: [ ":framework-sdkextensions-sources" ],
- libs: [ "framework-annotations-lib" ],
- dist: { dest: "framework-sdkextensions.txt" },
-}
-
-droidstubs {
- name: "framework-sdkextensions-stubs-srcs-publicapi",
- defaults: [
- "framework-module-stubs-defaults-publicapi",
- "framework-sdkextensions-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "framework-sdkextensions-stubs-srcs-systemapi",
- defaults: [
- "framework-module-stubs-defaults-systemapi",
- "framework-sdkextensions-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "framework-sdkextensions-api-module_libs_api",
- defaults: [
- "framework-module-api-defaults-module_libs_api",
- "framework-sdkextensions-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "framework-sdkextensions-stubs-srcs-module_libs_api",
- defaults: [
- "framework-module-stubs-defaults-module_libs_api",
- "framework-sdkextensions-stubs-defaults",
- ],
-}
-
-java_library {
- name: "framework-sdkextensions-stubs-publicapi",
- srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
- defaults: ["framework-module-stubs-lib-defaults-publicapi"],
- visibility: [
- "//frameworks/base", // Framework
- "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
- ],
- dist: { dest: "framework-sdkextensions.jar" },
-}
-
-java_library {
- name: "framework-sdkextensions-stubs-systemapi",
- srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
- defaults: ["framework-module-stubs-lib-defaults-systemapi"],
- visibility: [
- "//frameworks/base", // Framework
- "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
- ],
- dist: { dest: "framework-sdkextensions.jar" },
-}
-
-java_library {
- name: "framework-sdkextensions-stubs-module_libs_api",
- srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
- defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
- visibility: [
- "//frameworks/base", // Framework
- "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
- ],
- dist: { dest: "framework-sdkextensions.jar" },
-}
diff --git a/apex/sdkextensions/framework/api/current.txt b/apex/sdkextensions/framework/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/module-lib-current.txt b/apex/sdkextensions/framework/api/module-lib-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/module-lib-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/module-lib-removed.txt b/apex/sdkextensions/framework/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/removed.txt b/apex/sdkextensions/framework/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/system-current.txt b/apex/sdkextensions/framework/api/system-current.txt
deleted file mode 100644
index bbff4c5..0000000
--- a/apex/sdkextensions/framework/api/system-current.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-// Signature format: 2.0
-package android.os.ext {
-
- public class SdkExtensions {
- method public static int getExtensionVersion(int);
- }
-
-}
-
diff --git a/apex/sdkextensions/framework/api/system-removed.txt b/apex/sdkextensions/framework/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
deleted file mode 100644
index c268ff4..0000000
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.os.ext;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.os.Build.VERSION_CODES;
-import android.os.SystemProperties;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Methods for interacting with the extension SDK.
- *
- * This class provides information about the extension SDK version present
- * on this device. Use the {@link #getExtensionVersion(int) getExtension} to
- * query for the extension version for the given SDK version.
-
- * @hide
- */
-@SystemApi
-public class SdkExtensions {
-
- private static final int R_EXTENSION_INT;
- static {
- R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
- }
-
- /**
- * Values suitable as parameters for {@link #getExtensionVersion(int)}.
- * @hide
- */
- @IntDef(value = { VERSION_CODES.R })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SdkVersion {}
-
- private SdkExtensions() { }
-
- /**
- * Return the version of the extension to the given SDK.
- *
- * @param sdk the SDK version to get the extension version of.
- * @see SdkVersion
- * @throws IllegalArgumentException if sdk is not an sdk version with extensions
- */
- public static int getExtensionVersion(@SdkVersion int sdk) {
- if (sdk < VERSION_CODES.R) {
- throw new IllegalArgumentException(String.valueOf(sdk) + " does not have extensions");
- }
-
- if (sdk == VERSION_CODES.R) {
- return R_EXTENSION_INT;
- }
- return 0;
- }
-
-}
diff --git a/apex/sdkextensions/framework/java/android/os/ext/package.html b/apex/sdkextensions/framework/java/android/os/ext/package.html
deleted file mode 100644
index 34c1697..0000000
--- a/apex/sdkextensions/framework/java/android/os/ext/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-Provides APIs to interface with the SDK extensions.
-</BODY>
-</HTML>
diff --git a/apex/sdkextensions/gen_sdkinfo.py b/apex/sdkextensions/gen_sdkinfo.py
deleted file mode 100644
index 5af478b..0000000
--- a/apex/sdkextensions/gen_sdkinfo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sdk_pb2
-import sys
-
-if __name__ == '__main__':
- argv = sys.argv[1:]
- if not len(argv) == 4 or sorted([argv[0], argv[2]]) != ['-o', '-v']:
- print('usage: gen_sdkinfo -v <version> -o <output-file>')
- sys.exit(1)
-
- for i in range(len(argv)):
- if sys.argv[i] == '-o':
- filename = sys.argv[i+1]
- if sys.argv[i] == '-v':
- version = int(sys.argv[i+1])
-
- proto = sdk_pb2.SdkVersion()
- proto.version = version
- with open(filename, 'wb') as f:
- f.write(proto.SerializeToString())
diff --git a/apex/sdkextensions/manifest.json b/apex/sdkextensions/manifest.json
deleted file mode 100644
index 048f5c4..0000000
--- a/apex/sdkextensions/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.sdkext",
- "version": 1
-}
diff --git a/apex/sdkextensions/sdk.proto b/apex/sdkextensions/sdk.proto
deleted file mode 100644
index d15b935..0000000
--- a/apex/sdkextensions/sdk.proto
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto3";
-package com.android.sdkext.proto;
-
-option java_outer_classname = "SdkProto";
-option optimize_for = LITE_RUNTIME;
-
-message SdkVersion {
- int32 version = 1;
-}
diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp
deleted file mode 100644
index f2f5b32..0000000
--- a/apex/sdkextensions/testing/Android.bp
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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.
-
-apex_test {
- name: "test_com.android.sdkext",
- visibility: [ "//system/apex/tests" ],
- defaults: ["com.android.sdkext-defaults"],
- manifest: "test_manifest.json",
- prebuilts: [ "sdkinfo_45" ],
- file_contexts: ":com.android.sdkext-file_contexts",
- installable: false, // Should never be installed on the systemimage
- multilib: {
- prefer32: {
- binaries: ["derive_sdk_prefer32"],
- },
- },
- // The automated test infra ends up building this apex for 64+32-bit and
- // then installs it on a 32-bit-only device. Work around this weirdness
- // by preferring 32-bit.
- compile_multilib: "prefer32",
-}
-
-genrule {
- name: "sdkinfo_45_src",
- out: [ "sdkinfo.binarypb" ],
- tools: [ "gen_sdkinfo" ],
- cmd: "$(location) -v 45 -o $(out)",
-}
-
-prebuilt_etc {
- name: "sdkinfo_45",
- src: ":sdkinfo_45_src",
- filename: "sdkinfo.binarypb",
- installable: false,
-}
diff --git a/apex/sdkextensions/testing/test_manifest.json b/apex/sdkextensions/testing/test_manifest.json
deleted file mode 100644
index 1b4a2b0..0000000
--- a/apex/sdkextensions/testing/test_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.sdkext",
- "version": 2147483647
-}
diff --git a/api/current.txt b/api/current.txt
index e63cb45..8163426 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -41304,6 +41304,7 @@
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationValidityDurationSeconds();
+ method public boolean isDevicePropertiesAttestationIncluded();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
method public boolean isRandomizedEncryptionRequired();
@@ -41325,6 +41326,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(@NonNull java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSerialNumber(@NonNull java.math.BigInteger);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSubject(@NonNull javax.security.auth.x500.X500Principal);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setDevicePropertiesAttestationIncluded(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4a1bf0d..26153ca 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6572,6 +6572,7 @@
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
+ field public static final String NAMESPACE_BLUETOOTH = "bluetooth";
field public static final String NAMESPACE_CONNECTIVITY = "connectivity";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
field public static final String NAMESPACE_DEX_BOOT = "dex_boot";
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 306b8af..ffde094 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -144,6 +144,13 @@
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
+
+
+
+NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED:
+ New setting keys are not allowed. (Field: TETHER_OFFLOAD_DISABLED)
+NoSettingsProvider: android.provider.Settings.Global#TETHER_SUPPORTED:
+ New setting keys are not allowed. (Field: TETHER_SUPPORTED)
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 46f3aeb..47d87c50 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2298,6 +2298,75 @@
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
NoClone: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
+
+
+
+NoSettingsProvider: android.provider.Settings.Global#AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
+ New setting keys are not allowed. (Field: AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES)
+NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
+ New setting keys are not allowed. (Field: AUTOMATIC_POWER_SAVE_MODE)
+NoSettingsProvider: android.provider.Settings.Global#BATTERY_SAVER_CONSTANTS:
+ New setting keys are not allowed. (Field: BATTERY_SAVER_CONSTANTS)
+NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD:
+ New setting keys are not allowed. (Field: DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD)
+NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABLED:
+ New setting keys are not allowed. (Field: DYNAMIC_POWER_SAVINGS_ENABLED)
+NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
+ New setting keys are not allowed. (Field: HIDDEN_API_BLACKLIST_EXEMPTIONS)
+NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH:
+ New setting keys are not allowed. (Field: LOCATION_GLOBAL_KILL_SWITCH)
+NoSettingsProvider: android.provider.Settings.Global#LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST:
+ New setting keys are not allowed. (Field: LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST)
+NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE:
+ New setting keys are not allowed. (Field: LOW_POWER_MODE)
+NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE_STICKY:
+ New setting keys are not allowed. (Field: LOW_POWER_MODE_STICKY)
+NoSettingsProvider: android.provider.Settings.Global#NOTIFICATION_BUBBLES:
+ New setting keys are not allowed. (Field: NOTIFICATION_BUBBLES)
+NoSettingsProvider: android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES:
+ New setting keys are not allowed. (Field: OVERLAY_DISPLAY_DEVICES)
+NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED:
+ New setting keys are not allowed. (Field: TETHER_OFFLOAD_DISABLED)
+NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
+ New setting keys are not allowed. (Field: USE_OPEN_WIFI_PACKAGE)
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
+ New setting keys are not allowed. (Field: ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED)
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
+ New setting keys are not allowed. (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_FEATURE_FIELD_CLASSIFICATION:
+ New setting keys are not allowed. (Field: AUTOFILL_FEATURE_FIELD_CLASSIFICATION)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE:
+ New setting keys are not allowed. (Field: AUTOFILL_SERVICE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT:
+ New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE:
+ New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE:
+ New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_VALUE_LENGTH:
+ New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_VALUE_LENGTH)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MIN_VALUE_LENGTH:
+ New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MIN_VALUE_LENGTH)
+NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED:
+ New setting keys are not allowed. (Field: CONTENT_CAPTURE_ENABLED)
+NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES:
+ New setting keys are not allowed. (Field: DISABLED_PRINT_SERVICES)
+NoSettingsProvider: android.provider.Settings.Secure#DOZE_ALWAYS_ON:
+ New setting keys are not allowed. (Field: DOZE_ALWAYS_ON)
+NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
+ New setting keys are not allowed. (Field: ENABLED_VR_LISTENERS)
+NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_DELAY_MILLIS:
+ New setting keys are not allowed. (Field: LOCATION_ACCESS_CHECK_DELAY_MILLIS)
+NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_INTERVAL_MILLIS:
+ New setting keys are not allowed. (Field: LOCATION_ACCESS_CHECK_INTERVAL_MILLIS)
+NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
+ New setting keys are not allowed. (Field: NOTIFICATION_BADGING)
+NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
+ New setting keys are not allowed. (Field: SYNC_PARENT_SOUNDS)
+NoSettingsProvider: android.provider.Settings.Secure#USER_SETUP_COMPLETE:
+ New setting keys are not allowed. (Field: USER_SETUP_COMPLETE)
+NoSettingsProvider: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
+ New setting keys are not allowed. (Field: VOICE_INTERACTION_SERVICE)
diff --git a/cmds/device_config/Android.bp b/cmds/device_config/Android.bp
new file mode 100644
index 0000000..67e014a
--- /dev/null
+++ b/cmds/device_config/Android.bp
@@ -0,0 +1,7 @@
+// Copyright 2018 The Android Open Source Project
+//
+
+sh_binary {
+ name: "device_config",
+ src: "device_config",
+}
diff --git a/cmds/device_config/Android.mk b/cmds/device_config/Android.mk
deleted file mode 100644
index 4041e01..0000000
--- a/cmds/device_config/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2018 The Android Open Source Project
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := device_config
-LOCAL_SRC_FILES := device_config
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_PREBUILT)
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d9d697a..52e9385 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -5884,11 +5884,11 @@
* Logs when a data stall event occurs.
*
* Log from:
- * frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
*/
message DataStallEvent {
// Data stall evaluation type.
- // See frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+ // See packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
// Refer to the definition of DATA_STALL_EVALUATION_TYPE_*.
optional int32 evaluation_type = 1;
// See definition in data_stall_event.proto.
@@ -5901,6 +5901,10 @@
optional com.android.server.connectivity.CellularData cell_info = 5 [(log_mode) = MODE_BYTES];
// See definition in data_stall_event.proto.
optional com.android.server.connectivity.DnsEvent dns_event = 6 [(log_mode) = MODE_BYTES];
+ // The tcp packets fail rate from the latest tcp polling.
+ optional int32 tcp_fail_rate = 7;
+ // Number of packets sent since the last received packet.
+ optional int32 tcp_sent_since_last_recv = 8;
}
/*
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index b669d77..647f630 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -37,7 +37,6 @@
import libcore.timezone.ZoneInfoDb;
-import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -998,12 +997,7 @@
// Reject this timezone if it isn't an Olson zone we recognize.
if (mTargetSdkVersion >= Build.VERSION_CODES.M) {
- boolean hasTimeZone = false;
- try {
- hasTimeZone = ZoneInfoDb.getInstance().hasTimeZone(timeZone);
- } catch (IOException ignored) {
- }
-
+ boolean hasTimeZone = ZoneInfoDb.getInstance().hasTimeZone(timeZone);
if (!hasTimeZone) {
throw new IllegalArgumentException("Timezone: " + timeZone + " is not an Olson ID");
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 9a17346..2888fbd 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -110,8 +110,9 @@
* off to save power. Scanning is resumed when screen is turned on again. To avoid this, use
* {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}.
* <p>
- * An app must hold
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * An app must have
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
+ * in order to get results. An App targeting Android Q or later must have
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get results.
*
@@ -129,8 +130,9 @@
* resumed when screen is turned on again. To avoid this, do filetered scanning by
* using proper {@link ScanFilter}.
* <p>
- * An app must hold
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * An app must have
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
+ * in order to get results. An App targeting Android Q or later must have
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get results.
*
@@ -150,8 +152,9 @@
* the PendingIntent. Use this method of scanning if your process is not always running and it
* should be started when scan results are available.
* <p>
- * An app must hold
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * An app must have
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
+ * in order to get results. An App targeting Android Q or later must have
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get results.
* <p>
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 9086d49..275e38c 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -437,7 +437,7 @@
*/
private long mReportTimestamp;
- /** The detection method used to identify the suspected data stall */
+ /** A bitmask of the detection methods used to identify the suspected data stall */
@DetectionMethod private final int mDetectionMethod;
/** LinkProperties available on the Network at the reported timestamp */
@@ -499,9 +499,9 @@
}
/**
- * Returns the detection method used to identify this suspected data stall.
+ * Returns the bitmask of detection methods used to identify this suspected data stall.
*
- * @return The detection method used to identify the suspected data stall
+ * @return The bitmask of detection methods used to identify the suspected data stall
*/
public int getDetectionMethod() {
return mDetectionMethod;
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 836624b..c8ca618e 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,6 +25,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.content.pm.PackageManager;
import android.os.Process;
import android.security.Credentials;
import android.security.KeyStore;
@@ -101,6 +103,7 @@
private final boolean mIsBypassable; // Defaults in builder
private final boolean mIsMetered; // Defaults in builder
private final int mMaxMtu; // Defaults in builder
+ private final boolean mIsRestrictedToTestNetworks;
private Ikev2VpnProfile(
int type,
@@ -116,7 +119,8 @@
@NonNull List<String> allowedAlgorithms,
boolean isBypassable,
boolean isMetered,
- int maxMtu) {
+ int maxMtu,
+ boolean restrictToTestNetworks) {
super(type);
checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
@@ -140,6 +144,7 @@
mIsBypassable = isBypassable;
mIsMetered = isMetered;
mMaxMtu = maxMtu;
+ mIsRestrictedToTestNetworks = restrictToTestNetworks;
validate();
}
@@ -329,6 +334,15 @@
return mMaxMtu;
}
+ /**
+ * Returns whether or not this VPN profile is restricted to test networks.
+ *
+ * @hide
+ */
+ public boolean isRestrictedToTestNetworks() {
+ return mIsRestrictedToTestNetworks;
+ }
+
@Override
public int hashCode() {
return Objects.hash(
@@ -345,7 +359,8 @@
mAllowedAlgorithms,
mIsBypassable,
mIsMetered,
- mMaxMtu);
+ mMaxMtu,
+ mIsRestrictedToTestNetworks);
}
@Override
@@ -368,7 +383,8 @@
&& Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
&& mIsBypassable == other.mIsBypassable
&& mIsMetered == other.mIsMetered
- && mMaxMtu == other.mMaxMtu;
+ && mMaxMtu == other.mMaxMtu
+ && mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks;
}
/**
@@ -381,7 +397,8 @@
*/
@NonNull
public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
- final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
+ final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
+ mIsRestrictedToTestNetworks);
profile.type = mType;
profile.server = mServerAddr;
profile.ipsecIdentifier = mUserIdentity;
@@ -449,6 +466,9 @@
builder.setBypassable(profile.isBypassable);
builder.setMetered(profile.isMetered);
builder.setMaxMtu(profile.maxMtu);
+ if (profile.isRestrictedToTestNetworks) {
+ builder.restrictToTestNetworks();
+ }
switch (profile.type) {
case TYPE_IKEV2_IPSEC_USER_PASS:
@@ -621,6 +641,7 @@
private boolean mIsBypassable = false;
private boolean mIsMetered = true;
private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
+ private boolean mIsRestrictedToTestNetworks = false;
/**
* Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
@@ -628,6 +649,7 @@
* @param serverAddr the server that the VPN should connect to
* @param identity the identity string to be used for IKEv2 authentication
*/
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder(@NonNull String serverAddr, @NonNull String identity) {
checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr");
checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity");
@@ -661,6 +683,7 @@
* unrecognized format
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAuthUsernamePassword(
@NonNull String user,
@NonNull String pass,
@@ -696,6 +719,7 @@
* unrecognized format
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAuthDigitalSignature(
@NonNull X509Certificate userCert,
@NonNull PrivateKey key,
@@ -726,6 +750,7 @@
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAuthPsk(@NonNull byte[] psk) {
checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
@@ -749,6 +774,7 @@
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setBypassable(boolean isBypassable) {
mIsBypassable = isBypassable;
return this;
@@ -763,6 +789,7 @@
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setProxy(@Nullable ProxyInfo proxy) {
mProxyInfo = proxy;
return this;
@@ -779,6 +806,7 @@
* @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setMaxMtu(int mtu) {
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
@@ -806,6 +834,7 @@
* @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setMetered(boolean isMetered) {
mIsMetered = isMetered;
return this;
@@ -833,6 +862,7 @@
* @see IpSecAlgorithm
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
validateAllowedAlgorithms(algorithmNames);
@@ -842,11 +872,28 @@
}
/**
+ * Restricts this profile to use test networks (only).
+ *
+ * <p>This method is for testing only, and must not be used by apps. Calling
+ * provisionVpnProfile() with a profile where test-network usage is enabled will require the
+ * MANAGE_TEST_NETWORKS permission.
+ *
+ * @hide
+ */
+ @NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+ public Builder restrictToTestNetworks() {
+ mIsRestrictedToTestNetworks = true;
+ return this;
+ }
+
+ /**
* Validates, builds and provisions the VpnProfile.
*
* @throws IllegalArgumentException if any of the required keys or values were invalid
*/
@NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Ikev2VpnProfile build() {
return new Ikev2VpnProfile(
mType,
@@ -862,7 +909,8 @@
mAllowedAlgorithms,
mIsBypassable,
mIsMetered,
- mMaxMtu);
+ mMaxMtu,
+ mIsRestrictedToTestNetworks);
}
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 57d5d03..0fbffba 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -679,13 +679,14 @@
*/
public void restrictCapabilitesForTestNetwork(int creatorUid) {
final long originalCapabilities = mNetworkCapabilities;
+ final long originalTransportTypes = mTransportTypes;
final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
final int originalSignalStrength = mSignalStrength;
final int originalOwnerUid = getOwnerUid();
final int[] originalAdministratorUids = getAdministratorUids();
clearAll();
- // Reset the transports to only contain TRANSPORT_TEST.
- mTransportTypes = (1 << TRANSPORT_TEST);
+ mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
+ | (1 << TRANSPORT_TEST);
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
@@ -787,6 +788,13 @@
};
/**
+ * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+ */
+ private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+ // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
+ | 1 << TRANSPORT_ETHERNET;
+
+ /**
* Adds the given transport type to this {@code NetworkCapability} instance.
* Multiple transports may be applied. Note that when searching
* for a network to satisfy a request, any listed in the request will satisfy the request.
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index b7fb280..34e48eb 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.os.Process.CLAT_UID;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1047,73 +1045,54 @@
}
/**
- * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+ * Calculate and apply adjustments to captured statistics for 464xlat traffic.
*
- * <p>This mutates both base and stacked traffic stats, to account respectively for
- * double-counted traffic and IPv4/IPv6 header size difference.
+ * <p>This mutates stacked traffic stats, to account for IPv4/IPv6 header size difference.
*
- * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
- * packet on the stacked interface, and once as translated to an IPv6 packet on the
- * base interface. For correct stats accounting on the base interface, if using xt_qtaguid,
- * every rx 464xlat packet needs to be subtracted from the root UID on the base interface
- * (http://b/12249687, http:/b/33681750), and every tx 464xlat packet which was counted onto
- * clat uid should be ignored.
+ * <p>UID stats, which are only accounted on the stacked interface, need to be increased
+ * by 20 bytes/packet to account for translation overhead.
*
- * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
- * interface will not be counted. Thus, the adjustment on root uid is not needed. However, the
- * tx traffic counted in the same way xt_qtaguid does, so the traffic on clat uid still
- * needs to be ignored.
+ * <p>The potential additional overhead of 8 bytes/packet for ip fragments is ignored.
+ *
+ * <p>Interface stats need to sum traffic on both stacked and base interface because:
+ * - eBPF offloaded packets appear only on the stacked interface
+ * - Non-offloaded ingress packets appear only on the stacked interface
+ * (due to iptables raw PREROUTING drop rules)
+ * - Non-offloaded egress packets appear only on the stacked interface
+ * (due to ignoring traffic from clat daemon by uid match)
+ * (and of course the 20 bytes/packet overhead needs to be applied to stacked interface stats)
*
* <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
* {@code ConcurrentHashMap}
* @param baseTraffic Traffic on the base interfaces. Will be mutated.
* @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
- * @param useBpfStats True if eBPF is in use.
* @hide
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
- NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) {
- // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
- // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
- final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
-
+ NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
// For recycling
Entry entry = null;
- Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
-
for (int i = 0; i < stackedTraffic.size; i++) {
entry = stackedTraffic.getValues(i, entry);
- if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
- continue;
- }
- final String baseIface = stackedIfaces.get(entry.iface);
- if (baseIface == null) {
- continue;
- }
- // Subtract xt_qtaguid 464lat rx traffic seen for the root UID on the current base
- // interface. As for eBPF, the per uid stats is collected by different hook, the rx
- // packets on base interface will not be counted.
- adjust.iface = baseIface;
- if (!useBpfStats) {
- adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
- adjust.rxPackets = -entry.rxPackets;
- }
- adjustments.combineValues(adjust);
+ if (entry == null) continue;
+ if (entry.iface == null) continue;
+ if (!entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) continue;
// For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
// sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after
// unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
// difference for all packets (http://b/12249687, http:/b/33681750).
+ //
+ // Note: this doesn't account for LRO/GRO/GSO/TSO (ie. >mtu) traffic correctly, nor
+ // does it correctly account for the 8 extra bytes in the IPv6 fragmentation header.
+ //
+ // While the ebpf code path does try to simulate proper post segmentation packet
+ // counts, we have nothing of the sort of xt_qtaguid stats.
entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
stackedTraffic.setValues(i, entry);
}
-
- // Traffic on clat uid is v6 tx traffic that is already counted with app uid on the stacked
- // v4 interface, so it needs to be removed to avoid double-counting.
- baseTraffic.removeUids(new int[] {CLAT_UID});
- baseTraffic.combineAllValues(adjustments);
}
/**
@@ -1125,8 +1104,8 @@
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
* @hide
*/
- public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) {
- apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats);
+ public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
+ apply464xlatAdjustments(this, this, stackedIfaces);
}
/**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0f53d4f..ac16a4f 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -109,7 +109,7 @@
* The hardware variant (SKU), if available.
*/
@NonNull
- public static final String SKU = getString("ro.boot.product.hardware.sku");
+ public static final String SKU = getString("ro.boot.hardware.sku");
/**
* Whether this build was for an emulator device.
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 6632ca5..9b360ed 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -118,7 +118,21 @@
* by this Parcelable object instance.
*/
public @ContentsFlags int describeContents();
-
+
+ /**
+ * 'Stable' means this parcelable is guaranteed to be stable for multiple years.
+ * It must be guaranteed by setting stability field in aidl_interface,
+ * OR explicitly override this method from @JavaOnlyStableParcelable marked Parcelable.
+ * WARNING: isStable() is only expected to be overridden by auto-generated code,
+ * OR @JavaOnlyStableParcelable marked Parcelable only if there is guaranteed to
+ * be only once copy of the parcelable on the system.
+ * @return true if this parcelable is stable.
+ * @hide
+ */
+ default boolean isStable() {
+ return false;
+ }
+
/**
* Flatten this object in to a Parcel.
*
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
new file mode 100644
index 0000000..c37a2ff
--- /dev/null
+++ b/core/java/android/os/ParcelableHolder.java
@@ -0,0 +1,177 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.MathUtils;
+
+/**
+ * Parcelable containing the other Parcelable object.
+ * @hide
+ */
+public final class ParcelableHolder implements Parcelable {
+ /**
+ * This is set by {@link #setParcelable}.
+ * {@link #mParcelable} and {@link #mParcel} are mutually exclusive
+ * if {@link ParcelableHolder} contains value, otherwise, both are null.
+ */
+ private Parcelable mParcelable;
+ /**
+ * This is set by {@link #readFromParcel}.
+ * {@link #mParcelable} and {@link #mParcel} are mutually exclusive
+ * if {@link ParcelableHolder} contains value, otherwise, both are null.
+ */
+ private Parcel mParcel;
+ private boolean mIsStable = false;
+
+ public ParcelableHolder(boolean isStable) {
+ mIsStable = isStable;
+ }
+
+ private ParcelableHolder() {
+
+ }
+
+ /**
+ * {@link ParcelableHolder}'s stability is determined by the parcelable
+ * which contains this ParcelableHolder.
+ * For more detail refer to {@link Parcelable#isStable}.
+ */
+ @Override
+ public boolean isStable() {
+ return mIsStable;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<ParcelableHolder> CREATOR =
+ new Parcelable.Creator<ParcelableHolder>() {
+ @NonNull
+ @Override
+ public ParcelableHolder createFromParcel(@NonNull Parcel parcel) {
+ ParcelableHolder parcelable = new ParcelableHolder();
+ parcelable.readFromParcel(parcel);
+ return parcelable;
+ }
+
+ @NonNull
+ @Override
+ public ParcelableHolder[] newArray(int size) {
+ return new ParcelableHolder[size];
+ }
+ };
+
+
+ /**
+ * Write a parcelable into ParcelableHolder, the previous parcelable will be removed.
+ * @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
+ */
+ public synchronized boolean setParcelable(@Nullable Parcelable p) {
+ if (p != null && this.isStable() && !p.isStable()) {
+ return false;
+ }
+ mParcelable = p;
+ if (mParcel != null) {
+ mParcel.recycle();
+ mParcel = null;
+ }
+ return true;
+ }
+
+ /**
+ * @return the parcelable that was written by {@link #setParcelable} or {@link #readFromParcel},
+ * or {@code null} if the parcelable has not been written, or T is different from
+ * the type written by (@link #setParcelable}.
+ */
+ @Nullable
+ public synchronized <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) {
+ if (mParcel == null) {
+ if (!clazz.isInstance(mParcelable)) {
+ return null;
+ }
+ return (T) mParcelable;
+ }
+
+ mParcel.setDataPosition(0);
+
+ T parcelable = mParcel.readParcelable(clazz.getClassLoader());
+ if (!clazz.isInstance(parcelable)) {
+ return null;
+ }
+ mParcelable = parcelable;
+
+ mParcel.recycle();
+ mParcel = null;
+ return parcelable;
+ }
+
+ /**
+ * Read ParcelableHolder from a parcel.
+ */
+ public synchronized void readFromParcel(@NonNull Parcel parcel) {
+ this.mIsStable = parcel.readBoolean();
+
+ mParcelable = null;
+
+ if (mParcel == null) {
+ mParcel = Parcel.obtain();
+ }
+ mParcel.setDataPosition(0);
+ mParcel.setDataSize(0);
+
+ int dataSize = parcel.readInt();
+ if (dataSize < 0) {
+ throw new IllegalArgumentException("dataSize from parcel is negative");
+ }
+ int dataStartPos = parcel.dataPosition();
+
+ mParcel.appendFrom(parcel, dataStartPos, dataSize);
+ parcel.setDataPosition(MathUtils.addOrThrow(dataStartPos, dataSize));
+ }
+
+ @Override
+ public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeBoolean(this.mIsStable);
+
+ if (mParcel != null) {
+ parcel.writeInt(mParcel.dataSize());
+ parcel.appendFrom(mParcel, 0, mParcel.dataSize());
+ return;
+ }
+
+ int sizePos = parcel.dataPosition();
+ parcel.writeInt(0);
+ int dataStartPos = parcel.dataPosition();
+ parcel.writeParcelable(mParcelable, 0);
+ int dataSize = parcel.dataPosition() - dataStartPos;
+
+ parcel.setDataPosition(sizePos);
+ parcel.writeInt(dataSize);
+ parcel.setDataPosition(MathUtils.addOrThrow(parcel.dataPosition(), dataSize));
+ }
+
+ @Override
+ public synchronized int describeContents() {
+ if (mParcel != null) {
+ return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+ }
+ if (mParcelable != null) {
+ return mParcelable.describeContents();
+ }
+ return 0;
+ }
+}
diff --git a/core/java/android/os/Registrant.java b/core/java/android/os/Registrant.java
index d6afd04..bde7ec1 100644
--- a/core/java/android/os/Registrant.java
+++ b/core/java/android/os/Registrant.java
@@ -46,7 +46,7 @@
{
internalNotifyRegistrant (null, null);
}
-
+
@UnsupportedAppUsage
public void
notifyResult(Object result)
@@ -81,9 +81,7 @@
Message msg = Message.obtain();
msg.what = what;
-
msg.obj = new AsyncResult(userObj, result, exception);
-
h.sendMessage(msg);
}
}
@@ -126,4 +124,3 @@
int what;
Object userObj;
}
-
diff --git a/core/java/android/os/RegistrantList.java b/core/java/android/os/RegistrantList.java
index 98f949b..b36734b 100644
--- a/core/java/android/os/RegistrantList.java
+++ b/core/java/android/os/RegistrantList.java
@@ -42,9 +42,9 @@
{
// if the handler is already in the registrant list, remove it
remove(h);
- add(new Registrant(h, what, obj));
+ add(new Registrant(h, what, obj));
}
-
+
@UnsupportedAppUsage
public synchronized void
add(Registrant r)
@@ -59,13 +59,17 @@
{
for (int i = registrants.size() - 1; i >= 0 ; i--) {
Registrant r = (Registrant) registrants.get(i);
-
+
if (r.refH == null) {
registrants.remove(i);
}
}
}
+ public synchronized void removeAll() {
+ registrants.clear();
+ }
+
@UnsupportedAppUsage
public synchronized int
size()
@@ -88,7 +92,7 @@
r.internalNotifyRegistrant(result, exception);
}
}
-
+
@UnsupportedAppUsage
public /*synchronized*/ void
notifyRegistrants()
@@ -109,14 +113,14 @@
internalNotifyRegistrants (result, null);
}
-
+
@UnsupportedAppUsage
public /*synchronized*/ void
notifyRegistrants(AsyncResult ar)
{
internalNotifyRegistrants(ar.result, ar.exception);
}
-
+
@UnsupportedAppUsage
public synchronized void
remove(Handler h)
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index c837b93..5e1b223 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -110,6 +110,14 @@
public static final String NAMESPACE_AUTOFILL = "autofill";
/**
+ * Namespace for all Bluetooth related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_BLUETOOTH = "bluetooth";
+
+ /**
* Namespace for all networking connectivity related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7923b58..cadae5c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12061,17 +12061,6 @@
public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
/**
- * System VDSO global setting. This links to the "sys.vdso" system property.
- * The following values are supported:
- * false -> both 32 and 64 bit vdso disabled
- * 32 -> 32 bit vdso enabled
- * 64 -> 64 bit vdso enabled
- * Any other value defaults to both 32 bit and 64 bit true.
- * @hide
- */
- public static final String SYS_VDSO = "sys_vdso";
-
- /**
* UidCpuPower global setting. This links the sys.uidcpupower system property.
* The following values are supported:
* 0 -> /proc/uid_cpupower/* are disabled
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 248e321..8e8409d 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -21,7 +21,6 @@
import libcore.timezone.ZoneInfoDb;
import libcore.util.ZoneInfo;
-import java.io.IOException;
import java.util.Locale;
import java.util.TimeZone;
@@ -1106,19 +1105,14 @@
}
private static ZoneInfo lookupZoneInfo(String timezoneId) {
- try {
- ZoneInfo zoneInfo = ZoneInfoDb.getInstance().makeTimeZone(timezoneId);
- if (zoneInfo == null) {
- zoneInfo = ZoneInfoDb.getInstance().makeTimeZone("GMT");
- }
- if (zoneInfo == null) {
- throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
- }
- return zoneInfo;
- } catch (IOException e) {
- // This should not ever be thrown.
- throw new AssertionError("Error loading timezone: \"" + timezoneId + "\"", e);
+ ZoneInfo zoneInfo = ZoneInfoDb.getInstance().makeTimeZone(timezoneId);
+ if (zoneInfo == null) {
+ zoneInfo = ZoneInfoDb.getInstance().makeTimeZone("GMT");
}
+ if (zoneInfo == null) {
+ throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
+ }
+ return zoneInfo;
}
public void switchTimeZone(String timezone) {
diff --git a/core/java/android/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS
index 893a083..be4fbaa 100644
--- a/core/java/android/view/textclassifier/OWNERS
+++ b/core/java/android/view/textclassifier/OWNERS
@@ -7,4 +7,8 @@
joannechung@google.com
svetoslavganov@google.com
eugeniom@google.com
-samsellem@google.com
\ No newline at end of file
+samsellem@google.com
+adamhe@google.com
+augale@google.com
+lpeter@google.com
+tymtsai@google.com
\ No newline at end of file
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 9cbb035..f44f24a 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,6 +16,8 @@
package android.widget;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.annotation.InterpolatorRes;
import android.annotation.NonNull;
@@ -246,6 +248,8 @@
private AccessibilityEventSender mAccessibilityEventSender;
+ private ObjectAnimator mLastProgressAnimator;
+
/**
* Create a new progress bar with range 0...100 and initial progress of 0.
* @param context the application environment
@@ -1544,8 +1548,19 @@
animator.setAutoCancel(true);
animator.setDuration(PROGRESS_ANIM_DURATION);
animator.setInterpolator(PROGRESS_ANIM_INTERPOLATOR);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLastProgressAnimator = null;
+ }
+ });
animator.start();
+ mLastProgressAnimator = animator;
} else {
+ if (isPrimary && mLastProgressAnimator != null) {
+ mLastProgressAnimator.cancel();
+ mLastProgressAnimator = null;
+ }
setVisualProgress(id, scale);
}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 829bd8a..8ea5aa8 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -136,13 +136,19 @@
public boolean isMetered = false; // 21
public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; // 22
public boolean areAuthParamsInline = false; // 23
+ public final boolean isRestrictedToTestNetworks; // 24
// Helper fields.
@UnsupportedAppUsage
public transient boolean saveLogin = false;
public VpnProfile(String key) {
+ this(key, false);
+ }
+
+ public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
this.key = key;
+ this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
}
@UnsupportedAppUsage
@@ -171,6 +177,7 @@
isMetered = in.readBoolean();
maxMtu = in.readInt();
areAuthParamsInline = in.readBoolean();
+ isRestrictedToTestNetworks = in.readBoolean();
}
/**
@@ -220,6 +227,7 @@
out.writeBoolean(isMetered);
out.writeInt(maxMtu);
out.writeBoolean(areAuthParamsInline);
+ out.writeBoolean(isRestrictedToTestNetworks);
}
/**
@@ -237,12 +245,21 @@
String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
// Acceptable numbers of values are:
// 14-19: Standard profile, with option for serverCert, proxy
- // 24: Standard profile with serverCert, proxy and platform-VPN parameters.
- if ((values.length < 14 || values.length > 19) && values.length != 24) {
+ // 24: Standard profile with serverCert, proxy and platform-VPN parameters
+ // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
+ if ((values.length < 14 || values.length > 19)
+ && values.length != 24 && values.length != 25) {
return null;
}
- VpnProfile profile = new VpnProfile(key);
+ final boolean isRestrictedToTestNetworks;
+ if (values.length >= 25) {
+ isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]);
+ } else {
+ isRestrictedToTestNetworks = false;
+ }
+
+ VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks);
profile.name = values[0];
profile.type = Integer.parseInt(values[1]);
if (profile.type < 0 || profile.type > TYPE_MAX) {
@@ -283,6 +300,8 @@
profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
}
+ // isRestrictedToTestNetworks (values[24]) assigned as part of the constructor
+
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
return profile;
} catch (Exception e) {
@@ -330,6 +349,7 @@
builder.append(VALUE_DELIMITER).append(isMetered);
builder.append(VALUE_DELIMITER).append(maxMtu);
builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
+ builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
@@ -421,7 +441,8 @@
return Objects.hash(
key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
- proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
+ proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
+ isRestrictedToTestNetworks);
}
/** Checks VPN profiles for interior equality. */
@@ -453,7 +474,8 @@
&& isBypassable == other.isBypassable
&& isMetered == other.isMetered
&& maxMtu == other.maxMtu
- && areAuthParamsInline == other.areAuthParamsInline;
+ && areAuthParamsInline == other.areAuthParamsInline
+ && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks;
}
@NonNull
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 741a65e..2e15212 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -367,11 +367,17 @@
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
hidlManager.addDependency(hidlBase);
+ SharedLibraryInfo androidTestBase = new SharedLibraryInfo(
+ "/system/framework/android.test.base.jar", null /*packageName*/,
+ null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
+ null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
+
ApplicationLoaders.getDefault().createAndCacheNonBootclasspathSystemClassLoaders(
new SharedLibraryInfo[]{
// ordered dependencies first
hidlBase,
hidlManager,
+ androidTestBase,
});
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9bc4adc..27415c5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -231,7 +231,10 @@
"system/media/private/camera/include",
],
- header_libs: ["bionic_libc_platform_headers"],
+ header_libs: [
+ "bionic_libc_platform_headers",
+ "dnsproxyd_protocol_headers",
+ ],
static_libs: [
"libasync_safe",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b30fed6..544340e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -693,6 +693,7 @@
char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
char voldDecryptBuf[PROPERTY_VALUE_MAX];
+ char perfettoHprofOptBuf[sizeof("-XX:PerfettoHprof=") + PROPERTY_VALUE_MAX];
enum {
kEMDefault,
kEMIntPortable,
@@ -807,6 +808,16 @@
addOption("-verbose:gc");
//addOption("-verbose:class");
+ // On Android, we always want to allow loading the PerfettoHprof plugin.
+ // Even with this option set, we will still only actually load the plugin
+ // if we are on a userdebug build or the app is debuggable or profileable.
+ // This is enforced in art/runtime/runtime.cc.
+ //
+ // We want to be able to disable this, because this does not work on host,
+ // and we do not want to enable it in tests.
+ parseRuntimeOption("dalvik.vm.perfetto_hprof", perfettoHprofOptBuf, "-XX:PerfettoHprof=",
+ "true");
+
if (primary_zygote) {
addOption("-Xprimaryzygote");
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index ba7fe7f..03b9793 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -27,12 +27,13 @@
#include <netinet/ip.h>
#include <netinet/udp.h>
+#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
#include <android_runtime/AndroidRuntime.h>
#include <cutils/properties.h>
-#include <utils/misc.h>
-#include <utils/Log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
#include "NetdClient.h"
#include "core_jni_helpers.h"
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d723ecc..eb8d26e 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -475,7 +475,11 @@
if (fd < 0) return NULL;
fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (fd < 0) return NULL;
- return jniCreateFileDescriptor(env, fd);
+ jobject jifd = jniCreateFileDescriptor(env, fd);
+ if (jifd == NULL) {
+ close(fd);
+ }
+ return jifd;
}
return NULL;
}
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index c33405d..a812c35 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -69,7 +69,11 @@
return nullptr;
}
- return jniCreateFileDescriptor(env, fd);
+ jobject jifd = jniCreateFileDescriptor(env, fd);
+ if (jifd == nullptr) {
+ close(fd);
+ }
+ return jifd;
}
jint SharedMemory_nGetSize(JNIEnv* env, jobject, jobject fileDescriptor) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b2aef78..48a764e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -483,11 +483,8 @@
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- std::vector<uint8_t> byteData(parcel->dataSize());
- memcpy(byteData.data(), parcel->data(), parcel->dataSize());
-
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->setMetadata(ctrl, id, std::move(byteData));
+ transaction->setMetadata(ctrl, id, *parcel);
}
static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 3d99b72..829e1f7 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -905,13 +905,8 @@
optional SettingProto storage_full_threshold_bytes = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto storage_cache_percentage = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto storage_cache_max_bytes = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // System VDSO global setting. This links to the "sys.vdso" system property.
- // The following values are supported:
- // false -> both 32 and 64 bit vdso disabled
- // 32 -> 32 bit vdso enabled
- // 64 -> 64 bit vdso enabled
- // Any other value defaults to both 32 bit and 64 bit true.
- optional SettingProto vdso = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // Used to be sys_vdso
+ reserved 7;
// UidCpuPower global setting. This links the sys.uidcpupower system property.
// The following values are supported:
// 0 -> /proc/uid_cpupower/* are disabled
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1a2dfce..2fb281c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4584,12 +4584,12 @@
<!-- @SystemApi Allows to access all app shortcuts.
@hide -->
<permission android:name="android.permission.ACCESS_SHORTCUTS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
@hide -->
<permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows an application to read the runtime profiles of other apps.
@hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2ab4444..6551b28b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -452,10 +452,6 @@
-->
</string-array>
- <!-- Package name for the default CellBroadcastService module [DO NOT TRANSLATE] -->
- <string name="cellbroadcast_default_package" translatable="false">com.android.cellbroadcastservice
- </string>
-
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a844d8a..3cee340 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -745,7 +745,6 @@
<java-symbol type="string" name="config_ethernet_iface_regex" />
<java-symbol type="array" name="config_ethernet_interfaces" />
<java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
- <java-symbol type="string" name="cellbroadcast_default_package" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 9cc04d3..d5733e3 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -39,7 +39,7 @@
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
- <!-- Argentia: 5 digits, known short codes listed -->
+ <!-- Argentina: 5 digits, known short codes listed -->
<shortcode country="ar" pattern="\\d{5}" free="11711|28291" />
<!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.bp b/core/tests/coretests/apks/install_jni_lib/Android.bp
index f20f599..df19fa0 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.bp
+++ b/core/tests/coretests/apks/install_jni_lib/Android.bp
@@ -24,4 +24,5 @@
"-Wall",
"-Werror",
],
+ header_libs: ["jni_headers"]
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 802b106..cb91db1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -454,7 +454,6 @@
Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
- Settings.Global.SYS_VDSO,
Settings.Global.SYS_UIDCPUPOWER,
Settings.Global.SYS_TRACED,
Settings.Global.FPS_DEVISOR,
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
new file mode 100644
index 0000000..2194d4b
--- /dev/null
+++ b/core/tests/hdmitests/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "HdmiCecTests",
+ // Include all test java files
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "truth-prebuilt",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
deleted file mode 100644
index f155feb..0000000
--- a/core/tests/hdmitests/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils truth-prebuilt
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := HdmiCecTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp
index 6db0ba5..9e6c17f 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp
@@ -26,6 +26,7 @@
// All of the source files that we will compile.
srcs: ["native.cpp"],
+ header_libs: ["jni_headers"],
shared_libs: ["liblog"],
cflags: [
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp
index 582f2a7..91da6e4 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp
@@ -25,6 +25,7 @@
// All of the source files that we will compile.
srcs: ["native.cpp"],
+ header_libs: ["jni_headers"],
shared_libs: ["liblog"],
cflags: [
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp
index 3e043afe..662755d 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp
@@ -27,6 +27,7 @@
// All of the source files that we will compile.
srcs: ["native.cpp"],
+ header_libs: ["jni_headers"],
shared_libs: ["liblog"],
cflags: [
diff --git a/core/tests/packagemanagertests/Android.bp b/core/tests/packagemanagertests/Android.bp
new file mode 100644
index 0000000..6f39af8
--- /dev/null
+++ b/core/tests/packagemanagertests/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+ name: "FrameworksCorePackageManagerTests",
+ // We only want this apk build for tests.
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "mockito-target-minus-junit4",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
deleted file mode 100644
index 8c00d14..0000000
--- a/core/tests/packagemanagertests/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- frameworks-base-testutils \
- mockito-target-minus-junit4
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp
new file mode 100644
index 0000000..7f56992
--- /dev/null
+++ b/core/tests/privacytests/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+ name: "FrameworksPrivacyLibraryTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "junit",
+ "rappor-tests",
+ "androidx.test.rules",
+ "truth-prebuilt",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
deleted file mode 100644
index 7765977..0000000
--- a/core/tests/privacytests/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests androidx.test.rules truth-prebuilt
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
index ca655f1..4b43b41 100644
--- a/core/xsd/vts/Android.bp
+++ b/core/xsd/vts/Android.bp
@@ -40,3 +40,7 @@
],
test_config: "vts_permission_validate_test.xml",
}
+
+vts_config {
+ name: "VtsValidatePermission",
+}
diff --git a/core/xsd/vts/Android.mk b/core/xsd/vts/Android.mk
deleted file mode 100644
index a5754a4..0000000
--- a/core/xsd/vts/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsValidatePermission
-include test/vts/tools/build/Android.host_config.mk
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
new file mode 100644
index 0000000..3a3bea4
--- /dev/null
+++ b/data/fonts/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2011 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.
+
+prebuilt_font {
+ name: "DroidSansMono.ttf",
+ src: "DroidSansMono.ttf",
+ required: [
+ // Roboto-Regular.ttf provides DroidSans.ttf as a symlink to itself
+ "Roboto-Regular.ttf",
+ // Roboto-Bold.ttf provides DroidSans-Bold.ttf as a symlink to itself
+ "Roboto-Bold.ttf",
+ ],
+}
+
+prebuilt_font {
+ name: "AndroidClock.ttf",
+ src: "AndroidClock.ttf",
+}
+
+/////////////////////////////////
+// Copies the font configuration file into system/etc for the product as fonts.xml.
+// Additional fonts should be installed to /product/fonts/ alongside a corresponding
+// fonts_customiztion.xml in /product/etc/
+prebuilt_etc {
+ name: "fonts.xml",
+ src: "fonts.xml",
+}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 4226e08..a322b82 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -12,87 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# We have to use BUILD_PREBUILT instead of PRODUCT_COPY_FIES,
-# because MINIMAL_FONT_FOOTPRINT is only available in Android.mks.
-
LOCAL_PATH := $(call my-dir)
-##########################################
-# create symlink for given font
-# $(1): new font $(2): link target
-# should be used with eval: $(eval $(call ...))
-define create-font-symlink
-$(PRODUCT_OUT)/system/fonts/$(1) : $(PRODUCT_OUT)/system/fonts/$(2)
- @echo "Symlink: $$@ -> $$<"
- @mkdir -p $$(dir $$@)
- @rm -rf $$@
- $(hide) ln -sf $$(notdir $$<) $$@
-# this magic makes LOCAL_REQUIRED_MODULES work
-ALL_MODULES.$(1).INSTALLED := \
- $(ALL_MODULES.$(1).INSTALLED) $(PRODUCT_OUT)/system/fonts/$(1)
-endef
-
-##########################################
-# The following fonts are just symlinks, for backward compatibility.
-##########################################
-$(eval $(call create-font-symlink,DroidSans.ttf,Roboto-Regular.ttf))
-$(eval $(call create-font-symlink,DroidSans-Bold.ttf,Roboto-Bold.ttf))
-$(eval $(call create-font-symlink,DroidSerif-Regular.ttf,NotoSerif-Regular.ttf))
-$(eval $(call create-font-symlink,DroidSerif-Bold.ttf,NotoSerif-Bold.ttf))
-$(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf))
-$(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf))
-
-extra_font_files := \
- DroidSans.ttf \
- DroidSans-Bold.ttf
-
-################################
-# Use DroidSansMono to hang extra_font_files on
-include $(CLEAR_VARS)
-LOCAL_MODULE := DroidSansMono.ttf
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
-LOCAL_REQUIRED_MODULES := $(extra_font_files)
-include $(BUILD_PREBUILT)
-extra_font_files :=
-
-################################
-# Build the rest of font files as prebuilt.
-
-# $(1): The source file name in LOCAL_PATH.
-# It also serves as the module name and the dest file name.
-define build-one-font-module
-$(eval include $(CLEAR_VARS))\
-$(eval LOCAL_MODULE := $(1))\
-$(eval LOCAL_SRC_FILES := $(1))\
-$(eval LOCAL_MODULE_CLASS := ETC)\
-$(eval LOCAL_MODULE_TAGS := optional)\
-$(eval LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts)\
-$(eval include $(BUILD_PREBUILT))
-endef
-
-font_src_files := \
- AndroidClock.ttf
-
-$(foreach f, $(font_src_files), $(call build-one-font-module, $(f)))
-
-build-one-font-module :=
-font_src_files :=
-
-################################
-# Copies the font configuration file into system/etc for the product as fonts.xml.
-# Additional fonts should be installed to /product/fonts/ alongside a corresponding
-# fonts_customiztion.xml in /product/etc/
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fonts.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml
-
-include $(BUILD_PREBUILT)
-
# Run sanity tests on fonts on checkbuild
checkbuild: fontchain_lint
diff --git a/data/keyboards/Vendor_0f0d_Product_00c1.kl b/data/keyboards/Vendor_0f0d_Product_00c1.kl
new file mode 100644
index 0000000..c74512a
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_00c1.kl
@@ -0,0 +1,55 @@
+# 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.
+
+# Device name: HORI CO.,LTD. HORIPAD S
+# HORIPAD for Nintendo Switch, USB game controller
+# https://hori.co.uk/horipad-for-nintendo-switch/
+
+# Button labeled as "Y" but should really produce keycode "X"
+key 0x130 BUTTON_X
+# Button labeled as "B" but should really produce keycode "A"
+key 0x131 BUTTON_A
+# Button labeled as "A" but should really produce keycode "B"
+key 0x132 BUTTON_B
+# Button labeled as "X" but should really product keycode "Y"
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Minus
+key 0x138 BUTTON_SELECT
+# Plus
+key 0x139 BUTTON_START
+
+# Analog stick buttons
+key 0x13a BUTTON_THUMBL
+key 0x13b BUTTON_THUMBR
+
+# Home
+key 0x13c HOME
+# Capture
+key 0x13d BUTTON_MODE
+
+# Left analog stick
+axis 0x00 X
+axis 0x01 Y
+# Right analog stick
+axis 0x02 Z
+axis 0x05 RZ
+# D-pad
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 91aac83..11775ca 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -17,11 +17,10 @@
package android.security.keystore;
import android.annotation.Nullable;
+import android.os.Build;
import android.security.Credentials;
-import android.security.GateKeeper;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
-import android.security.KeyStoreException;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
@@ -52,6 +51,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -494,6 +494,20 @@
if (challenge != null) {
KeymasterArguments args = new KeymasterArguments();
args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge);
+
+ if (mSpec.isDevicePropertiesAttestationIncluded()) {
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND,
+ Build.BRAND.getBytes(StandardCharsets.UTF_8));
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE,
+ Build.DEVICE.getBytes(StandardCharsets.UTF_8));
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT,
+ Build.PRODUCT.getBytes(StandardCharsets.UTF_8));
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER,
+ Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8));
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL,
+ Build.MODEL.getBytes(StandardCharsets.UTF_8));
+ }
+
return getAttestationChain(privateKeyAlias, keyPair, args);
}
@@ -603,8 +617,14 @@
private Iterable<byte[]> getAttestationChain(String privateKeyAlias,
KeyPair keyPair, KeymasterArguments args)
throws ProviderException {
- KeymasterCertificateChain outChain = new KeymasterCertificateChain();
- int errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain);
+ final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
+ final int errorCode;
+ if (mSpec.isDevicePropertiesAttestationIncluded()
+ && mSpec.getAttestationChallenge() == null) {
+ throw new ProviderException("An attestation challenge must be provided when requesting "
+ + "device properties attestation.");
+ }
+ errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain);
if (errorCode != KeyStore.NO_ERROR) {
throw new ProviderException("Failed to generate attestation certificate chain",
KeyStore.getKeyStoreException(errorCode));
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 630a6dd..e7e410a 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -24,6 +24,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.os.Build;
import android.security.GateKeeper;
import android.security.KeyStore;
import android.text.TextUtils;
@@ -264,6 +265,7 @@
private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mUserPresenceRequired;
private final byte[] mAttestationChallenge;
+ private final boolean mDevicePropertiesAttestationIncluded;
private final boolean mUniqueIdIncluded;
private final boolean mUserAuthenticationValidWhileOnBody;
private final boolean mInvalidatedByBiometricEnrollment;
@@ -301,6 +303,7 @@
int userAuthenticationValidityDurationSeconds,
boolean userPresenceRequired,
byte[] attestationChallenge,
+ boolean devicePropertiesAttestationIncluded,
boolean uniqueIdIncluded,
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
@@ -350,6 +353,7 @@
mUserPresenceRequired = userPresenceRequired;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
+ mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded;
mUniqueIdIncluded = uniqueIdIncluded;
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
@@ -644,6 +648,21 @@
}
/**
+ * Returns {@code true} if attestation for the base device properties ({@link Build#BRAND},
+ * {@link Build#DEVICE}, {@link Build#MANUFACTURER}, {@link Build#MODEL}, {@link Build#PRODUCT})
+ * was requested to be added in the attestation certificate for the generated key.
+ *
+ * {@link javax.crypto.KeyGenerator#generateKey()} will throw
+ * {@link java.security.ProviderException} if device properties attestation fails or is not
+ * supported.
+ *
+ * @see Builder#setDevicePropertiesAttestationIncluded(boolean)
+ */
+ public boolean isDevicePropertiesAttestationIncluded() {
+ return mDevicePropertiesAttestationIncluded;
+ }
+
+ /**
* @hide This is a system-only API
*
* Returns {@code true} if the attestation certificate will contain a unique ID field.
@@ -734,6 +753,7 @@
private int mUserAuthenticationValidityDurationSeconds = -1;
private boolean mUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
+ private boolean mDevicePropertiesAttestationIncluded = false;
private boolean mUniqueIdIncluded = false;
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
@@ -797,6 +817,8 @@
sourceSpec.getUserAuthenticationValidityDurationSeconds();
mUserPresenceRequired = sourceSpec.isUserPresenceRequired();
mAttestationChallenge = sourceSpec.getAttestationChallenge();
+ mDevicePropertiesAttestationIncluded =
+ sourceSpec.isDevicePropertiesAttestationIncluded();
mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
@@ -1252,6 +1274,31 @@
}
/**
+ * Sets whether to include the base device properties in the attestation certificate.
+ *
+ * <p>If {@code attestationChallenge} is not {@code null}, the public key certificate for
+ * this key pair will contain an extension that describes the details of the key's
+ * configuration and authorizations, including the device properties values (brand, device,
+ * manufacturer, model, product). These should be the same as in ({@link Build#BRAND},
+ * {@link Build#DEVICE}, {@link Build#MANUFACTURER}, {@link Build#MODEL},
+ * {@link Build#PRODUCT}). The attestation certificate chain can
+ * be retrieved with {@link java.security.KeyStore#getCertificateChain(String)}.
+ *
+ * <p> If {@code attestationChallenge} is {@code null}, the public key certificate for
+ * this key pair will not contain the extension with the requested attested values.
+ *
+ * <p> {@link javax.crypto.KeyGenerator#generateKey()} will throw
+ * {@link java.security.ProviderException} if device properties attestation fails or is not
+ * supported.
+ */
+ @NonNull
+ public Builder setDevicePropertiesAttestationIncluded(
+ boolean devicePropertiesAttestationIncluded) {
+ mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded;
+ return this;
+ }
+
+ /**
* @hide Only system apps can use this method.
*
* Sets whether to include a temporary unique ID field in the attestation certificate.
@@ -1360,6 +1407,7 @@
mUserAuthenticationValidityDurationSeconds,
mUserPresenceRequired,
mAttestationChallenge,
+ mDevicePropertiesAttestationIncluded,
mUniqueIdIncluded,
mUserAuthenticationValidWhileOnBody,
mInvalidatedByBiometricEnrollment,
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index d8030fb..0518f45 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -16,8 +16,8 @@
package android.security.keystore;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import java.math.BigInteger;
import java.security.spec.AlgorithmParameterSpec;
@@ -99,6 +99,7 @@
out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
out.writeBoolean(mSpec.isUserPresenceRequired());
out.writeByteArray(mSpec.getAttestationChallenge());
+ out.writeBoolean(mSpec.isDevicePropertiesAttestationIncluded());
out.writeBoolean(mSpec.isUniqueIdIncluded());
out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
@@ -154,6 +155,7 @@
final int userAuthenticationValidityDurationSeconds = in.readInt();
final boolean userPresenceRequired = in.readBoolean();
final byte[] attestationChallenge = in.createByteArray();
+ final boolean devicePropertiesAttestationIncluded = in.readBoolean();
final boolean uniqueIdIncluded = in.readBoolean();
final boolean userAuthenticationValidWhileOnBody = in.readBoolean();
final boolean invalidatedByBiometricEnrollment = in.readBoolean();
@@ -185,6 +187,7 @@
userAuthenticationValidityDurationSeconds,
userPresenceRequired,
attestationChallenge,
+ devicePropertiesAttestationIncluded,
uniqueIdIncluded,
userAuthenticationValidWhileOnBody,
invalidatedByBiometricEnrollment,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 89d3cc4..fd4371c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -17,9 +17,11 @@
srcs: [
"PointerController.cpp",
"SpriteController.cpp",
+ "SpriteIcon.cpp",
],
shared_libs: [
+ "libbinder",
"libcutils",
"liblog",
"libutils",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index abf0837..5c2ef15 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -257,19 +257,24 @@
void PointerController::setPresentation(Presentation presentation) {
AutoMutex _l(mLock);
- if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources, mLocked.viewport.displayId);
+ if (mLocked.presentation == presentation) {
+ return;
}
- if (mLocked.presentation != presentation) {
- mLocked.presentation = presentation;
- mLocked.presentationChanged = true;
+ mLocked.presentation = presentation;
+ mLocked.presentationChanged = true;
- if (presentation != PRESENTATION_SPOT) {
- fadeOutAndReleaseAllSpotsLocked();
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
+
+ if (presentation == PRESENTATION_POINTER) {
+ if (mLocked.additionalMouseResources.empty()) {
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources,
+ mLocked.viewport.displayId);
}
-
+ fadeOutAndReleaseAllSpotsLocked();
updatePointerLocked();
}
}
@@ -291,6 +296,9 @@
#endif
AutoMutex _l(mLock);
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
std::vector<Spot*> newSpots;
std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
@@ -337,6 +345,9 @@
#endif
AutoMutex _l(mLock);
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
fadeOutAndReleaseAllSpotsLocked();
}
@@ -758,6 +769,10 @@
}
void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
+
mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 52305b8..ebc622b 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -100,6 +100,7 @@
virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
+ virtual void setDisplayViewport(const DisplayViewport& viewport);
virtual void setPresentation(Presentation presentation);
virtual void setSpots(const PointerCoords* spotCoords,
@@ -108,7 +109,6 @@
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..5f481ca6 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -23,13 +23,6 @@
#include <utils/String8.h>
#include <gui/Surface.h>
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-
-#include <android/native_window.h>
-
namespace android {
// --- SpriteController ---
@@ -132,8 +125,8 @@
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
- update.state.surfaceWidth = update.state.icon.bitmap.width();
- update.state.surfaceHeight = update.state.icon.bitmap.height();
+ update.state.surfaceWidth = update.state.icon.width();
+ update.state.surfaceHeight = update.state.icon.height();
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
update.state.surfaceControl = obtainSurface(
@@ -154,8 +147,8 @@
}
if (update.state.wantSurfaceVisible()) {
- int32_t desiredWidth = update.state.icon.bitmap.width();
- int32_t desiredHeight = update.state.icon.bitmap.height();
+ int32_t desiredWidth = update.state.icon.width();
+ int32_t desiredHeight = update.state.icon.height();
if (update.state.surfaceWidth < desiredWidth
|| update.state.surfaceHeight < desiredHeight) {
needApplyTransaction = true;
@@ -196,40 +189,9 @@
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
&& update.state.wantSurfaceVisible()) {
sp<Surface> surface = update.state.surfaceControl->getSurface();
- ANativeWindow_Buffer outBuffer;
- status_t status = surface->lock(&outBuffer, NULL);
- if (status) {
- ALOGE("Error %d locking sprite surface before drawing.", status);
- } else {
- SkBitmap surfaceBitmap;
- ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
- outBuffer.bits, bpr);
-
- SkCanvas surfaceCanvas(surfaceBitmap);
-
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
-
- if (outBuffer.width > update.state.icon.bitmap.width()) {
- paint.setColor(0); // transparent fill color
- surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0,
- outBuffer.width, update.state.icon.bitmap.height()), paint);
- }
- if (outBuffer.height > update.state.icon.bitmap.height()) {
- paint.setColor(0); // transparent fill color
- surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(),
- outBuffer.width, outBuffer.height), paint);
- }
-
- status = surface->unlockAndPost();
- if (status) {
- ALOGE("Error %d unlocking and posting sprite surface after drawing.", status);
- } else {
- update.state.surfaceDrawn = true;
- update.surfaceChanged = surfaceChanged = true;
- }
+ if (update.state.icon.draw(surface)) {
+ update.state.surfaceDrawn = true;
+ update.surfaceChanged = surfaceChanged = true;
}
}
}
@@ -245,7 +207,8 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
+ | DIRTY_ICON_STYLE))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -274,6 +237,21 @@
update.state.transformationMatrix.dtdy);
}
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible
+ || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+ Parcel p;
+ p.writeInt32(update.state.icon.style);
+ p.writeFloat(update.state.icon.hotSpotX);
+ p.writeFloat(update.state.icon.hotSpotY);
+
+ // Pass cursor metadata in the sprite surface so that when Android is running as a
+ // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
+ // update mouse cursor in the host OS.
+ t.setMetadata(
+ update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+ }
+
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +375,14 @@
} else {
dirty = DIRTY_BITMAP;
}
+
+ if (mLocked.state.icon.style != icon.style) {
+ mLocked.state.icon.style = icon.style;
+ dirty |= DIRTY_ICON_STYLE;
+ }
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
- dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..137b564 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -22,7 +22,7 @@
#include <gui/SurfaceComposerClient.h>
-#include <SkBitmap.h>
+#include "SpriteIcon.h"
namespace android {
@@ -52,38 +52,6 @@
};
/*
- * Icon that a sprite displays, including its hotspot.
- */
-struct SpriteIcon {
- inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
- inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
- bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
-
- SkBitmap bitmap;
- float hotSpotX;
- float hotSpotY;
-
- inline SpriteIcon copy() const {
- SkBitmap bitmapCopy;
- if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) {
- bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
- 0, 0);
- }
- return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
- }
-
- inline void reset() {
- bitmap.reset();
- hotSpotX = 0;
- hotSpotY = 0;
- }
-
- inline bool isValid() const {
- return !bitmap.isNull() && !bitmap.empty();
- }
-};
-
-/*
* A sprite is a simple graphical object that is displayed on-screen above other layers.
* The basic sprite class is an interface.
* The implementation is provided by the sprite controller.
@@ -149,15 +117,15 @@
SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
/* Creates a new sprite, initially invisible. */
- sp<Sprite> createSprite();
+ virtual sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
- void openTransaction();
- void closeTransaction();
+ virtual void openTransaction();
+ virtual void closeTransaction();
private:
enum {
@@ -174,13 +142,14 @@
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
DIRTY_DISPLAY_ID = 1 << 7,
+ DIRTY_ICON_STYLE = 1 << 8,
};
/* Describes the state of a sprite.
* This structure is designed so that it can be copied during updates so that
* surfaces can be resized and redrawn without blocking the client by holding a lock
* on the sprites for a long time.
- * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
+ * Note that the SpriteIcon holds a reference to a shared (and immutable) bitmap. */
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp
new file mode 100644
index 0000000..10cc458
--- /dev/null
+++ b/libs/input/SpriteIcon.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "SpriteIcon.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+
+#include <android/native_window.h>
+#include <log/log.h>
+
+namespace android {
+
+bool SpriteIcon::draw(sp<Surface> surface) const {
+ ANativeWindow_Buffer outBuffer;
+ status_t status = surface->lock(&outBuffer, NULL);
+ if (status) {
+ ALOGE("Error %d locking sprite surface before drawing.", status);
+ return false;
+ }
+
+ SkBitmap surfaceBitmap;
+ ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
+ surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+ outBuffer.bits, bpr);
+
+ SkCanvas surfaceCanvas(surfaceBitmap);
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ surfaceCanvas.drawBitmap(bitmap, 0, 0, &paint);
+
+ if (outBuffer.width > width()) {
+ paint.setColor(0); // transparent fill color
+ surfaceCanvas.drawRect(SkRect::MakeLTRB(width(), 0, outBuffer.width, height()), paint);
+ }
+ if (outBuffer.height > height()) {
+ paint.setColor(0); // transparent fill color
+ surfaceCanvas.drawRect(SkRect::MakeLTRB(0, height(), outBuffer.width, outBuffer.height),
+ paint);
+ }
+
+ status = surface->unlockAndPost();
+ if (status) {
+ ALOGE("Error %d unlocking and posting sprite surface after drawing.", status);
+ }
+ return !status;
+}
+
+} // namespace android
diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h
new file mode 100644
index 0000000..c535faf
--- /dev/null
+++ b/libs/input/SpriteIcon.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_SPRITE_ICON_H
+#define _UI_SPRITE_ICON_H
+
+#include <SkBitmap.h>
+#include <gui/Surface.h>
+
+namespace android {
+
+/*
+ * Icon that a sprite displays, including its hotspot.
+ */
+struct SpriteIcon {
+ inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
+ inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+ bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+
+ SkBitmap bitmap;
+ int32_t style;
+ float hotSpotX;
+ float hotSpotY;
+
+ inline SpriteIcon copy() const {
+ SkBitmap bitmapCopy;
+ if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) {
+ bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
+ 0, 0);
+ }
+ return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
+ }
+
+ inline void reset() {
+ bitmap.reset();
+ style = 0;
+ hotSpotX = 0;
+ hotSpotY = 0;
+ }
+
+ inline bool isValid() const { return !bitmap.isNull() && !bitmap.empty(); }
+
+ inline int32_t width() const { return bitmap.width(); }
+ inline int32_t height() const { return bitmap.height(); }
+
+ // Draw the bitmap onto the given surface. Returns true if it's successful, or false otherwise.
+ // Note it doesn't set any metadata to the surface.
+ bool draw(const sp<Surface> surface) const;
+};
+
+
+} // namespace android
+
+#endif // _UI_SPRITE_ICON_H
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..fe74c62
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libinputservice_test"
+ }
+ ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..e83b2a7
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,45 @@
+// 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.
+
+cc_test {
+ name: "libinputservice_test",
+ srcs: [
+ "PointerController_test.cpp",
+ ],
+ shared_libs: [
+ "libinputservice",
+ "libgui",
+ "libhwui",
+ "libutils",
+ ],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
+ header_libs: [
+ "libbase_headers",
+ "libinputflinger_headers",
+ ],
+ include_dirs: [
+ "frameworks/base/libs",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
new file mode 100644
index 0000000..a157426
--- /dev/null
+++ b/libs/input/tests/PointerController_test.cpp
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+#include "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+
+#include <atomic>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+enum TestCursorType {
+ CURSOR_TYPE_DEFAULT = 0,
+ CURSOR_TYPE_HOVER,
+ CURSOR_TYPE_TOUCH,
+ CURSOR_TYPE_ANCHOR,
+ CURSOR_TYPE_ADDITIONAL,
+ CURSOR_TYPE_ADDITIONAL_ANIM,
+ CURSOR_TYPE_CUSTOM = -1,
+};
+
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Test;
+
+std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
+ return std::make_pair(type * 10, type * 10 + 5);
+}
+
+class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
+public:
+ virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
+ virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
+ virtual int32_t getDefaultPointerIconId() override;
+ virtual int32_t getCustomPointerIconId() override;
+
+ bool allResourcesAreLoaded();
+ bool noResourcesAreLoaded();
+
+private:
+ void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+
+ bool pointerIconLoaded{false};
+ bool pointerResourcesLoaded{false};
+ bool additionalMouseResourcesLoaded{false};
+};
+
+void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
+ loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+ pointerIconLoaded = true;
+}
+
+void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
+ int32_t) {
+ loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
+ loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
+ loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+ pointerResourcesLoaded = true;
+}
+
+void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
+ std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources,
+ int32_t) {
+ SpriteIcon icon;
+ PointerAnimation anim;
+
+ // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
+ int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
+ loadPointerIconForType(&icon, cursorType);
+ (*outResources)[cursorType] = icon;
+
+ // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
+ cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
+ loadPointerIconForType(&icon, cursorType);
+ anim.animationFrames.push_back(icon);
+ anim.durationPerFrame = 10;
+ (*outResources)[cursorType] = icon;
+ (*outAnimationResources)[cursorType] = anim;
+
+ additionalMouseResourcesLoaded = true;
+}
+
+int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+ return CURSOR_TYPE_DEFAULT;
+}
+
+int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+ return CURSOR_TYPE_CUSTOM;
+}
+
+bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
+ return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
+}
+
+bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
+ return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
+}
+
+void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
+ icon->style = type;
+ std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
+ icon->hotSpotX = hotSpot.first;
+ icon->hotSpotY = hotSpot.second;
+}
+class PointerControllerTest : public Test {
+protected:
+ PointerControllerTest();
+ ~PointerControllerTest();
+
+ void ensureDisplayViewportIsSet();
+
+ sp<MockSprite> mPointerSprite;
+ sp<MockPointerControllerPolicyInterface> mPolicy;
+ sp<MockSpriteController> mSpriteController;
+ sp<PointerController> mPointerController;
+
+private:
+ void loopThread();
+
+ std::atomic<bool> mRunning = true;
+ class MyLooper : public Looper {
+ public:
+ MyLooper() : Looper(false) {}
+ ~MyLooper() = default;
+ };
+ sp<MyLooper> mLooper;
+ std::thread mThread;
+};
+
+PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
+ mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+
+ mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+ mPolicy = new MockPointerControllerPolicyInterface();
+
+ EXPECT_CALL(*mSpriteController, createSprite())
+ .WillOnce(Return(mPointerSprite));
+
+ mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+}
+
+PointerControllerTest::~PointerControllerTest() {
+ mRunning.store(false, std::memory_order_relaxed);
+ mThread.join();
+}
+
+void PointerControllerTest::ensureDisplayViewportIsSet() {
+ DisplayViewport viewport;
+ viewport.displayId = ADISPLAY_ID_DEFAULT;
+ viewport.logicalRight = 1600;
+ viewport.logicalBottom = 1200;
+ viewport.physicalRight = 800;
+ viewport.physicalBottom = 600;
+ viewport.deviceWidth = 400;
+ viewport.deviceHeight = 300;
+ mPointerController->setDisplayViewport(viewport);
+
+ // The first call to setDisplayViewport should trigger the loading of the necessary resources.
+ EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
+}
+
+void PointerControllerTest::loopThread() {
+ Looper::setForThread(mLooper);
+
+ while (mRunning.load(std::memory_order_relaxed)) {
+ mLooper->pollOnce(100);
+ }
+}
+
+TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+ ensureDisplayViewportIsSet();
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->reloadPointerResources();
+}
+
+TEST_F(PointerControllerTest, updatePointerIcon) {
+ ensureDisplayViewportIsSet();
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ int32_t type = CURSOR_TYPE_ADDITIONAL;
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, type),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->updatePointerIcon(type);
+}
+
+TEST_F(PointerControllerTest, setCustomPointerIcon) {
+ ensureDisplayViewportIsSet();
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ int32_t style = CURSOR_TYPE_CUSTOM;
+ float hotSpotX = 15;
+ float hotSpotY = 20;
+
+ SpriteIcon icon;
+ icon.style = style;
+ icon.hotSpotX = hotSpotX;
+ icon.hotSpotY = hotSpotY;
+
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, style),
+ Field(&SpriteIcon::hotSpotX, hotSpotX),
+ Field(&SpriteIcon::hotSpotY, hotSpotY))));
+ mPointerController->setCustomPointerIcon(icon);
+}
+
+TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
+ mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+ mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
+ mPointerController->clearSpots();
+ mPointerController->setPosition(1.0f, 1.0f);
+ mPointerController->move(1.0f, 1.0f);
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+
+ EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
+
+ ensureDisplayViewportIsSet();
+}
+
+} // namespace android
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
new file mode 100644
index 0000000..013b79c
--- /dev/null
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MOCK_SPRITE_H
+#define _MOCK_SPRITE_H
+
+#include <input/SpriteController.h>
+
+#include <gmock/gmock.h>
+
+namespace android {
+
+class MockSprite : public Sprite {
+public:
+ virtual ~MockSprite() = default;
+
+ MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
+ MOCK_METHOD(void, setVisible, (bool), (override));
+ MOCK_METHOD(void, setPosition, (float, float), (override));
+ MOCK_METHOD(void, setLayer, (int32_t), (override));
+ MOCK_METHOD(void, setAlpha, (float), (override));
+ MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
+ MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+};
+
+} // namespace android
+
+#endif // _MOCK_SPRITE_H
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
new file mode 100644
index 0000000..a034f66
--- /dev/null
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -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.
+ */
+
+#ifndef _MOCK_SPRITE_CONTROLLER_H
+#define _MOCK_SPRITE_CONTROLLER_H
+
+#include "MockSprite.h"
+
+#include <input/SpriteController.h>
+
+namespace android {
+
+class MockSpriteController : public SpriteController {
+
+public:
+ MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+ ~MockSpriteController() {}
+
+ MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
+ MOCK_METHOD(void, openTransaction, (), (override));
+ MOCK_METHOD(void, closeTransaction, (), (override));
+};
+
+} // namespace android
+
+#endif // _MOCK_SPRITE_CONTROLLER_H
diff --git a/lowpan/tests/Android.bp b/lowpan/tests/Android.bp
new file mode 100644
index 0000000..ad2bc27
--- /dev/null
+++ b/lowpan/tests/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+
+// Make test APK
+// ============================================================
+android_test {
+ name: "FrameworksLowpanApiTests",
+ srcs: ["**/*.java"],
+ // Filter all src files to just java files
+ jacoco: {
+ include_filter: ["android.net.lowpan.*"],
+ exclude_filter: [
+ "android.net.lowpan.LowpanInterfaceTest*",
+ "android.net.lowpan.LowpanManagerTest*",
+ ],
+ },
+ static_libs: [
+ "androidx.test.rules",
+ "guava",
+ "mockito-target-minus-junit4",
+ "frameworks-base-testutils",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ certificate: "platform",
+}
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
deleted file mode 100644
index 832ed2f..0000000
--- a/lowpan/tests/Android.mk
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Make test APK
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-# This list is generated from the java source files in this module
-# The list is a comma separated list of class names with * matching zero or more characters.
-# Example:
-# Input files: src/com/android/server/lowpan/Test.java src/com/android/server/lowpan/AnotherTest.java
-# Generated exclude list: com.android.server.lowpan.Test*,com.android.server.lowpan.AnotherTest*
-
-# Filter all src files to just java files
-local_java_files := $(filter %.java,$(LOCAL_SRC_FILES))
-# Transform java file names into full class names.
-# This only works if the class name matches the file name and the directory structure
-# matches the package.
-local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
-# Convert class name list to jacoco exclude list
-# This appends a * to all classes and replace the space separators with commas.
-# These patterns will match all classes in this module and their inner classes.
-jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
-
-jacoco_include := android.net.lowpan.*
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
-LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- guava \
- mockito-target-minus-junit4 \
- frameworks-base-testutils \
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.runner \
- android.test.base \
-
-LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_PACKAGE)
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index e783788..c0eb5e8 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -229,9 +229,16 @@
}
private void setParent(MtpObject parent) {
+ if (this.getStorageId() != parent.getStorageId()) {
+ mStorage = Preconditions.checkNotNull(parent.getStorage());
+ }
mParent = parent;
}
+ private MtpStorage getStorage() {
+ return mStorage;
+ }
+
private void setDir(boolean dir) {
if (dir != mIsDir) {
mIsDir = dir;
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 9f52d85..8f36dec 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -93,14 +93,6 @@
},
}
-// Header-only library used for atrace in platform NDK builds
-cc_library_headers {
- name: "libandroid_trace",
- host_supported: true,
- vendor_available: true,
- export_include_dirs: ["trace"],
-}
-
// Network library.
cc_library_shared {
name: "libandroid_net",
diff --git a/native/android/trace/android/trace.h b/native/android/trace/android/trace.h
deleted file mode 100644
index 4e569e5..0000000
--- a/native/android/trace/android/trace.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-#ifndef ANDROID_ATRACE_HEADERS_H
-#define ANDROID_ATRACE_HEADERS_H
-
-#include <cutils/trace.h>
-
-inline void ATrace_beginSection(const char* sectionName) {
- atrace_begin(ATRACE_TAG_APP, sectionName);
-}
-
-inline void ATrace_endSection() {
- atrace_end(ATRACE_TAG_APP);
-}
-
-#endif // ANDROID_ATRACE_HEADERS_H
diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp
index ea15b43..4fc6e8d 100644
--- a/packages/CtsShim/build/jni/Android.bp
+++ b/packages/CtsShim/build/jni/Android.bp
@@ -17,5 +17,6 @@
cc_library_shared {
name: "libshim_jni",
srcs: ["Shim.c"],
+ header_libs: ["jni_headers"],
sdk_version: "24",
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 21129f9..65f68cc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1352,9 +1352,6 @@
Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
GlobalSettingsProto.Sys.STORAGE_CACHE_MAX_BYTES);
dumpSetting(s, p,
- Settings.Global.SYS_VDSO,
- GlobalSettingsProto.Sys.VDSO);
- dumpSetting(s, p,
Settings.Global.SYS_UIDCPUPOWER,
GlobalSettingsProto.Sys.UIDCPUPOWER);
p.end(sysToken);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 24d0896..c1ec9b0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -299,22 +299,6 @@
android:excludeFromRecents="true"
android:exported="false" />
- <!--
- The following is used as a no-op/null home activity when
- no other MAIN/HOME activity is present (e.g., in CSI).
- -->
- <activity android:name=".NullHome"
- android:excludeFromRecents="true"
- android:label=""
- android:screenOrientation="nosensor">
- <!-- The priority here is set to be lower than that for Settings -->
- <intent-filter android:priority="-1100">
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
<receiver
android:name=".BugreportReceiver"
android:permission="android.permission.DUMP">
diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/packages/Shell/res/layout/null_home_finishing_boot.xml
deleted file mode 100644
index 5f9563a..0000000
--- a/packages/Shell/res/layout/null_home_finishing_boot.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?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
- -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#80000000"
- android:forceHasOverlappingRendering="false">
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="center"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="40sp"
- android:textColor="?android:attr/textColorPrimary"
- android:text="@*android:string/android_start_title"/>
- <ProgressBar
- style="@android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="12.75dp"
- android:colorControlActivated="?android:attr/textColorPrimary"
- android:indeterminate="true"/>
- </LinearLayout>
-</FrameLayout>
diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java
deleted file mode 100644
index bd97561..0000000
--- a/packages/Shell/src/com/android/shell/NullHome.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.shell;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-/**
- * This covers the fallback case where no launcher is available.
- * Usually Settings.apk has one fallback home activity.
- * Settings.apk, however, is not part of CSI, which needs to be
- * standalone (bootable and testable).
- */
-public class NullHome extends Activity {
- private static final String TAG = "NullHome";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.i(TAG, "onCreate");
- setContentView(R.layout.null_home_finishing_boot);
- }
-
- protected void onDestroy() {
- super.onDestroy();
- Log.i(TAG, "onDestroy");
- }
-}
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index c8cf7f50..304f81b 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -43,7 +43,7 @@
winsonc@google.com
#Android Auto
-stenning@google.com
+hseog@google.com
#Android TV
rgl@google.com
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index eb72b81..4116afc 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -25,7 +25,7 @@
],
static_libs: [
"androidx.annotation_annotation",
- "netd_aidl_interface-V3-java",
+ "netd_aidl_interface-unstable-java",
"netlink-client",
"networkstack-aidl-interfaces-java",
"android.hardware.tetheroffload.config-V1.0-java",
@@ -33,7 +33,7 @@
"net-utils-framework-common",
],
libs: [
- "framework-tethering",
+ "framework-tethering.impl",
"unsupportedappusage",
],
plugins: ["java_api_finder"],
@@ -96,7 +96,7 @@
"res",
],
libs: [
- "framework-tethering",
+ "framework-tethering.impl",
],
jarjar_rules: "jarjar-rules.txt",
optimize: {
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 7ac9d25..c8becce 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -13,69 +13,31 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// AIDL interfaces between the core system and the tethering mainline module.
-aidl_interface {
- name: "tethering-aidl-interfaces",
- unstable: true,
- local_include_dir: "src",
- include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
- srcs: [
- // @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause
- // compilation to fail (b/148001843).
- "src/android/net/IIntResultListener.aidl",
- "src/android/net/ITetheringConnector.aidl",
- "src/android/net/ITetheringEventCallback.aidl",
- "src/android/net/TetheringCallbackStartedParcel.aidl",
- "src/android/net/TetheringConfigurationParcel.aidl",
- "src/android/net/TetheringRequestParcel.aidl",
- "src/android/net/TetherStatesParcel.aidl",
- ],
- backend: {
- ndk: {
- enabled: false,
- },
- cpp: {
- enabled: false,
- },
- java: {
- apex_available: [
- "//apex_available:platform",
- "com.android.tethering",
- ],
- },
- },
-}
-
-java_library {
+java_sdk_library {
name: "framework-tethering",
- sdk_version: "module_current",
+ defaults: ["framework-module-defaults"],
+
+ // Allow access to the stubs from anywhere.
+ visibility: ["//visibility:public"],
+
+ // Restrict access to implementation library.
+ impl_library_visibility: [
+ "//visibility:override", // Ignore the visibility property.
+ "//frameworks/base/packages/Tethering:__subpackages__",
+ ],
+
srcs: [
- "src/android/net/TetheredClient.java",
- "src/android/net/TetheringManager.java",
- "src/android/net/TetheringConstants.java",
+ ":framework-tethering-srcs",
],
- static_libs: [
- "tethering-aidl-interfaces-java",
- ],
+
jarjar_rules: "jarjar-rules.txt",
installable: true,
- libs: [
- "framework-annotations-lib",
- ],
-
hostdex: true, // for hiddenapi check
- visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
permitted_packages: ["android.net"],
}
-stubs_defaults {
- name: "framework-tethering-stubs-defaults",
- srcs: [":framework-tethering-srcs"],
- dist: { dest: "framework-tethering.txt" },
-}
-
filegroup {
name: "framework-tethering-srcs",
srcs: [
@@ -93,56 +55,3 @@
],
path: "src"
}
-
-droidstubs {
- name: "framework-tethering-stubs-srcs-publicapi",
- defaults: [
- "framework-module-stubs-defaults-publicapi",
- "framework-tethering-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "framework-tethering-stubs-srcs-systemapi",
- defaults: [
- "framework-module-stubs-defaults-systemapi",
- "framework-tethering-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "framework-tethering-api-module_libs_api",
- defaults: [
- "framework-module-api-defaults-module_libs_api",
- "framework-tethering-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "framework-tethering-stubs-srcs-module_libs_api",
- defaults: [
- "framework-module-stubs-defaults-module_libs_api",
- "framework-tethering-stubs-defaults",
- ],
-}
-
-java_library {
- name: "framework-tethering-stubs-publicapi",
- srcs: [":framework-tethering-stubs-srcs-publicapi"],
- defaults: ["framework-module-stubs-lib-defaults-publicapi"],
- dist: { dest: "framework-tethering.jar" },
-}
-
-java_library {
- name: "framework-tethering-stubs-systemapi",
- srcs: [":framework-tethering-stubs-srcs-systemapi"],
- defaults: ["framework-module-stubs-lib-defaults-systemapi"],
- dist: { dest: "framework-tethering.jar" },
-}
-
-java_library {
- name: "framework-tethering-stubs-module_libs_api",
- srcs: [":framework-tethering-stubs-srcs-module_libs_api"],
- defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
- dist: { dest: "framework-tethering.jar" },
-}
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-lint-baseline.txt b/packages/Tethering/common/TetheringLib/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..af7ad52
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-lint-baseline.txt
@@ -0,0 +1,39 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+ Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+ Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+ Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+CallbackInterface: android.net.TetheringManager.StartTetheringCallback:
+ Callbacks must be abstract class instead of interface to enable extension in future API levels: StartTetheringCallback
+CallbackInterface: android.net.TetheringManager.TetheringEventCallback:
+ Callbacks must be abstract class instead of interface to enable extension in future API levels: TetheringEventCallback
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+ Managers must always be obtained from Context; no direct constructors
+
+
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
+ android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
+ android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+
+
+StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
+ Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
diff --git a/packages/Tethering/common/TetheringLib/api/system-lint-baseline.txt b/packages/Tethering/common/TetheringLib/api/system-lint-baseline.txt
new file mode 100644
index 0000000..f8d291c
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/system-lint-baseline.txt
@@ -0,0 +1,25 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+CallbackInterface: android.net.TetheringManager.StartTetheringCallback:
+ Callbacks must be abstract class instead of interface to enable extension in future API levels: StartTetheringCallback
+CallbackInterface: android.net.TetheringManager.TetheringEventCallback:
+ Callbacks must be abstract class instead of interface to enable extension in future API levels: TetheringEventCallback
+
+
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
+ android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
+ android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+
+
+StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
+ Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index 4f8ad8a..4710a30 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -173,6 +173,18 @@
return this;
}
+ /**
+ * Set whether the DHCP server should request a new prefix from IpServer when receiving
+ * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB
+ * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests.
+ *
+ * <p>If not set, the default value is false.
+ */
+ public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) {
+ this.changePrefixOnDecline = changePrefixOnDecline;
+ return this;
+ }
+
private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
int[] res = new int[addrs.size()];
int i = 0;
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index d993306..830f6a0 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -16,14 +16,13 @@
package android.net.ip;
-import static android.net.InetAddresses.parseNumericAddress;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
+import static android.net.util.PrefixUtils.asIpPrefix;
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
@@ -34,7 +33,6 @@
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.TetherOffloadRuleParcel;
import android.net.TetheredClient;
import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
@@ -50,6 +48,7 @@
import android.net.shared.RouteUtils;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
+import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -60,15 +59,18 @@
import android.util.SparseArray;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.tethering.BpfCoordinator;
+import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
+import com.android.networkstack.tethering.PrivateAddressCoordinator;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
-import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -106,18 +108,8 @@
private static final byte DOUG_ADAMS = (byte) 42;
- private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
- private static final int USB_PREFIX_LENGTH = 24;
- private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
- private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
- private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
- private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
- private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
- private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24;
-
// TODO: have PanService use some visible version of this constant
- private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
- private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
+ private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24";
// TODO: have this configurable
private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -156,6 +148,14 @@
* Notify that the DHCP leases changed in one of the IpServers.
*/
public void dhcpLeasesChanged() { }
+
+ /**
+ * Request Tethering change.
+ *
+ * @param tetheringType the downstream type of this IpServer.
+ * @param enabled enable or disable tethering.
+ */
+ public void requestEnableTethering(int tetheringType, boolean enabled) { }
}
/** Capture IpServer dependencies, for injection. */
@@ -185,6 +185,7 @@
return 0;
}
}
+
/** Create a DhcpServer instance to be used by IpServer. */
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb);
@@ -212,16 +213,24 @@
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
// new neighbor cache entry on our interface
public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
+ // request from DHCP server that it wants to have a new prefix
+ public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12;
+ // request from PrivateAddressCoordinator to restart tethering.
+ public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13;
private final State mInitialState;
private final State mLocalHotspotState;
private final State mTetheredState;
private final State mUnavailableState;
+ private final State mWaitingForRestartState;
private final SharedLog mLog;
private final INetd mNetd;
+ @NonNull
+ private final BpfCoordinator mBpfCoordinator;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
+ private final PrivateAddressCoordinator mPrivateAddressCoordinator;
private final String mIfaceName;
private final int mInterfaceType;
@@ -248,7 +257,6 @@
private int mDhcpServerStartIndex = 0;
private IDhcpServer mDhcpServer;
private RaParams mLastRaParams;
- private LinkAddress mIpv4Address;
private LinkAddress mStaticIpv4ServerAddr;
private LinkAddress mStaticIpv4ClientAddr;
@@ -264,54 +272,24 @@
}
}
- static class Ipv6ForwardingRule {
- public final int upstreamIfindex;
- public final int downstreamIfindex;
- public final Inet6Address address;
- public final MacAddress srcMac;
- public final MacAddress dstMac;
-
- Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
- MacAddress srcMac, MacAddress dstMac) {
- this.upstreamIfindex = upstreamIfindex;
- this.downstreamIfindex = downstreamIfIndex;
- this.address = address;
- this.srcMac = srcMac;
- this.dstMac = dstMac;
- }
-
- public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
- return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
- dstMac);
- }
-
- // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
- // would be error-prone due to generated stable AIDL classes not having a copy constructor.
- public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
- final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
- parcel.inputInterfaceIndex = upstreamIfindex;
- parcel.outputInterfaceIndex = downstreamIfindex;
- parcel.destination = address.getAddress();
- parcel.prefixLength = 128;
- parcel.srcL2Address = srcMac.toByteArray();
- parcel.dstL2Address = dstMac.toByteArray();
- return parcel;
- }
- }
private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
new LinkedHashMap<>();
private final IpNeighborMonitor mIpNeighborMonitor;
+ private LinkAddress mIpv4Address;
+
// TODO: Add a dependency object to pass the data members or variables from the tethering
// object. It helps to reduce the arguments of the constructor.
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
- Dependencies deps) {
+ INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
+ boolean usingLegacyDhcp, boolean usingBpfOffload,
+ PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
+ mBpfCoordinator = coordinator;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
@@ -319,6 +297,7 @@
mLinkProperties = new LinkProperties();
mUsingLegacyDhcp = usingLegacyDhcp;
mUsingBpfOffload = usingBpfOffload;
+ mPrivateAddressCoordinator = addressCoordinator;
mDeps = deps;
resetLinkProperties();
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -339,9 +318,11 @@
mLocalHotspotState = new LocalHotspotState();
mTetheredState = new TetheredState();
mUnavailableState = new UnavailableState();
+ mWaitingForRestartState = new WaitingForRestartState();
addState(mInitialState);
addState(mLocalHotspotState);
addState(mTetheredState);
+ addState(mWaitingForRestartState, mTetheredState);
addState(mUnavailableState);
setInitialState(mInitialState);
@@ -374,6 +355,11 @@
return new LinkProperties(mLinkProperties);
}
+ /** The address which IpServer is using. */
+ public LinkAddress getAddress() {
+ return mIpv4Address;
+ }
+
/**
* Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
* thread.
@@ -462,7 +448,7 @@
handleError();
}
}
- }, new DhcpLeaseCallback());
+ }, new DhcpEventCallback());
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -475,7 +461,7 @@
}
}
- private class DhcpLeaseCallback extends IDhcpEventCallbacks.Stub {
+ private class DhcpEventCallback extends IDhcpEventCallbacks.Stub {
@Override
public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
final ArrayList<TetheredClient> leases = new ArrayList<>();
@@ -509,8 +495,9 @@
}
@Override
- public void onNewPrefixRequest(IpPrefix currentPrefix) {
- //TODO: add specific implementation.
+ public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
+ Objects.requireNonNull(currentPrefix);
+ sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix);
}
@Override
@@ -524,26 +511,38 @@
}
}
+ private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) {
+ Objects.requireNonNull(ipv4Address);
+ return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST);
+ }
+
+ private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter,
+ @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr,
+ @Nullable Inet4Address clientAddr) {
+ final boolean changePrefixOnDecline =
+ (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null);
+ return new DhcpServingParamsParcelExt()
+ .setDefaultRouters(defaultRouter)
+ .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
+ .setDnsServers(dnsServer)
+ .setServerAddr(serverAddr)
+ .setMetered(true)
+ .setSingleClientAddr(clientAddr)
+ .setChangePrefixOnDecline(changePrefixOnDecline);
+ // TODO: also advertise link MTU
+ }
+
private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
if (mUsingLegacyDhcp) {
return true;
}
final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
- final int prefixLen = serverLinkAddr.getPrefixLength();
final Inet4Address clientAddr = clientLinkAddr == null ? null :
(Inet4Address) clientLinkAddr.getAddress();
- final DhcpServingParamsParcel params;
- params = new DhcpServingParamsParcelExt()
- .setDefaultRouters(addr)
- .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
- .setDnsServers(addr)
- .setServerAddr(serverLinkAddr)
- .setMetered(true)
- .setSingleClientAddr(clientAddr);
- // TODO: also advertise link MTU
-
+ final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */,
+ addr /* dnsServer */, serverLinkAddr, clientAddr);
mDhcpServerStartIndex++;
mDeps.makeDhcpServer(
mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
@@ -570,7 +569,7 @@
});
mDhcpServer = null;
} catch (RemoteException e) {
- mLog.e("Error stopping DHCP", e);
+ mLog.e("Error stopping DHCP server", e);
// Not much more we can do here
}
}
@@ -591,6 +590,7 @@
// NOTE: All of configureIPv4() will be refactored out of existence
// into calls to InterfaceController, shared with startIPv4().
mInterfaceCtrl.clearIPv4Address();
+ mPrivateAddressCoordinator.releaseDownstream(this);
mIpv4Address = null;
mStaticIpv4ServerAddr = null;
mStaticIpv4ClientAddr = null;
@@ -599,43 +599,24 @@
private boolean configureIPv4(boolean enabled) {
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
- // TODO: Replace this hard-coded information with dynamically selected
- // config passed down to us by a higher layer IP-coordinating element.
- final Inet4Address srvAddr;
- int prefixLen = 0;
- try {
- if (mStaticIpv4ServerAddr != null) {
- srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress();
- prefixLen = mStaticIpv4ServerAddr.getPrefixLength();
- } else if (mInterfaceType == TetheringManager.TETHERING_USB
- || mInterfaceType == TetheringManager.TETHERING_NCM) {
- srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
- prefixLen = USB_PREFIX_LENGTH;
- } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
- srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address());
- prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
- } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
- srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR);
- prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
- } else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) {
- // TODO: randomize address for tethering too, similarly to wifi
- srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR);
- prefixLen = ETHERNET_IFACE_PREFIX_LENGTH;
- } else {
- // BT configures the interface elsewhere: only start DHCP.
- // TODO: make all tethering types behave the same way, and delete the bluetooth
- // code that calls into NetworkManagementService directly.
- srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
- mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
- return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
- }
- mIpv4Address = new LinkAddress(srvAddr, prefixLen);
- } catch (IllegalArgumentException e) {
- mLog.e("Error selecting ipv4 address", e);
- if (!enabled) stopDhcp();
+ if (enabled) {
+ mIpv4Address = requestIpv4Address();
+ }
+
+ if (mIpv4Address == null) {
+ mLog.e("No available ipv4 address");
return false;
}
+ if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
+ // BT configures the interface elsewhere: only start DHCP.
+ // TODO: make all tethering types behave the same way, and delete the bluetooth
+ // code that calls into NetworkManagementService directly.
+ return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
+ }
+
+ final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);
+
final Boolean setIfaceUp;
if (mInterfaceType == TetheringManager.TETHERING_WIFI
|| mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
@@ -652,29 +633,24 @@
return false;
}
- // Directly-connected route.
- final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
- mIpv4Address.getPrefixLength());
- final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST);
if (enabled) {
mLinkProperties.addLinkAddress(mIpv4Address);
- mLinkProperties.addRoute(route);
+ mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address));
} else {
mLinkProperties.removeLinkAddress(mIpv4Address);
- mLinkProperties.removeRoute(route);
+ mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address));
}
-
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
}
- private String getRandomWifiIPv4Address() {
- try {
- byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress();
- bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
- return InetAddress.getByAddress(bytes).getHostAddress();
- } catch (Exception e) {
- return WIFI_HOST_IFACE_ADDR;
+ private LinkAddress requestIpv4Address() {
+ if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
+
+ if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
+ return new LinkAddress(BLUETOOTH_IFACE_ADDR);
}
+
+ return mPrivateAddressCoordinator.requestDownstreamAddress(this);
}
private boolean startIPv6() {
@@ -710,7 +686,7 @@
//
// TODO: Evaluate using a data structure than is more directly suited to
// communicating only the relevant information.
- private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
+ private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
if (mRaDaemon == null) return;
// Avoid unnecessary work on spurious updates.
@@ -725,15 +701,10 @@
final String upstreamIface = v6only.getInterfaceName();
params = new RaParams();
- // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest
- // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering
- // offload happy. This hack should be reverted once we have the kernel fixed up.
- // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
- // see RouterAdvertisementDaemon.java putMtu()
- params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
+ params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
- if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
+ if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -749,6 +720,14 @@
}
upstreamIfindex = mDeps.getIfindex(upstreamIface);
+
+ // Add upstream index to name mapping for the tether stats usage in the coordinator.
+ // Although this mapping could be added by both class Tethering and IpServer, adding
+ // mapping from IpServer guarantees that the mapping is added before the adding
+ // forwarding rules. That is because there are different state machines in both
+ // classes. It is hard to guarantee the link property update order between multiple
+ // state machines.
+ mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface);
}
// If v6only is null, we pass in null to setRaParams(), which handles
@@ -761,21 +740,43 @@
mLastIPv6UpstreamIfindex = upstreamIfindex;
}
+ private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
+ final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
+ mNetd, toBeRemoved);
+ if (removalFailures > 0) {
+ mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
+ removalFailures));
+ }
+
+ for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
+ }
+
+ private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
+ try {
+ // It's safe to call networkAddInterface() even if
+ // the interface is already in the local_network.
+ mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
+ try {
+ // Add routes from local network. Note that adding routes that
+ // already exist does not cause an error (EEXIST is silently ignored).
+ RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
+ } catch (IllegalStateException e) {
+ mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
+ return;
+ }
+ } catch (ServiceSpecificException | RemoteException e) {
+ mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
+ return;
+ }
+
+ for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
+ }
+
private void configureLocalIPv6Routes(
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
// [1] Remove the routes that are deprecated.
if (!deprecatedPrefixes.isEmpty()) {
- final ArrayList<RouteInfo> toBeRemoved =
- getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
- // Remove routes from local network.
- final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
- mNetd, toBeRemoved);
- if (removalFailures > 0) {
- mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
- removalFailures));
- }
-
- for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
+ removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes));
}
// [2] Add only the routes that have not previously been added.
@@ -786,24 +787,7 @@
}
if (!addedPrefixes.isEmpty()) {
- final ArrayList<RouteInfo> toBeAdded =
- getLocalRoutesFor(mIfaceName, addedPrefixes);
- try {
- // It's safe to call networkAddInterface() even if
- // the interface is already in the local_network.
- mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
- try {
- // Add routes from local network. Note that adding routes that
- // already exist does not cause an error (EEXIST is silently ignored).
- RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
- } catch (IllegalStateException e) {
- mLog.e("Failed to add IPv6 routes to local table: " + e);
- }
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
- }
-
- for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
+ addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes));
}
}
}
@@ -945,12 +929,72 @@
}
}
- private byte getHopLimit(String upstreamIface) {
+ private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
+ if (!currentPrefix.contains(mIpv4Address.getAddress())
+ || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
+ Log.e(TAG, "Invalid prefix: " + currentPrefix);
+ return;
+ }
+
+ final LinkAddress deprecatedLinkAddress = mIpv4Address;
+ mIpv4Address = requestIpv4Address();
+ if (mIpv4Address == null) {
+ mLog.e("Fail to request a new downstream prefix");
+ return;
+ }
+ final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
+
+ // Add new IPv4 address on the interface.
+ if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
+ mLog.e("Failed to add new IP " + srvAddr);
+ return;
+ }
+
+ // Remove deprecated routes from local network.
+ removeRoutesFromLocalNetwork(
+ Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
+ mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
+
+ // Add new routes to local network.
+ addRoutesToLocalNetwork(
+ Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
+ mLinkProperties.addLinkAddress(mIpv4Address);
+
+ // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
+ // listen on the interface configured with new IPv4 address, that results DNS validation
+ // failure of downstream client even if appropriate routes have been configured.
+ try {
+ mNetd.tetherApplyDnsInterfaces();
+ } catch (ServiceSpecificException | RemoteException e) {
+ mLog.e("Failed to update local DNS caching server");
+ return;
+ }
+ sendLinkProperties();
+
+ // Notify DHCP server that new prefix/route has been applied on IpServer.
+ final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
+ (Inet4Address) mStaticIpv4ClientAddr.getAddress();
+ final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
+ srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
+ try {
+ mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int statusCode) {
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error updating DHCP serving params: " + statusCode);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ mLog.e("Error updating DHCP serving params", e);
+ }
+ }
+
+ private byte getHopLimit(String upstreamIface, int adjustTTL) {
try {
int upstreamHopLimit = Integer.parseUnsignedInt(
mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
- // Add one hop to account for this forwarding device
- upstreamHopLimit++;
+ upstreamHopLimit = upstreamHopLimit + adjustTTL;
// Cap the hop limit to 255.
return (byte) Integer.min(upstreamHopLimit, 255);
} catch (Exception e) {
@@ -1038,7 +1082,7 @@
transitionTo(mUnavailableState);
break;
case CMD_IPV6_TETHER_UPDATE:
- updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+ updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
break;
default:
return NOT_HANDLED;
@@ -1056,11 +1100,9 @@
}
try {
- final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
- mIpv4Address.getPrefixLength());
- NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix);
+ NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
- mLog.e("Error Tethering: " + e);
+ mLog.e("Error Tethering", e);
mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
return;
}
@@ -1104,7 +1146,7 @@
if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
break;
case CMD_IPV6_TETHER_UPDATE:
- updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+ updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
sendLinkProperties();
break;
case CMD_IP_FORWARDING_ENABLE_ERROR:
@@ -1115,6 +1157,14 @@
mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
transitionTo(mInitialState);
break;
+ case CMD_NEW_PREFIX_REQUEST:
+ handleNewPrefixRequest((IpPrefix) message.obj);
+ break;
+ case CMD_NOTIFY_PREFIX_CONFLICT:
+ mLog.i("restart tethering: " + mInterfaceType);
+ mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
+ transitionTo(mWaitingForRestartState);
+ break;
default:
return false;
}
@@ -1296,6 +1346,28 @@
}
}
+ class WaitingForRestartState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ logMessage(this, message.what);
+ switch (message.what) {
+ case CMD_TETHER_UNREQUESTED:
+ transitionTo(mInitialState);
+ mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
+ mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
+ break;
+ case CMD_INTERFACE_DOWN:
+ transitionTo(mUnavailableState);
+ mLog.i("Untethered (interface down) and restarting" + mIfaceName);
+ mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ }
+
// Accumulate routes representing "prefixes to be assigned to the local
// interface", for subsequent modification of local_network routing.
private static ArrayList<RouteInfo> getLocalRoutesFor(
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index dd67ddd..b17b4ba 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -15,19 +15,94 @@
*/
package android.net.util;
+import android.net.TetherStatsParcel;
import android.net.TetheringRequestParcel;
+import androidx.annotation.NonNull;
+
import java.io.FileDescriptor;
import java.net.SocketException;
import java.util.Objects;
/**
- * Native methods for tethering utilization.
+ * The classes and the methods for tethering utilization.
*
* {@hide}
*/
public class TetheringUtils {
/**
+ * The object which records offload Tx/Rx forwarded bytes/packets.
+ * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
+ * this class as well.
+ */
+ public static class ForwardedStats {
+ public final long rxBytes;
+ public final long rxPackets;
+ public final long txBytes;
+ public final long txPackets;
+
+ public ForwardedStats() {
+ rxBytes = 0;
+ rxPackets = 0;
+ txBytes = 0;
+ txPackets = 0;
+ }
+
+ public ForwardedStats(long rxBytes, long txBytes) {
+ this.rxBytes = rxBytes;
+ this.rxPackets = 0;
+ this.txBytes = txBytes;
+ this.txPackets = 0;
+ }
+
+ public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ }
+
+ public ForwardedStats(@NonNull TetherStatsParcel tetherStats) {
+ rxBytes = tetherStats.rxBytes;
+ rxPackets = tetherStats.rxPackets;
+ txBytes = tetherStats.txBytes;
+ txPackets = tetherStats.txPackets;
+ }
+
+ public ForwardedStats(@NonNull ForwardedStats other) {
+ rxBytes = other.rxBytes;
+ rxPackets = other.rxPackets;
+ txBytes = other.txBytes;
+ txPackets = other.txPackets;
+ }
+
+ /** Add Tx/Rx bytes/packets and return the result as a new object. */
+ @NonNull
+ public ForwardedStats add(@NonNull ForwardedStats other) {
+ return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets,
+ txBytes + other.txBytes, txPackets + other.txPackets);
+ }
+
+ /** Subtract Tx/Rx bytes/packets and return the result as a new object. */
+ @NonNull
+ public ForwardedStats subtract(@NonNull ForwardedStats other) {
+ // TODO: Perhaps throw an exception if any negative difference value just in case.
+ final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0);
+ final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0);
+ final long txBytesDiff = Math.max(txBytes - other.txBytes, 0);
+ final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0);
+ return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff);
+ }
+
+ /** Returns the string representation of this object. */
+ @NonNull
+ public String toString() {
+ return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes,
+ rxPackets, txBytes, txPackets);
+ }
+ }
+
+ /**
* Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
* @param fd the socket's {@link FileDescriptor}.
* @param ifIndex the interface index.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
new file mode 100644
index 0000000..6b854f2
--- /dev/null
+++ b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -0,0 +1,329 @@
+/*
+ * 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;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
+
+import android.app.usage.NetworkStatsManager;
+import android.net.INetd;
+import android.net.MacAddress;
+import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
+import android.net.TetherOffloadRuleParcel;
+import android.net.TetherStatsParcel;
+import android.net.netstats.provider.NetworkStatsProvider;
+import android.net.util.SharedLog;
+import android.net.util.TetheringUtils.ForwardedStats;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.Inet6Address;
+
+/**
+ * This coordinator is responsible for providing BPF offload relevant functionality.
+ * - Get tethering stats.
+ *
+ * @hide
+ */
+public class BpfCoordinator {
+ private static final String TAG = BpfCoordinator.class.getSimpleName();
+ @VisibleForTesting
+ static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable.
+
+ @VisibleForTesting
+ enum StatsType {
+ STATS_PER_IFACE,
+ STATS_PER_UID,
+ }
+
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final INetd mNetd;
+ @NonNull
+ private final SharedLog mLog;
+ @NonNull
+ private final Dependencies mDeps;
+ @Nullable
+ private final BpfTetherStatsProvider mStatsProvider;
+ private boolean mStarted = false;
+
+ // Maps upstream interface index to offloaded traffic statistics.
+ // Always contains the latest total bytes/packets, since each upstream was started, received
+ // from the BPF maps for each interface.
+ private SparseArray<ForwardedStats> mStats = new SparseArray<>();
+
+ // Maps upstream interface index to interface names.
+ // Store all interface name since boot. Used for lookup what interface name it is from the
+ // tether stats got from netd because netd reports interface index to present an interface.
+ // TODO: Remove the unused interface name.
+ private SparseArray<String> mInterfaceNames = new SparseArray<>();
+
+ // Runnable that used by scheduling next polling of stats.
+ private final Runnable mScheduledPollingTask = () -> {
+ updateForwardedStatsFromNetd();
+ maybeSchedulePollingStats();
+ };
+
+ @VisibleForTesting
+ static class Dependencies {
+ int getPerformPollInterval() {
+ // TODO: Consider make this configurable.
+ return DEFAULT_PERFORM_POLL_INTERVAL_MS;
+ }
+ }
+
+ BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd,
+ @NonNull NetworkStatsManager nsm, @NonNull SharedLog log, @NonNull Dependencies deps) {
+ mHandler = handler;
+ mNetd = netd;
+ mLog = log.forSubComponent(TAG);
+ BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
+ try {
+ nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
+ } catch (RuntimeException e) {
+ // TODO: Perhaps not allow to use BPF offload because the reregistration failure
+ // implied that no data limit could be applies on a metered upstream if any.
+ Log.wtf(TAG, "Cannot register offload stats provider: " + e);
+ provider = null;
+ }
+ mStatsProvider = provider;
+ mDeps = deps;
+ }
+
+ /**
+ * Start BPF tethering offload stats polling when the first upstream is started.
+ * Note that this can be only called on handler thread.
+ * TODO: Perhaps check BPF support before starting.
+ * TODO: Start the stats polling only if there is any client on the downstream.
+ */
+ public void start() {
+ if (mStarted) return;
+
+ mStarted = true;
+ maybeSchedulePollingStats();
+
+ mLog.i("BPF tethering coordinator started");
+ }
+
+ /**
+ * Stop BPF tethering offload stats polling and cleanup upstream parameters.
+ * Note that this can be only called on handler thread.
+ */
+ public void stop() {
+ if (!mStarted) return;
+
+ // Stop scheduled polling tasks and poll the latest stats from BPF maps.
+ if (mHandler.hasCallbacks(mScheduledPollingTask)) {
+ mHandler.removeCallbacks(mScheduledPollingTask);
+ }
+ updateForwardedStatsFromNetd();
+
+ mStarted = false;
+
+ mLog.i("BPF tethering coordinator stopped");
+ }
+
+ /**
+ * Add upstream name to lookup table. The lookup table is used for tether stats interface name
+ * lookup because the netd only reports interface index in BPF tether stats but the service
+ * expects the interface name in NetworkStats object.
+ * Note that this can be only called on handler thread.
+ */
+ public void addUpstreamNameToLookupTable(int upstreamIfindex, String upstreamIface) {
+ if (upstreamIfindex == 0) return;
+
+ // The same interface index to name mapping may be added by different IpServer objects or
+ // re-added by reconnection on the same upstream interface. Ignore the duplicate one.
+ final String iface = mInterfaceNames.get(upstreamIfindex);
+ if (iface == null) {
+ mInterfaceNames.put(upstreamIfindex, upstreamIface);
+ } else if (iface != upstreamIface) {
+ Log.wtf(TAG, "The upstream interface name " + upstreamIface
+ + " is different from the existing interface name "
+ + iface + " for index " + upstreamIfindex);
+ }
+ }
+
+ /** IPv6 forwarding rule class. */
+ public static class Ipv6ForwardingRule {
+ public final int upstreamIfindex;
+ public final int downstreamIfindex;
+ public final Inet6Address address;
+ public final MacAddress srcMac;
+ public final MacAddress dstMac;
+
+ public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
+ MacAddress srcMac, MacAddress dstMac) {
+ this.upstreamIfindex = upstreamIfindex;
+ this.downstreamIfindex = downstreamIfIndex;
+ this.address = address;
+ this.srcMac = srcMac;
+ this.dstMac = dstMac;
+ }
+
+ /** Return a new rule object which updates with new upstream index. */
+ public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
+ return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
+ dstMac);
+ }
+
+ /**
+ * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
+ * would be error-prone due to generated stable AIDL classes not having a copy constructor.
+ */
+ public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
+ final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
+ parcel.inputInterfaceIndex = upstreamIfindex;
+ parcel.outputInterfaceIndex = downstreamIfindex;
+ parcel.destination = address.getAddress();
+ parcel.prefixLength = 128;
+ parcel.srcL2Address = srcMac.toByteArray();
+ parcel.dstL2Address = dstMac.toByteArray();
+ return parcel;
+ }
+ }
+
+ /**
+ * A BPF tethering stats provider to provide network statistics to the system.
+ * Note that this class's data may only be accessed on the handler thread.
+ */
+ @VisibleForTesting
+ class BpfTetherStatsProvider extends NetworkStatsProvider {
+ // The offloaded traffic statistics per interface that has not been reported since the
+ // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams
+ // and has pending tether stats delta are included in this NetworkStats object.
+ private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+
+ // The same stats as above, but counts network stats per uid.
+ private NetworkStats mUidStats = new NetworkStats(0L, 0);
+
+ @Override
+ public void onRequestStatsUpdate(int token) {
+ mHandler.post(() -> pushTetherStats());
+ }
+
+ @Override
+ public void onSetAlert(long quotaBytes) {
+ // no-op
+ }
+
+ @Override
+ public void onSetLimit(@NonNull String iface, long quotaBytes) {
+ // no-op
+ }
+
+ @VisibleForTesting
+ void pushTetherStats() {
+ try {
+ // The token is not used for now. See b/153606961.
+ notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats);
+
+ // Clear the accumulated tether stats delta after reported. Note that create a new
+ // empty object because NetworkStats#clear is @hide.
+ mIfaceStats = new NetworkStats(0L, 0);
+ mUidStats = new NetworkStats(0L, 0);
+ } catch (RuntimeException e) {
+ mLog.e("Cannot report network stats: ", e);
+ }
+ }
+
+ private void accumulateDiff(@NonNull NetworkStats ifaceDiff,
+ @NonNull NetworkStats uidDiff) {
+ mIfaceStats = mIfaceStats.add(ifaceDiff);
+ mUidStats = mUidStats.add(uidDiff);
+ }
+ }
+
+ @NonNull
+ private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
+ @NonNull ForwardedStats diff) {
+ NetworkStats stats = new NetworkStats(0L, 0);
+ final String iface = mInterfaceNames.get(ifIndex);
+ if (iface == null) {
+ // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd.
+ // For now, netd may add the empty stats for the upstream which is not monitored by
+ // the coordinator. Silently ignore it.
+ return stats;
+ }
+ final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
+ // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for
+ // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked.
+ return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets,
+ diff.txBytes, diff.txPackets, 0L /* operations */));
+ }
+
+ private void updateForwardedStatsFromNetd() {
+ final TetherStatsParcel[] tetherStatsList;
+ try {
+ // The reported tether stats are total data usage for all currently-active upstream
+ // interfaces since tethering start.
+ tetherStatsList = mNetd.tetherOffloadGetStats();
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Problem fetching tethering stats: ", e);
+ return;
+ }
+
+ for (TetherStatsParcel tetherStats : tetherStatsList) {
+ final Integer ifIndex = tetherStats.ifIndex;
+ final ForwardedStats curr = new ForwardedStats(tetherStats);
+ final ForwardedStats base = mStats.get(ifIndex);
+ final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
+
+ // Update the local cache for counting tether stats delta.
+ mStats.put(ifIndex, curr);
+
+ // Update the accumulated tether stats delta to the stats provider for the service
+ // querying.
+ if (mStatsProvider != null) {
+ try {
+ mStatsProvider.accumulateDiff(
+ buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff),
+ buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Log.wtf("Fail to update the accumulated stats delta for interface index "
+ + ifIndex + " : ", e);
+ }
+ }
+ }
+ }
+
+ private void maybeSchedulePollingStats() {
+ if (!mStarted) return;
+
+ if (mHandler.hasCallbacks(mScheduledPollingTask)) {
+ mHandler.removeCallbacks(mScheduledPollingTask);
+ }
+
+ mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval());
+ }
+}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
index d450c46..f3dcaa2 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
@@ -161,11 +161,28 @@
private void updateIPv6TetheringInterfaces() {
for (IpServer ipServer : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
- ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+ ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp);
break;
}
}
+ private int getTtlAdjustment() {
+ if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) {
+ return 0;
+ }
+
+ // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1
+ // for carrier requirement.
+ if (mUpstreamNetworkState.networkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ return -1;
+ }
+
+ // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary
+ // distinction between tethered and untethered traffic.
+ return 1;
+ }
+
private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
final Downstream ds = findDownstream(ipServer);
if (ds == null) return null;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index 293f8ea..fe92204 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -66,6 +66,7 @@
private final Handler mHandler;
private final SharedLog mLog;
+ private final Dependencies mDeps;
private IOffloadControl mOffloadControl;
private TetheringOffloadCallback mTetheringOffloadCallback;
private ControlCallback mControlCallback;
@@ -126,8 +127,76 @@
}
public OffloadHardwareInterface(Handler h, SharedLog log) {
+ this(h, log, new Dependencies(log));
+ }
+
+ OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
mHandler = h;
mLog = log.forSubComponent(TAG);
+ mDeps = deps;
+ }
+
+ /** Capture OffloadHardwareInterface dependencies, for injection. */
+ static class Dependencies {
+ private final SharedLog mLog;
+
+ Dependencies(SharedLog log) {
+ mLog = log;
+ }
+
+ public IOffloadConfig getOffloadConfig() {
+ try {
+ return IOffloadConfig.getService(true /*retry*/);
+ } catch (RemoteException | NoSuchElementException e) {
+ mLog.e("getIOffloadConfig error " + e);
+ return null;
+ }
+ }
+
+ public IOffloadControl getOffloadControl() {
+ try {
+ return IOffloadControl.getService(true /*retry*/);
+ } catch (RemoteException | NoSuchElementException e) {
+ mLog.e("tethering offload control not supported: " + e);
+ return null;
+ }
+ }
+
+ public NativeHandle createConntrackSocket(final int groups) {
+ final FileDescriptor fd;
+ try {
+ fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
+ } catch (ErrnoException e) {
+ mLog.e("Unable to create conntrack socket " + e);
+ return null;
+ }
+
+ final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
+ try {
+ Os.bind(fd, sockAddr);
+ } catch (ErrnoException | SocketException e) {
+ mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ie) {
+ // Nothing we can do here
+ }
+ return null;
+ }
+ try {
+ Os.connect(fd, sockAddr);
+ } catch (ErrnoException | SocketException e) {
+ mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ie) {
+ // Nothing we can do here
+ }
+ return null;
+ }
+
+ return new NativeHandle(fd, true);
+ }
}
/** Get default value indicating whether offload is supported. */
@@ -141,13 +210,7 @@
* share them with offload management process.
*/
public boolean initOffloadConfig() {
- IOffloadConfig offloadConfig;
- try {
- offloadConfig = IOffloadConfig.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("getIOffloadConfig error " + e);
- return false;
- }
+ final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
if (offloadConfig == null) {
mLog.e("Could not find IOffloadConfig service");
return false;
@@ -159,11 +222,11 @@
//
// h2 provides a file descriptor bound to the following netlink groups
// (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
- final NativeHandle h1 = createConntrackSocket(
+ final NativeHandle h1 = mDeps.createConntrackSocket(
NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
if (h1 == null) return false;
- final NativeHandle h2 = createConntrackSocket(
+ final NativeHandle h2 = mDeps.createConntrackSocket(
NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
if (h2 == null) {
closeFdInNativeHandle(h1);
@@ -198,53 +261,12 @@
}
}
- private NativeHandle createConntrackSocket(final int groups) {
- FileDescriptor fd;
- try {
- fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
- } catch (ErrnoException e) {
- mLog.e("Unable to create conntrack socket " + e);
- return null;
- }
-
- final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
- try {
- Os.bind(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
- try {
- Os.connect(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
-
- return new NativeHandle(fd, true);
- }
-
/** Initialize the tethering offload HAL. */
public boolean initOffloadControl(ControlCallback controlCb) {
mControlCallback = controlCb;
if (mOffloadControl == null) {
- try {
- mOffloadControl = IOffloadControl.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("tethering offload control not supported: " + e);
- return false;
- }
+ mOffloadControl = mDeps.getOffloadControl();
if (mOffloadControl == null) {
mLog.e("tethering IOffloadControl.getService() returned null");
return false;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
new file mode 100644
index 0000000..160a166
--- /dev/null
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -0,0 +1,254 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.ip.IpServer;
+import android.net.util.PrefixUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * This class coordinate IP addresses conflict problem.
+ *
+ * Tethering downstream IP addresses may conflict with network assigned addresses. This
+ * coordinator is responsible for recording all of network assigned addresses and dispatched
+ * free address to downstream interfaces.
+ *
+ * This class is not thread-safe and should be accessed on the same tethering internal thread.
+ * @hide
+ */
+public class PrivateAddressCoordinator {
+ public static final int PREFIX_LENGTH = 24;
+
+ private static final int MAX_UBYTE = 256;
+ private static final int BYTE_MASK = 0xff;
+ // reserved for bluetooth tethering.
+ private static final int BLUETOOTH_RESERVED = 44;
+ private static final byte DEFAULT_ID = (byte) 42;
+
+ // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
+ // address may be requested before coordinator get current upstream notification. To ensure
+ // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
+ // when tethering is down. Instead coordinator would remove all depcreted upstreams from
+ // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprectedUpstreams().
+ private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
+ private final ArraySet<IpServer> mDownstreams;
+ // IANA has reserved the following three blocks of the IP address space for private intranets:
+ // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
+ // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
+ private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
+ private final IpPrefix mTetheringPrefix;
+ private final ConnectivityManager mConnectivityMgr;
+
+ public PrivateAddressCoordinator(Context context) {
+ mDownstreams = new ArraySet<>();
+ mUpstreamPrefixMap = new ArrayMap<>();
+ mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
+ mConnectivityMgr = (ConnectivityManager) context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+
+ /**
+ * Record a new upstream IpPrefix which may conflict with tethering downstreams.
+ * The downstreams will be notified if a conflict is found.
+ */
+ public void updateUpstreamPrefix(final Network network, final LinkProperties lp) {
+ final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses());
+ if (ipv4Prefixes.isEmpty()) {
+ removeUpstreamPrefix(network);
+ return;
+ }
+
+ mUpstreamPrefixMap.put(network, ipv4Prefixes);
+ handleMaybePrefixConflict(ipv4Prefixes);
+ }
+
+ private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
+ final ArrayList<IpPrefix> list = new ArrayList<>();
+ for (LinkAddress address : linkAddresses) {
+ if (!address.isIpv4()) continue;
+
+ list.add(PrefixUtils.asIpPrefix(address));
+ }
+
+ return list;
+ }
+
+ private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
+ for (IpServer downstream : mDownstreams) {
+ final IpPrefix target = getDownstreamPrefix(downstream);
+ if (target == null) continue;
+
+ for (IpPrefix source : prefixes) {
+ if (isConflictPrefix(source, target)) {
+ downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ break;
+ }
+ }
+ }
+ }
+
+ /** Remove IpPrefix records corresponding to input network. */
+ public void removeUpstreamPrefix(final Network network) {
+ mUpstreamPrefixMap.remove(network);
+ }
+
+ private void maybeRemoveDeprectedUpstreams() {
+ if (!mDownstreams.isEmpty() || mUpstreamPrefixMap.isEmpty()) return;
+
+ final ArrayList<Network> toBeRemoved = new ArrayList<>();
+ List<Network> allNetworks = Arrays.asList(mConnectivityMgr.getAllNetworks());
+ for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
+ final Network network = mUpstreamPrefixMap.keyAt(i);
+ if (!allNetworks.contains(network)) toBeRemoved.add(network);
+ }
+
+ mUpstreamPrefixMap.removeAll(toBeRemoved);
+ }
+
+ /**
+ * Pick a random available address and mark its prefix as in use for the provided IpServer,
+ * returns null if there is no available address.
+ */
+ @Nullable
+ public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
+ maybeRemoveDeprectedUpstreams();
+
+ // Address would be 192.168.[subAddress]/24.
+ final byte[] bytes = mTetheringPrefix.getRawAddress();
+ final int subAddress = getRandomSubAddr();
+ final int subNet = (subAddress >> 8) & BYTE_MASK;
+ bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
+ for (int i = 0; i < MAX_UBYTE; i++) {
+ final int newSubNet = (subNet + i) & BYTE_MASK;
+ if (newSubNet == BLUETOOTH_RESERVED) continue;
+
+ bytes[2] = (byte) newSubNet;
+ final InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(bytes);
+ } catch (UnknownHostException e) {
+ throw new IllegalStateException("Invalid address, shouldn't happen.", e);
+ }
+
+ final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH);
+ // Check whether this prefix is in use.
+ if (isDownstreamPrefixInUse(prefix)) continue;
+ // Check whether this prefix is conflict with any current upstream network.
+ if (isConflictWithUpstream(prefix)) continue;
+
+ mDownstreams.add(ipServer);
+ return new LinkAddress(addr, PREFIX_LENGTH);
+ }
+
+ // No available address.
+ return null;
+ }
+
+ /** Get random sub address value. Return value is in 0 ~ 0xffff. */
+ @VisibleForTesting
+ public int getRandomSubAddr() {
+ return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
+ }
+
+ private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
+ final byte subId = (byte) (source & BYTE_MASK);
+ for (byte value : excluded) {
+ if (subId == value) return DEFAULT_ID;
+ }
+
+ return subId;
+ }
+
+ /** Release downstream record for IpServer. */
+ public void releaseDownstream(final IpServer ipServer) {
+ mDownstreams.remove(ipServer);
+ }
+
+ /** Clear current upstream prefixes records. */
+ public void clearUpstreamPrefixes() {
+ mUpstreamPrefixMap.clear();
+ }
+
+ private boolean isConflictWithUpstream(final IpPrefix source) {
+ for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
+ final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
+ for (IpPrefix target : list) {
+ if (isConflictPrefix(source, target)) return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
+ if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
+ return prefix2.contains(prefix1.getAddress());
+ }
+
+ return prefix1.contains(prefix2.getAddress());
+ }
+
+ private boolean isDownstreamPrefixInUse(final IpPrefix source) {
+ // This class always generates downstream prefixes with the same prefix length, so
+ // prefixes cannot be contained in each other. They can only be equal to each other.
+ for (IpServer downstream : mDownstreams) {
+ final IpPrefix prefix = getDownstreamPrefix(downstream);
+ if (source.equals(prefix)) return true;
+ }
+ return false;
+ }
+
+ private IpPrefix getDownstreamPrefix(final IpServer downstream) {
+ final LinkAddress address = downstream.getAddress();
+ if (address == null) return null;
+
+ return PrefixUtils.asIpPrefix(address);
+ }
+
+ void dump(final IndentingPrintWriter pw) {
+ pw.decreaseIndent();
+ pw.println("mUpstreamPrefixMap:");
+ pw.increaseIndent();
+ for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
+ pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
+ }
+ pw.decreaseIndent();
+ pw.println("mDownstreams:");
+ pw.increaseIndent();
+ for (IpServer ipServer : mDownstreams) {
+ pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index bca86ca..00723ac 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -109,8 +109,10 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -205,6 +207,7 @@
new SparseArray<>();
// used to synchronize public access to members
+ // TODO(b/153621704): remove mPublicSync to make Tethering lock free
private final Object mPublicSync;
private final Context mContext;
private final ArrayMap<String, TetherState> mTetherStates;
@@ -228,6 +231,9 @@
private final ConnectedClientsTracker mConnectedClientsTracker;
private final TetheringThreadExecutor mExecutor;
private final TetheringNotificationUpdater mNotificationUpdater;
+ private final UserManager mUserManager;
+ private final BpfCoordinator mBpfCoordinator;
+ private final PrivateAddressCoordinator mPrivateAddressCoordinator;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
private ITetheringEventCallback mTetheringEventCallback = null;
@@ -279,6 +285,8 @@
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new LinkedHashSet<>();
+ mBpfCoordinator = mDeps.getBpfCoordinator(
+ mHandler, mNetd, mLog, new BpfCoordinator.Dependencies());
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
@@ -305,23 +313,25 @@
mStateReceiver = new StateReceiver();
- final UserManager userManager = (UserManager) mContext.getSystemService(
- Context.USER_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(
- userManager, this, mNotificationUpdater);
+ mUserManager, this, mNotificationUpdater);
mExecutor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
mNetdCallback = new NetdCallback();
+ mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext);
// Load tethering configuration.
updateConfiguration();
+
+ startStateMachineUpdaters();
}
/**
* Start to register callbacks.
* Call this function when tethering is ready to handle callback events.
*/
- public void startStateMachineUpdaters() {
+ private void startStateMachineUpdaters() {
try {
mNetd.registerUnsolicitedEventListener(mNetdCallback);
} catch (RemoteException e) {
@@ -779,7 +789,7 @@
// TODO: Figure out how to update for local hotspot mode interfaces.
private void sendTetherStateChangedBroadcast() {
- if (!mDeps.isTetheringSupported()) return;
+ if (!isTetheringSupported()) return;
final ArrayList<String> availableList = new ArrayList<>();
final ArrayList<String> tetherList = new ArrayList<>();
@@ -1020,14 +1030,14 @@
@VisibleForTesting
protected static class UserRestrictionActionListener {
- private final UserManager mUserManager;
+ private final UserManager mUserMgr;
private final Tethering mWrapper;
private final TetheringNotificationUpdater mNotificationUpdater;
public boolean mDisallowTethering;
public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
@NonNull TetheringNotificationUpdater updater) {
- mUserManager = um;
+ mUserMgr = um;
mWrapper = wrapper;
mNotificationUpdater = updater;
mDisallowTethering = false;
@@ -1037,7 +1047,7 @@
// getUserRestrictions gets restriction for this process' user, which is the primary
// user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary
// user. See UserManager.DISALLOW_CONFIG_TETHERING.
- final Bundle restrictions = mUserManager.getUserRestrictions();
+ final Bundle restrictions = mUserMgr.getUserRestrictions();
final boolean newlyDisallowed =
restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
final boolean prevDisallowed = mDisallowTethering;
@@ -1612,6 +1622,14 @@
}
}
+ private void addUpstreamPrefixes(final UpstreamNetworkState ns) {
+ mPrivateAddressCoordinator.updateUpstreamPrefix(ns.network, ns.linkProperties);
+ }
+
+ private void removeUpstreamPrefixes(final UpstreamNetworkState ns) {
+ mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network);
+ }
+
@VisibleForTesting
void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
@@ -1620,6 +1638,14 @@
}
final UpstreamNetworkState ns = (UpstreamNetworkState) o;
+ switch (arg1) {
+ case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
+ addUpstreamPrefixes(ns);
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_LOST:
+ removeUpstreamPrefixes(ns);
+ break;
+ }
if (ns == null || !pertainsToCurrentUpstream(ns)) {
// TODO: In future, this is where upstream evaluation and selection
@@ -1681,6 +1707,9 @@
chooseUpstreamType(true);
mTryCell = false;
}
+
+ // TODO: Check the upstream interface if it is managed by BPF offload.
+ mBpfCoordinator.start();
}
@Override
@@ -1693,6 +1722,7 @@
mTetherUpstream = null;
reportUpstreamChanged(null);
}
+ mBpfCoordinator.stop();
}
private boolean updateUpstreamWanted() {
@@ -1988,7 +2018,7 @@
mHandler.post(() -> {
mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
- parcel.tetheringSupported = mDeps.isTetheringSupported();
+ parcel.tetheringSupported = isTetheringSupported();
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
parcel.states =
@@ -2111,6 +2141,20 @@
}
}
+ // if ro.tether.denied = true we default to no tethering
+ // gservices could set the secure setting to 1 though to enable it on a build where it
+ // had previously been turned off.
+ boolean isTetheringSupported() {
+ final int defaultVal =
+ SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
+ final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
+ final boolean tetherEnabledInSettings = tetherSupported
+ && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+
+ return tetherEnabledInSettings && hasTetherableConfiguration();
+ }
+
void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
// Binder.java closes the resource for us.
@SuppressWarnings("resource")
@@ -2172,6 +2216,11 @@
mOffloadController.dump(pw);
pw.decreaseIndent();
+ pw.println("Private address coordinator:");
+ pw.increaseIndent();
+ mPrivateAddressCoordinator.dump(pw);
+ pw.decreaseIndent();
+
pw.println("Log:");
pw.increaseIndent();
if (argsContain(args, "--short")) {
@@ -2213,6 +2262,11 @@
public void dhcpLeasesChanged() {
updateConnectedClients(null /* wifiClients */);
}
+
+ @Override
+ public void requestEnableTethering(int tetheringType, boolean enabled) {
+ enableTetheringInternal(tetheringType, enabled, null);
+ }
};
}
@@ -2294,9 +2348,10 @@
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
- new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
+ new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
+ mConfig.enableBpfOffload, mPrivateAddressCoordinator,
+ mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 91a6e29..48a600d 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -132,7 +132,7 @@
isDunRequired = checkDunRequired(ctx);
chooseUpstreamAutomatically = getResourceBoolean(
- res, R.bool.config_tether_upstream_automatic, false /** default value */);
+ res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
legacyDhcpRanges = getLegacyDhcpRanges(res);
@@ -375,30 +375,31 @@
// Priority 1: Device config
// Priority 2: Resource config
// Priority 3: Default value
- final boolean resourceValue = getResourceBoolean(
+ final boolean defaultValue = getResourceBoolean(
res, R.bool.config_tether_enable_bpf_offload, true /** default value */);
- // Due to the limitation of static mock for testing, using #getProperty directly instead
- // of getDeviceConfigBoolean. getDeviceConfigBoolean is not invoked because it uses
- // #getBoolean to get the boolean device config. The test can't know that the returned
- // boolean value comes from device config or default value (because of null property
- // string). Because the test would like to verify null property boolean string case,
- // use DeviceConfig.getProperty here. See also the test case testBpfOffload{*} in
- // TetheringConfigurationTest.java.
- final String value = DeviceConfig.getProperty(
- NAMESPACE_CONNECTIVITY, OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD);
- return (value != null) ? Boolean.parseBoolean(value) : resourceValue;
+ return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue);
}
private boolean getEnableLegacyDhcpServer(final Resources res) {
return getResourceBoolean(
- res, R.bool.config_tether_enable_legacy_dhcp_server, false /** default value */)
- || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
+ res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */)
+ || getDeviceConfigBoolean(
+ TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */);
+ }
+
+ private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
+ // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
+ // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
+ // returned boolean value comes from device config or default value (because of null
+ // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
+ final String value = getDeviceConfigProperty(name);
+ return value != null ? Boolean.parseBoolean(value) : defaultValue;
}
@VisibleForTesting
- protected boolean getDeviceConfigBoolean(final String name) {
- return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */);
+ protected String getDeviceConfigProperty(String name) {
+ return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
}
private Resources getResources(Context ctx, int subId) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index ce546c7..d637c86 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -41,6 +41,17 @@
*/
public abstract class TetheringDependencies {
/**
+ * Get a reference to the BpfCoordinator to be used by tethering.
+ */
+ public @NonNull BpfCoordinator getBpfCoordinator(
+ @NonNull Handler handler, @NonNull INetd netd, @NonNull SharedLog log,
+ @NonNull BpfCoordinator.Dependencies deps) {
+ final NetworkStatsManager statsManager =
+ (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
+ return new BpfCoordinator(handler, netd, statsManager, log, deps);
+ }
+
+ /**
* Get a reference to the offload hardware interface to be used by tethering.
*/
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index af349f2..613328d 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -17,8 +17,10 @@
package com.android.networkstack.tethering;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -40,15 +42,12 @@
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.ip.IpServer;
-import android.net.util.SharedLog;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.SystemProperties;
-import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
@@ -68,21 +67,14 @@
public class TetheringService extends Service {
private static final String TAG = TetheringService.class.getSimpleName();
- private final SharedLog mLog = new SharedLog(TAG);
private TetheringConnector mConnector;
- private Context mContext;
- private TetheringDependencies mDeps;
- private Tethering mTethering;
- private UserManager mUserManager;
@Override
public void onCreate() {
- mLog.mark("onCreate");
- mDeps = getTetheringDependencies();
- mContext = mDeps.getContext();
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mTethering = makeTethering(mDeps);
- mTethering.startStateMachineUpdaters();
+ final TetheringDependencies deps = makeTetheringDependencies();
+ // The Tethering object needs a fully functional context to start, so this can't be done
+ // in the constructor.
+ mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
}
/**
@@ -94,21 +86,10 @@
return new Tethering(deps);
}
- /**
- * Create a binder connector for the system server to communicate with the tethering.
- */
- private synchronized IBinder makeConnector() {
- if (mConnector == null) {
- mConnector = new TetheringConnector(mTethering, TetheringService.this);
- }
- return mConnector;
- }
-
@NonNull
@Override
public IBinder onBind(Intent intent) {
- mLog.mark("onBind");
- return makeConnector();
+ return mConnector;
}
private static class TetheringConnector extends ITetheringConnector.Stub {
@@ -248,7 +229,7 @@
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
- if (!mService.isTetheringSupported()) {
+ if (!mTethering.isTetheringSupported()) {
listener.onResult(TETHER_ERROR_UNSUPPORTED);
return true;
}
@@ -266,7 +247,7 @@
receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
return true;
}
- if (!mService.isTetheringSupported()) {
+ if (!mTethering.isTetheringSupported()) {
receiver.send(TETHER_ERROR_UNSUPPORTED, null);
return true;
}
@@ -274,21 +255,33 @@
return false;
}
+ private boolean hasNetworkStackPermission() {
+ return checkCallingOrSelfPermission(NETWORK_STACK)
+ || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean hasTetherPrivilegedPermission() {
- return mService.checkCallingOrSelfPermission(TETHER_PRIVILEGED) == PERMISSION_GRANTED;
+ return checkCallingOrSelfPermission(TETHER_PRIVILEGED);
+ }
+
+ private boolean checkCallingOrSelfPermission(final String permission) {
+ return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
private boolean hasTetherChangePermission(final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged) {
+ if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false;
+
if (hasTetherPrivilegedPermission()) return true;
- if (onlyAllowPrivileged || mTethering.isTetherProvisioningRequired()) return false;
+ if (mTethering.isTetherProvisioningRequired()) return false;
int uid = Binder.getCallingUid();
+
// If callerPkg's uid is not same as Binder.getCallingUid(),
// checkAndNoteWriteSettingsOperation will return false and the operation will be
// denied.
- return TetheringService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg,
+ return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg,
callingAttributionTag, false /* throwException */);
}
@@ -300,28 +293,14 @@
}
}
- // if ro.tether.denied = true we default to no tethering
- // gservices could set the secure setting to 1 though to enable it on a build where it
- // had previously been turned off.
- private boolean isTetheringSupported() {
- final int defaultVal =
- SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
- final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
- final boolean tetherEnabledInSettings = tetherSupported
- && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-
- return tetherEnabledInSettings && mTethering.hasTetherableConfiguration();
- }
-
/**
* Check if the package is a allowed to write settings. This also accounts that such an access
* happened.
*
* @return {@code true} iff the package is allowed to write settings.
*/
- // TODO: Remove method and replace with direct call once R code is pushed to AOSP
- private static boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+ @VisibleForTesting
+ boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
@NonNull String callingPackage, @Nullable String callingAttributionTag,
boolean throwException) {
return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
@@ -332,87 +311,79 @@
* An injection method for testing.
*/
@VisibleForTesting
- public TetheringDependencies getTetheringDependencies() {
- if (mDeps == null) {
- mDeps = new TetheringDependencies() {
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- // TODO: b/147280869, add a proper system API to replace this.
- final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
- return trackDefaultRequest;
- }
+ public TetheringDependencies makeTetheringDependencies() {
+ return new TetheringDependencies() {
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ // TODO: b/147280869, add a proper system API to replace this.
+ final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ return trackDefaultRequest;
+ }
- @Override
- public Looper getTetheringLooper() {
- final HandlerThread tetherThread = new HandlerThread("android.tethering");
- tetherThread.start();
- return tetherThread.getLooper();
- }
+ @Override
+ public Looper getTetheringLooper() {
+ final HandlerThread tetherThread = new HandlerThread("android.tethering");
+ tetherThread.start();
+ return tetherThread.getLooper();
+ }
- @Override
- public boolean isTetheringSupported() {
- return TetheringService.this.isTetheringSupported();
- }
+ @Override
+ public Context getContext() {
+ return TetheringService.this;
+ }
- @Override
- public Context getContext() {
- return TetheringService.this;
- }
+ @Override
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies() {
+ @Override
+ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+ DhcpServerCallbacks cb) {
+ try {
+ final INetworkStackConnector service = getNetworkStackConnector();
+ if (service == null) return;
- @Override
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies() {
- @Override
- public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb) {
+ service.makeDhcpServer(ifName, params, cb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Fail to make dhcp server");
try {
- final INetworkStackConnector service = getNetworkStackConnector();
- if (service == null) return;
-
- service.makeDhcpServer(ifName, params, cb);
- } catch (RemoteException e) {
- Log.e(TAG, "Fail to make dhcp server");
- try {
- cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
- } catch (RemoteException re) { }
- }
+ cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+ } catch (RemoteException re) { }
}
- };
- }
-
- // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
- // networkStackClient.
- static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
- private INetworkStackConnector getNetworkStackConnector() {
- IBinder connector;
- try {
- final long before = System.currentTimeMillis();
- while ((connector = NetworkStack.getService()) == null) {
- if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
- return null;
- }
- Thread.sleep(200);
- }
- } catch (InterruptedException e) {
- Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
- return null;
}
- return INetworkStackConnector.Stub.asInterface(connector);
- }
+ };
+ }
- @Override
- public BluetoothAdapter getBluetoothAdapter() {
- return BluetoothAdapter.getDefaultAdapter();
+ // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
+ // networkStackClient.
+ static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
+ private INetworkStackConnector getNetworkStackConnector() {
+ IBinder connector;
+ try {
+ final long before = System.currentTimeMillis();
+ while ((connector = NetworkStack.getService()) == null) {
+ if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+ Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
+ return null;
+ }
+ Thread.sleep(200);
+ }
+ } catch (InterruptedException e) {
+ Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
+ return null;
}
- };
- }
- return mDeps;
+ return INetworkStackConnector.Stub.asInterface(connector);
+ }
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ return BluetoothAdapter.getDefaultAdapter();
+ }
+ };
}
}
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 3305ed0..ed69b7d 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -63,7 +63,6 @@
// NetworkStackTests.
android_test {
name: "TetheringCoverageTests",
- certificate: "platform",
platform_apis: true,
test_suites: ["device-tests", "mts"],
test_config: "AndroidTest_Coverage.xml",
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 4bac9da..74df113 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -76,7 +76,7 @@
public class EthernetTetheringTest {
private static final String TAG = EthernetTetheringTest.class.getSimpleName();
- private static final int TIMEOUT_MS = 1000;
+ private static final int TIMEOUT_MS = 5000;
private static final int PACKET_READ_TIMEOUT_MS = 100;
private static final int DHCP_DISCOVER_ATTEMPTS = 10;
private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
@@ -338,7 +338,8 @@
private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
return enableEthernetTethering(iface,
- new TetheringRequest.Builder(TETHERING_ETHERNET).build());
+ new TetheringRequest.Builder(TETHERING_ETHERNET)
+ .setShouldShowEntitlementUi(false).build());
}
private int getMTU(TestNetworkInterface iface) throws SocketException {
@@ -508,7 +509,8 @@
LinkAddress localAddr = local == null ? null : new LinkAddress(local);
LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
return new TetheringRequest.Builder(TETHERING_ETHERNET)
- .setStaticIpv4Addresses(localAddr, clientAddr).build();
+ .setStaticIpv4Addresses(localAddr, clientAddr)
+ .setShouldShowEntitlementUi(false).build();
}
private void assertInvalidStaticIpv4Request(String iface, String local, String client)
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 9b7d683..1572768 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -29,7 +29,7 @@
sdk_version: "core_platform",
libs: [
"framework-minus-apex",
- "framework-tethering",
+ "framework-tethering.impl",
],
visibility: ["//cts/tests/tests/tethering"],
}
@@ -59,7 +59,7 @@
"ext",
"framework-minus-apex",
"framework-res",
- "framework-tethering",
+ "framework-tethering.impl",
],
jni_libs: [
// For mockito extended
@@ -82,7 +82,7 @@
android_test {
name: "TetheringTests",
- certificate: "platform",
+ platform_apis: true,
test_suites: [
"device-tests",
"mts",
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 31eaabf..355342f 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -16,9 +16,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering.tests.unit">
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
-
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<service
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index b9622da..433aacf 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -17,7 +17,9 @@
package android.net.ip;
import static android.net.INetd.IF_STATE_UP;
+import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -38,6 +40,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -67,12 +70,15 @@
import android.net.RouteInfo;
import android.net.TetherOffloadRuleParcel;
import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpEventCallbacks;
import android.net.dhcp.IDhcpServer;
import android.net.dhcp.IDhcpServerCallbacks;
import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
+import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
+import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -81,6 +87,9 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.networkstack.tethering.BpfCoordinator;
+import com.android.networkstack.tethering.PrivateAddressCoordinator;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -94,6 +103,7 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -103,7 +113,7 @@
private static final String UPSTREAM_IFACE2 = "upstream1";
private static final int UPSTREAM_IFINDEX = 101;
private static final int UPSTREAM_IFINDEX2 = 102;
- private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
+ private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
private static final int DHCP_LEASE_TIME_SECS = 3600;
private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
@@ -113,13 +123,18 @@
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
+ private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24");
+ private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+
@Mock private INetd mNetd;
+ @Mock private BpfCoordinator mBpfCoordinator;
@Mock private IpServer.Callback mCallback;
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
+ @Mock private PrivateAddressCoordinator mAddressCoordinator;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -166,8 +181,8 @@
neighborCaptor.capture());
mIpServer = new IpServer(
- IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
- mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
+ IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
+ mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies);
mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
@@ -192,14 +207,16 @@
if (upstreamIface != null) {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(upstreamIface);
- dispatchTetherConnectionChanged(upstreamIface, lp);
+ dispatchTetherConnectionChanged(upstreamIface, lp, 0);
}
- reset(mNetd, mCallback);
+ reset(mNetd, mCallback, mAddressCoordinator);
+ when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
}
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
+ when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
}
@Test
@@ -207,8 +224,8 @@
when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
- mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
- mDependencies);
+ mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */,
+ DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
@@ -271,16 +288,17 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mCallback);
+ InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
+ inOrder.verify(mAddressCoordinator).releaseDownstream(any());
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
}
@Test
@@ -288,7 +306,8 @@
initStateMachine(TETHERING_USB);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mCallback, mNetd);
+ InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -300,7 +319,7 @@
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
}
@Test
@@ -308,7 +327,8 @@
initStateMachine(TETHERING_WIFI_P2P);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
- InOrder inOrder = inOrder(mCallback, mNetd);
+ InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -320,7 +340,7 @@
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
}
@Test
@@ -386,18 +406,19 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mCallback);
+ InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
+ inOrder.verify(mAddressCoordinator).releaseDownstream(any());
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
}
@Test
@@ -477,7 +498,7 @@
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
+ assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
}
@Test
@@ -485,7 +506,7 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
+ assertDhcpStarted(mBluetoothPrefix);
}
@Test
@@ -493,7 +514,66 @@
initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
+ assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
+ }
+
+ @Test
+ public void startsDhcpServerOnNcm() throws Exception {
+ initStateMachine(TETHERING_NCM);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+
+ assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
+ }
+
+ @Test
+ public void testOnNewPrefixRequest() throws Exception {
+ initStateMachine(TETHERING_NCM);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
+
+ final IDhcpEventCallbacks eventCallbacks;
+ final ArgumentCaptor<IDhcpEventCallbacks> dhcpEventCbsCaptor =
+ ArgumentCaptor.forClass(IDhcpEventCallbacks.class);
+ verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), dhcpEventCbsCaptor.capture());
+ eventCallbacks = dhcpEventCbsCaptor.getValue();
+ assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
+
+ final ArgumentCaptor<LinkProperties> lpCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
+ InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
+ inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
+ // One for ipv4 route, one for ipv6 link local route.
+ inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
+ any(), any());
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
+ verifyNoMoreInteractions(mCallback, mAddressCoordinator);
+
+ // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
+ // onNewPrefixRequest callback.
+ final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
+ when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(newAddress);
+ eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
+ mLooper.dispatchAll();
+
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
+ inOrder.verify(mNetd).tetherApplyDnsInterfaces();
+ inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
+ verifyNoMoreInteractions(mCallback);
+
+ final LinkProperties linkProperties = lpCaptor.getValue();
+ final List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses();
+ assertEquals(1, linkProperties.getLinkAddresses().size());
+ assertEquals(1, linkProperties.getRoutes().size());
+ final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(),
+ linkAddresses.get(0).getPrefixLength());
+ assertNotEquals(prefix, new IpPrefix("192.168.42.0/24"));
+
+ verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any());
+ assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix);
}
@Test
@@ -637,7 +717,7 @@
InOrder inOrder = inOrder(mNetd);
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
@@ -645,7 +725,7 @@
reset(mNetd);
// When the upstream is lost, rules are removed.
- dispatchTetherConnectionChanged(null, null);
+ dispatchTetherConnectionChanged(null, null, 0);
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
reset(mNetd);
@@ -658,19 +738,19 @@
// Rules can be added again once upstream IPv6 connectivity is available.
lp.setInterfaceName(UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
// If upstream IPv6 connectivity is lost, rules are removed.
reset(mNetd);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
// When the interface goes down, rules are removed.
lp.setInterfaceName(UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
@@ -731,19 +811,69 @@
verify(mIpNeighborMonitor, never()).start();
}
- private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
- verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
- verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
+ private LinkProperties buildIpv6OnlyLinkProperties(final String iface) {
+ final LinkProperties linkProp = new LinkProperties();
+ linkProp.setInterfaceName(iface);
+ linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST));
+ final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888");
+ linkProp.addDnsServer(dns);
+
+ return linkProp;
+ }
+
+ @Test
+ public void testAdjustTtlValue() throws Exception {
+ final ArgumentCaptor<RaParams> raParamsCaptor =
+ ArgumentCaptor.forClass(RaParams.class);
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+ verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+ final RaParams noV6Params = raParamsCaptor.getValue();
+ assertEquals(65, noV6Params.hopLimit);
+ reset(mRaDaemon);
+
+ when(mNetd.getProcSysNet(
+ INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64");
+ final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1);
+ verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+ final RaParams nonCellularParams = raParamsCaptor.getValue();
+ assertEquals(65, nonCellularParams.hopLimit);
+ reset(mRaDaemon);
+
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+ verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+ final RaParams noUpstream = raParamsCaptor.getValue();
+ assertEquals(65, nonCellularParams.hopLimit);
+ reset(mRaDaemon);
+
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
+ verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+ final RaParams cellularParams = raParamsCaptor.getValue();
+ assertEquals(63, cellularParams.hopLimit);
+ reset(mRaDaemon);
+ }
+
+ private void assertDhcpServingParams(final DhcpServingParamsParcel params,
+ final IpPrefix prefix) {
// Last address byte is random
- assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
- assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
+ assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr)));
+ assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength);
assertEquals(1, params.defaultRouters.length);
assertEquals(params.serverAddr, params.defaultRouters[0]);
assertEquals(1, params.dnsServers.length);
assertEquals(params.serverAddr, params.dnsServers[0]);
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
+ if (mIpServer.interfaceType() == TETHERING_NCM) {
+ assertTrue(params.changePrefixOnDecline);
+ }
+ }
+
+ private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
+ verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
+ verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
+ assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix);
}
/**
@@ -774,9 +904,10 @@
* @param upstreamIface String name of upstream interface (or null)
* @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
*/
- private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) {
+ private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp,
+ int ttlAdjustment) {
dispatchTetherConnectionChanged(upstreamIface);
- mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
+ mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp);
mLooper.dispatchAll();
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
new file mode 100644
index 0000000..b029b43
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
+
+import static com.android.networkstack.tethering.BpfCoordinator
+ .DEFAULT_PERFORM_POLL_INTERVAL_MS;
+import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
+import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
+import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
+import android.net.INetd;
+import android.net.NetworkStats;
+import android.net.TetherStatsParcel;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.TestableNetworkStatsProviderCbBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BpfCoordinatorTest {
+ @Mock private NetworkStatsManager mStatsManager;
+ @Mock private INetd mNetd;
+ // Late init since methods must be called by the thread that created this object.
+ private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
+ private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
+ private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
+ private final TestLooper mTestLooper = new TestLooper();
+ private BpfCoordinator.Dependencies mDeps =
+ new BpfCoordinator.Dependencies() {
+ @Override
+ int getPerformPollInterval() {
+ return DEFAULT_PERFORM_POLL_INTERVAL_MS;
+ }
+ };
+
+ @Before public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private void waitForIdle() {
+ mTestLooper.dispatchAll();
+ }
+
+ private void setupFunctioningNetdInterface() throws Exception {
+ when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
+ }
+
+ @NonNull
+ private BpfCoordinator makeBpfCoordinator() throws Exception {
+ BpfCoordinator coordinator = new BpfCoordinator(
+ new Handler(mTestLooper.getLooper()), mNetd, mStatsManager, new SharedLog("test"),
+ mDeps);
+ final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
+ tetherStatsProviderCaptor =
+ ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
+ verify(mStatsManager).registerNetworkStatsProvider(anyString(),
+ tetherStatsProviderCaptor.capture());
+ mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
+ assertNotNull(mTetherStatsProvider);
+ mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
+ mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
+ return coordinator;
+ }
+
+ @NonNull
+ private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how,
+ @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING,
+ SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes,
+ rxPackets, txBytes, txPackets, 0L);
+ }
+
+ @NonNull
+ private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex,
+ long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ final TetherStatsParcel parcel = new TetherStatsParcel();
+ parcel.ifIndex = ifIndex;
+ parcel.rxBytes = rxBytes;
+ parcel.rxPackets = rxPackets;
+ parcel.txBytes = txBytes;
+ parcel.txPackets = txPackets;
+ return parcel;
+ }
+
+ private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception {
+ when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ }
+
+ @Test
+ public void testGetForwardedStats() throws Exception {
+ setupFunctioningNetdInterface();
+
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ coordinator.start();
+
+ final String wlanIface = "wlan0";
+ final Integer wlanIfIndex = 100;
+ final String mobileIface = "rmnet_data0";
+ final Integer mobileIfIndex = 101;
+
+ // Add interface name to lookup table. In realistic case, the upstream interface name will
+ // be added by IpServer when IpServer has received with a new IPv6 upstream update event.
+ coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface);
+ coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
+
+ // [1] Both interface stats are changed.
+ // Setup the tether stats of wlan and mobile interface. Note that move forward the time of
+ // the looper to make sure the new tether stats has been updated by polling update thread.
+ setTetherOffloadStatsList(new TetherStatsParcel[] {
+ buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
+ buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)});
+
+ final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400));
+
+ final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200))
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400));
+
+ // Force pushing stats update to verify the stats reported.
+ // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for
+ // verifying the notification.
+ mTetherStatsProvider.pushTetherStats();
+ mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
+
+ // [2] Only one interface stats is changed.
+ // The tether stats of mobile interface is accumulated and The tether stats of wlan
+ // interface is the same.
+ setTetherOffloadStatsList(new TetherStatsParcel[] {
+ buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
+ buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)});
+
+ final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40));
+
+ final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40));
+
+ // Force pushing stats update to verify that only diff of stats is reported.
+ mTetherStatsProvider.pushTetherStats();
+ mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff,
+ expectedUidStatsDiff);
+
+ // [3] Stop coordinator.
+ // Shutdown the coordinator and clear the invocation history, especially the
+ // tetherOffloadGetStats() calls.
+ coordinator.stop();
+ clearInvocations(mNetd);
+
+ // Verify the polling update thread stopped.
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ verify(mNetd, never()).tetherOffloadGetStats();
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index cdd0e24..72fa916 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -147,9 +147,8 @@
doReturn(false).when(
() -> SystemProperties.getBoolean(
eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn(null).when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
index 820f255..f2b5314 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
@@ -128,7 +128,7 @@
final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class);
mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream);
- verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0),
+ verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
lp.capture());
final LinkProperties v6OnlyLink = lp.getValue();
assertOnlyOneV6AddressAndNoV4(v6OnlyLink);
@@ -140,7 +140,7 @@
mNotifyList.remove(firstServer);
mIPv6TetheringCoordinator.removeActiveDownstream(firstServer);
verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0),
+ verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
lp.capture());
final LinkProperties localOnlyLink = lp.getValue();
assertNotNull(localOnlyLink);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
index 1c81c12..071a290e 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -15,13 +15,20 @@
*/
package com.android.networkstack.tethering;
+import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static org.mockito.Mockito.mock;
+import android.content.Context;
import android.content.Intent;
import android.net.ITetheringConnector;
import android.os.Binder;
import android.os.IBinder;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
public class MockTetheringService extends TetheringService {
private final Tethering mTethering = mock(Tethering.class);
@@ -35,6 +42,15 @@
return mTethering;
}
+ @Override
+ boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+ @NonNull String callingPackage, @Nullable String callingAttributionTag,
+ boolean throwException) {
+ // Test this does not verify the calling package / UID, as calling package could be shell
+ // and not match the UID.
+ return context.checkCallingOrSelfPermission(WRITE_SETTINGS) == PERMISSION_GRANTED;
+ }
+
public Tethering getTethering() {
return mTethering;
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
new file mode 100644
index 0000000..f8ff1cb
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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;
+
+import static android.net.util.TetheringUtils.uint16;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
+import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
+import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
+import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.test.TestLooper;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class OffloadHardwareInterfaceTest {
+ private static final String RMNET0 = "test_rmnet_data0";
+
+ private final TestLooper mTestLooper = new TestLooper();
+
+ private OffloadHardwareInterface mOffloadHw;
+ private ITetheringOffloadCallback mTetheringOffloadCallback;
+ private OffloadHardwareInterface.ControlCallback mControlCallback;
+
+ @Mock private IOffloadConfig mIOffloadConfig;
+ @Mock private IOffloadControl mIOffloadControl;
+ @Mock private NativeHandle mNativeHandle;
+
+ class MyDependencies extends OffloadHardwareInterface.Dependencies {
+ MyDependencies(SharedLog log) {
+ super(log);
+ }
+
+ @Override
+ public IOffloadConfig getOffloadConfig() {
+ return mIOffloadConfig;
+ }
+
+ @Override
+ public IOffloadControl getOffloadControl() {
+ return mIOffloadControl;
+ }
+
+ @Override
+ public NativeHandle createConntrackSocket(final int groups) {
+ return mNativeHandle;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final SharedLog log = new SharedLog("test");
+ mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
+ new MyDependencies(log));
+ mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
+ }
+
+ private void startOffloadHardwareInterface() throws Exception {
+ mOffloadHw.initOffloadConfig();
+ mOffloadHw.initOffloadControl(mControlCallback);
+ final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
+ ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
+ verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
+ mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
+ }
+
+ @Test
+ public void testGetForwardedStats() throws Exception {
+ startOffloadHardwareInterface();
+ final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
+ verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
+ assertNotNull(stats);
+ }
+
+ @Test
+ public void testSetLocalPrefixes() throws Exception {
+ startOffloadHardwareInterface();
+ final ArrayList<String> localPrefixes = new ArrayList<>();
+ localPrefixes.add("127.0.0.0/8");
+ localPrefixes.add("fe80::/64");
+ mOffloadHw.setLocalPrefixes(localPrefixes);
+ verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
+ }
+
+ @Test
+ public void testSetDataLimit() throws Exception {
+ startOffloadHardwareInterface();
+ final long limit = 12345;
+ mOffloadHw.setDataLimit(RMNET0, limit);
+ verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
+ }
+
+ @Test
+ public void testSetUpstreamParameters() throws Exception {
+ startOffloadHardwareInterface();
+ final String v4addr = "192.168.10.1";
+ final String v4gateway = "192.168.10.255";
+ final ArrayList<String> v6gws = new ArrayList<>(0);
+ v6gws.add("2001:db8::1");
+ mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
+ verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
+ eq(v6gws), any());
+
+ final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
+ mOffloadHw.setUpstreamParameters(null, null, null, null);
+ verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
+ mArrayListCaptor.capture(), any());
+ assertEquals(mArrayListCaptor.getValue().size(), 0);
+ }
+
+ @Test
+ public void testUpdateDownstreamPrefix() throws Exception {
+ startOffloadHardwareInterface();
+ final String ifName = "wlan1";
+ final String prefix = "192.168.43.0/24";
+ mOffloadHw.addDownstreamPrefix(ifName, prefix);
+ verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());
+
+ mOffloadHw.removeDownstreamPrefix(ifName, prefix);
+ verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
+ }
+
+ @Test
+ public void testTetheringOffloadCallback() throws Exception {
+ startOffloadHardwareInterface();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStarted();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStoppedError();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStoppedUnsupported();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onSupportAvailable();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onStoppedLimitReached();
+
+ final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
+ mTetheringOffloadCallback.updateTimeout(tcpParams);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
+ eq(tcpParams.src.addr),
+ eq(uint16(tcpParams.src.port)),
+ eq(tcpParams.dst.addr),
+ eq(uint16(tcpParams.dst.port)));
+
+ final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
+ mTetheringOffloadCallback.updateTimeout(udpParams);
+ mTestLooper.dispatchAll();
+ verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
+ eq(udpParams.src.addr),
+ eq(uint16(udpParams.src.port)),
+ eq(udpParams.dst.addr),
+ eq(uint16(udpParams.dst.port)));
+ }
+
+ private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
+ final NatTimeoutUpdate params = new NatTimeoutUpdate();
+ params.proto = proto;
+ params.src.addr = "192.168.43.200";
+ params.src.port = 100;
+ params.dst.addr = "172.50.46.169";
+ params.dst.port = 150;
+ return params;
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
new file mode 100644
index 0000000..93efd49
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -0,0 +1,254 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.ip.IpServer;
+import android.net.util.NetworkConstants;
+import android.net.util.PrefixUtils;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class PrivateAddressCoordinatorTest {
+ private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
+ private static final String TEST_WIFI_IFNAME = "test_wlan0";
+
+ @Mock private IpServer mHotspotIpServer;
+ @Mock private IpServer mUsbIpServer;
+ @Mock private IpServer mEthernetIpServer;
+ @Mock private Context mContext;
+ @Mock private ConnectivityManager mConnectivityMgr;
+
+ private PrivateAddressCoordinator mPrivateAddressCoordinator;
+ private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+ private final Network mWifiNetwork = new Network(1);
+ private final Network mMobileNetwork = new Network(2);
+ private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
+ when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
+ mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext));
+ }
+
+ @Test
+ public void testDownstreamPrefixRequest() throws Exception {
+ LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+ assertNotEquals(hotspotPrefix, mBluetoothPrefix);
+
+ address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ final IpPrefix testDupRequest = PrefixUtils.asIpPrefix(address);
+ assertNotEquals(hotspotPrefix, testDupRequest);
+ assertNotEquals(mBluetoothPrefix, testDupRequest);
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+ address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mUsbIpServer);
+ final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
+ assertNotEquals(usbPrefix, mBluetoothPrefix);
+ assertNotEquals(usbPrefix, hotspotPrefix);
+ mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
+ }
+
+ @Test
+ public void testRequestDownstreamAddress() throws Exception {
+ LinkAddress expectedAddress = new LinkAddress("192.168.43.42/24");
+ int fakeSubAddr = 0x2b00;
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+ LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ assertEquals(actualAddress, expectedAddress);
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+ fakeSubAddr = 0x2b01;
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+ actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ assertEquals(actualAddress, expectedAddress);
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+ fakeSubAddr = 0x2bff;
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+ actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ assertEquals(actualAddress, expectedAddress);
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+ expectedAddress = new LinkAddress("192.168.43.5/24");
+ fakeSubAddr = 0x2b05;
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+ actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ assertEquals(actualAddress, expectedAddress);
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+ }
+
+ @Test
+ public void testReserveBluetoothPrefix() throws Exception {
+ final int fakeSubAddr = 0x2c05;
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+ LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+ assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix);
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+ }
+
+ @Test
+ public void testNoConflictDownstreamPrefix() throws Exception {
+ final int fakeHotspotSubAddr = 0x2b05;
+ final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+ LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+ assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
+ when(mHotspotIpServer.getAddress()).thenReturn(address);
+
+ address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mUsbIpServer);
+ final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
+ assertNotEquals(predefinedPrefix, usbPrefix);
+
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+ mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
+ address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mUsbIpServer);
+ final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
+ assertEquals("Fail to reselect available perfix: ", predefinedPrefix, allowUseFreePrefix);
+ }
+
+ private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
+ boolean isMobile) {
+ final String testIface;
+ final String testIpv4Address;
+ if (isMobile) {
+ testIface = TEST_MOBILE_IFNAME;
+ testIpv4Address = "10.0.0.1";
+ } else {
+ testIface = TEST_WIFI_IFNAME;
+ testIpv4Address = "192.168.43.5";
+ }
+
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(testIface);
+
+ if (withIPv4) {
+ prop.addLinkAddress(
+ new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address),
+ NetworkConstants.IPV4_ADDR_BITS));
+ }
+
+ if (withIPv6) {
+ prop.addLinkAddress(
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
+ NetworkConstants.RFC7421_PREFIX_LENGTH));
+ }
+ return prop;
+ }
+
+ @Test
+ public void testNoConflictUpstreamPrefix() throws Exception {
+ final int fakeHotspotSubId = 43;
+ final int fakeHotspotSubAddr = 0x2b05;
+ final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
+ // Force always get subAddress "43.5" for conflict testing.
+ when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+ // 1. Enable hotspot with prefix 192.168.43.0/24
+ final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
+ assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
+ when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
+ // 2. Update v6 only mobile network, hotspot prefix should not be removed.
+ List<String> testConflicts;
+ final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp);
+ verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
+ // 3. Update v4 only mobile network, hotspot prefix should not be removed.
+ final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp);
+ verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ // 4. Update v4v6 mobile network, hotspot prefix should not be removed.
+ final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp);
+ verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ // 5. Update v6 only wifi network, hotspot prefix should not be removed.
+ final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp);
+ verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
+ // 6. Update v4 only wifi network, it conflict with hotspot prefix.
+ final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
+ verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ reset(mHotspotIpServer);
+ // 7. Restart hotspot again and its prefix is different previous.
+ mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+ final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mHotspotIpServer);
+ final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
+ assertNotEquals(hotspotPrefix, hotspotPrefix2);
+ when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
+ verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ // 7. Usb tethering can be enabled and its prefix is different with conflict one.
+ final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mUsbIpServer);
+ final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
+ assertNotEquals(predefinedPrefix, usbPrefix);
+ assertNotEquals(hotspotPrefix2, usbPrefix);
+ when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
+ // 8. Disable wifi upstream, then wifi's prefix can be selected again.
+ mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
+ final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
+ mEthernetIpServer);
+ final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
+ assertEquals(predefinedPrefix, ethPrefix);
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index fbfa871..1999ad7 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -30,7 +30,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -109,9 +108,9 @@
.mockStatic(DeviceConfig.class)
.strictness(Strictness.WARN)
.startMocking();
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn(null).when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
new String[0]);
@@ -328,9 +327,9 @@
public void testNewDhcpServerDisabled() {
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
true);
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn("false").when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
final TetheringConfiguration enableByRes =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -338,9 +337,9 @@
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
- doReturn(true).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn("true").when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
final TetheringConfiguration enableByDevConfig =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -351,9 +350,9 @@
public void testNewDhcpServerEnabled() {
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
- doReturn(false).when(
- () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+ doReturn("false").when(
+ () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
final TetheringConfiguration cfg =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 745468f..7d5471f 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -130,7 +130,7 @@
context = TestContext(InstrumentationRegistry.getInstrumentation().context)
doReturn(notificationManager).`when`(mockContext)
.getSystemService(Context.NOTIFICATION_SERVICE)
- fakeTetheringThread = HandlerThread(this::class.simpleName)
+ fakeTetheringThread = HandlerThread(this::class.java.simpleName)
fakeTetheringThread.start()
notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper)
setupResources()
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 7df9fc6..7bba67b 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -16,20 +16,29 @@
package com.android.networkstack.tethering;
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.Manifest.permission.WRITE_SETTINGS;
import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.app.UiAutomation;
import android.content.Intent;
import android.net.IIntResultListener;
import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
import android.net.TetheringRequestParcel;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.ResultReceiver;
import androidx.test.InstrumentationRegistry;
@@ -51,13 +60,14 @@
@SmallTest
public final class TetheringServiceTest {
private static final String TEST_IFACE_NAME = "test_wlan0";
- private static final String TEST_CALLER_PKG = "test_pkg";
+ private static final String TEST_CALLER_PKG = "com.android.shell";
private static final String TEST_ATTRIBUTION_TAG = null;
@Mock private ITetheringEventCallback mITetheringEventCallback;
@Rule public ServiceTestRule mServiceTestRule;
private Tethering mTethering;
private Intent mMockServiceIntent;
private ITetheringConnector mTetheringConnector;
+ private UiAutomation mUiAutomation;
private class TestTetheringResult extends IIntResultListener.Stub {
private int mResult = -1; // Default value that does not match any result code.
@@ -71,9 +81,26 @@
}
}
+ private class MyResultReceiver extends ResultReceiver {
+ MyResultReceiver(Handler handler) {
+ super(handler);
+ }
+ private int mResult = -1; // Default value that does not match any result code.
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mResult = resultCode;
+ }
+
+ public void assertResult(int expected) {
+ assertEquals(expected, mResult);
+ }
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
mServiceTestRule = new ServiceTestRule();
mMockServiceIntent = new Intent(
InstrumentationRegistry.getTargetContext(),
@@ -83,116 +110,375 @@
mTetheringConnector = mockConnector.getTetheringConnector();
final MockTetheringService service = mockConnector.getService();
mTethering = service.getTethering();
- verify(mTethering).startStateMachineUpdaters();
- when(mTethering.hasTetherableConfiguration()).thenReturn(true);
}
@After
public void tearDown() throws Exception {
mServiceTestRule.unbindService();
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ private interface TestTetheringCall {
+ void runTetheringCall(TestTetheringResult result) throws Exception;
+ }
+
+ private void runAsNoPermission(final TestTetheringCall test) throws Exception {
+ runTetheringCall(test, new String[0]);
+ }
+
+ private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
+ runTetheringCall(test, TETHER_PRIVILEGED);
+ }
+
+ private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
+ runTetheringCall(test, ACCESS_NETWORK_STATE);
+ }
+
+ private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
+ runTetheringCall(test, WRITE_SETTINGS);
+ }
+
+ private void runTetheringCall(final TestTetheringCall test, String... permissions)
+ throws Exception {
+ if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions);
+ try {
+ when(mTethering.isTetheringSupported()).thenReturn(true);
+ test.runTetheringCall(new TestTetheringResult());
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private void verifyNoMoreInteractionsForTethering() {
+ verifyNoMoreInteractions(mTethering);
+ verifyNoMoreInteractions(mITetheringEventCallback);
+ reset(mTethering, mITetheringEventCallback);
+ }
+
+ private void runTether(final TestTetheringResult result) throws Exception {
+ when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
+ mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).tether(TEST_IFACE_NAME);
+ result.assertResult(TETHER_ERROR_NO_ERROR);
}
@Test
public void testTether() throws Exception {
- when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
- final TestTetheringResult result = new TestTetheringResult();
- mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).hasTetherableConfiguration();
- verify(mTethering).tether(TEST_IFACE_NAME);
- verifyNoMoreInteractions(mTethering);
+ runAsNoPermission((result) -> {
+ mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runTether(result);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runTether(result);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runUnTether(final TestTetheringResult result) throws Exception {
+ when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
+ mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).untether(TEST_IFACE_NAME);
result.assertResult(TETHER_ERROR_NO_ERROR);
}
@Test
public void testUntether() throws Exception {
- when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
- final TestTetheringResult result = new TestTetheringResult();
- mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).hasTetherableConfiguration();
- verify(mTethering).untether(TEST_IFACE_NAME);
- verifyNoMoreInteractions(mTethering);
+ runAsNoPermission((result) -> {
+ mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runUnTether(result);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runUnTether(result);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runSetUsbTethering(final TestTetheringResult result) throws Exception {
+ when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
+ mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).setUsbTethering(true /* enable */);
result.assertResult(TETHER_ERROR_NO_ERROR);
}
@Test
public void testSetUsbTethering() throws Exception {
- when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
- final TestTetheringResult result = new TestTetheringResult();
- mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
- TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).hasTetherableConfiguration();
- verify(mTethering).setUsbTethering(true /* enable */);
- verifyNoMoreInteractions(mTethering);
- result.assertResult(TETHER_ERROR_NO_ERROR);
+ runAsNoPermission((result) -> {
+ mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runSetUsbTethering(result);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runSetUsbTethering(result);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ }
+
+ private void runStartTethering(final TestTetheringResult result,
+ final TetheringRequestParcel request) throws Exception {
+ mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).startTethering(eq(request), eq(result));
}
@Test
public void testStartTethering() throws Exception {
- final TestTetheringResult result = new TestTetheringResult();
final TetheringRequestParcel request = new TetheringRequestParcel();
request.tetheringType = TETHERING_WIFI;
- mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).hasTetherableConfiguration();
- verify(mTethering).startTethering(eq(request), eq(result));
- verifyNoMoreInteractions(mTethering);
+
+ runAsNoPermission((result) -> {
+ mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runStartTethering(result, request);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runStartTethering(result, request);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result)
+ throws Exception {
+ final TetheringRequestParcel request = new TetheringRequestParcel();
+ request.tetheringType = TETHERING_WIFI;
+ request.exemptFromEntitlementCheck = true;
+ mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
}
@Test
- public void testStopTethering() throws Exception {
- final TestTetheringResult result = new TestTetheringResult();
- mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).hasTetherableConfiguration();
+ public void testFailToBypassEntitlementWithoutNeworkStackPermission() throws Exception {
+ final TetheringRequestParcel request = new TetheringRequestParcel();
+ request.tetheringType = TETHERING_WIFI;
+ request.exemptFromEntitlementCheck = true;
+
+ runAsNoPermission((result) -> {
+ runStartTetheringAndVerifyNoPermission(result);
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runStartTetheringAndVerifyNoPermission(result);
+ });
+
+ runAsWriteSettings((result) -> {
+ runStartTetheringAndVerifyNoPermission(result);
+ });
+ }
+
+ private void runStopTethering(final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetheringSupported();
verify(mTethering).stopTethering(TETHERING_WIFI);
- verifyNoMoreInteractions(mTethering);
result.assertResult(TETHER_ERROR_NO_ERROR);
}
@Test
- public void testRequestLatestTetheringEntitlementResult() throws Exception {
- final ResultReceiver result = new ResultReceiver(null);
+ public void testStopTethering() throws Exception {
+ runAsNoPermission((result) -> {
+ mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runStopTethering(result);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runStopTethering(result);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runRequestLatestTetheringEntitlementResult() throws Exception {
+ final MyResultReceiver result = new MyResultReceiver(null);
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).hasTetherableConfiguration();
+ verify(mTethering).isTetheringSupported();
verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
eq(result), eq(true) /* showEntitlementUi */);
+ }
+
+ @Test
+ public void testRequestLatestTetheringEntitlementResult() throws Exception {
+ // Run as no permission.
+ final MyResultReceiver result = new MyResultReceiver(null);
+ mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
+ true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractions(mTethering);
+
+ runAsTetherPrivileged((none) -> {
+ runRequestLatestTetheringEntitlementResult();
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((none) -> {
+ runRequestLatestTetheringEntitlementResult();
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runRegisterTetheringEventCallback() throws Exception {
+ mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+ TEST_CALLER_PKG);
+ verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback));
}
@Test
public void testRegisterTetheringEventCallback() throws Exception {
- mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+ runAsNoPermission((result) -> {
+ mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+ TEST_CALLER_PKG);
+ verify(mITetheringEventCallback).onCallbackStopped(
+ TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((none) -> {
+ runRegisterTetheringEventCallback();
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsAccessNetworkState((none) -> {
+ runRegisterTetheringEventCallback();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runUnregisterTetheringEventCallback() throws Exception {
+ mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
TEST_CALLER_PKG);
- verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback));
- verifyNoMoreInteractions(mTethering);
+ verify(mTethering).unregisterTetheringEventCallback(eq(mITetheringEventCallback));
}
@Test
public void testUnregisterTetheringEventCallback() throws Exception {
- mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
- TEST_CALLER_PKG);
- verify(mTethering).unregisterTetheringEventCallback(
- eq(mITetheringEventCallback));
- verifyNoMoreInteractions(mTethering);
+ runAsNoPermission((result) -> {
+ mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
+ TEST_CALLER_PKG);
+ verify(mITetheringEventCallback).onCallbackStopped(
+ TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((none) -> {
+ runUnregisterTetheringEventCallback();
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsAccessNetworkState((none) -> {
+ runUnregisterTetheringEventCallback();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runStopAllTethering(final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).untetherAll();
+ result.assertResult(TETHER_ERROR_NO_ERROR);
}
@Test
public void testStopAllTethering() throws Exception {
- final TestTetheringResult result = new TestTetheringResult();
- mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).hasTetherableConfiguration();
- verify(mTethering).untetherAll();
- verifyNoMoreInteractions(mTethering);
+ runAsNoPermission((result) -> {
+ mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runStopAllTethering(result);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runStopAllTethering(result);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+ }
+
+ private void runIsTetheringSupported(final TestTetheringResult result) throws Exception {
+ mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).isTetheringSupported();
result.assertResult(TETHER_ERROR_NO_ERROR);
}
@Test
public void testIsTetheringSupported() throws Exception {
- final TestTetheringResult result = new TestTetheringResult();
- mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).hasTetherableConfiguration();
- verifyNoMoreInteractions(mTethering);
- result.assertResult(TETHER_ERROR_NO_ERROR);
+ runAsNoPermission((result) -> {
+ mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering).isTetherProvisioningRequired();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsTetherPrivileged((result) -> {
+ runIsTetheringSupported(result);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsWriteSettings((result) -> {
+ runIsTetheringSupported(result);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 85c2f2b..329d8a5 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -32,6 +32,7 @@
import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
@@ -84,6 +85,7 @@
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
+import android.net.EthernetManager.TetheredInterfaceCallback;
import android.net.EthernetManager.TetheredInterfaceRequest;
import android.net.IIntResultListener;
import android.net.INetd;
@@ -169,9 +171,11 @@
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
- private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ private static final String TEST_WIFI_IFNAME = "test_wlan0";
+ private static final String TEST_WLAN_IFNAME = "test_wlan1";
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
private static final String TEST_NCM_IFNAME = "test_ncm0";
+ private static final String TEST_ETH_IFNAME = "test_eth0";
private static final String TETHERING_NAME = "Tethering";
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
@@ -199,6 +203,7 @@
@Mock private ConnectivityManager mCm;
@Mock private EthernetManager mEm;
@Mock private TetheringNotificationUpdater mNotificationUpdater;
+ @Mock private BpfCoordinator mBpfCoordinator;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -279,10 +284,11 @@
|| ifName.equals(TEST_WLAN_IFNAME)
|| ifName.equals(TEST_MOBILE_IFNAME)
|| ifName.equals(TEST_P2P_IFNAME)
- || ifName.equals(TEST_NCM_IFNAME));
+ || ifName.equals(TEST_NCM_IFNAME)
+ || ifName.equals(TEST_ETH_IFNAME));
final String[] ifaces = new String[] {
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
- TEST_NCM_IFNAME};
+ TEST_NCM_IFNAME, TEST_ETH_IFNAME};
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
}
@@ -312,8 +318,8 @@
}
@Override
- protected boolean getDeviceConfigBoolean(final String name) {
- return false;
+ protected String getDeviceConfigProperty(final String name) {
+ return null;
}
@Override
@@ -332,6 +338,12 @@
}
@Override
+ public BpfCoordinator getBpfCoordinator(Handler handler, INetd netd,
+ SharedLog log, BpfCoordinator.Dependencies deps) {
+ return mBpfCoordinator;
+ }
+
+ @Override
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return mOffloadHardwareInterface;
}
@@ -485,24 +497,12 @@
MockitoAnnotations.initMocks(this);
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[] { "test_rndis\\d" });
- when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
- .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
- .thenReturn(new String[] { "test_ncm\\d" });
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
- TEST_NCM_IFNAME});
+ TEST_NCM_IFNAME, TEST_ETH_IFNAME});
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0];
@@ -515,6 +515,7 @@
mServiceContext = new TestContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ setTetheringSupported(true /* supported */);
mIntents = new Vector<>();
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -525,7 +526,6 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTethering = makeTethering();
- mTethering.startStateMachineUpdaters();
verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
verify(mNetd).registerUnsolicitedEventListener(any());
final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
@@ -536,6 +536,31 @@
mPhoneStateListener = phoneListenerCaptor.getValue();
}
+ private void setTetheringSupported(final boolean supported) {
+ Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED,
+ supported ? 1 : 0);
+ when(mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported);
+ // Setup tetherable configuration.
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+ .thenReturn(new String[] { "test_rndis\\d" });
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+ .thenReturn(new String[]{ "test_wlan\\d" });
+ when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
+ .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
+ .thenReturn(new String[] { "test_ncm\\d" });
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+ when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
+ }
+
+ private void initTetheringUpstream(UpstreamNetworkState upstreamState) {
+ when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ }
+
private Tethering makeTethering() {
mTetheringDependencies.reset();
return new Tethering(mTetheringDependencies);
@@ -672,9 +697,7 @@
}
private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
- when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
- .thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null);
@@ -700,7 +723,7 @@
verify(mNetd, times(1)).interfaceGetList();
// UpstreamNetworkMonitor should receive selected upstream
- verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+ verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
@@ -872,8 +895,7 @@
// Then 464xlat comes up
upstreamState = buildMobile464xlatUpstreamState();
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
- .thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
// Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
@@ -1344,9 +1366,7 @@
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
// 2. Enable wifi tethering.
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
- .thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
mLooper.dispatchAll();
@@ -1723,7 +1743,7 @@
final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
stateMachine.chooseUpstreamType(true);
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
@@ -1735,7 +1755,7 @@
final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ initTetheringUpstream(upstreamState);
stateMachine.chooseUpstreamType(true);
stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
@@ -1828,6 +1848,109 @@
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
sendConfigurationChanged();
}
+
+ private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address,
+ final int prefixLength, final Network network) {
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_WIFI_IFNAME);
+
+ prop.addLinkAddress(
+ new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address),
+ prefixLength));
+
+ final NetworkCapabilities capabilities = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ return new UpstreamNetworkState(prop, capabilities, network);
+ }
+
+ @Test
+ public void testHandleIpConflict() throws Exception {
+ final Network wifiNetwork = new Network(200);
+ final Network[] allNetworks = { wifiNetwork };
+ when(mCm.getAllNetworks()).thenReturn(allNetworks);
+ UpstreamNetworkState upstreamNetwork = null;
+ runUsbTethering(upstreamNetwork);
+ final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
+ ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
+ verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
+ final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr;
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
+ reset(mNetd, mUsbManager);
+ upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork);
+ mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
+ Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+ 0,
+ upstreamNetwork);
+ mLooper.dispatchAll();
+ // verify trun off usb tethering
+ verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ mTethering.interfaceRemoved(TEST_USB_IFNAME);
+ mLooper.dispatchAll();
+ // verify restart usb tethering
+ verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+ }
+
+ @Test
+ public void testNoAddressAvailable() throws Exception {
+ final Network wifiNetwork = new Network(200);
+ final Network[] allNetworks = { wifiNetwork };
+ when(mCm.getAllNetworks()).thenReturn(allNetworks);
+ final String upstreamAddress = "192.168.0.100";
+ runUsbTethering(null);
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
+ reset(mUsbManager);
+ final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
+ when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
+ final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
+ ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
+ mLooper.dispatchAll();
+ verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
+ TetheredInterfaceCallback ethCallback = callbackCaptor.getValue();
+ ethCallback.onAvailable(TEST_ETH_IFNAME);
+ mLooper.dispatchAll();
+ reset(mUsbManager, mEm);
+
+ final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState(
+ upstreamAddress, 16, wifiNetwork);
+ mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
+ Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+ 0,
+ upstreamNetwork);
+ mLooper.dispatchAll();
+ // verify trun off usb tethering
+ verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ // verify trun off ethernet tethering
+ verify(mockRequest).release();
+ mTethering.interfaceRemoved(TEST_USB_IFNAME);
+ ethCallback.onUnavailable();
+ mLooper.dispatchAll();
+ // verify restart usb tethering
+ verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+ // verify restart ethernet tethering
+ verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
+ ethCallback = callbackCaptor.getValue();
+ ethCallback.onAvailable(TEST_ETH_IFNAME);
+
+ reset(mUsbManager, mEm);
+ when(mNetd.interfaceGetList())
+ .thenReturn(new String[] {
+ TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
+ TEST_NCM_IFNAME, TEST_ETH_IFNAME});
+
+ mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+ sendUsbBroadcast(true, true, true, TETHERING_USB);
+ mLooper.dispatchAll();
+ assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME);
+ assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME);
+ assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME));
+ assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME));
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/services/Android.bp b/services/Android.bp
index 0d221b9..57d9a38 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -72,7 +72,7 @@
libs: [
"android.hidl.manager-V1.0-java",
- "framework-tethering-stubs-module_libs_api",
+ "framework-tethering.stubs.module_lib",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d9a5349..9cfa03e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -28,9 +28,9 @@
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
- "android.net.ipsec.ike.stubs.module_libs_api",
+ "android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
- "framework-tethering-stubs-module_libs_api",
+ "framework-tethering.stubs.module_lib",
],
required: [
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 635f184..483d4b6 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1374,10 +1374,9 @@
if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
return;
}
- String action = blocked ? "BLOCKED" : "UNBLOCKED";
- log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
- nri.mUid, nri.request.requestId, net.netId));
- mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+ final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ mNetworkInfoBlockingLogs.log(String.format(
+ "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.netId));
}
/**
@@ -3079,23 +3078,7 @@
@Override
public void notifyDataStallSuspected(DataStallReportParcelable p) {
- final PersistableBundle extras = new PersistableBundle();
- switch (p.detectionMethod) {
- case DETECTION_METHOD_DNS_EVENTS:
- extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, p.dnsConsecutiveTimeouts);
- break;
- case DETECTION_METHOD_TCP_METRICS:
- extras.putInt(KEY_TCP_PACKET_FAIL_RATE, p.tcpPacketFailRate);
- extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS,
- p.tcpMetricsCollectionPeriodMillis);
- break;
- default:
- log("Unknown data stall detection method, ignoring: " + p.detectionMethod);
- return;
- }
-
- proxyDataStallToConnectivityDiagnosticsHandler(
- p.detectionMethod, mNetId, p.timestampMillis, extras);
+ ConnectivityService.this.notifyDataStallSuspected(p, mNetId);
}
@Override
@@ -3109,11 +3092,25 @@
}
}
- private void proxyDataStallToConnectivityDiagnosticsHandler(int detectionMethod, int netId,
- long timestampMillis, @NonNull PersistableBundle extras) {
+ private void notifyDataStallSuspected(DataStallReportParcelable p, int netId) {
+ log("Data stall detected with methods: " + p.detectionMethod);
+
+ final PersistableBundle extras = new PersistableBundle();
+ int detectionMethod = 0;
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_DNS_EVENTS)) {
+ extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, p.dnsConsecutiveTimeouts);
+ detectionMethod |= DETECTION_METHOD_DNS_EVENTS;
+ }
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_TCP_METRICS)) {
+ extras.putInt(KEY_TCP_PACKET_FAIL_RATE, p.tcpPacketFailRate);
+ extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS,
+ p.tcpMetricsCollectionPeriodMillis);
+ detectionMethod |= DETECTION_METHOD_TCP_METRICS;
+ }
+
final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
- ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
- detectionMethod, netId, timestampMillis);
+ ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, detectionMethod, netId,
+ p.timestampMillis);
msg.setData(new Bundle(extras));
// NetworkStateTrackerHandler currently doesn't take any actions based on data
@@ -3122,6 +3119,10 @@
mConnectivityDiagnosticsHandler.sendMessage(msg);
}
+ private boolean hasDataStallDetectionMethod(DataStallReportParcelable p, int detectionMethod) {
+ return (p.detectionMethod & detectionMethod) != 0;
+ }
+
private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
return isPrivateDnsValidationRequired(nai.networkCapabilities);
}
@@ -8181,7 +8182,24 @@
+ "creators");
}
- proxyDataStallToConnectivityDiagnosticsHandler(
- detectionMethod, network.netId, timestampMillis, extras);
+ // Instead of passing the data stall directly to the ConnectivityDiagnostics handler, treat
+ // this as a Data Stall received directly from NetworkMonitor. This requires wrapping the
+ // Data Stall information as a DataStallReportParcelable and passing to
+ // #notifyDataStallSuspected. This ensures that unknown Data Stall detection methods are
+ // still passed to ConnectivityDiagnostics (with new detection methods masked).
+ final DataStallReportParcelable p = new DataStallReportParcelable();
+ p.timestampMillis = timestampMillis;
+ p.detectionMethod = detectionMethod;
+
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_DNS_EVENTS)) {
+ p.dnsConsecutiveTimeouts = extras.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS);
+ }
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_TCP_METRICS)) {
+ p.tcpPacketFailRate = extras.getInt(KEY_TCP_PACKET_FAIL_RATE);
+ p.tcpMetricsCollectionPeriodMillis = extras.getInt(
+ KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS);
+ }
+
+ notifyDataStallSuspected(p, network.netId);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ece21f6..166c4f3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12857,7 +12857,9 @@
pw.print(" unmapped + ");
pw.print(stringifyKBSize(ionPool));
pw.println(" pools)");
- kernelUsed += ionUnmapped;
+ // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+ // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+ kernelUsed += ionHeap;
}
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
@@ -13594,7 +13596,9 @@
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
- kernelUsed += ionUnmapped;
+ // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+ // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+ kernelUsed += ionHeap;
}
memInfoBuilder.append(" Used RAM: ");
memInfoBuilder.append(stringifyKBSize(
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 34d0bed..3091a71 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -198,6 +198,9 @@
if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
stopPrefixDiscovery();
}
+ if (!mPrefixDiscoveryRunning) {
+ setPrefix64(mNat64PrefixInUse);
+ }
}
/**
@@ -221,6 +224,10 @@
mIface = null;
mBaseIface = null;
+ if (!mPrefixDiscoveryRunning) {
+ setPrefix64(null);
+ }
+
if (isPrefixDiscoveryNeeded()) {
if (!mPrefixDiscoveryRunning) {
startPrefixDiscovery();
@@ -308,6 +315,16 @@
return requiresClat(mNetwork) && mNat64PrefixFromRa == null;
}
+ private void setPrefix64(IpPrefix prefix) {
+ final String prefixString = (prefix != null) ? prefix.toString() : "";
+ try {
+ mDnsResolver.setPrefix64(getNetId(), prefixString);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+ + prefix + ": " + e);
+ }
+ }
+
private void maybeHandleNat64PrefixChange() {
final IpPrefix newPrefix = selectNat64Prefix();
if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0114b5b..0cda7ca 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -65,6 +65,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
+import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnManager;
@@ -2225,12 +2226,27 @@
@Override
public void run() {
- // Explicitly use only the network that ConnectivityService thinks is the "best." In
- // other words, only ever use the currently selected default network. This does mean
- // that in both onLost() and onConnected(), any old sessions MUST be torn down. This
- // does NOT include VPNs.
+ // Unless the profile is restricted to test networks, explicitly use only the network
+ // that ConnectivityService thinks is the "best." In other words, only ever use the
+ // currently selected default network. This does mean that in both onLost() and
+ // onConnected(), any old sessions MUST be torn down. This does NOT include VPNs.
+ //
+ // When restricted to test networks, select any network with TRANSPORT_TEST. Since the
+ // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
+ // this is considered safe.
final ConnectivityManager cm = ConnectivityManager.from(mContext);
- cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
+ final NetworkRequest req;
+
+ if (mProfile.isRestrictedToTestNetworks()) {
+ req = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .build();
+ } else {
+ req = cm.getDefaultRequest();
+ }
+
+ cm.requestNetwork(req, mNetworkCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -2851,6 +2867,23 @@
return Credentials.PLATFORM_VPN + mUserHandle + "_" + packageName;
}
+ @VisibleForTesting
+ void validateRequiredFeatures(VpnProfile profile) {
+ switch (profile.type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_IPSEC_TUNNELS)) {
+ throw new UnsupportedOperationException(
+ "Ikev2VpnProfile(s) requires PackageManager.FEATURE_IPSEC_TUNNELS");
+ }
+ break;
+ default:
+ return;
+ }
+ }
+
/**
* Stores an app-provisioned VPN profile and returns whether the app is already prepared.
*
@@ -2867,6 +2900,12 @@
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
+ validateRequiredFeatures(profile);
+
+ if (profile.isRestrictedToTestNetworks) {
+ mContext.enforceCallingPermission(Manifest.permission.MANAGE_TEST_NETWORKS,
+ "Test-mode profiles require the MANAGE_TEST_NETWORKS permission");
+ }
final byte[] encodedProfile = profile.encode();
if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 3dac106..86ad0b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -152,12 +152,10 @@
/**
* Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
- * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
+ * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
*/
- public void apply464xlatAdjustments(NetworkStats baseTraffic,
- NetworkStats stackedTraffic, boolean useBpfStats) {
- NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces,
- useBpfStats);
+ public void apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic) {
+ NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
}
public NetworkStatsFactory() {
@@ -380,7 +378,7 @@
// network, the overhead is their fault.
// No locking here: apply464xlatAdjustments behaves fine with an add-only
// ConcurrentHashMap.
- delta.apply464xlatAdjustments(mStackedIfaces, mUseBpfStats);
+ delta.apply464xlatAdjustments(mStackedIfaces);
// Migrate data usage over a VPN to the TUN network.
for (VpnInfo info : vpnArray) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 5d1cc2a..ba538e1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1306,21 +1306,39 @@
}
// Traffic occurring on stacked interfaces is usually clatd.
- // UID stats are always counted on the stacked interface and never
- // on the base interface, because the packets on the base interface
- // do not actually match application sockets until they are translated.
//
- // Interface stats are more complicated. Packets subject to BPF offload
- // never appear on the base interface and only appear on the stacked
- // interface, so to ensure those packets increment interface stats, interface
- // stats from stacked interfaces must be collected.
+ // UID stats are always counted on the stacked interface and never on the base
+ // interface, because the packets on the base interface do not actually match
+ // application sockets (they're not IPv4) and thus the app uid is not known.
+ // For receive this is obvious: packets must be translated from IPv6 to IPv4
+ // before the application socket can be found.
+ // For transmit: either they go through the clat daemon which by virtue of going
+ // through userspace strips the original socket association during the IPv4 to
+ // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+ // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+ // which don't trigger again post ebpf translation.
+ // (as such stats accounted to the clat uid are ignored)
+ //
+ // Interface stats are more complicated.
+ //
+ // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+ // *all* statistics are collected by iptables on the stacked v4-* interface.
+ //
+ // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+ // in ip6tables raw prerouting and thus even non-offloaded packets are only
+ // accounted for on the stacked interface.
+ //
+ // For egress, packets subject to eBPF offload never appear on the base interface
+ // and only appear on the stacked interface. Thus to ensure packets increment
+ // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+ // (or non eBPF offloaded) TX they would appear on both, however egress interface
+ // accounting is explicitly bypassed for traffic from the clat uid.
+ //
final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
if (stackedIface != null) {
- if (mUseBpfTrafficStats) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
- }
+ findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
if (isMobile) {
mobileIfaces.add(stackedIface);
@@ -1859,14 +1877,13 @@
// fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
- mStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
- mUseBpfTrafficStats);
+ mStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
uidSnapshot.combineAllValues(tetherSnapshot);
// get a stale copy of uid stats snapshot provided by providers.
final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
providerStats.filter(UID_ALL, ifaces, TAG_ALL);
- mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats, mUseBpfTrafficStats);
+ mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats);
uidSnapshot.combineAllValues(providerStats);
uidSnapshot.combineAllValues(mUidOperations);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 341a88d..4bb17a2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -33,9 +33,9 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.sysprop.ApexProperties;
import android.util.Slog;
@@ -73,12 +73,7 @@
*/
static ApexManager create(Context systemContext) {
if (ApexProperties.updatable().orElse(false)) {
- try {
- return new ApexManagerImpl(systemContext, IApexService.Stub.asInterface(
- ServiceManager.getServiceOrThrow("apexservice")));
- } catch (ServiceManager.ServiceNotFoundException e) {
- throw new IllegalStateException("Required service apexservice not available");
- }
+ return new ApexManagerImpl(systemContext);
} else {
return new ApexManagerFlattenedApex();
}
@@ -247,8 +242,7 @@
* APEX packages.
*/
@VisibleForTesting
- static class ApexManagerImpl extends ApexManager {
- private final IApexService mApexService;
+ protected static class ApexManagerImpl extends ApexManager {
private final Context mContext;
private final Object mLock = new Object();
/**
@@ -261,9 +255,8 @@
@GuardedBy("mLock")
private List<PackageInfo> mAllPackagesCache;
- ApexManagerImpl(Context context, IApexService apexService) {
+ ApexManagerImpl(Context context) {
mContext = context;
- mApexService = apexService;
}
/**
@@ -286,10 +279,23 @@
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ /**
+ * Retrieve the service from ServiceManager. If the service is not running, it will be
+ * started, and this function will block until it is ready.
+ */
+ @VisibleForTesting
+ protected IApexService waitForApexService() {
+ try {
+ return IApexService.Stub.asInterface(Binder.waitForService("apexservice"));
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Required service apexservice not available");
+ }
+ }
+
@Override
List<ActiveApexInfo> getActiveApexInfos() {
try {
- return Arrays.stream(mApexService.getActivePackages())
+ return Arrays.stream(waitForApexService().getActivePackages())
.map(apexInfo -> new ActiveApexInfo(
new File(
Environment.getApexDirectory() + File.separator
@@ -324,7 +330,7 @@
mAllPackagesCache = new ArrayList<>();
HashSet<String> activePackagesSet = new HashSet<>();
HashSet<String> factoryPackagesSet = new HashSet<>();
- final ApexInfo[] allPkgs = mApexService.getAllPackages();
+ final ApexInfo[] allPkgs = waitForApexService().getAllPackages();
for (ApexInfo ai : allPkgs) {
// If the device is using flattened APEX, don't report any APEX
// packages since they won't be managed or updated by PackageManager.
@@ -431,7 +437,8 @@
@Override
@Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
try {
- ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
+ ApexSessionInfo apexSessionInfo =
+ waitForApexService().getStagedSessionInfo(sessionId);
if (apexSessionInfo.isUnknown) {
return null;
}
@@ -450,7 +457,7 @@
ApexSessionParams params = new ApexSessionParams();
params.sessionId = sessionId;
params.childSessionIds = childSessionIds;
- mApexService.submitStagedSession(params, apexInfoList);
+ waitForApexService().submitStagedSession(params, apexInfoList);
return apexInfoList;
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
@@ -465,7 +472,7 @@
@Override
void markStagedSessionReady(int sessionId) throws PackageManagerException {
try {
- mApexService.markStagedSessionReady(sessionId);
+ waitForApexService().markStagedSessionReady(sessionId);
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
throw new RuntimeException(re);
@@ -479,7 +486,7 @@
@Override
void markStagedSessionSuccessful(int sessionId) {
try {
- mApexService.markStagedSessionSuccessful(sessionId);
+ waitForApexService().markStagedSessionSuccessful(sessionId);
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
throw new RuntimeException(re);
@@ -498,7 +505,7 @@
@Override
boolean revertActiveSessions() {
try {
- mApexService.revertActiveSessions();
+ waitForApexService().revertActiveSessions();
return true;
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
@@ -512,7 +519,7 @@
@Override
boolean abortStagedSession(int sessionId) throws PackageManagerException {
try {
- mApexService.abortStagedSession(sessionId);
+ waitForApexService().abortStagedSession(sessionId);
return true;
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
@@ -527,7 +534,7 @@
@Override
boolean uninstallApex(String apexPackagePath) {
try {
- mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
+ waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
return true;
} catch (Exception e) {
return false;
@@ -578,7 +585,7 @@
ipw.increaseIndent();
ipw.println("APEX session state:");
ipw.increaseIndent();
- final ApexSessionInfo[] sessions = mApexService.getSessions();
+ final ApexSessionInfo[] sessions = waitForApexService().getSessions();
for (ApexSessionInfo si : sessions) {
ipw.println("Session ID: " + si.sessionId);
ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 72fc189..9d56263 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -28,12 +28,14 @@
import android.graphics.Bitmap.Config;
import android.os.Process;
import android.os.SystemClock;
+import android.os.UserManagerInternal;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.AtomicFile;
+import com.android.server.LocalServices;
import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
import java.io.File;
@@ -73,6 +75,7 @@
private boolean mStarted;
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
+ private final UserManagerInternal mUserManagerInternal;
private final float mReducedScale;
/**
@@ -84,6 +87,7 @@
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver;
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
if (service.mLowRamTaskSnapshotsAndRecents) {
// Use very low res snapshots if we are using Go version of recents.
mReducedScale = LOW_RAM_RECENTS_REDUCED_SCALE;
@@ -172,7 +176,7 @@
return;
}
}
- SystemClock.sleep(100);
+ SystemClock.sleep(DELAY_MS);
}
}
@@ -218,7 +222,7 @@
private boolean createDirectory(int userId) {
final File dir = getDirectory(userId);
- return dir.exists() || dir.mkdirs();
+ return dir.exists() || dir.mkdir();
}
private void deleteSnapshot(int taskId, int userId) {
@@ -243,18 +247,26 @@
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
WriteQueueItem next;
+ boolean isReadyToWrite = false;
synchronized (mLock) {
if (mPaused) {
next = null;
} else {
next = mWriteQueue.poll();
if (next != null) {
- next.onDequeuedLocked();
+ if (next.isReady()) {
+ isReadyToWrite = true;
+ next.onDequeuedLocked();
+ } else {
+ mWriteQueue.addLast(next);
+ }
}
}
}
if (next != null) {
- next.write();
+ if (isReadyToWrite) {
+ next.write();
+ }
SystemClock.sleep(DELAY_MS);
}
synchronized (mLock) {
@@ -274,6 +286,13 @@
};
private abstract class WriteQueueItem {
+ /**
+ * @return {@code true} if item is ready to have {@link WriteQueueItem#write} called
+ */
+ boolean isReady() {
+ return true;
+ }
+
abstract void write();
/**
@@ -313,6 +332,11 @@
}
@Override
+ boolean isReady() {
+ return mUserManagerInternal.isUserUnlocked(mUserId);
+ }
+
+ @Override
void write() {
if (!createDirectory(mUserId)) {
Slog.e(TAG, "Unable to create snapshot directory for user dir="
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e1f8544..404863a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7717,14 +7717,19 @@
@Override
public void syncInputTransactions() {
- waitForAnimationsToComplete();
+ long token = Binder.clearCallingIdentity();
+ try {
+ waitForAnimationsToComplete();
- synchronized (mGlobalLock) {
- mWindowPlacerLocked.performSurfacePlacementIfScheduled();
- mRoot.forAllDisplays(displayContent ->
+ synchronized (mGlobalLock) {
+ mWindowPlacerLocked.performSurfacePlacementIfScheduled();
+ mRoot.forAllDisplays(displayContent ->
displayContent.getInputMonitor().updateInputWindowsImmediately());
+ }
+ new SurfaceControl.Transaction().syncInputWindows().apply(true);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- new SurfaceControl.Transaction().syncInputWindows().apply(true);
}
private void waitForAnimationsToComplete() {
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index e79612f..a99c0a3 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -40,6 +40,7 @@
#include <linux/rtc.h>
#include <array>
+#include <limits>
#include <memory>
namespace android {
@@ -213,22 +214,20 @@
static jint android_server_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis)
{
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
- struct timeval tv;
- int ret;
- if (millis <= 0 || millis / 1000LL >= INT_MAX) {
+ if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) {
return -1;
}
- tv.tv_sec = (time_t) (millis / 1000LL);
- tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL);
+ struct timeval tv;
+ tv.tv_sec = (millis / 1000LL);
+ tv.tv_usec = ((millis % 1000LL) * 1000LL);
- ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec);
+ ALOGD("Setting time of day to sec=%ld", tv.tv_sec);
- ret = impl->setTime(&tv);
-
- if(ret < 0) {
- ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno));
+ int ret = impl->setTime(&tv);
+ if (ret < 0) {
+ ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno));
ret = -1;
}
return ret;
diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp
index aef0b25..c945999 100644
--- a/services/core/jni/com_android_server_SerialService.cpp
+++ b/services/core/jni/com_android_server_SerialService.cpp
@@ -48,6 +48,7 @@
jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
if (fileDescriptor == NULL) {
+ close(fd);
return NULL;
}
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index ff1ec04..72dce4d 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -19,6 +19,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
#include "MtpDescriptors.h"
@@ -88,6 +89,7 @@
}
jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
if (fileDescriptor == NULL) {
+ close(fd);
return NULL;
}
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
@@ -120,35 +122,30 @@
}
static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject /* thiz */, jstring jFunction) {
- const char *function = env->GetStringUTFChars(jFunction, NULL);
+ ScopedUtfChars function(env, jFunction);
bool ptp = false;
int fd = -1;
- if (!strcmp(function, "ptp")) {
+ if (!strcmp(function.c_str(), "ptp")) {
ptp = true;
}
- if (!strcmp(function, "mtp") || ptp) {
+ if (!strcmp(function.c_str(), "mtp") || ptp) {
fd = TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP0 : FFS_MTP_EP0, O_RDWR));
if (fd < 0) {
- ALOGE("could not open control for %s %s", function, strerror(errno));
- goto error;
+ ALOGE("could not open control for %s %s", function.c_str(), strerror(errno));
+ return NULL;
}
if (!writeDescriptors(fd, ptp)) {
- goto error;
+ close(fd);
+ return NULL;
}
}
- if (function != NULL) {
- env->ReleaseStringUTFChars(jFunction, function);
- }
- return jniCreateFileDescriptor(env, fd);
-error:
- if (fd != -1) {
+ jobject jifd = jniCreateFileDescriptor(env, fd);
+ if (jifd == NULL) {
+ // OutOfMemoryError will be pending.
close(fd);
}
- if (function != NULL) {
- env->ReleaseStringUTFChars(jFunction, function);
- }
- return NULL;
+ return jifd;
}
static const JNINativeMethod method_table[] = {
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 24f2014..a40bcd0 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -134,6 +134,7 @@
jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
if (fileDescriptor == NULL) {
+ close(newFD);
return NULL;
}
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 79d935f..8ac2c4f 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -20,6 +20,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
@@ -99,24 +100,45 @@
int fd = open(path, O_RDWR);
if (fd < 0) {
ALOGE("open failed on %s for index %d", path, i);
- return NULL;
+ goto release_fds;
}
-
- jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
- env->SetObjectArrayElement(fds, i, fileDescriptor);
- env->DeleteLocalRef(fileDescriptor);
+ ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd));
+ if (jifd.get() == NULL) {
+ goto release_fds;
+ }
+ env->SetObjectArrayElement(fds, i, jifd.get());
}
// create a pipe to use for unblocking our input thread
- int pipeFD[2];
- pipe(pipeFD);
- jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]);
- env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor);
- env->DeleteLocalRef(fileDescriptor);
- // store our end of the pipe in mPipeFD
- env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
+ {
+ int pipeFD[2];
+ if (pipe(pipeFD) == -1) {
+ ALOGE("pipe() failed, errno = %d", errno);
+ goto release_fds;
+ }
+ ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, pipeFD[0]));
+ if (jifd.get() == NULL) {
+ close(pipeFD[0]);
+ close(pipeFD[1]);
+ goto release_fds;
+ }
+ env->SetObjectArrayElement(fds, subdevice_count, jifd.get());
+ // store our end of the pipe in mPipeFD
+ env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
+ }
return fds;
+
+release_fds:
+ for (int i = 0; i < subdevice_count + 1; ++i) {
+ ScopedLocalRef<jobject> jifd(env, env->GetObjectArrayElement(fds, i));
+ if (jifd.get() == NULL) {
+ break;
+ }
+ int fd = jniGetFDFromFileDescriptor(env, jifd.get());
+ close(fd);
+ }
+ return NULL;
}
static void
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index d331d1f..89bcb7f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -166,6 +166,7 @@
outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
bitmapCopy->rowBytes(), 0, 0);
}
+ outSpriteIcon->style = outPointerIcon->style;
outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
}
@@ -318,7 +319,6 @@
void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
- const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
int32_t getPointerDisplayId();
void updatePointerDisplayLocked();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
@@ -396,16 +396,6 @@
return false;
}
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
- REQUIRES(mLock) {
- for (const DisplayViewport& v : mLocked.viewports) {
- if (v.displayId == displayId) {
- return &v;
- }
- }
- return nullptr;
-}
-
void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
std::vector<DisplayViewport> viewports;
@@ -554,6 +544,8 @@
outConfig->setDisplayViewports(mLocked.viewports);
+ outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;
+
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
}
@@ -571,8 +563,6 @@
updateInactivityTimeoutLocked();
}
- updatePointerDisplayLocked();
-
return controller;
}
@@ -587,23 +577,6 @@
return pointerDisplayId;
}
-void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
- ATRACE_CALL();
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
- if (viewport == nullptr) {
- ALOGW("Can't find pointer display viewport, fallback to default display.");
- viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
- }
-
- if (viewport != nullptr) {
- controller->setDisplayViewport(*viewport);
- }
- }
-}
-
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
@@ -1252,7 +1225,8 @@
status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
displayContext.get(), &pointerIcon);
if (!status && !pointerIcon.isNullIcon()) {
- *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
+ *icon = SpriteIcon(
+ pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
} else {
*icon = SpriteIcon();
}
@@ -1293,10 +1267,12 @@
milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
animationData.animationFrames.reserve(numFrames);
animationData.animationFrames.push_back(SpriteIcon(
- pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ pointerIcon.bitmap, pointerIcon.style,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
for (size_t i = 0; i < numFrames - 1; ++i) {
animationData.animationFrames.push_back(SpriteIcon(
- pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ pointerIcon.bitmapFrames[i], pointerIcon.style,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
}
}
}
@@ -1732,6 +1708,7 @@
pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
spriteIcon.bitmap.rowBytes(), 0, 0);
}
+ spriteIcon.style = pointerIcon.style;
spriteIcon.hotSpotX = pointerIcon.hotSpotX;
spriteIcon.hotSpotY = pointerIcon.hotSpotY;
im->setCustomPointerIcon(spriteIcon);
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 098b2ef..4e1a234 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -301,6 +301,7 @@
JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper);
Mutex mLock;
+ Mutex mStreamLock;
jweak mThiz;
sp<Looper> mLooper;
@@ -338,6 +339,7 @@
}
int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
if (connections.indexOfKey(streamId) < 0) {
connections.add(streamId, Connection());
@@ -412,6 +414,7 @@
}
int JTvInputHal::removeStream(int deviceId, int streamId) {
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
if (connections.indexOfKey(streamId) < 0) {
return BAD_VALUE;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0167a3b..23b1512 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -165,6 +165,8 @@
import com.android.server.wm.WindowManagerGlobalLock;
import com.android.server.wm.WindowManagerService;
+import libcore.timezone.ZoneInfoDb;
+
import dalvik.system.VMRuntime;
import java.io.File;
@@ -395,8 +397,9 @@
// Default the timezone property to GMT if not set.
//
String timezoneProperty = SystemProperties.get("persist.sys.timezone");
- if (timezoneProperty == null || timezoneProperty.isEmpty()) {
- Slog.w(TAG, "Timezone not set; setting to GMT.");
+ if (!isValidTimeZoneId(timezoneProperty)) {
+ Slog.w(TAG, "persist.sys.timezone is not valid (" + timezoneProperty
+ + "); setting to GMT.");
SystemProperties.set("persist.sys.timezone", "GMT");
}
@@ -564,6 +567,12 @@
throw new RuntimeException("Main thread loop unexpectedly exited");
}
+ private static boolean isValidTimeZoneId(String timezoneProperty) {
+ return timezoneProperty != null
+ && !timezoneProperty.isEmpty()
+ && ZoneInfoDb.getInstance().hasTimeZone(timezoneProperty);
+ }
+
private boolean isFirstBootOrUpgrade() {
return mPackageManagerService.isFirstBoot() || mPackageManagerService.isDeviceUpgrading();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 30d1035..c086718 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -21,8 +21,10 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -68,7 +70,9 @@
@Before
public void setUp() throws RemoteException {
mContext = InstrumentationRegistry.getInstrumentation().getContext();
- mApexManager = new ApexManager.ApexManagerImpl(mContext, mApexService);
+ ApexManager.ApexManagerImpl managerImpl = spy(new ApexManager.ApexManagerImpl(mContext));
+ doReturn(mApexService).when(managerImpl).waitForApexService();
+ mApexManager = managerImpl;
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index e004cd3..f25110f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -23,6 +23,9 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
import android.graphics.Canvas;
@@ -32,9 +35,14 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.UserManager;
+import android.os.UserManagerInternal;
+
+import com.android.server.LocalServices;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import java.io.File;
@@ -50,10 +58,26 @@
TaskSnapshotLoader mLoader;
int mTestUserId;
+ @BeforeClass
+ public static void setUpOnce() {
+ final UserManagerInternal userManager = mock(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, userManager);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ }
+
@Before
public void setUp() {
final UserManager um = UserManager.get(getInstrumentation().getTargetContext());
mTestUserId = um.getUserHandle();
+
+ final UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ when(userManagerInternal.isUserUnlocked(mTestUserId)).thenReturn(true);
+
mPersister = new TaskSnapshotPersister(mWm, userId -> FILES_DIR);
mLoader = new TaskSnapshotLoader(mPersister);
mPersister.start();
diff --git a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
new file mode 100644
index 0000000..6c63755
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.internal.telephony;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Telephony;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * This class provides utility functions related to CellBroadcast.
+ */
+public class CellBroadcastUtils {
+ private static final String TAG = "CellBroadcastUtils";
+ private static final boolean VDBG = false;
+
+ /**
+ * Utility method to query the default CBR's package name.
+ */
+ public static String getDefaultCellBroadcastReceiverPackageName(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ ResolveInfo resolveInfo = packageManager.resolveActivity(
+ new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION),
+ PackageManager.MATCH_SYSTEM_ONLY);
+ String packageName;
+
+ if (resolveInfo == null) {
+ Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: no package found");
+ return null;
+ }
+
+ packageName = resolveInfo.activityInfo.applicationInfo.packageName;
+
+ if (VDBG) {
+ Log.d(TAG, "getDefaultCellBroadcastReceiverPackageName: found package: " + packageName);
+ }
+
+ if (TextUtils.isEmpty(packageName) || packageManager.checkPermission(
+ android.Manifest.permission.READ_CELL_BROADCASTS, packageName)
+ == PackageManager.PERMISSION_DENIED) {
+ Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: returning null; "
+ + "permission check failed for : " + packageName);
+ return null;
+ }
+
+ return packageName;
+ }
+}
diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java
index 68c9d99..d9b5de5 100644
--- a/telephony/java/android/telephony/PinResult.java
+++ b/telephony/java/android/telephony/PinResult.java
@@ -37,6 +37,7 @@
PIN_RESULT_TYPE_SUCCESS,
PIN_RESULT_TYPE_INCORRECT,
PIN_RESULT_TYPE_FAILURE,
+ PIN_RESULT_TYPE_ABORTED,
})
public @interface PinResultType {}
@@ -55,6 +56,11 @@
*/
public static final int PIN_RESULT_TYPE_FAILURE = PhoneConstants.PIN_GENERAL_FAILURE;
+ /**
+ * Indicates that the pin attempt was aborted.
+ */
+ public static final int PIN_RESULT_TYPE_ABORTED = PhoneConstants.PIN_OPERATION_ABORTED;
+
private static final PinResult sFailedResult =
new PinResult(PinResult.PIN_RESULT_TYPE_FAILURE, -1);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e57402d..7d1398b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10314,7 +10314,7 @@
* <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
* carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android
* platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
- * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
*
* <p>Apps which have carrier-specific configurations or business logic can use the carrier id
* as an Android platform-wide identifier for carriers.
@@ -10376,7 +10376,7 @@
*
* <p>For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()}
* <p>Specific carrier ids are defined in the same way as carrier id
- * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
* except each with a "parent" id linking to its top-level carrier id.
*
* @return Returns fine-grained carrier id of the current subscription.
@@ -10425,7 +10425,7 @@
* This is used for fallback when configurations/logic for exact carrier id
* {@link #getSimCarrierId()} are not found.
*
- * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
* can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
* was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
* by default. After carrier id table update, a new carrier id was assigned. If apps don't
@@ -10452,7 +10452,7 @@
* used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()}
* are not found.
*
- * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
* can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
* was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
* by default. After carrier id table update, a new carrier id was assigned. If apps don't
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9ec3f61..6840e8e 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -430,7 +430,6 @@
/**
* @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
* notifies the framework.
- * @hide
*/
private void setListener(IImsMmTelListener listener) {
synchronized (mLock) {
@@ -442,6 +441,16 @@
}
/**
+ * @return the listener associated with this MmTelFeature. May be null if it has not been set
+ * by the framework yet.
+ */
+ private IImsMmTelListener getListener() {
+ synchronized (mLock) {
+ return mListener;
+ }
+ }
+
+ /**
* The current capability status that this MmTelFeature has defined is available. This
* configuration will be used by the platform to figure out which capabilities are CURRENTLY
* available to be used.
@@ -489,15 +498,14 @@
throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+ "null.");
}
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Session is not available.");
- }
- try {
- mListener.onIncomingCall(c.getServiceImpl(), extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onIncomingCall(c.getServiceImpl(), extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -516,15 +524,14 @@
throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+ "null.");
}
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Session is not available.");
- }
- try {
- mListener.onRejectedCall(callProfile, reason);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onRejectedCall(callProfile, reason);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -533,15 +540,14 @@
* @hide
*/
public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Session is not available.");
- }
- try {
- mListener.onIncomingCall(c, extras);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onIncomingCall(c, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -552,15 +558,14 @@
*/
@SystemApi @TestApi
public final void notifyVoiceMessageCountUpdate(int count) {
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Session is not available.");
- }
- try {
- mListener.onVoiceMessageCountUpdate(count);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ IImsMmTelListener listener = getListener();
+ if (listener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ listener.onVoiceMessageCountUpdate(count);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 6eea88f..db9fdf5 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -111,6 +111,7 @@
public static final int PIN_RESULT_SUCCESS = 0;
public static final int PIN_PASSWORD_INCORRECT = 1;
public static final int PIN_GENERAL_FAILURE = 2;
+ public static final int PIN_OPERATION_ABORTED = 3;
/**
* Return codes for <code>enableApnType()</code>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp
index a23ac38..125deb5 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp
@@ -33,5 +33,6 @@
"-Werror",
"-Wno-unused-parameter",
],
+ header_libs: ["jni_headers"],
stl: "c++_static",
}
diff --git a/tests/GamePerformance/Android.bp b/tests/GamePerformance/Android.bp
new file mode 100644
index 0000000..648fd81
--- /dev/null
+++ b/tests/GamePerformance/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "GamePerformance",
+ // Don't include this package in any target
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*.java"],
+ static_libs: ["android-support-test"],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/GamePerformance/Android.mk b/tests/GamePerformance/Android.mk
deleted file mode 100644
index 58654de..0000000
--- a/tests/GamePerformance/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
-LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
-
-LOCAL_PACKAGE_NAME := GamePerformance
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-
-include $(BUILD_PACKAGE)
diff --git a/tests/NullHomeTest/Android.bp b/tests/NullHomeTest/Android.bp
new file mode 100644
index 0000000..99248bf
--- /dev/null
+++ b/tests/NullHomeTest/Android.bp
@@ -0,0 +1,22 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// 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.
+
+android_test {
+ name: "NullHomeTest",
+ srcs: ["src/**/*.java"],
+ certificate: "platform",
+ platform_apis: true,
+ static_libs: ["android-support-test"],
+ test_suites: ["device-tests"],
+}
diff --git a/tests/NullHomeTest/AndroidManifest.xml b/tests/NullHomeTest/AndroidManifest.xml
new file mode 100644
index 0000000..dc6402e
--- /dev/null
+++ b/tests/NullHomeTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.nullhome"
+ android:sharedUserId="android.uid.system" >
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.nullhome"
+ android:label="Check if no null Home exists/is enabled" />
+
+ <application android:label="Null Home Test">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java b/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java
new file mode 100644
index 0000000..1d77cdc5
--- /dev/null
+++ b/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.test.nullhome;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * Check if NullHome/SystemUserHome activity does not exist/is disabled.
+ *
+ * SystemUserHome is only enabled in bootable CSI (csi_x86, csi_arm64)
+ * products and should not be enabled in other products.
+ *
+ * Shell's NullHome is empty and caused issues in sevaral manual GUI tests
+ * that try to select/use it, and should be removed.
+ *
+ * Settings' FallbackHome is fine because it's specially handled by Settings.
+ *
+ */
+
+@RunWith(JUnit4.class)
+public class NullHomeTest {
+ private static final String TAG = "NullHomeTest";
+ private Context mContext;
+ private PackageManager mPm;
+
+ @Before
+ public void before() {
+ Log.d(TAG, "beforeClass()");
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mPm = mContext.getPackageManager();
+ }
+
+ @Test
+ public void checkNullHome() {
+ final List<ResolveInfo> homeActivities = new ArrayList<>();
+
+ mPm.getHomeActivities(homeActivities);
+ for (ResolveInfo activity : homeActivities) {
+ Log.d(TAG, "Home activity: " + activity.activityInfo.packageName);
+ Assert.assertNotEquals(activity.activityInfo.packageName,
+ "com.android.internal.app.SystemUserHomeActivity");
+ Assert.assertNotEquals(activity.activityInfo.packageName,
+ "com.android.shell");
+ }
+ }
+}
diff --git a/tests/ProtoInputStreamTests/Android.bp b/tests/ProtoInputStreamTests/Android.bp
new file mode 100644
index 0000000..ecc40566
--- /dev/null
+++ b/tests/ProtoInputStreamTests/Android.bp
@@ -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.
+
+android_test {
+ name: "ProtoInputStreamTests",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.proto",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+ libs: ["android.test.runner"],
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "mockito-target-minus-junit4",
+ ],
+}
diff --git a/tests/ProtoInputStreamTests/Android.mk b/tests/ProtoInputStreamTests/Android.mk
deleted file mode 100644
index eb747cc..0000000
--- a/tests/ProtoInputStreamTests/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := ProtoInputStreamTests
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_MODULE_TAGS := tests optional
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-proto-files-under, src)
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- frameworks-base-testutils \
- mockito-target-minus-junit4
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index 09c0e48..f5a4234 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -16,50 +16,55 @@
* limitations under the License.
*/
-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.net.integrationtests">
+ package="com.android.server.net.integrationtests">
<!-- For ConnectivityService registerReceiverAsUser (receiving broadcasts) -->
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<!-- PermissionMonitor sets network permissions for each user -->
- <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_USERS"/>
<!-- ConnectivityService sends notifications to BatteryStats -->
- <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
<!-- Reading network status -->
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.NETWORK_FACTORY" />
- <uses-permission android:name="android.permission.NETWORK_STACK" />
- <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
- <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.NETWORK_FACTORY"/>
+ <!-- Obtain LinkProperties callbacks with sensitive fields -->
+ <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
+ <uses-permission android:name="android.permission.NETWORK_STACK"/>
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<!-- Reading DeviceConfig flags -->
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
<application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
<!-- This manifest is merged with the base manifest of the real NetworkStack app.
- Remove the NetworkStackService from the base (real) manifest, and replace with a test
- service that responds to the same intent -->
+ Remove the NetworkStackService from the base (real) manifest, and replace with a test
+ service that responds to the same intent -->
<service android:name=".TestNetworkStackService"
- android:process="com.android.server.net.integrationtests.testnetworkstack">
+ android:process="com.android.server.net.integrationtests.testnetworkstack"
+ android:exported="true">
<intent-filter>
<action android:name="android.net.INetworkStackConnector.Test"/>
</intent-filter>
</service>
<service android:name=".NetworkStackInstrumentationService"
- android:process="com.android.server.net.integrationtests.testnetworkstack">
+ android:process="com.android.server.net.integrationtests.testnetworkstack"
+ android:exported="true">
<intent-filter>
<action android:name=".INetworkStackInstrumentation"/>
</intent-filter>
</service>
<service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
- android:process="com.android.server.net.integrationtests.testnetworkstack"
- android:permission="android.permission.BIND_JOB_SERVICE"/>
+ android:process="com.android.server.net.integrationtests.testnetworkstack"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.net.integrationtests"
- android:label="Frameworks Net Integration Tests" />
+ android:targetPackage="com.android.server.net.integrationtests"
+ android:label="Frameworks Net Integration Tests"/>
</manifest>
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index c4801aa..bc069e1 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -28,10 +28,13 @@
import android.net.INetworkPolicyManager
import android.net.INetworkStatsService
import android.net.LinkProperties
+import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkRequest
import android.net.TestNetworkStackClient
+import android.net.Uri
import android.net.metrics.IpConnectivityLog
import android.os.ConditionVariable
import android.os.IBinder
@@ -64,6 +67,8 @@
import org.mockito.MockitoAnnotations
import org.mockito.Spy
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.fail
@@ -110,6 +115,10 @@
private val bindingCondition = ConditionVariable(false)
private val realContext get() = InstrumentationRegistry.getInstrumentation().context
+ private val httpProbeUrl get() =
+ realContext.getResources().getString(R.string.config_captive_portal_http_url)
+ private val httpsProbeUrl get() =
+ realContext.getResources().getString(R.string.config_captive_portal_https_url)
private class InstrumentationServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
@@ -188,12 +197,8 @@
val testCallback = TestableNetworkCallback()
cm.registerNetworkCallback(request, testCallback)
- nsInstrumentation.addHttpResponse(HttpResponse(
- "http://test.android.com",
- responseCode = 204, contentLength = 42, redirectUrl = null))
- nsInstrumentation.addHttpResponse(HttpResponse(
- "https://secure.test.android.com",
- responseCode = 204, contentLength = 42, redirectUrl = null))
+ nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
+ nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
@@ -204,4 +209,52 @@
testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
assertEquals(2, nsInstrumentation.getRequestUrls().size)
}
+
+ @Test
+ fun testCapportApi() {
+ val request = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build()
+ val testCb = TestableNetworkCallback()
+ val apiUrl = "https://capport.android.com"
+
+ cm.registerNetworkCallback(request, testCb)
+ nsInstrumentation.addHttpResponse(HttpResponse(
+ apiUrl,
+ """
+ |{
+ | "captive": true,
+ | "user-portal-url": "https://login.capport.android.com",
+ | "venue-info-url": "https://venueinfo.capport.android.com"
+ |}
+ """.trimMargin()))
+
+ // Tests will fail if a non-mocked query is received: mock the HTTPS probe, but not the
+ // HTTP probe as it should not be sent.
+ // Even if the HTTPS probe succeeds, a portal should be detected as the API takes precedence
+ // in that case.
+ nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
+
+ val lp = LinkProperties()
+ lp.captivePortalApiUrl = Uri.parse(apiUrl)
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context)
+ networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
+
+ na.addCapability(NET_CAPABILITY_INTERNET)
+ na.connect()
+
+ testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS)
+
+ val capportData = testCb.expectLinkPropertiesThat(na, TEST_TIMEOUT_MS) {
+ it.captivePortalData != null
+ }.lp.captivePortalData
+ assertNotNull(capportData)
+ assertTrue(capportData.isCaptive)
+ assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl)
+ assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl)
+
+ val nc = testCb.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL, na, TEST_TIMEOUT_MS)
+ assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED))
+ }
}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
index 45073d8..e206313 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
@@ -22,16 +22,21 @@
data class HttpResponse(
val requestUrl: String,
val responseCode: Int,
- val contentLength: Long,
- val redirectUrl: String?
+ val content: String = "",
+ val redirectUrl: String? = null
) : Parcelable {
- constructor(p: Parcel): this(p.readString(), p.readInt(), p.readLong(), p.readString())
+ constructor(p: Parcel): this(p.readString(), p.readInt(), p.readString(), p.readString())
+ constructor(requestUrl: String, contentBody: String): this(
+ requestUrl,
+ responseCode = 200,
+ content = contentBody,
+ redirectUrl = null)
override fun writeToParcel(dest: Parcel, flags: Int) {
with(dest) {
writeString(requestUrl)
writeInt(responseCode)
- writeLong(contentLength)
+ writeString(content)
writeString(redirectUrl)
}
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
index 4827d29..e807952 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
@@ -65,6 +65,9 @@
*
* <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
* used to mock the query response.
+ *
+ * <p>All requests that are expected to be sent must have a mock response: if an unexpected
+ * request is seen, the test will fail.
*/
override fun addHttpResponse(response: HttpResponse) {
httpResponses.getValue(response.requestUrl).add(response)
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index 8c2de40..a44ad1e 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -33,9 +33,11 @@
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
+import java.io.ByteArrayInputStream
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
+import java.nio.charset.StandardCharsets
private const val TEST_NETID = 42
@@ -71,11 +73,13 @@
private inner class TestNetwork(netId: Int) : Network(netId) {
override fun openConnection(url: URL): URLConnection {
val response = InstrumentationConnector.processRequest(url)
+ val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8)
val connection = mock(HttpURLConnection::class.java)
doReturn(response.responseCode).`when`(connection).responseCode
- doReturn(response.contentLength).`when`(connection).contentLengthLong
+ doReturn(responseBytes.size.toLong()).`when`(connection).contentLengthLong
doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
+ doReturn(ByteArrayInputStream(responseBytes)).`when`(connection).inputStream
return connection
}
}
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index 442ac56..0b13800 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -105,7 +105,7 @@
private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) {
return new NetworkAttributes.Builder()
- .setGroupHint(hint)
+ .setCluster(hint)
.setMtu(mtu)
.build();
}
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 98f705f..735fa7c 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -909,8 +909,8 @@
13805 /* txPackets */,
0 /* operations */);
- // Traffic measured for the root uid on the base interface if eBPF is in use.
- final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+ // Traffic measured for the root uid on the base interface.
+ final NetworkStats.Entry rootUidEntry = new NetworkStats.Entry(
baseIface, rootUid, SET_DEFAULT, TAG_NONE,
163577 /* rxBytes */,
187 /* rxPackets */,
@@ -918,17 +918,6 @@
97 /* txPackets */,
0 /* operations */);
- // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
- // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
- // overhead (20 bytes per packet), in rx direction.
- final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
- baseIface, rootUid, SET_DEFAULT, TAG_NONE,
- 31113087 /* rxBytes */,
- 22588 /* rxPackets */,
- 17607 /* txBytes */,
- 97 /* txPackets */,
- 0 /* operations */);
-
final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
otherIface, appUid, SET_DEFAULT, TAG_NONE,
2600 /* rxBytes */,
@@ -937,21 +926,14 @@
3 /* txPackets */,
0 /* operations */);
- final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
+ final NetworkStats stats = new NetworkStats(TEST_START, 3)
.insertEntry(appEntry)
- .insertEntry(xtRootUidEntry)
+ .insertEntry(rootUidEntry)
.insertEntry(otherEntry);
- final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
- .insertEntry(appEntry)
- .insertEntry(ebpfRootUidEntry)
- .insertEntry(otherEntry);
+ stats.apply464xlatAdjustments(stackedIface);
- statsXt.apply464xlatAdjustments(stackedIface, false);
- statsEbpf.apply464xlatAdjustments(stackedIface, true);
-
- assertEquals(3, statsXt.size());
- assertEquals(3, statsEbpf.size());
+ assertEquals(3, stats.size());
final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
v4Iface, appUid, SET_DEFAULT, TAG_NONE,
30949510,
@@ -966,12 +948,9 @@
17607,
97,
0);
- assertEquals(expectedAppUid, statsXt.getValues(0, null));
- assertEquals(expectedRootUid, statsXt.getValues(1, null));
- assertEquals(otherEntry, statsXt.getValues(2, null));
- assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
- assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
- assertEquals(otherEntry, statsEbpf.getValues(2, null));
+ assertEquals(expectedAppUid, stats.getValues(0, null));
+ assertEquals(expectedRootUid, stats.getValues(1, null));
+ assertEquals(otherEntry, stats.getValues(2, null));
}
@Test
@@ -996,7 +975,7 @@
.insertEntry(secondEntry);
// Empty map: no adjustment
- stats.apply464xlatAdjustments(new ArrayMap<>(), false);
+ stats.apply464xlatAdjustments(new ArrayMap<>());
assertEquals(2, stats.size());
assertEquals(firstEntry, stats.getValues(0, null));
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 1a3ea609..02f52865 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -54,7 +54,7 @@
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000);
- builder.setGroupHint("groupHint");
+ builder.setCluster("groupHint");
builder.setDnsAddresses(Arrays.asList(
InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
InetAddress.getByName("6.7.8.9")));
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
index ceca6f0..e5daa71 100644
--- a/tests/net/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -33,7 +33,9 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/** Unit tests for {@link VpnProfile}. */
@SmallTest
@@ -41,6 +43,9 @@
public class VpnProfileTest {
private static final String DUMMY_PROFILE_KEY = "Test";
+ private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
+ private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+
@Test
public void testDefaults() throws Exception {
final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
@@ -67,10 +72,11 @@
assertFalse(p.isMetered);
assertEquals(1360, p.maxMtu);
assertFalse(p.areAuthParamsInline);
+ assertFalse(p.isRestrictedToTestNetworks);
}
private VpnProfile getSampleIkev2Profile(String key) {
- final VpnProfile p = new VpnProfile(key);
+ final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
p.name = "foo";
p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
@@ -116,7 +122,7 @@
@Test
public void testParcelUnparcel() {
- assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
}
@Test
@@ -159,14 +165,41 @@
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
}
+ private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
+ // Sort to ensure when we remove, we can do it from greatest first.
+ Arrays.sort(missingIndices);
+
+ final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
+ final List<String> parts =
+ new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
+
+ // Remove from back first to ensure indexing is consistent.
+ for (int i = missingIndices.length - 1; i >= 0; i--) {
+ parts.remove(missingIndices[i]);
+ }
+
+ return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
+ }
+
@Test
public void testEncodeDecodeInvalidNumberOfValues() {
- final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
- final String encoded = new String(profile.encode());
- final byte[] tooFewValues =
- encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_AUTH_PARAMS_INLINE,
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
- assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
+ }
+
+ @Test
+ public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
+
+ // Verify decoding without isRestrictedToTestNetworks defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.isRestrictedToTestNetworks);
}
@Test
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 5e2ef04..ea4982e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5494,6 +5494,8 @@
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
// For safety reasons a VPN without underlying networks is considered metered.
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ // A VPN without underlying networks is not suspended.
+ assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
// Connect cell and use it as an underlying network.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -6320,6 +6322,7 @@
int netId = network.getNetId();
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
callback.assertNoCallback();
assertEquals(pref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
@@ -6329,6 +6332,7 @@
mCellNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mCellNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
// If the RA prefix appears while DNS discovery is in progress, discovery is stopped and
@@ -6338,6 +6342,7 @@
expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
// Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS
// discovery has succeeded.
@@ -6345,6 +6350,7 @@
mCellNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mCellNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
@@ -6352,13 +6358,40 @@
expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
- // If the RA prefix reappears, clatd is restarted and prefix discovery is stopped.
+ // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
+ // discovery is not stopped, and there are no callbacks.
+ lp.setNat64Prefix(pref64FromDns);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ callback.assertNoCallback();
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // If the RA is later withdrawn, nothing happens again.
+ lp.setNat64Prefix(null);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ callback.assertNoCallback();
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // If the RA prefix changes, clatd is restarted and prefix discovery is stopped.
lp.setNat64Prefix(pref64FromRa);
mCellNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+
+ // Stopping prefix discovery results in a prefix removed notification.
+ mService.mNetdEventCallback.onNat64PrefixEvent(netId, false /* added */,
+ pref64FromDnsStr, 96);
+
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
// If the RA prefix changes, clatd is restarted and prefix discovery is not started.
@@ -6366,7 +6399,9 @@
mCellNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa);
inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
@@ -6379,11 +6414,45 @@
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
// The transition between no prefix and DNS prefix is tested in testStackedLinkProperties.
+ // If the same prefix is learned first by DNS and then by RA, and clat is later stopped,
+ // (e.g., because the network disconnects) setPrefix64(netid, "") is never called.
+ lp.setNat64Prefix(null);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+ inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+ mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
+ pref64FromDnsStr, 96);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
+
+ lp.setNat64Prefix(pref64FromDns);
+ mCellNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but
+ // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that
+ // clat has been stopped, or the test will be flaky.
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ waitFor(cv);
+
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
mCm.unregisterNetworkCallback(callback);
}
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index 42d4cf3..a10a3c8 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+// Don't warn about deprecated types anywhere in this test, because LegacyTypeTracker's very reason
+// for existence is to power deprecated APIs. The annotation has to apply to the whole file because
+// otherwise warnings will be generated by the imports of deprecated constants like TYPE_xxx.
+@file:Suppress("DEPRECATION")
+
package com.android.server
import android.net.ConnectivityManager.TYPE_ETHERNET
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f8d8a56..4ccf79a 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -199,6 +199,8 @@
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+ .thenReturn(true);
when(mSystemServices.isCallerSystem()).thenReturn(true);
// Used by {@link Notification.Builder}
@@ -731,6 +733,20 @@
}
@Test
+ public void testProvisionVpnProfileNoIpsecTunnels() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+ .thenReturn(false);
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ checkProvisionVpnProfile(
+ vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+ fail("Expected exception due to missing feature");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
public void testProvisionVpnProfilePreconsented() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 4473492..e4996d9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -446,7 +446,7 @@
assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 0L);
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L);
assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
@@ -468,9 +468,7 @@
long appRxBytesAfter = 439237478L;
assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore);
- long rootRxBytesBefore = 1394011L;
- long rootRxBytesAfter = 1398634L;
- assertEquals("UID 0 traffic should be ~0", 4623, rootRxBytesAfter - rootRxBytesBefore);
+ long rootRxBytes = 330187296L;
mFactory.noteStackedIface("v4-wlan0", "wlan0");
NetworkStats stats;
@@ -478,12 +476,12 @@
// Stats snapshot before the download
stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 0L);
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
// Stats snapshot after the download
stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
}
/**
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat
index 6cd7499..f04b32f 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat
+++ b/tests/net/res/raw/xt_qtaguid_with_clat
@@ -7,7 +7,7 @@
7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 wlan0 0x0 0 0 11058671 7892 0 0 11043898 7811 13117 61 1656 20 0 0 0 0 0 0
+10 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@@ -41,5 +41,3 @@
41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-44 wlan0 0x0 1029 0 0 0 312046 5113 0 0 0 0 0 0 306544 5046 3230 38 2272 29
-45 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
index 9f86153..12d98ca 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
@@ -9,7 +9,7 @@
9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 440746376 329772 0 0 439660007 315369 232001 1276 854368 13127 0 0 0 0 0 0
+12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index b37fae6..a1d6d411 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -1,5 +1,4 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
-5 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
+4 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8d99ac7..7218ae3 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -222,6 +222,10 @@
return sLoopers.get(test);
}
+ public static void remove(Object test) {
+ sLoopers.remove(test);
+ }
+
static class LooperFrameworkMethod extends FrameworkMethod {
private HandlerThread mHandlerThread;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 77e2a6b..7afb000 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1659,16 +1659,24 @@
return 1;
}
- // Determine the package name under which to merge resources.
- if (options_.rename_resources_package) {
- context_->SetCompilationPackage(options_.rename_resources_package.value());
- } else if (Maybe<AppInfo> maybe_app_info =
+ // First extract the package name without modifying it (via --rename-manifest-package).
+ if (Maybe<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
// Extract the package name from the manifest ignoring the value of --rename-manifest-package.
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
}
+ // Determine the package name under which to merge resources.
+ if (options_.rename_resources_package) {
+ if (!options_.custom_java_package) {
+ // Generate the R.java under the original package name instead of the package name specified
+ // through --rename-resources-package.
+ options_.custom_java_package = context_->GetCompilationPackage();
+ }
+ context_->SetCompilationPackage(options_.rename_resources_package.value());
+ }
+
// Now that the compilation package is set, load the dependencies. This will also extract
// the Android framework's versionCode and versionName, if they exist.
if (!LoadSymbolsFromIncludePaths()) {
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 0b2077d..de6b478 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -18,10 +18,10 @@
"""
import argparse
from collections import defaultdict
-import os
-import sys
-import re
import functools
+import os
+import re
+import sys
# Names of flags recognized by the `hiddenapi` tool.
FLAG_WHITELIST = "whitelist"
@@ -30,6 +30,7 @@
FLAG_GREYLIST_MAX_O = "greylist-max-o"
FLAG_GREYLIST_MAX_P = "greylist-max-p"
FLAG_GREYLIST_MAX_Q = "greylist-max-q"
+FLAG_GREYLIST_MAX_R = "greylist-max-r"
FLAG_CORE_PLATFORM_API = "core-platform-api"
FLAG_PUBLIC_API = "public-api"
FLAG_SYSTEM_API = "system-api"
@@ -43,13 +44,14 @@
FLAG_GREYLIST_MAX_O,
FLAG_GREYLIST_MAX_P,
FLAG_GREYLIST_MAX_Q,
+ FLAG_GREYLIST_MAX_R,
]
ALL_FLAGS = FLAGS_API_LIST + [
FLAG_CORE_PLATFORM_API,
FLAG_PUBLIC_API,
FLAG_SYSTEM_API,
FLAG_TEST_API,
- ]
+]
FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
ALL_FLAGS_SET = set(ALL_FLAGS)